зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team on a CLOSED TREE
This commit is contained in:
Коммит
15c18ce28f
|
@ -15,12 +15,12 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
<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="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -19,12 +19,12 @@
|
|||
<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="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="27eb2f04e149fc2c9976d881b1b5984bbe7ee089"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="12ff7481566587aa4198cf1287598acb3a999973"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
{
|
||||
"git": {
|
||||
"git_revision": "f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e",
|
||||
"git_revision": "b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9",
|
||||
"remote": "https://git.mozilla.org/releases/gaia.git",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "ad7f7540499e0cf54ddd5cf41ff39886cf63acfb",
|
||||
"revision": "2112488412d044b4b466f97e4e268b0f73a3c862",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -18,9 +18,9 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f004530b30a63c08a16d82536858600446b2abf5"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -15,12 +15,12 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="f3d9981dccfa4dfdfcb865d95fdcfb85e4077e1e"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="b994cedaa7ef9bfadcbe841601d9dc8d2e5379f9"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="7e0fe55ac52323eace5a6119ab2b911fc4f64495"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="58909a53f638af022ab09f7a8f6976b0cae8f133"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="31a7849fe9a8b743d6f5e5facc212f0ef9d57499"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
|
||||
|
|
|
@ -99,6 +99,8 @@ const TEST_DATA = [{
|
|||
}]
|
||||
}];
|
||||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/SyncRunnable.h"
|
||||
#include "mozilla/unused.h"
|
||||
|
@ -165,6 +166,7 @@ public:
|
|||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
|
||||
|
@ -178,6 +180,7 @@ public:
|
|||
mSize,
|
||||
mImage,
|
||||
nullptr,
|
||||
nullptr,
|
||||
getter_AddRefs(stream),
|
||||
mEncoder);
|
||||
}
|
||||
|
@ -234,6 +237,7 @@ ImageEncoder::ExtractData(nsAString& aType,
|
|||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream)
|
||||
{
|
||||
nsCOMPtr<imgIEncoder> encoder = ImageEncoder::GetImageEncoder(aType);
|
||||
|
@ -242,10 +246,9 @@ ImageEncoder::ExtractData(nsAString& aType,
|
|||
}
|
||||
|
||||
return ExtractDataInternal(aType, aOptions, nullptr, 0, aSize, nullptr,
|
||||
aContext, aStream, encoder);
|
||||
aContext, aRenderer, aStream, encoder);
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
nsresult
|
||||
ImageEncoder::ExtractDataFromLayersImageAsync(nsAString& aType,
|
||||
|
@ -341,6 +344,7 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
|||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder)
|
||||
{
|
||||
|
@ -366,6 +370,11 @@ ImageEncoder::ExtractDataInternal(const nsAString& aType,
|
|||
rv = aContext->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aRenderer) {
|
||||
NS_ConvertUTF16toUTF8 encoderType(aType);
|
||||
rv = aRenderer->GetInputStream(encoderType.get(),
|
||||
nsPromiseFlatString(aOptions).get(),
|
||||
getter_AddRefs(imgStream));
|
||||
} else if (aImage) {
|
||||
// It is safe to convert PlanarYCbCr format from YUV to RGB off-main-thread.
|
||||
// Other image formats could have problem to convert format off-main-thread.
|
||||
|
|
|
@ -19,6 +19,7 @@ class nsICanvasRenderingContextInternal;
|
|||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class Image;
|
||||
} // namespace layers
|
||||
|
||||
|
@ -40,6 +41,7 @@ public:
|
|||
const nsAString& aOptions,
|
||||
const nsIntSize aSize,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream);
|
||||
|
||||
// Extracts data asynchronously. aType may change to "image/png" if we had to
|
||||
|
@ -84,7 +86,7 @@ public:
|
|||
nsIInputStream** aStream);
|
||||
|
||||
private:
|
||||
// When called asynchronously, aContext is null.
|
||||
// When called asynchronously, aContext and aRenderer are null.
|
||||
static nsresult
|
||||
ExtractDataInternal(const nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
|
@ -93,6 +95,7 @@ private:
|
|||
const nsIntSize aSize,
|
||||
layers::Image* aImage,
|
||||
nsICanvasRenderingContextInternal* aContext,
|
||||
layers::AsyncCanvasRenderer* aRenderer,
|
||||
nsIInputStream** aStream,
|
||||
imgIEncoder* aEncoder);
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "mozilla/dom/StructuredClone.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/MessagePortBinding.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
||||
#include "mozilla/dom/PMessagePort.h"
|
||||
#include "mozilla/dom/StructuredCloneTags.h"
|
||||
#include "mozilla/dom/SubtleCryptoBinding.h"
|
||||
|
@ -1064,6 +1066,25 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread);
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
nsRefPtr<OffscreenCanvas> canvas = OffscreenCanvas::CreateFromCloneData(data);
|
||||
delete data;
|
||||
|
||||
JS::Rooted<JS::Value> value(aCx);
|
||||
if (!GetOrCreateDOMReflector(aCx, canvas, &value)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return false;
|
||||
}
|
||||
|
||||
aReturnObject.set(&value.toObject());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1095,6 +1116,24 @@ StructuredCloneHolder::CustomWriteTransferHandler(JSContext* aCx,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread) {
|
||||
OffscreenCanvas* canvas = nullptr;
|
||||
rv = UNWRAP_OBJECT(OffscreenCanvas, aObj, canvas);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
*aExtraData = 0;
|
||||
*aTag = SCTAG_DOM_CANVAS;
|
||||
*aOwnership = JS::SCTAG_TMO_CUSTOM;
|
||||
*aContent = canvas->ToCloneData();
|
||||
MOZ_ASSERT(*aContent);
|
||||
canvas->SetNeutered();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -1112,6 +1151,17 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag,
|
|||
MOZ_ASSERT(!aContent);
|
||||
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
|
||||
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aTag == SCTAG_DOM_CANVAS) {
|
||||
MOZ_ASSERT(mSupportedContext == SameProcessSameThread ||
|
||||
mSupportedContext == SameProcessDifferentThread);
|
||||
MOZ_ASSERT(aContent);
|
||||
OffscreenCanvasCloneData* data =
|
||||
static_cast<OffscreenCanvasCloneData*>(aContent);
|
||||
delete data;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,9 @@ enum StructuredCloneTags {
|
|||
|
||||
SCTAG_DOM_FORMDATA,
|
||||
|
||||
// This tag is for OffscreenCanvas.
|
||||
SCTAG_DOM_CANVAS,
|
||||
|
||||
SCTAG_DOM_MAX
|
||||
};
|
||||
|
||||
|
|
|
@ -5040,7 +5040,7 @@ nsDocument::MozSetImageElement(const nsAString& aImageElementId,
|
|||
if (entry) {
|
||||
entry->SetImageElement(aElement);
|
||||
if (entry->IsEmpty()) {
|
||||
mIdentifierMap.RemoveEntry(aImageElementId);
|
||||
mIdentifierMap.RemoveEntry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9403,7 +9403,7 @@ nsDocument::ForgetLink(Link* aLink)
|
|||
NS_ASSERTION(entry || mStyledLinksCleared,
|
||||
"Document knows nothing about this Link!");
|
||||
#endif
|
||||
(void)mStyledLinks.RemoveEntry(aLink);
|
||||
mStyledLinks.RemoveEntry(aLink);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -10122,11 +10122,14 @@ nsIDocument::RegisterActivityObserver(nsISupports* aSupports)
|
|||
bool
|
||||
nsIDocument::UnregisterActivityObserver(nsISupports* aSupports)
|
||||
{
|
||||
if (!mActivityObservers)
|
||||
if (!mActivityObservers) {
|
||||
return false;
|
||||
if (!mActivityObservers->GetEntry(aSupports))
|
||||
}
|
||||
nsPtrHashKey<nsISupports>* entry = mActivityObservers->GetEntry(aSupports);
|
||||
if (!entry) {
|
||||
return false;
|
||||
mActivityObservers->RemoveEntry(aSupports);
|
||||
}
|
||||
mActivityObservers->RemoveEntry(entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,265 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "CanvasRenderingContextHelper.h"
|
||||
#include "ImageEncoder.h"
|
||||
#include "mozilla/dom/CanvasRenderingContext2D.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDOMJSUtils.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsJSUtils.h"
|
||||
#include "WebGL1Context.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
void
|
||||
CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
|
||||
nsIGlobalObject* aGlobal,
|
||||
FileCallback& aCallback,
|
||||
const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsAutoString type;
|
||||
nsContentUtils::ASCIIToLower(aType, type);
|
||||
|
||||
nsAutoString params;
|
||||
bool usingCustomParseOptions;
|
||||
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
// We disallow canvases of width or height zero, and set them to 1, so
|
||||
// we will have a discrepancy with the sizes of the canvas and the context.
|
||||
// That discrepancy is OK, the rest are not.
|
||||
nsIntSize elementSize = GetWidthHeight();
|
||||
if ((elementSize.width != mCurrentContext->GetWidth() &&
|
||||
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
|
||||
(elementSize.height != mCurrentContext->GetHeight() &&
|
||||
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
}
|
||||
|
||||
// Encoder callback when encoding is complete.
|
||||
class EncodeCallback : public EncodeCompleteCallback
|
||||
{
|
||||
public:
|
||||
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
|
||||
: mGlobal(aGlobal)
|
||||
, mFileCallback(aCallback) {}
|
||||
|
||||
// This is called on main thread.
|
||||
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
|
||||
{
|
||||
nsRefPtr<Blob> blob = aBlob;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t size = blob->GetSize(rv);
|
||||
if (rv.Failed()) {
|
||||
rv.SuppressException();
|
||||
} else {
|
||||
AutoJSAPI jsapi;
|
||||
if (jsapi.Init(mGlobal)) {
|
||||
JS_updateMallocCounter(jsapi.cx(), size);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
|
||||
|
||||
mFileCallback->Call(*newBlob, rv);
|
||||
|
||||
mGlobal = nullptr;
|
||||
mFileCallback = nullptr;
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsRefPtr<FileCallback> mFileCallback;
|
||||
};
|
||||
|
||||
nsRefPtr<EncodeCompleteCallback> callback =
|
||||
new EncodeCallback(aGlobal, &aCallback);
|
||||
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetWidthHeight(),
|
||||
callback);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
MOZ_ASSERT(aContextType != CanvasContextType::NoContext);
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret;
|
||||
|
||||
switch (aContextType) {
|
||||
case CanvasContextType::NoContext:
|
||||
break;
|
||||
|
||||
case CanvasContextType::Canvas2D:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
|
||||
ret = new CanvasRenderingContext2D();
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL1:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL1Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL2:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL2Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(ret);
|
||||
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
CanvasRenderingContextHelper::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
CanvasContextType contextType;
|
||||
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType))
|
||||
return nullptr;
|
||||
|
||||
if (!mCurrentContext) {
|
||||
// This canvas doesn't have a context yet.
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContext(contextType);
|
||||
if (!context) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure that the context participates in CC. Note that returning a
|
||||
// CC participant from QI doesn't addref.
|
||||
nsXPCOMCycleCollectionParticipant* cp = nullptr;
|
||||
CallQueryInterface(context, &cp);
|
||||
if (!cp) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCurrentContext = context.forget();
|
||||
mCurrentContextType = contextType;
|
||||
|
||||
aRv = UpdateContext(aCx, aContextOptions);
|
||||
if (aRv.Failed()) {
|
||||
aRv = NS_OK; // See bug 645792
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// We already have a context of some type.
|
||||
if (contextType != mCurrentContextType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
|
||||
return context.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
CanvasRenderingContextHelper::UpdateContext(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aNewContextOptions)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return NS_OK;
|
||||
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
|
||||
|
||||
nsresult rv = currentContext->SetIsOpaque(GetOpaqueAttr());
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetDimensions(sz.width, sz.height);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
CanvasRenderingContextHelper::ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& outParams,
|
||||
bool* const outUsingCustomParseOptions)
|
||||
{
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (aType.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
outParams.AppendLiteral("quality=");
|
||||
outParams.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the aParams check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
*outUsingCustomParseOptions = false;
|
||||
if (outParams.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsAutoJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
outParams.Append(parseOptions);
|
||||
*outUsingCustomParseOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
||||
#define MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsSize.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsIGlobalObject;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class FileCallback;
|
||||
|
||||
enum class CanvasContextType : uint8_t {
|
||||
NoContext,
|
||||
Canvas2D,
|
||||
WebGL1,
|
||||
WebGL2
|
||||
};
|
||||
|
||||
/**
|
||||
* Povides common RenderingContext functionality used by both OffscreenCanvas
|
||||
* and HTMLCanvasElement.
|
||||
*/
|
||||
class CanvasRenderingContextHelper
|
||||
{
|
||||
public:
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual bool GetOpaqueAttr() = 0;
|
||||
|
||||
protected:
|
||||
virtual nsresult UpdateContext(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aNewContextOptions);
|
||||
|
||||
virtual nsresult ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& outParams,
|
||||
bool* const outCustomParseOptions);
|
||||
|
||||
void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback,
|
||||
const nsAString& aType, JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType);
|
||||
|
||||
virtual nsIntSize GetWidthHeight() = 0;
|
||||
|
||||
CanvasContextType mCurrentContextType;
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
|
|
@ -23,12 +23,47 @@
|
|||
|
||||
#include "CanvasUtils.h"
|
||||
#include "mozilla/gfx/Matrix.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
namespace CanvasUtils {
|
||||
|
||||
bool
|
||||
GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
|
||||
{
|
||||
if (str.EqualsLiteral("2d")) {
|
||||
*out_type = dom::CanvasContextType::Canvas2D;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str.EqualsLiteral("experimental-webgl")) {
|
||||
*out_type = dom::CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBGL_CONFORMANT
|
||||
if (str.EqualsLiteral("webgl")) {
|
||||
/* WebGL 1.0, $2.1 "Context Creation":
|
||||
* If the user agent supports both the webgl and experimental-webgl
|
||||
* canvas context types, they shall be treated as aliases.
|
||||
*/
|
||||
*out_type = dom::CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (WebGL2Context::IsSupported()) {
|
||||
if (str.EqualsLiteral("webgl2")) {
|
||||
*out_type = dom::CanvasContextType::WebGL2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This security check utility might be called from an source that never taints
|
||||
* others. For example, while painting a CanvasPattern, which is created from an
|
||||
|
|
|
@ -21,6 +21,7 @@ class HTMLCanvasElement;
|
|||
|
||||
namespace CanvasUtils {
|
||||
|
||||
bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type);
|
||||
|
||||
// Check that the rectangle [x,y,w,h] is a subrectangle of [0,0,realWidth,realHeight]
|
||||
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
/* -*- 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 "OffscreenCanvas.h"
|
||||
|
||||
#include "mozilla/dom/OffscreenCanvasBinding.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/layers/CanvasClient.h"
|
||||
#include "mozilla/layers/ImageBridgeChild.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "CanvasRenderingContext2D.h"
|
||||
#include "CanvasUtils.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "WebGL1Context.h"
|
||||
#include "WebGL2Context.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered)
|
||||
: mRenderer(aRenderer)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mNeutered(aNeutered)
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer)
|
||||
: mAttrDirty(false)
|
||||
, mNeutered(false)
|
||||
, mWidth(aWidth)
|
||||
, mHeight(aHeight)
|
||||
, mCompositorBackendType(aCompositorBackend)
|
||||
, mCanvasClient(nullptr)
|
||||
, mCanvasRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
OffscreenCanvas::~OffscreenCanvas()
|
||||
{
|
||||
ClearResources();
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
OffscreenCanvas::GetParentObject() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
OffscreenCanvas::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
OffscreenCanvas::ClearResources()
|
||||
{
|
||||
if (mCanvasClient) {
|
||||
mCanvasClient->Clear();
|
||||
ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
|
||||
mCanvasClient = nullptr;
|
||||
|
||||
if (mCanvasRenderer) {
|
||||
nsCOMPtr<nsIThread> activeThread = mCanvasRenderer->GetActiveThread();
|
||||
MOZ_RELEASE_ASSERT(activeThread);
|
||||
MOZ_RELEASE_ASSERT(activeThread == NS_GetCurrentThread());
|
||||
mCanvasRenderer->SetCanvasClient(nullptr);
|
||||
mCanvasRenderer->mContext = nullptr;
|
||||
mCanvasRenderer->mGLContext = nullptr;
|
||||
mCanvasRenderer->ResetActiveThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports>
|
||||
OffscreenCanvas::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We only support WebGL in workers for now
|
||||
CanvasContextType contextType;
|
||||
if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!(contextType == CanvasContextType::WebGL1 ||
|
||||
contextType == CanvasContextType::WebGL2))
|
||||
{
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsISupports> result =
|
||||
CanvasRenderingContextHelper::GetContext(aCx,
|
||||
aContextId,
|
||||
aContextOptions,
|
||||
aRv);
|
||||
|
||||
if (!mCurrentContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mCanvasRenderer) {
|
||||
WebGLContext* webGL = static_cast<WebGLContext*>(mCurrentContext.get());
|
||||
gl::GLContext* gl = webGL->GL();
|
||||
mCanvasRenderer->mContext = mCurrentContext;
|
||||
mCanvasRenderer->SetActiveThread();
|
||||
mCanvasRenderer->mGLContext = gl;
|
||||
mCanvasRenderer->SetIsAlphaPremultiplied(webGL->IsPremultAlpha() || !gl->Caps().alpha);
|
||||
|
||||
if (ImageBridgeChild::IsCreated()) {
|
||||
TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
|
||||
mCanvasClient = ImageBridgeChild::GetSingleton()->
|
||||
CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
|
||||
mCanvasRenderer->SetCanvasClient(mCanvasClient);
|
||||
|
||||
gl::GLScreenBuffer* screen = gl->Screen();
|
||||
gl::SurfaceCaps caps = screen->mCaps;
|
||||
auto forwarder = mCanvasClient->GetForwarder();
|
||||
|
||||
UniquePtr<gl::SurfaceFactory> factory =
|
||||
gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
|
||||
|
||||
if (factory)
|
||||
screen->Morph(Move(factory));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
OffscreenCanvas::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CanvasRenderingContextHelper::CreateContext(aContextType);
|
||||
|
||||
ret->SetOffscreenCanvas(this);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
void
|
||||
OffscreenCanvas::CommitFrameToCompositor()
|
||||
{
|
||||
// The attributes has changed, we have to notify main
|
||||
// thread to change canvas size.
|
||||
if (mAttrDirty) {
|
||||
if (mCanvasRenderer) {
|
||||
mCanvasRenderer->SetWidth(mWidth);
|
||||
mCanvasRenderer->SetHeight(mHeight);
|
||||
mCanvasRenderer->NotifyElementAboutAttributesChanged();
|
||||
}
|
||||
mAttrDirty = false;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
static_cast<WebGLContext*>(mCurrentContext.get())->PresentScreenBuffer();
|
||||
}
|
||||
|
||||
if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
|
||||
mCanvasRenderer->NotifyElementAboutInvalidation();
|
||||
ImageBridgeChild::GetSingleton()->
|
||||
UpdateAsyncCanvasRenderer(mCanvasRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
OffscreenCanvasCloneData*
|
||||
OffscreenCanvas::ToCloneData()
|
||||
{
|
||||
return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth, mHeight,
|
||||
mCompositorBackendType, mNeutered);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<OffscreenCanvas>
|
||||
OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
|
||||
{
|
||||
MOZ_ASSERT(aData);
|
||||
nsRefPtr<OffscreenCanvas> wc =
|
||||
new OffscreenCanvas(aData->mWidth, aData->mHeight,
|
||||
aData->mCompositorBackendType, aData->mRenderer);
|
||||
if (aData->mNeutered) {
|
||||
wc->SetNeutered();
|
||||
}
|
||||
return wc.forget();
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
OffscreenCanvas::PrefEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("gfx.offscreencanvas.enabled");
|
||||
} else {
|
||||
WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
return workerPrivate->OffscreenCanvasEnabled();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return PrefEnabled(aCx, aObj);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, mCurrentContext)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OffscreenCanvas)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,179 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
#ifndef MOZILLA_DOM_OFFSCREENCANVAS_H_
|
||||
#define MOZILLA_DOM_OFFSCREENCANVAS_H_
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "CanvasRenderingContextHelper.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class CanvasClient;
|
||||
} // namespace layers
|
||||
|
||||
namespace dom {
|
||||
|
||||
// This is helper class for transferring OffscreenCanvas to worker thread.
|
||||
// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
|
||||
// Canvas to worker thread directly. Thus, we create this helper class and
|
||||
// store necessary data in it then pass it to worker thread.
|
||||
struct OffscreenCanvasCloneData final
|
||||
{
|
||||
OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
|
||||
uint32_t aWidth, uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
bool aNeutered);
|
||||
~OffscreenCanvasCloneData();
|
||||
|
||||
RefPtr<layers::AsyncCanvasRenderer> mRenderer;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
bool mNeutered;
|
||||
};
|
||||
|
||||
class OffscreenCanvas final : public DOMEventTargetHelper
|
||||
, public CanvasRenderingContextHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
|
||||
|
||||
OffscreenCanvas(uint32_t aWidth,
|
||||
uint32_t aHeight,
|
||||
layers::LayersBackend aCompositorBackend,
|
||||
layers::AsyncCanvasRenderer* aRenderer);
|
||||
|
||||
OffscreenCanvas* GetParentObject() const;
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void ClearResources();
|
||||
|
||||
uint32_t Width() const
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
uint32_t Height() const
|
||||
{
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWidth != aWidth) {
|
||||
mWidth = aWidth;
|
||||
CanvasAttrChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
|
||||
{
|
||||
if (mNeutered) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mHeight != aHeight) {
|
||||
mHeight = aHeight;
|
||||
CanvasAttrChanged();
|
||||
}
|
||||
}
|
||||
|
||||
nsICanvasRenderingContextInternal* GetContext() const
|
||||
{
|
||||
return mCurrentContext;
|
||||
}
|
||||
|
||||
static already_AddRefed<OffscreenCanvas>
|
||||
CreateFromCloneData(OffscreenCanvasCloneData* aData);
|
||||
|
||||
static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
// Return true on main-thread, and return gfx.offscreencanvas.enabled
|
||||
// on worker thread.
|
||||
static bool PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
OffscreenCanvasCloneData* ToCloneData();
|
||||
|
||||
void CommitFrameToCompositor();
|
||||
|
||||
virtual bool GetOpaqueAttr() override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual nsIntSize GetWidthHeight() override
|
||||
{
|
||||
return nsIntSize(mWidth, mHeight);
|
||||
}
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType) override;
|
||||
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
void SetNeutered()
|
||||
{
|
||||
mNeutered = true;
|
||||
}
|
||||
|
||||
bool IsNeutered() const
|
||||
{
|
||||
return mNeutered;
|
||||
}
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const
|
||||
{
|
||||
return mCompositorBackendType;
|
||||
}
|
||||
|
||||
private:
|
||||
~OffscreenCanvas();
|
||||
|
||||
void CanvasAttrChanged()
|
||||
{
|
||||
mAttrDirty = true;
|
||||
UpdateContext(nullptr, JS::NullHandleValue);
|
||||
}
|
||||
|
||||
bool mAttrDirty;
|
||||
bool mNeutered;
|
||||
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
|
||||
layers::LayersBackend mCompositorBackendType;
|
||||
|
||||
layers::CanvasClient* mCanvasClient;
|
||||
RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MOZILLA_DOM_OFFSCREENCANVAS_H_
|
|
@ -22,6 +22,7 @@
|
|||
#include "ImageEncoder.h"
|
||||
#include "Layers.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/HTMLVideoElement.h"
|
||||
#include "mozilla/dom/ImageData.h"
|
||||
#include "mozilla/EnumeratedArrayCycleCollection.h"
|
||||
|
@ -79,125 +80,6 @@ using namespace mozilla::gfx;
|
|||
using namespace mozilla::gl;
|
||||
using namespace mozilla::layers;
|
||||
|
||||
WebGLObserver::WebGLObserver(WebGLContext* webgl)
|
||||
: mWebGL(webgl)
|
||||
{
|
||||
}
|
||||
|
||||
WebGLObserver::~WebGLObserver()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::Destroy()
|
||||
{
|
||||
UnregisterMemoryPressureEvent();
|
||||
UnregisterVisibilityChangeEvent();
|
||||
mWebGL = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::RegisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
if (canvas) {
|
||||
nsIDocument* document = canvas->OwnerDoc();
|
||||
|
||||
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::UnregisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
|
||||
if (canvas) {
|
||||
nsIDocument* document = canvas->OwnerDoc();
|
||||
|
||||
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::RegisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
if (observerService)
|
||||
observerService->AddObserver(this, "memory-pressure", false);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLObserver::UnregisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mWebGL)
|
||||
return;
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
// Do not assert on observerService here. This might be triggered by
|
||||
// the cycle collector at a late enough time, that XPCOM services are
|
||||
// no longer available. See bug 1029504.
|
||||
if (observerService)
|
||||
observerService->RemoveObserver(this, "memory-pressure");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLObserver::Observe(nsISupports*, const char* topic, const char16_t*)
|
||||
{
|
||||
if (!mWebGL || strcmp(topic, "memory-pressure")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool wantToLoseContext = mWebGL->mLoseContextOnMemoryPressure;
|
||||
|
||||
if (!mWebGL->mCanLoseContextInForeground &&
|
||||
ProcessPriorityManager::CurrentProcessIsForeground())
|
||||
{
|
||||
wantToLoseContext = false;
|
||||
}
|
||||
|
||||
if (wantToLoseContext)
|
||||
mWebGL->ForceLoseContext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLObserver::HandleEvent(nsIDOMEvent* event)
|
||||
{
|
||||
nsAutoString type;
|
||||
event->GetType(type);
|
||||
if (!mWebGL || !type.EqualsLiteral("visibilitychange"))
|
||||
return NS_OK;
|
||||
|
||||
HTMLCanvasElement* canvas = mWebGL->GetCanvas();
|
||||
MOZ_ASSERT(canvas);
|
||||
|
||||
if (canvas && !canvas->OwnerDoc()->Hidden())
|
||||
mWebGL->ForceRestoreContext();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WebGLContextOptions::WebGLContextOptions()
|
||||
: alpha(true)
|
||||
, depth(true)
|
||||
|
@ -208,7 +90,7 @@ WebGLContextOptions::WebGLContextOptions()
|
|||
, failIfMajorPerformanceCaveat(false)
|
||||
{
|
||||
// Set default alpha state based on preference.
|
||||
if (Preferences::GetBool("webgl.default-no-alpha", false))
|
||||
if (gfxPrefs::WebGLDefaultNoAlpha())
|
||||
alpha = false;
|
||||
}
|
||||
|
||||
|
@ -282,7 +164,10 @@ WebGLContext::WebGLContext()
|
|||
mPixelStorePackAlignment = 4;
|
||||
mPixelStoreUnpackAlignment = 4;
|
||||
|
||||
WebGLMemoryTracker::AddWebGLContext(this);
|
||||
if (NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, not thread safe
|
||||
WebGLMemoryTracker::AddWebGLContext(this);
|
||||
}
|
||||
|
||||
mAllowContextRestore = true;
|
||||
mLastLossWasSimulated = false;
|
||||
|
@ -296,15 +181,12 @@ WebGLContext::WebGLContext()
|
|||
mAlreadyWarnedAboutFakeVertexAttrib0 = false;
|
||||
mAlreadyWarnedAboutViewportLargerThanDest = false;
|
||||
|
||||
mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
|
||||
mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
|
||||
if (mMaxWarnings < -1) {
|
||||
GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
|
||||
mMaxWarnings = 0;
|
||||
}
|
||||
|
||||
mContextObserver = new WebGLObserver(this);
|
||||
MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
|
||||
|
||||
mLastUseIndex = 0;
|
||||
|
||||
InvalidateBufferFetching();
|
||||
|
@ -319,10 +201,12 @@ WebGLContext::WebGLContext()
|
|||
WebGLContext::~WebGLContext()
|
||||
{
|
||||
RemovePostRefreshObserver();
|
||||
mContextObserver->Destroy();
|
||||
|
||||
DestroyResourcesAndContext();
|
||||
WebGLMemoryTracker::RemoveWebGLContext(this);
|
||||
if (NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, not thread safe
|
||||
WebGLMemoryTracker::RemoveWebGLContext(this);
|
||||
}
|
||||
|
||||
mContextLossHandler->DisableTimer();
|
||||
mContextLossHandler = nullptr;
|
||||
|
@ -331,8 +215,6 @@ WebGLContext::~WebGLContext()
|
|||
void
|
||||
WebGLContext::DestroyResourcesAndContext()
|
||||
{
|
||||
mContextObserver->UnregisterMemoryPressureEvent();
|
||||
|
||||
if (!gl)
|
||||
return;
|
||||
|
||||
|
@ -431,6 +313,35 @@ WebGLContext::Invalidate()
|
|||
mCanvasElement->InvalidateCanvasContent(nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::OnVisibilityChange()
|
||||
{
|
||||
if (!IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mRestoreWhenVisible || mLastLossWasSimulated) {
|
||||
return;
|
||||
}
|
||||
|
||||
ForceRestoreContext();
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::OnMemoryPressure()
|
||||
{
|
||||
bool shouldLoseContext = mLoseContextOnMemoryPressure;
|
||||
|
||||
if (!mCanLoseContextInForeground &&
|
||||
ProcessPriorityManager::CurrentProcessIsForeground())
|
||||
{
|
||||
shouldLoseContext = false;
|
||||
}
|
||||
|
||||
if (shouldLoseContext)
|
||||
ForceLoseContext();
|
||||
}
|
||||
|
||||
//
|
||||
// nsICanvasRenderingContextInternal
|
||||
//
|
||||
|
@ -513,7 +424,7 @@ static bool
|
|||
IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
|
||||
{
|
||||
int32_t status;
|
||||
if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
|
||||
if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
|
||||
return false;
|
||||
|
||||
return status != nsIGfxInfo::FEATURE_STATUS_OK;
|
||||
|
@ -524,19 +435,29 @@ HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
|
|||
{
|
||||
int32_t status;
|
||||
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_OPENGL_LAYERS,
|
||||
&status);
|
||||
if (status)
|
||||
return true;
|
||||
|
||||
|
@ -593,11 +514,14 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
|
|||
// we should really have this behind a
|
||||
// |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
|
||||
// for now it's just behind a pref for testing/evaluation.
|
||||
baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
|
||||
baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
do {
|
||||
auto canvasElement = webgl->GetCanvas();
|
||||
if (!canvasElement)
|
||||
break;
|
||||
|
||||
auto ownerDoc = canvasElement->OwnerDoc();
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(ownerDoc);
|
||||
if (!docWidget)
|
||||
|
@ -618,7 +542,7 @@ BaseCaps(const WebGLContextOptions& options, WebGLContext* webgl)
|
|||
|
||||
// Done with baseCaps construction.
|
||||
|
||||
bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
|
||||
bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
|
||||
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
|
||||
if (!forceAllowAA &&
|
||||
IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
|
||||
|
@ -738,7 +662,7 @@ bool
|
|||
WebGLContext::CreateAndInitGL(bool forceEnabled)
|
||||
{
|
||||
bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
|
||||
bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
|
||||
bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
|
||||
|
||||
if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
|
||||
disableANGLE = true;
|
||||
|
@ -819,10 +743,6 @@ WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
|
|||
NS_IMETHODIMP
|
||||
WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
||||
{
|
||||
// Early error return cases
|
||||
if (!GetCanvas())
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (signedWidth < 0 || signedHeight < 0) {
|
||||
GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -832,7 +752,10 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
uint32_t height = signedHeight;
|
||||
|
||||
// Early success return cases
|
||||
GetCanvas()->InvalidateCanvas();
|
||||
|
||||
// May have a OffscreenCanvas instead of an HTMLCanvasElement
|
||||
if (GetCanvas())
|
||||
GetCanvas()->InvalidateCanvas();
|
||||
|
||||
// Zero-sized surfaces can cause problems.
|
||||
if (width == 0)
|
||||
|
@ -905,10 +828,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
// pick up the old generation.
|
||||
++mGeneration;
|
||||
|
||||
// Get some prefs for some preferred/overriden things
|
||||
NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
|
||||
|
||||
bool disabled = Preferences::GetBool("webgl.disabled", false);
|
||||
bool disabled = gfxPrefs::WebGLDisabled();
|
||||
|
||||
// TODO: When we have software webgl support we should use that instead.
|
||||
disabled |= gfxPlatform::InSafeMode();
|
||||
|
@ -931,7 +851,7 @@ WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
|
|||
}
|
||||
|
||||
// Alright, now let's start trying.
|
||||
bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
|
||||
bool forceEnabled = gfxPrefs::WebGLForceEnabled();
|
||||
ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
|
||||
|
||||
MOZ_ASSERT(!gl);
|
||||
|
@ -1052,6 +972,11 @@ WebGLContext::LoseOldestWebGLContextIfLimitExceeded()
|
|||
#endif
|
||||
MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
// XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
|
||||
return;
|
||||
}
|
||||
|
||||
// it's important to update the index on a new context before losing old contexts,
|
||||
// otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
|
||||
// when choosing which one to lose first.
|
||||
|
@ -1139,32 +1064,8 @@ WebGLContext::GetImageBuffer(uint8_t** out_imageBuffer, int32_t* out_format)
|
|||
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
|
||||
DataSourceSurface::MappedSurface map;
|
||||
if (!dataSurface->Map(DataSourceSurface::MapType::READ, &map))
|
||||
return;
|
||||
|
||||
uint8_t* imageBuffer = new (fallible) uint8_t[mWidth * mHeight * 4];
|
||||
if (!imageBuffer) {
|
||||
dataSurface->Unmap();
|
||||
return;
|
||||
}
|
||||
memcpy(imageBuffer, map.mData, mWidth * mHeight * 4);
|
||||
|
||||
dataSurface->Unmap();
|
||||
|
||||
int32_t format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
|
||||
if (!mOptions.premultipliedAlpha) {
|
||||
// We need to convert to INPUT_FORMAT_RGBA, otherwise
|
||||
// we are automatically considered premult, and unpremult'd.
|
||||
// Yes, it is THAT silly.
|
||||
// Except for different lossy conversions by color,
|
||||
// we could probably just change the label, and not change the data.
|
||||
gfxUtils::ConvertBGRAtoRGBA(imageBuffer, mWidth * mHeight * 4);
|
||||
format = imgIEncoder::INPUT_FORMAT_RGBA;
|
||||
}
|
||||
|
||||
*out_imageBuffer = imageBuffer;
|
||||
*out_format = format;
|
||||
return gfxUtils::GetImageBuffer(dataSurface, mOptions.premultipliedAlpha,
|
||||
out_imageBuffer, out_format);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -1176,20 +1077,18 @@ WebGLContext::GetInputStream(const char* mimeType,
|
|||
if (!gl)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsCString enccid("@mozilla.org/image/encoder;2?type=");
|
||||
enccid += mimeType;
|
||||
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(enccid.get());
|
||||
if (!encoder)
|
||||
// Use GetSurfaceSnapshot() to make sure that appropriate y-flip gets applied
|
||||
bool premult;
|
||||
RefPtr<SourceSurface> snapshot =
|
||||
GetSurfaceSnapshot(mOptions.premultipliedAlpha ? nullptr : &premult);
|
||||
if (!snapshot)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
nsAutoArrayPtr<uint8_t> imageBuffer;
|
||||
int32_t format = 0;
|
||||
GetImageBuffer(getter_Transfers(imageBuffer), &format);
|
||||
if (!imageBuffer)
|
||||
return NS_ERROR_FAILURE;
|
||||
MOZ_ASSERT(mOptions.premultipliedAlpha || !premult, "We must get unpremult when we ask for it!");
|
||||
|
||||
return ImageEncoder::GetInputStream(mWidth, mHeight, imageBuffer, format,
|
||||
encoder, encoderOptions, out_stream);
|
||||
RefPtr<DataSourceSurface> dataSurface = snapshot->GetDataSurface();
|
||||
return gfxUtils::GetInputStream(dataSurface, mOptions.premultipliedAlpha, mimeType,
|
||||
encoderOptions, out_stream);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1268,25 +1167,26 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
|||
}
|
||||
|
||||
WebGLContextUserData* userData = nullptr;
|
||||
if (builder->IsPaintingToWindow()) {
|
||||
// Make the layer tell us whenever a transaction finishes (including
|
||||
// the current transaction), so we can clear our invalidation state and
|
||||
// start invalidating again. We need to do this for the layer that is
|
||||
// being painted to a window (there shouldn't be more than one at a time,
|
||||
// and if there is, flushing the invalidation state more often than
|
||||
// necessary is harmless).
|
||||
if (builder->IsPaintingToWindow() && mCanvasElement) {
|
||||
// Make the layer tell us whenever a transaction finishes (including
|
||||
// the current transaction), so we can clear our invalidation state and
|
||||
// start invalidating again. We need to do this for the layer that is
|
||||
// being painted to a window (there shouldn't be more than one at a time,
|
||||
// and if there is, flushing the invalidation state more often than
|
||||
// necessary is harmless).
|
||||
|
||||
// The layer will be destroyed when we tear down the presentation
|
||||
// (at the latest), at which time this userData will be destroyed,
|
||||
// releasing the reference to the element.
|
||||
// The userData will receive DidTransactionCallbacks, which flush the
|
||||
// the invalidation state to indicate that the canvas is up to date.
|
||||
userData = new WebGLContextUserData(mCanvasElement);
|
||||
canvasLayer->SetDidTransactionCallback(
|
||||
WebGLContextUserData::DidTransactionCallback, userData);
|
||||
canvasLayer->SetPreTransactionCallback(
|
||||
WebGLContextUserData::PreTransactionCallback, userData);
|
||||
// The layer will be destroyed when we tear down the presentation
|
||||
// (at the latest), at which time this userData will be destroyed,
|
||||
// releasing the reference to the element.
|
||||
// The userData will receive DidTransactionCallbacks, which flush the
|
||||
// the invalidation state to indicate that the canvas is up to date.
|
||||
userData = new WebGLContextUserData(mCanvasElement);
|
||||
canvasLayer->SetDidTransactionCallback(
|
||||
WebGLContextUserData::DidTransactionCallback, userData);
|
||||
canvasLayer->SetPreTransactionCallback(
|
||||
WebGLContextUserData::PreTransactionCallback, userData);
|
||||
}
|
||||
|
||||
canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
|
||||
|
||||
CanvasLayer::Data data;
|
||||
|
@ -1308,14 +1208,36 @@ WebGLContext::GetCanvasLayer(nsDisplayListBuilder* builder,
|
|||
layers::LayersBackend
|
||||
WebGLContext::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
if (mCanvasElement) {
|
||||
return mCanvasElement->GetCompositorBackendType();
|
||||
} else if (mOffscreenCanvas) {
|
||||
return mOffscreenCanvas->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::Commit()
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
mOffscreenCanvas->CommitFrameToCompositor();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
|
||||
{
|
||||
if (mCanvasElement) {
|
||||
MOZ_RELEASE_ASSERT(!mOffscreenCanvas);
|
||||
retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
|
||||
} else if (mOffscreenCanvas) {
|
||||
retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
|
||||
} else {
|
||||
retval.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
|
||||
{
|
||||
|
@ -1624,7 +1546,7 @@ WebGLContext::RunContextLossTimer()
|
|||
mContextLossHandler->RunTimer();
|
||||
}
|
||||
|
||||
class UpdateContextLossStatusTask : public nsRunnable
|
||||
class UpdateContextLossStatusTask : public nsCancelableRunnable
|
||||
{
|
||||
nsRefPtr<WebGLContext> mWebGL;
|
||||
|
||||
|
@ -1635,10 +1557,16 @@ public:
|
|||
}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mWebGL->UpdateContextLossStatus();
|
||||
if (mWebGL)
|
||||
mWebGL->UpdateContextLossStatus();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD Cancel() {
|
||||
mWebGL = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -1665,7 +1593,7 @@ WebGLContext::EnqueueUpdateContextLossStatus()
|
|||
void
|
||||
WebGLContext::UpdateContextLossStatus()
|
||||
{
|
||||
if (!mCanvasElement) {
|
||||
if (!mCanvasElement && !mOffscreenCanvas) {
|
||||
// the canvas is gone. That happens when the page was closed before we got
|
||||
// this timer event. In this case, there's nothing to do here, just don't crash.
|
||||
return;
|
||||
|
@ -1693,12 +1621,23 @@ WebGLContext::UpdateContextLossStatus()
|
|||
// callback, so do that now.
|
||||
|
||||
bool useDefaultHandler;
|
||||
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextlost"),
|
||||
true,
|
||||
true,
|
||||
&useDefaultHandler);
|
||||
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextlost"),
|
||||
true,
|
||||
true,
|
||||
&useDefaultHandler);
|
||||
} else {
|
||||
// OffscreenCanvas case
|
||||
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
|
||||
event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
|
||||
event->SetTrusted(true);
|
||||
mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
|
||||
}
|
||||
|
||||
// We sent the callback, so we're just 'regular lost' now.
|
||||
mContextStatus = ContextLost;
|
||||
// If we're told to use the default handler, it means the script
|
||||
|
@ -1750,11 +1689,22 @@ WebGLContext::UpdateContextLossStatus()
|
|||
|
||||
// Revival!
|
||||
mContextStatus = ContextNotLost;
|
||||
nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextrestored"),
|
||||
true,
|
||||
true);
|
||||
|
||||
if (mCanvasElement) {
|
||||
nsContentUtils::DispatchTrustedEvent(
|
||||
mCanvasElement->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
|
||||
NS_LITERAL_STRING("webglcontextrestored"),
|
||||
true,
|
||||
true);
|
||||
} else {
|
||||
nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
|
||||
event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
|
||||
event->SetTrusted(true);
|
||||
bool unused;
|
||||
mOffscreenCanvas->DispatchEvent(event, &unused);
|
||||
}
|
||||
|
||||
mEmitContextLostErrorOnce = true;
|
||||
return;
|
||||
}
|
||||
|
@ -1772,12 +1722,6 @@ WebGLContext::ForceLoseContext(bool simulateLosing)
|
|||
DestroyResourcesAndContext();
|
||||
mLastLossWasSimulated = simulateLosing;
|
||||
|
||||
// Register visibility change observer to defer the context restoring.
|
||||
// Restore the context when the app is visible.
|
||||
if (mRestoreWhenVisible && !mLastLossWasSimulated) {
|
||||
mContextObserver->RegisterVisibilityChangeEvent();
|
||||
}
|
||||
|
||||
// Queue up a task, since we know the status changed.
|
||||
EnqueueUpdateContextLossStatus();
|
||||
}
|
||||
|
@ -1789,8 +1733,6 @@ WebGLContext::ForceRestoreContext()
|
|||
mContextStatus = ContextLostAwaitingRestore;
|
||||
mAllowContextRestore = true; // Hey, you did say 'force'.
|
||||
|
||||
mContextObserver->UnregisterVisibilityChangeEvent();
|
||||
|
||||
// Queue up a task, since we know the status changed.
|
||||
EnqueueUpdateContextLossStatus();
|
||||
}
|
||||
|
@ -1925,6 +1867,7 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
|
|||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
|
||||
mCanvasElement,
|
||||
mOffscreenCanvas,
|
||||
mExtensions,
|
||||
mBound2DTextures,
|
||||
mBoundCubeMapTextures,
|
||||
|
|
|
@ -40,7 +40,11 @@
|
|||
// Generated
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMWebGLRenderingContext.h"
|
||||
#include "nsICanvasRenderingContextInternal.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
|
||||
|
||||
class nsIDocShell;
|
||||
|
@ -80,7 +84,6 @@ class WebGLContextLossHandler;
|
|||
class WebGLBuffer;
|
||||
class WebGLExtensionBase;
|
||||
class WebGLFramebuffer;
|
||||
class WebGLObserver;
|
||||
class WebGLProgram;
|
||||
class WebGLQuery;
|
||||
class WebGLRenderbuffer;
|
||||
|
@ -95,6 +98,7 @@ class WebGLVertexArray;
|
|||
namespace dom {
|
||||
class Element;
|
||||
class ImageData;
|
||||
class OwningHTMLCanvasElementOrOffscreenCanvas;
|
||||
struct WebGLContextAttributes;
|
||||
template<typename> struct Nullable;
|
||||
} // namespace dom
|
||||
|
@ -184,7 +188,6 @@ class WebGLContext
|
|||
friend class WebGLExtensionLoseContext;
|
||||
friend class WebGLExtensionVertexArray;
|
||||
friend class WebGLMemoryTracker;
|
||||
friend class WebGLObserver;
|
||||
|
||||
enum {
|
||||
UNPACK_FLIP_Y_WEBGL = 0x9240,
|
||||
|
@ -214,6 +217,9 @@ public:
|
|||
|
||||
NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
|
||||
|
||||
virtual void OnVisibilityChange() override;
|
||||
virtual void OnMemoryPressure() override;
|
||||
|
||||
// nsICanvasRenderingContextInternal
|
||||
virtual int32_t GetWidth() const override;
|
||||
virtual int32_t GetHeight() const override;
|
||||
|
@ -361,8 +367,11 @@ public:
|
|||
void AssertCachedBindings();
|
||||
void AssertCachedState();
|
||||
|
||||
// WebIDL WebGLRenderingContext API
|
||||
dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
|
||||
|
||||
// WebIDL WebGLRenderingContext API
|
||||
void Commit();
|
||||
void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
|
||||
GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
|
||||
GLsizei DrawingBufferHeight() const {
|
||||
return IsContextLost() ? 0 : mHeight;
|
||||
|
@ -1508,8 +1517,6 @@ protected:
|
|||
ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
|
||||
#endif
|
||||
|
||||
nsRefPtr<WebGLObserver> mContextObserver;
|
||||
|
||||
public:
|
||||
// console logging helpers
|
||||
void GenerateWarning(const char* fmt, ...);
|
||||
|
@ -1614,32 +1621,6 @@ WebGLContext::ValidateObject(const char* info, ObjectType* object)
|
|||
return ValidateObjectAssumeNonNull(info, object);
|
||||
}
|
||||
|
||||
// Listen visibilitychange and memory-pressure event for context lose/restore
|
||||
class WebGLObserver final
|
||||
: public nsIObserver
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit WebGLObserver(WebGLContext* webgl);
|
||||
|
||||
void Destroy();
|
||||
|
||||
void RegisterVisibilityChangeEvent();
|
||||
void UnregisterVisibilityChangeEvent();
|
||||
|
||||
void RegisterMemoryPressureEvent();
|
||||
void UnregisterMemoryPressureEvent();
|
||||
|
||||
private:
|
||||
~WebGLObserver();
|
||||
|
||||
WebGLContext* mWebGL;
|
||||
};
|
||||
|
||||
size_t RoundUpToMultipleOf(size_t value, size_t multiple);
|
||||
|
||||
bool
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "WebGLContext.h"
|
||||
#include "WebGLContextUtils.h"
|
||||
#include "WebGLExtensions.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
|
||||
#include "nsString.h"
|
||||
|
@ -74,12 +75,15 @@ bool WebGLContext::IsExtensionSupported(JSContext* cx,
|
|||
|
||||
// Chrome contexts need access to debug information even when
|
||||
// webgl.disable-extensions is set. This is used in the graphics
|
||||
// section of about:support.
|
||||
if (xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)))
|
||||
// section of about:support
|
||||
if (NS_IsMainThread() &&
|
||||
xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
|
||||
allowPrivilegedExts = true;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.enable-privileged-extensions", false))
|
||||
if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
|
||||
allowPrivilegedExts = true;
|
||||
}
|
||||
|
||||
if (allowPrivilegedExts) {
|
||||
switch (ext) {
|
||||
|
@ -181,9 +185,7 @@ WebGLContext::IsExtensionSupported(WebGLExtensionID ext) const
|
|||
break;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
|
||||
IsWebGL2())
|
||||
{
|
||||
if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) {
|
||||
switch (ext) {
|
||||
case WebGLExtensionID::EXT_disjoint_timer_query:
|
||||
return WebGLExtensionDisjointTimerQuery::IsSupported(this);
|
||||
|
|
|
@ -1390,7 +1390,10 @@ WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
|
|||
if (IsContextLost())
|
||||
return;
|
||||
|
||||
if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
|
||||
if (mCanvasElement &&
|
||||
mCanvasElement->IsWriteOnly() &&
|
||||
!nsContentUtils::IsCallerChrome())
|
||||
{
|
||||
GenerateWarning("readPixels: Not allowed");
|
||||
return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,103 @@
|
|||
#include "nsITimer.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "WebGLContext.h"
|
||||
#include "mozilla/dom/WorkerPrivate.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Begin worker specific code
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// On workers we can only dispatch CancelableRunnables, so we have to wrap the
|
||||
// timer's EventTarget to use our own cancelable runnable
|
||||
|
||||
class ContextLossWorkerEventTarget final : public nsIEventTarget
|
||||
{
|
||||
public:
|
||||
explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
|
||||
: mEventTarget(aEventTarget)
|
||||
{
|
||||
MOZ_ASSERT(aEventTarget);
|
||||
}
|
||||
|
||||
NS_DECL_NSIEVENTTARGET
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
protected:
|
||||
~ContextLossWorkerEventTarget() {}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIEventTarget> mEventTarget;
|
||||
};
|
||||
|
||||
class ContextLossWorkerRunnable final : public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
|
||||
: mRunnable(aRunnable)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_NSICANCELABLERUNNABLE
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
NS_FORWARD_NSIRUNNABLE(mRunnable->)
|
||||
|
||||
protected:
|
||||
~ContextLossWorkerRunnable() {}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIRunnable> mRunnable;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
|
||||
nsISupports)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event(aEvent);
|
||||
return Dispatch(event.forget(), aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aEvent,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> eventRef(aEvent);
|
||||
nsRefPtr<ContextLossWorkerRunnable> wrappedEvent =
|
||||
new ContextLossWorkerRunnable(eventRef);
|
||||
return mEventTarget->Dispatch(wrappedEvent, aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
|
||||
{
|
||||
return mEventTarget->IsOnCurrentThread(aResult);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ContextLossWorkerRunnable, nsICancelableRunnable,
|
||||
nsIRunnable)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ContextLossWorkerRunnable::Cancel()
|
||||
{
|
||||
mRunnable = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// End worker-specific code
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
|
||||
: mWeakWebGL(webgl)
|
||||
, mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
|
||||
, mIsTimerRunning(false)
|
||||
, mShouldRunTimerAgain(false)
|
||||
, mIsDisabled(false)
|
||||
, mFeatureAdded(false)
|
||||
#ifdef DEBUG
|
||||
, mThread(NS_GetCurrentThread())
|
||||
#endif
|
||||
|
@ -90,6 +178,17 @@ WebGLContextLossHandler::RunTimer()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
dom::workers::WorkerPrivate* workerPrivate =
|
||||
dom::workers::GetCurrentThreadWorkerPrivate();
|
||||
nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
|
||||
mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
|
||||
if (!mFeatureAdded) {
|
||||
workerPrivate->AddFeature(workerPrivate->GetJSContext(), this);
|
||||
mFeatureAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
StartTimer(1000);
|
||||
|
||||
mIsTimerRunning = true;
|
||||
|
@ -104,6 +203,14 @@ WebGLContextLossHandler::DisableTimer()
|
|||
|
||||
mIsDisabled = true;
|
||||
|
||||
if (mFeatureAdded) {
|
||||
dom::workers::WorkerPrivate* workerPrivate =
|
||||
dom::workers::GetCurrentThreadWorkerPrivate();
|
||||
MOZ_RELEASE_ASSERT(workerPrivate);
|
||||
workerPrivate->RemoveFeature(workerPrivate->GetJSContext(), this);
|
||||
mFeatureAdded = false;
|
||||
}
|
||||
|
||||
// We can't just Cancel() the timer, as sometimes we end up
|
||||
// receiving a callback after calling Cancel(). This could cause us
|
||||
// to receive the callback after object destruction.
|
||||
|
@ -116,4 +223,16 @@ WebGLContextLossHandler::DisableTimer()
|
|||
mTimer->SetDelay(0);
|
||||
}
|
||||
|
||||
bool
|
||||
WebGLContextLossHandler::Notify(JSContext* aCx, dom::workers::Status aStatus)
|
||||
{
|
||||
bool isWorkerRunning = aStatus < dom::workers::Closing;
|
||||
if (!isWorkerRunning && mIsTimerRunning) {
|
||||
mIsTimerRunning = false;
|
||||
this->Release();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "WorkerFeature.h"
|
||||
|
||||
class nsIThread;
|
||||
class nsITimer;
|
||||
|
@ -17,13 +18,14 @@ class nsITimer;
|
|||
namespace mozilla {
|
||||
class WebGLContext;
|
||||
|
||||
class WebGLContextLossHandler
|
||||
class WebGLContextLossHandler : public dom::workers::WorkerFeature
|
||||
{
|
||||
WeakPtr<WebGLContext> mWeakWebGL;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
bool mIsTimerRunning;
|
||||
bool mShouldRunTimerAgain;
|
||||
bool mIsDisabled;
|
||||
bool mFeatureAdded;
|
||||
DebugOnly<nsIThread*> mThread;
|
||||
|
||||
public:
|
||||
|
@ -33,6 +35,7 @@ public:
|
|||
|
||||
void RunTimer();
|
||||
void DisableTimer();
|
||||
bool Notify(JSContext* aCx, dom::workers::Status aStatus) override;
|
||||
|
||||
protected:
|
||||
~WebGLContextLossHandler();
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
|
||||
nsISupports* data, bool)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <algorithm>
|
||||
#include "angle/ShaderLang.h"
|
||||
#include "CanvasUtils.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/CheckedInt.h"
|
||||
|
@ -1665,11 +1666,11 @@ WebGLContext::InitAndValidateGL()
|
|||
return false;
|
||||
}
|
||||
|
||||
mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
|
||||
mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
|
||||
mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-pressure", false);
|
||||
mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
|
||||
mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);
|
||||
mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
|
||||
mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
|
||||
mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
|
||||
mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
|
||||
mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
|
||||
|
||||
if (MinCapabilityMode())
|
||||
mDisableFragHighP = true;
|
||||
|
@ -1878,10 +1879,7 @@ WebGLContext::InitAndValidateGL()
|
|||
#endif
|
||||
|
||||
// Check the shader validator pref
|
||||
NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
|
||||
|
||||
mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
|
||||
mBypassShaderValidation);
|
||||
mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
|
||||
|
||||
// initialize shader translator
|
||||
if (!ShInitialize()) {
|
||||
|
@ -1937,9 +1935,6 @@ WebGLContext::InitAndValidateGL()
|
|||
mDefaultVertexArray->BindVertexArray();
|
||||
}
|
||||
|
||||
if (mLoseContextOnMemoryPressure)
|
||||
mContextObserver->RegisterMemoryPressureEvent();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
|
||||
nsISupports* data, bool)
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "WebGLShaderValidator.h"
|
||||
|
||||
#include "angle/ShaderLang.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "GLContext.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "MurmurHash3.h"
|
||||
|
@ -43,7 +44,7 @@ ChooseValidatorCompileOptions(const ShBuiltInResources& resources,
|
|||
options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
|
||||
}
|
||||
|
||||
if (Preferences::GetBool("webgl.all-angle-options", false)) {
|
||||
if (gfxPrefs::WebGLAllANGLEOptions()) {
|
||||
return options |
|
||||
SH_VALIDATE_LOOP_INDEXING |
|
||||
SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
|
||||
|
|
|
@ -28,10 +28,12 @@ EXPORTS.mozilla.dom += [
|
|||
'CanvasPath.h',
|
||||
'CanvasPattern.h',
|
||||
'CanvasRenderingContext2D.h',
|
||||
'CanvasRenderingContextHelper.h',
|
||||
'CanvasUtils.h',
|
||||
'ImageBitmap.h',
|
||||
'ImageBitmapSource.h',
|
||||
'ImageData.h',
|
||||
'OffscreenCanvas.h',
|
||||
'TextMetrics.h',
|
||||
'WebGLVertexArrayObject.h',
|
||||
]
|
||||
|
@ -40,11 +42,13 @@ EXPORTS.mozilla.dom += [
|
|||
UNIFIED_SOURCES += [
|
||||
'CanvasImageCache.cpp',
|
||||
'CanvasRenderingContext2D.cpp',
|
||||
'CanvasRenderingContextHelper.cpp',
|
||||
'CanvasUtils.cpp',
|
||||
'DocumentRendererChild.cpp',
|
||||
'DocumentRendererParent.cpp',
|
||||
'ImageBitmap.cpp',
|
||||
'ImageData.cpp',
|
||||
'OffscreenCanvas.cpp',
|
||||
]
|
||||
|
||||
# WebGL Sources
|
||||
|
@ -150,6 +154,7 @@ LOCAL_INCLUDES += [
|
|||
'/dom/base',
|
||||
'/dom/html',
|
||||
'/dom/svg',
|
||||
'/dom/workers',
|
||||
'/dom/xul',
|
||||
'/gfx/gl',
|
||||
'/image',
|
||||
|
|
|
@ -12,12 +12,13 @@
|
|||
#include "nsIDocShell.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "GraphicsFilter.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
#define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
|
||||
{ 0x3cc9e801, 0x1806, 0x4ff6, \
|
||||
{ 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
|
||||
{ 0xb84f2fed, 0x9d4b, 0x430b, \
|
||||
{ 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
|
||||
|
||||
class gfxASurface;
|
||||
class nsDisplayListBuilder;
|
||||
|
@ -80,6 +81,11 @@ public:
|
|||
return mCanvasElement;
|
||||
}
|
||||
|
||||
void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
|
||||
{
|
||||
mOffscreenCanvas = aOffscreenCanvas;
|
||||
}
|
||||
|
||||
// Dimensions of the canvas, in pixels.
|
||||
virtual int32_t GetWidth() const = 0;
|
||||
virtual int32_t GetHeight() const = 0;
|
||||
|
@ -152,6 +158,10 @@ public:
|
|||
// Given a point, return hit region ID if it exists or an empty string if it doesn't
|
||||
virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
|
||||
|
||||
virtual void OnVisibilityChange() {}
|
||||
|
||||
virtual void OnMemoryPressure() {}
|
||||
|
||||
//
|
||||
// shmem support
|
||||
//
|
||||
|
@ -164,6 +174,7 @@ public:
|
|||
|
||||
protected:
|
||||
nsRefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement;
|
||||
nsRefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;
|
||||
nsRefPtr<nsRefreshDriver> mRefreshDriver;
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ support-files =
|
|||
imagebitmap_on_worker.js
|
||||
imagebitmap_structuredclone.js
|
||||
imagebitmap_structuredclone_iframe.html
|
||||
offscreencanvas.js
|
||||
offscreencanvas_mask.svg
|
||||
offscreencanvas_neuter.js
|
||||
offscreencanvas_serviceworker_inner.html
|
||||
|
||||
[test_2d.clearRect.image.offscreen.html]
|
||||
[test_2d.clip.winding.html]
|
||||
|
@ -261,3 +265,22 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # bug 1040965
|
|||
[test_createPattern_broken.html]
|
||||
[test_setlinedash.html]
|
||||
[test_filter.html]
|
||||
[test_offscreencanvas_basic_webgl.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_dynamic_fallback.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_sharedworker.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_serviceworker.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = buildapp == 'b2g'
|
||||
[test_offscreencanvas_neuter.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_many.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = (toolkit == 'android' || toolkit == 'gonk' || toolkit == 'windows' || toolkit == 'gtk2' || toolkit == 'gtk3')
|
||||
[test_offscreencanvas_sizechange.html]
|
||||
tags = offscreencanvas
|
||||
[test_offscreencanvas_subworker.html]
|
||||
tags = offscreencanvas
|
||||
skip-if = (toolkit == 'android' || toolkit == 'gonk' || toolkit == 'windows' || toolkit == 'gtk2' || toolkit == 'gtk3')
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
/* WebWorker for test_offscreencanvas_*.html */
|
||||
var port = null;
|
||||
|
||||
function ok(expect, msg) {
|
||||
if (port) {
|
||||
port.postMessage({type: "test", result: !!expect, name: msg});
|
||||
} else {
|
||||
postMessage({type: "test", result: !!expect, name: msg});
|
||||
}
|
||||
}
|
||||
|
||||
function finish() {
|
||||
if (port) {
|
||||
port.postMessage({type: "finish"});
|
||||
} else {
|
||||
postMessage({type: "finish"});
|
||||
}
|
||||
}
|
||||
|
||||
function drawCount(count) {
|
||||
if (port) {
|
||||
port.postMessage({type: "draw", count: count});
|
||||
} else {
|
||||
postMessage({type: "draw", count: count});
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
// WebGL Drawing Functions
|
||||
//--------------------------------------------------------------------
|
||||
function createDrawFunc(canvas) {
|
||||
var gl;
|
||||
|
||||
try {
|
||||
gl = canvas.getContext("experimental-webgl");
|
||||
} catch (e) {}
|
||||
|
||||
if (!gl) {
|
||||
ok(false, "WebGL is unavailable");
|
||||
return null;
|
||||
}
|
||||
|
||||
var vertSrc = "attribute vec2 position; \
|
||||
void main(void) { \
|
||||
gl_Position = vec4(position, 0.0, 1.0); \
|
||||
}";
|
||||
|
||||
var fragSrc = "precision mediump float; \
|
||||
void main(void) { \
|
||||
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0); \
|
||||
}";
|
||||
|
||||
// Returns a valid shader, or null on errors.
|
||||
var createShader = function(src, t) {
|
||||
var shader = gl.createShader(t);
|
||||
|
||||
gl.shaderSource(shader, src);
|
||||
gl.compileShader(shader);
|
||||
|
||||
return shader;
|
||||
};
|
||||
|
||||
var createProgram = function(vsSrc, fsSrc) {
|
||||
var vs = createShader(vsSrc, gl.VERTEX_SHADER);
|
||||
var fs = createShader(fsSrc, gl.FRAGMENT_SHADER);
|
||||
|
||||
var prog = gl.createProgram();
|
||||
gl.attachShader(prog, vs);
|
||||
gl.attachShader(prog, fs);
|
||||
gl.linkProgram(prog);
|
||||
|
||||
if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
|
||||
var str = "Shader program linking failed:";
|
||||
str += "\nShader program info log:\n" + gl.getProgramInfoLog(prog);
|
||||
str += "\n\nVert shader log:\n" + gl.getShaderInfoLog(vs);
|
||||
str += "\n\nFrag shader log:\n" + gl.getShaderInfoLog(fs);
|
||||
console.log(str);
|
||||
ok(false, "Shader program linking failed");
|
||||
return null;
|
||||
}
|
||||
|
||||
return prog;
|
||||
};
|
||||
|
||||
gl.disable(gl.DEPTH_TEST);
|
||||
|
||||
var program = createProgram(vertSrc, fragSrc);
|
||||
ok(program, "Creating shader program");
|
||||
|
||||
program.positionAttr = gl.getAttribLocation(program, "position");
|
||||
ok(program.positionAttr >= 0, "position attribute should be valid");
|
||||
|
||||
var vertCoordArr = new Float32Array([
|
||||
-1, -1,
|
||||
1, -1,
|
||||
-1, 1,
|
||||
1, 1,
|
||||
]);
|
||||
var vertCoordBuff = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertCoordBuff);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, vertCoordArr, gl.STATIC_DRAW);
|
||||
|
||||
var checkGLError = function(prefix, refValue) {
|
||||
if (!refValue) {
|
||||
refValue = 0;
|
||||
}
|
||||
|
||||
var error = gl.getError();
|
||||
ok(error == refValue,
|
||||
prefix + 'gl.getError should be 0x' + refValue.toString(16) +
|
||||
', was 0x' + error.toString(16) + '.');
|
||||
};
|
||||
|
||||
var testPixel = function(x, y, refData, infoString) {
|
||||
var pixel = new Uint8Array(4);
|
||||
gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
|
||||
|
||||
var pixelMatches = pixel[0] == refData[0] &&
|
||||
pixel[1] == refData[1] &&
|
||||
pixel[2] == refData[2] &&
|
||||
pixel[3] == refData[3];
|
||||
ok(pixelMatches, infoString);
|
||||
};
|
||||
|
||||
var preDraw = function(prefix) {
|
||||
gl.clearColor(1.0, 0.0, 0.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
testPixel(0, 0, [255, 0, 0, 255], prefix + 'Should be red before drawing.');
|
||||
};
|
||||
|
||||
var postDraw = function(prefix) {
|
||||
testPixel(0, 0, [0, 255, 0, 255], prefix + 'Should be green after drawing.');
|
||||
};
|
||||
|
||||
gl.useProgram(program);
|
||||
gl.enableVertexAttribArray(program.position);
|
||||
gl.vertexAttribPointer(program.position, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
// Start drawing
|
||||
checkGLError('after setup');
|
||||
|
||||
return function(prefix) {
|
||||
if (prefix) {
|
||||
prefix = "[" + prefix + "] ";
|
||||
} else {
|
||||
prefix = "";
|
||||
}
|
||||
|
||||
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||
|
||||
preDraw(prefix);
|
||||
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
|
||||
postDraw(prefix);
|
||||
gl.commit();
|
||||
checkGLError(prefix);
|
||||
};
|
||||
}
|
||||
|
||||
/* entry point */
|
||||
function entryFunction(testStr, subtests, offscreenCanvas) {
|
||||
var test = testStr;
|
||||
var canvas = offscreenCanvas;
|
||||
|
||||
if (test != "subworker") {
|
||||
ok(canvas, "Canvas successfully transfered to worker");
|
||||
ok(canvas.getContext, "Canvas has getContext");
|
||||
|
||||
ok(canvas.width == 64, "OffscreenCanvas width should be 64");
|
||||
ok(canvas.height == 64, "OffscreenCanvas height should be 64");
|
||||
}
|
||||
|
||||
var draw;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Basic WebGL test
|
||||
//------------------------------------------------------------------------
|
||||
if (test == "webgl") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
if (count++ > 20) {
|
||||
clearInterval(iid);
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
draw("loop " +count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Test dynamic fallback
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_fallback") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
return;
|
||||
}
|
||||
|
||||
var count = 0;
|
||||
var iid = setInterval(function() {
|
||||
++count;
|
||||
draw("loop " + count);
|
||||
drawCount(count);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Canvas Size Change from Worker
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "webgl_changesize") {
|
||||
draw = createDrawFunc(canvas);
|
||||
if (!draw) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
draw("64x64");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 128;
|
||||
canvas.height = 128;
|
||||
draw("Increased to 128x128");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 32;
|
||||
canvas.width = 32;
|
||||
draw("Decreased to 32x32");
|
||||
|
||||
setTimeout(function() {
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
draw("Increased to 64x64");
|
||||
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
}, 0);
|
||||
}, 0);
|
||||
}, 0);
|
||||
}
|
||||
//------------------------------------------------------------------------
|
||||
// Using OffscreenCanvas from sub workers
|
||||
//------------------------------------------------------------------------
|
||||
else if (test == "subworker") {
|
||||
/* subworker tests take a list of tests to run on children */
|
||||
var stillRunning = 0;
|
||||
subtests.forEach(function (subtest) {
|
||||
++stillRunning;
|
||||
var subworker = new Worker('offscreencanvas.js');
|
||||
subworker.onmessage = function(evt) {
|
||||
/* report finish to parent when all children are finished */
|
||||
if (evt.data.type == "finish") {
|
||||
subworker.terminate();
|
||||
if (--stillRunning == 0) {
|
||||
ok(true, "Worker is done");
|
||||
finish();
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* relay all other messages to parent */
|
||||
postMessage(evt.data);
|
||||
};
|
||||
|
||||
var findTransferables = function(t) {
|
||||
if (t.test == "subworker") {
|
||||
var result = [];
|
||||
t.subtests.forEach(function(test) {
|
||||
result = result.concat(findTransferables(test));
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return [t.canvas];
|
||||
}
|
||||
};
|
||||
|
||||
subworker.postMessage(subtest, findTransferables(subtest));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onmessage = function(evt) {
|
||||
port = evt.ports[0];
|
||||
entryFunction(evt.data.test, evt.data.subtests, evt.data.canvas);
|
||||
};
|
||||
|
||||
onconnect = function(evt) {
|
||||
port = evt.ports[0];
|
||||
|
||||
port.addEventListener('message', function(evt) {
|
||||
entryFunction(evt.data.test, evt.data.subtests, evt.data.canvas);
|
||||
});
|
||||
|
||||
port.start();
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
|
||||
<mask id="fade_mask_both" maskUnits="objectBoundingBox" maskContentUnits="objectBoundingBox">
|
||||
<linearGradient id="fade_gradient_both" gradientUnits="objectBoundingBox" x2="0" y2="1">
|
||||
<stop stop-color="white" stop-opacity="0" offset="0"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.2"></stop>
|
||||
<stop stop-color="white" stop-opacity="1" offset="0.8"></stop>
|
||||
<stop stop-color="white" stop-opacity="0" offset="1"></stop>
|
||||
</linearGradient>
|
||||
<rect x="0" y="0" width="1" height="1" fill="url(#fade_gradient_both)"></rect>
|
||||
</mask>
|
||||
</svg>
|
После Ширина: | Высота: | Размер: 638 B |
|
@ -0,0 +1 @@
|
|||
/* empty worker for test_offscreencanvas_disable.html */
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<script>
|
||||
function ok(expect, msg) {
|
||||
parent.postMessage({type: "test", result: !!expect, name: msg}, "*");
|
||||
}
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
|
||||
var messageChannel = new MessageChannel();
|
||||
messageChannel.port1.onmessage = function(evt) {
|
||||
parent.postMessage(evt.data, "*");
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
navigator.serviceWorker.ready.then(function() {
|
||||
navigator.serviceWorker.controller.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas, messageChannel.port2]);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<canvas id="c-ref" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function testToDataURL() {
|
||||
// testing toDataURL
|
||||
// Fill c-ref with green color.
|
||||
var c = document.getElementById("c-ref");
|
||||
var ctx = c.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
ok(c.toDataURL() == htmlCanvas.toDataURL(), "toDataURL should return a 64x64 green square");
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
testToDataURL();
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
worker.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas(initWithMask) {
|
||||
var canvas = document.createElement("canvas");
|
||||
canvas.width = 64;
|
||||
canvas.height = 64;
|
||||
document.body.appendChild(canvas);
|
||||
if (initWithMask) {
|
||||
canvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getRefSnapshot(initWithMask) {
|
||||
var refCanvas = createCanvas(!initWithMask);
|
||||
var ctx = refCanvas.getContext("2d");
|
||||
ctx.rect(0, 0, 64, 64);
|
||||
ctx.fillStyle = "#00FF00";
|
||||
ctx.fill();
|
||||
var result = snapshotWindow(window);
|
||||
document.body.removeChild(refCanvas);
|
||||
return result;
|
||||
}
|
||||
|
||||
function runTest(initWithMask) {
|
||||
var htmlCanvas = createCanvas(initWithMask);
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "draw") {
|
||||
if (msg.count === 10) {
|
||||
// Change the fallback state dynamically when drawing count reaches 10.
|
||||
if (initWithMask) {
|
||||
htmlCanvas.style.mask = "";
|
||||
} else {
|
||||
htmlCanvas.style.mask = "url('offscreencanvas_mask.svg#fade_mask_both')";
|
||||
}
|
||||
} else if (msg.count === 20) {
|
||||
var snapshotFallback = snapshotWindow(window);
|
||||
worker.terminate();
|
||||
document.body.removeChild(htmlCanvas);
|
||||
|
||||
var results = compareSnapshots(snapshotFallback, getRefSnapshot(initWithMask), true);
|
||||
ok(results[0], "after dynamic fallback, screenshots should be the same");
|
||||
|
||||
if (initWithMask) {
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
runTest(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
|
||||
worker.postMessage({test: 'webgl_fallback', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest.bind(false));
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
This test needs several workers run offscreen canvas simultaneously.
|
||||
So we choose 8 workers, 4 of them run basic webgl drawing test and
|
||||
others run size changing test.
|
||||
-->
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas() {
|
||||
var htmlCanvas = document.createElement('canvas');
|
||||
htmlCanvas.width = 64;
|
||||
htmlCanvas.height = 64;
|
||||
document.body.appendChild(htmlCanvas);
|
||||
return htmlCanvas;
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
var stillRunning = 0;
|
||||
|
||||
var startWorker = function(canvas, test) {
|
||||
stillRunning++;
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
if (--stillRunning == 0)
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = canvas.transferControlToOffscreen();
|
||||
worker.postMessage({test: test, canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
/* create 4 workers that do the regular drawing test and 4 workers
|
||||
that do the size change test */
|
||||
for (var i = 0; i < 4; i++) {
|
||||
startWorker(createCanvas(), 'webgl');
|
||||
}
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
startWorker(createCanvas(), 'webgl_changesize');
|
||||
}
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true]
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,78 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>OffscreenCanvas: Test neutering</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas_neuter.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
/* check html canvas is neuterd */
|
||||
is(htmlCanvas.width, 64, "HTML canvas has correct width");
|
||||
SimpleTest.doesThrow(
|
||||
function() { htmlCanvas.width = 128; },
|
||||
"Can't change html canvas' width after transferControlToOffscreen");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { htmlCanvas.height = 128; },
|
||||
"Can't change html canvas' height after transferControlToOffscreen");
|
||||
|
||||
ok(!htmlCanvas.getContext("2d"), "Can't getContext after transferControlToOffscreen");
|
||||
ok(!htmlCanvas.getContext("webgl"), "Can't getContext after transferControlToOffscreen");
|
||||
ok(!htmlCanvas.getContext("webgl2"), "Can't getContext after transferControlToOffscreen");
|
||||
|
||||
worker.postMessage(offscreenCanvas, [offscreenCanvas]);
|
||||
|
||||
/* check parent offscreencanvas is neutered after being transfered */
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.width = 128; },
|
||||
"Can't change transfered worker canvas width");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.height = 128; },
|
||||
"Can't change transfered worker canvas height");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("2d") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("webgl") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
SimpleTest.doesThrow(
|
||||
function() { offscreenCanvas.getContext("webgl2") },
|
||||
"Can't getContext on transfered worker canvas");
|
||||
|
||||
// Transfer a neutered offscreencanvas should be ok.
|
||||
worker.postMessage(offscreenCanvas, [offscreenCanvas]);
|
||||
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
window.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
navigator.serviceWorker.register('offscreencanvas.js', { scope: "."})
|
||||
// Wait until the service worker is active.
|
||||
.then(navigator.serviceWorker.ready)
|
||||
// ...and then show the interface for the commands once it's ready.
|
||||
.then(function() {
|
||||
iframe = document.createElement("iframe");
|
||||
iframe.setAttribute('src', "offscreencanvas_serviceworker_inner.html");
|
||||
document.body.appendChild(iframe);
|
||||
})
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
["dom.serviceWorkers.exemptFromPerDomainMax", true],
|
||||
["dom.serviceWorkers.interception.enabled", true],
|
||||
["dom.serviceWorkers.enabled", true],
|
||||
["dom.serviceWorkers.testing.enabled", true]
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new SharedWorker("offscreencanvas.js");
|
||||
|
||||
ok(htmlCanvas, "Should have HTML canvas element");
|
||||
ok(worker, "Web worker successfully created");
|
||||
|
||||
ok(htmlCanvas.transferControlToOffscreen, "HTMLCanvasElement has transferControlToOffscreen function");
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
ok(offscreenCanvas, "Expected transferControlToOffscreen to succeed");
|
||||
|
||||
worker.port.start();
|
||||
|
||||
// We don't support transferring OffscreenCanvas via shared worker.
|
||||
SimpleTest.doesThrow(
|
||||
function() {
|
||||
worker.port.postMessage({test: 'webgl', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
},
|
||||
"OffscreenCanvas cannot transfer to shared worker"
|
||||
);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,41 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebGL in OffscreenCanvas</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="c" width="64" height="64"></canvas>
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function runTest() {
|
||||
|
||||
var htmlCanvas = document.getElementById("c");
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var offscreenCanvas = htmlCanvas.transferControlToOffscreen();
|
||||
worker.postMessage({test: 'webgl_changesize', canvas: offscreenCanvas}, [offscreenCanvas]);
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,90 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>OffscreenCanvas: Test subworkers</title>
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
|
||||
</head>
|
||||
<body>
|
||||
<!--
|
||||
We want to test offscreen canvas works well when it running on worker
|
||||
and nested worker simultaneously. So we create 10 canvas and dispatch
|
||||
it to different workers and sub-workers.
|
||||
-->
|
||||
<script>
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function createCanvas() {
|
||||
var htmlCanvas = document.createElement('canvas');
|
||||
htmlCanvas.width = 64;
|
||||
htmlCanvas.height = 64;
|
||||
document.body.appendChild(htmlCanvas);
|
||||
return htmlCanvas.transferControlToOffscreen();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
|
||||
var worker = new Worker("offscreencanvas.js");
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
var msg = evt.data || {};
|
||||
if (msg.type == "test") {
|
||||
ok(msg.result, msg.name);
|
||||
}
|
||||
if (msg.type == "finish") {
|
||||
worker.terminate();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
var findTransferables = function(t) {
|
||||
if (t.test == "subworker") {
|
||||
var result = [];
|
||||
t.subtests.forEach(function(test) {
|
||||
result = result.concat(findTransferables(test));
|
||||
});
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return [t.canvas];
|
||||
}
|
||||
};
|
||||
|
||||
var testData =
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'webgl', canvas: createCanvas()}
|
||||
]},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl', canvas: createCanvas()},
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'webgl', canvas: createCanvas()}
|
||||
]},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()},
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'subworker', subtests: [
|
||||
{test: 'webgl_changesize', canvas: createCanvas()}
|
||||
]}
|
||||
]}
|
||||
]},
|
||||
]}
|
||||
]};
|
||||
|
||||
worker.postMessage(testData, findTransferables(testData));
|
||||
}
|
||||
|
||||
SpecialPowers.pushPrefEnv({'set': [
|
||||
['gfx.offscreencanvas.enabled', true],
|
||||
['webgl.force-enabled', true],
|
||||
]}, runTest);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -19,8 +19,10 @@
|
|||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/HTMLCanvasElementBinding.h"
|
||||
#include "mozilla/dom/MouseEvent.h"
|
||||
#include "mozilla/dom/OffscreenCanvas.h"
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/layers/AsyncCanvasRenderer.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -239,18 +241,135 @@ HTMLCanvasPrintState::NotifyDone()
|
|||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
|
||||
: mElement(aElement)
|
||||
{
|
||||
RegisterVisibilityChangeEvent();
|
||||
RegisterMemoryPressureEvent();
|
||||
}
|
||||
|
||||
HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::Destroy()
|
||||
{
|
||||
UnregisterMemoryPressureEvent();
|
||||
UnregisterVisibilityChangeEvent();
|
||||
mElement = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* document = mElement->OwnerDoc();
|
||||
document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true, false);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* document = mElement->OwnerDoc();
|
||||
document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
|
||||
this, true);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
MOZ_ASSERT(observerService);
|
||||
|
||||
if (observerService)
|
||||
observerService->AddObserver(this, "memory-pressure", false);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
|
||||
{
|
||||
if (!mElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
mozilla::services::GetObserverService();
|
||||
|
||||
// Do not assert on observerService here. This might be triggered by
|
||||
// the cycle collector at a late enough time, that XPCOM services are
|
||||
// no longer available. See bug 1029504.
|
||||
if (observerService)
|
||||
observerService->RemoveObserver(this, "memory-pressure");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
|
||||
{
|
||||
if (!mElement || strcmp(aTopic, "memory-pressure")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->OnMemoryPressure();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
HTMLCanvasElementObserver::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
if (!mElement || !type.EqualsLiteral("visibilitychange")) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mElement->OnVisibilityChange();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
|
||||
: nsGenericHTMLElement(aNodeInfo),
|
||||
mResetLayer(true) ,
|
||||
mWriteOnly(false)
|
||||
{
|
||||
}
|
||||
|
||||
HTMLCanvasElement::~HTMLCanvasElement()
|
||||
{
|
||||
if (mContextObserver) {
|
||||
mContextObserver->Destroy();
|
||||
mContextObserver = nullptr;
|
||||
}
|
||||
|
||||
ResetPrintCallback();
|
||||
if (mRequestedFrameRefreshObserver) {
|
||||
mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
|
||||
}
|
||||
|
||||
if (mAsyncCanvasRenderer) {
|
||||
mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLCanvasElement, nsGenericHTMLElement,
|
||||
|
@ -272,6 +391,22 @@ HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
|||
return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
|
||||
{
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret =
|
||||
CanvasRenderingContextHelper::CreateContext(aContextType);
|
||||
|
||||
// Add Observer for webgl canvas.
|
||||
if (aContextType == CanvasContextType::WebGL1 ||
|
||||
aContextType == CanvasContextType::WebGL2) {
|
||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||
}
|
||||
|
||||
ret->SetCanvasElement(this);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
HTMLCanvasElement::GetWidthHeight()
|
||||
{
|
||||
|
@ -556,51 +691,10 @@ HTMLCanvasElement::ExtractData(nsAString& aType,
|
|||
aOptions,
|
||||
GetSize(),
|
||||
mCurrentContext,
|
||||
mAsyncCanvasRenderer,
|
||||
aStream);
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions)
|
||||
{
|
||||
// Quality parameter is only valid for the image/jpeg MIME type
|
||||
if (aType.EqualsLiteral("image/jpeg")) {
|
||||
if (aEncoderOptions.isNumber()) {
|
||||
double quality = aEncoderOptions.toNumber();
|
||||
// Quality must be between 0.0 and 1.0, inclusive
|
||||
if (quality >= 0.0 && quality <= 1.0) {
|
||||
aParams.AppendLiteral("quality=");
|
||||
aParams.AppendInt(NS_lround(quality * 100.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we haven't parsed the aParams check for proprietary options.
|
||||
// The proprietary option -moz-parse-options will take a image lib encoder
|
||||
// parse options string as is and pass it to the encoder.
|
||||
*usingCustomParseOptions = false;
|
||||
if (aParams.Length() == 0 && aEncoderOptions.isString()) {
|
||||
NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
|
||||
nsAutoJSString paramString;
|
||||
if (!paramString.init(aCx, aEncoderOptions.toString())) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (StringBeginsWith(paramString, mozParseOptions)) {
|
||||
nsDependentSubstring parseOptions = Substring(paramString,
|
||||
mozParseOptions.Length(),
|
||||
paramString.Length() -
|
||||
mozParseOptions.Length());
|
||||
aParams.Append(parseOptions);
|
||||
*usingCustomParseOptions = true;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
|
||||
const nsAString& aMimeType,
|
||||
|
@ -659,84 +753,38 @@ HTMLCanvasElement::ToBlob(JSContext* aCx,
|
|||
return;
|
||||
}
|
||||
|
||||
nsAutoString type;
|
||||
nsContentUtils::ASCIIToLower(aType, type);
|
||||
|
||||
nsAutoString params;
|
||||
bool usingCustomParseOptions;
|
||||
aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
|
||||
if (aRv.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
// We disallow canvases of width or height zero, and set them to 1, so
|
||||
// we will have a discrepancy with the sizes of the canvas and the context.
|
||||
// That discrepancy is OK, the rest are not.
|
||||
nsIntSize elementSize = GetWidthHeight();
|
||||
if ((elementSize.width != mCurrentContext->GetWidth() &&
|
||||
(elementSize.width != 0 || mCurrentContext->GetWidth() != 1)) ||
|
||||
(elementSize.height != mCurrentContext->GetHeight() &&
|
||||
(elementSize.height != 0 || mCurrentContext->GetHeight() != 1))) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* imageBuffer = nullptr;
|
||||
int32_t format = 0;
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->GetImageBuffer(&imageBuffer, &format);
|
||||
}
|
||||
|
||||
// Encoder callback when encoding is complete.
|
||||
class EncodeCallback : public EncodeCompleteCallback
|
||||
{
|
||||
public:
|
||||
EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
|
||||
: mGlobal(aGlobal)
|
||||
, mFileCallback(aCallback) {}
|
||||
|
||||
// This is called on main thread.
|
||||
nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
|
||||
{
|
||||
nsRefPtr<Blob> blob = aBlob;
|
||||
|
||||
ErrorResult rv;
|
||||
uint64_t size = blob->GetSize(rv);
|
||||
if (rv.Failed()) {
|
||||
rv.SuppressException();
|
||||
} else {
|
||||
AutoJSAPI jsapi;
|
||||
if (jsapi.Init(mGlobal)) {
|
||||
JS_updateMallocCounter(jsapi.cx(), size);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
|
||||
|
||||
mFileCallback->Call(*newBlob, rv);
|
||||
|
||||
mGlobal = nullptr;
|
||||
mFileCallback = nullptr;
|
||||
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsRefPtr<FileCallback> mFileCallback;
|
||||
};
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
|
||||
MOZ_ASSERT(global);
|
||||
nsRefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, &aCallback);
|
||||
aRv = ImageEncoder::ExtractDataAsync(type,
|
||||
params,
|
||||
usingCustomParseOptions,
|
||||
imageBuffer,
|
||||
format,
|
||||
GetSize(),
|
||||
callback);
|
||||
|
||||
CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
|
||||
aParams, aRv);
|
||||
|
||||
}
|
||||
|
||||
OffscreenCanvas*
|
||||
HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
|
||||
{
|
||||
if (mCurrentContext) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mOffscreenCanvas) {
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
nsRefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
|
||||
renderer->SetWidth(sz.width);
|
||||
renderer->SetHeight(sz.height);
|
||||
|
||||
mOffscreenCanvas = new OffscreenCanvas(sz.width,
|
||||
sz.height,
|
||||
GetCompositorBackendType(),
|
||||
renderer);
|
||||
mContextObserver = new HTMLCanvasElementObserver(this);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
return mOffscreenCanvas;
|
||||
}
|
||||
|
||||
already_AddRefed<File>
|
||||
|
@ -807,76 +855,6 @@ HTMLCanvasElement::MozGetAsBlobImpl(const nsAString& aName,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
|
||||
{
|
||||
if (str.EqualsLiteral("2d")) {
|
||||
*out_type = CanvasContextType::Canvas2D;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (str.EqualsLiteral("experimental-webgl")) {
|
||||
*out_type = CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBGL_CONFORMANT
|
||||
if (str.EqualsLiteral("webgl")) {
|
||||
/* WebGL 1.0, $2.1 "Context Creation":
|
||||
* If the user agent supports both the webgl and experimental-webgl
|
||||
* canvas context types, they shall be treated as aliases.
|
||||
*/
|
||||
*out_type = CanvasContextType::WebGL1;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (WebGL2Context::IsSupported()) {
|
||||
if (str.EqualsLiteral("webgl2")) {
|
||||
*out_type = CanvasContextType::WebGL2;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContextForCanvas(CanvasContextType contextType, HTMLCanvasElement* canvas)
|
||||
{
|
||||
MOZ_ASSERT(contextType != CanvasContextType::NoContext);
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> ret;
|
||||
|
||||
switch (contextType) {
|
||||
case CanvasContextType::NoContext:
|
||||
break;
|
||||
case CanvasContextType::Canvas2D:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
|
||||
ret = new CanvasRenderingContext2D();
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL1:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL1Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
break;
|
||||
|
||||
case CanvasContextType::WebGL2:
|
||||
Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
|
||||
|
||||
ret = WebGL2Context::Create();
|
||||
if (!ret)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
MOZ_ASSERT(ret);
|
||||
|
||||
ret->SetCanvasElement(canvas);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::GetContext(const nsAString& aContextId,
|
||||
nsISupports** aContext)
|
||||
|
@ -890,45 +868,14 @@ already_AddRefed<nsISupports>
|
|||
HTMLCanvasElement::GetContext(JSContext* aCx,
|
||||
const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& rv)
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
CanvasContextType contextType;
|
||||
if (!GetCanvasContextType(aContextId, &contextType))
|
||||
if (mOffscreenCanvas) {
|
||||
return nullptr;
|
||||
|
||||
if (!mCurrentContext) {
|
||||
// This canvas doesn't have a context yet.
|
||||
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContextForCanvas(contextType, this);
|
||||
if (!context)
|
||||
return nullptr;
|
||||
|
||||
// Ensure that the context participates in CC. Note that returning a
|
||||
// CC participant from QI doesn't addref.
|
||||
nsXPCOMCycleCollectionParticipant* cp = nullptr;
|
||||
CallQueryInterface(context, &cp);
|
||||
if (!cp) {
|
||||
rv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mCurrentContext = context.forget();
|
||||
mCurrentContextType = contextType;
|
||||
|
||||
rv = UpdateContext(aCx, aContextOptions);
|
||||
if (rv.Failed()) {
|
||||
rv = NS_OK; // See bug 645792
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// We already have a context of some type.
|
||||
if (contextType != mCurrentContextType)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
|
||||
return context.forget();
|
||||
return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
|
||||
aContextOptions, aRv);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -950,7 +897,7 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
|
|||
// This canvas doesn't have a context yet.
|
||||
|
||||
nsRefPtr<nsICanvasRenderingContextInternal> context;
|
||||
context = CreateContextForCanvas(contextType, this);
|
||||
context = CreateContext(contextType);
|
||||
if (!context) {
|
||||
*aContext = nullptr;
|
||||
return NS_OK;
|
||||
|
@ -972,36 +919,6 @@ HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return NS_OK;
|
||||
|
||||
nsIntSize sz = GetWidthHeight();
|
||||
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
|
||||
|
||||
nsresult rv = currentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = currentContext->SetDimensions(sz.width, sz.height);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCurrentContext = nullptr;
|
||||
return rv;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsIntSize
|
||||
HTMLCanvasElement::GetSize()
|
||||
|
@ -1105,6 +1022,12 @@ HTMLCanvasElement::GetIsOpaque()
|
|||
return mCurrentContext->GetIsOpaque();
|
||||
}
|
||||
|
||||
return GetOpaqueAttr();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLCanvasElement::GetOpaqueAttr()
|
||||
{
|
||||
return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
|
||||
}
|
||||
|
||||
|
@ -1113,16 +1036,57 @@ HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
|
|||
CanvasLayer *aOldLayer,
|
||||
LayerManager *aManager)
|
||||
{
|
||||
if (!mCurrentContext)
|
||||
return nullptr;
|
||||
// The address of sOffscreenCanvasLayerUserDataDummy is used as the user
|
||||
// data key for retained LayerManagers managed by FrameLayerBuilder.
|
||||
// We don't much care about what value in it, so just assign a dummy
|
||||
// value for it.
|
||||
static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
|
||||
|
||||
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
|
||||
if (mCurrentContext) {
|
||||
return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
if (!mResetLayer &&
|
||||
aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
|
||||
nsRefPtr<CanvasLayer> ret = aOldLayer;
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
|
||||
if (!layer) {
|
||||
NS_WARNING("CreateCanvasLayer failed!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LayerUserData* userData = nullptr;
|
||||
layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
|
||||
|
||||
CanvasLayer::Data data;
|
||||
data.mRenderer = GetAsyncCanvasRenderer();
|
||||
data.mSize = GetWidthHeight();
|
||||
layer->Initialize(data);
|
||||
|
||||
layer->Updated();
|
||||
return layer.forget();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager *aManager)
|
||||
HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
|
||||
{
|
||||
return !mCurrentContext || mCurrentContext->ShouldForceInactiveLayer(aManager);
|
||||
if (mCurrentContext) {
|
||||
return mCurrentContext->ShouldForceInactiveLayer(aManager);
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
// TODO: We should handle offscreen canvas case.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1237,5 +1201,155 @@ HTMLCanvasElement::GetSurfaceSnapshot(bool* aPremultAlpha)
|
|||
return mCurrentContext->GetSurfaceSnapshot(aPremultAlpha);
|
||||
}
|
||||
|
||||
AsyncCanvasRenderer*
|
||||
HTMLCanvasElement::GetAsyncCanvasRenderer()
|
||||
{
|
||||
if (!mAsyncCanvasRenderer) {
|
||||
mAsyncCanvasRenderer = new AsyncCanvasRenderer();
|
||||
mAsyncCanvasRenderer->mHTMLCanvasElement = this;
|
||||
}
|
||||
|
||||
return mAsyncCanvasRenderer;
|
||||
}
|
||||
|
||||
layers::LayersBackend
|
||||
HTMLCanvasElement::GetCompositorBackendType() const
|
||||
{
|
||||
nsIWidget* docWidget = nsContentUtils::WidgetForDocument(OwnerDoc());
|
||||
if (docWidget) {
|
||||
layers::LayerManager* layerManager = docWidget->GetLayerManager();
|
||||
return layerManager->GetCompositorBackendType();
|
||||
}
|
||||
|
||||
return LayersBackend::LAYERS_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElement::OnVisibilityChange()
|
||||
{
|
||||
if (OwnerDoc()->Hidden()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mOffscreenCanvas) {
|
||||
class Runnable final : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer && mRenderer->mContext) {
|
||||
mRenderer->mContext->OnVisibilityChange();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->OnVisibilityChange();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
HTMLCanvasElement::OnMemoryPressure()
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
class Runnable final : public nsCancelableRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer && mRenderer->mContext) {
|
||||
mRenderer->mContext->OnMemoryPressure();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
|
||||
nsCOMPtr<nsIThread> activeThread = mAsyncCanvasRenderer->GetActiveThread();
|
||||
if (activeThread) {
|
||||
activeThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCurrentContext) {
|
||||
mCurrentContext->OnMemoryPressure();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
|
||||
{
|
||||
HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (element->GetWidthHeight() == aRenderer->GetSize()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gfx::IntSize asyncCanvasSize = aRenderer->GetSize();
|
||||
|
||||
ErrorResult rv;
|
||||
element->SetUnsignedIntAttr(nsGkAtoms::width, asyncCanvasSize.width, rv);
|
||||
if (rv.Failed()) {
|
||||
NS_WARNING("Failed to set width attribute to a canvas element asynchronously.");
|
||||
}
|
||||
|
||||
element->SetUnsignedIntAttr(nsGkAtoms::height, asyncCanvasSize.height, rv);
|
||||
if (rv.Failed()) {
|
||||
NS_WARNING("Failed to set height attribute to a canvas element asynchronously.");
|
||||
}
|
||||
|
||||
element->mResetLayer = true;
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
|
||||
{
|
||||
HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
element->InvalidateCanvasContent(nullptr);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -8,20 +8,27 @@
|
|||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIDOMHTMLCanvasElement.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsError.h"
|
||||
|
||||
#include "mozilla/dom/CanvasRenderingContextHelper.h"
|
||||
#include "mozilla/gfx/Rect.h"
|
||||
#include "mozilla/layers/LayersTypes.h"
|
||||
|
||||
class nsICanvasRenderingContextInternal;
|
||||
class nsITimerCallback;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class WebGLContext;
|
||||
|
||||
namespace layers {
|
||||
class AsyncCanvasRenderer;
|
||||
class CanvasLayer;
|
||||
class Image;
|
||||
class LayerManager;
|
||||
|
@ -35,14 +42,33 @@ class CanvasCaptureMediaStream;
|
|||
class File;
|
||||
class FileCallback;
|
||||
class HTMLCanvasPrintState;
|
||||
class OffscreenCanvas;
|
||||
class PrintCallback;
|
||||
class RequestedFrameRefreshObserver;
|
||||
|
||||
enum class CanvasContextType : uint8_t {
|
||||
NoContext,
|
||||
Canvas2D,
|
||||
WebGL1,
|
||||
WebGL2
|
||||
// Listen visibilitychange and memory-pressure event and inform
|
||||
// context when event is fired.
|
||||
class HTMLCanvasElementObserver final : public nsIObserver
|
||||
, public nsIDOMEventListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement);
|
||||
void Destroy();
|
||||
|
||||
void RegisterVisibilityChangeEvent();
|
||||
void UnregisterVisibilityChangeEvent();
|
||||
|
||||
void RegisterMemoryPressureEvent();
|
||||
void UnregisterMemoryPressureEvent();
|
||||
|
||||
private:
|
||||
~HTMLCanvasElementObserver();
|
||||
|
||||
HTMLCanvasElement* mElement;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -84,13 +110,15 @@ protected:
|
|||
};
|
||||
|
||||
class HTMLCanvasElement final : public nsGenericHTMLElement,
|
||||
public nsIDOMHTMLCanvasElement
|
||||
public nsIDOMHTMLCanvasElement,
|
||||
public CanvasRenderingContextHelper
|
||||
{
|
||||
enum {
|
||||
DEFAULT_CANVAS_WIDTH = 300,
|
||||
DEFAULT_CANVAS_HEIGHT = 150
|
||||
};
|
||||
|
||||
typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
|
||||
typedef layers::CanvasLayer CanvasLayer;
|
||||
typedef layers::LayerManager LayerManager;
|
||||
|
||||
|
@ -116,6 +144,11 @@ public:
|
|||
}
|
||||
void SetHeight(uint32_t aHeight, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUnsignedIntAttr(nsGkAtoms::height, aHeight, aRv);
|
||||
}
|
||||
uint32_t Width()
|
||||
|
@ -124,30 +157,45 @@ public:
|
|||
}
|
||||
void SetWidth(uint32_t aWidth, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUnsignedIntAttr(nsGkAtoms::width, aWidth, aRv);
|
||||
}
|
||||
already_AddRefed<nsISupports>
|
||||
|
||||
virtual already_AddRefed<nsISupports>
|
||||
GetContext(JSContext* aCx, const nsAString& aContextId,
|
||||
JS::Handle<JS::Value> aContextOptions,
|
||||
ErrorResult& aRv);
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
void ToDataURL(JSContext* aCx, const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
nsAString& aDataURL, ErrorResult& aRv)
|
||||
{
|
||||
aRv = ToDataURL(aType, aParams, aCx, aDataURL);
|
||||
}
|
||||
|
||||
void ToBlob(JSContext* aCx,
|
||||
FileCallback& aCallback,
|
||||
const nsAString& aType,
|
||||
JS::Handle<JS::Value> aParams,
|
||||
ErrorResult& aRv);
|
||||
|
||||
OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
|
||||
|
||||
bool MozOpaque() const
|
||||
{
|
||||
return GetBoolAttr(nsGkAtoms::moz_opaque);
|
||||
}
|
||||
void SetMozOpaque(bool aValue, ErrorResult& aRv)
|
||||
{
|
||||
if (mOffscreenCanvas) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
|
||||
}
|
||||
already_AddRefed<File> MozGetAsFile(const nsAString& aName,
|
||||
|
@ -204,6 +252,7 @@ public:
|
|||
* across its entire area.
|
||||
*/
|
||||
bool GetIsOpaque();
|
||||
virtual bool GetOpaqueAttr() override;
|
||||
|
||||
virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
|
||||
|
||||
|
@ -282,19 +331,25 @@ public:
|
|||
|
||||
nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
|
||||
|
||||
layers::LayersBackend GetCompositorBackendType() const;
|
||||
|
||||
void OnVisibilityChange();
|
||||
|
||||
void OnMemoryPressure();
|
||||
|
||||
static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
static void InvalidateFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
|
||||
|
||||
protected:
|
||||
virtual ~HTMLCanvasElement();
|
||||
|
||||
virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsIntSize GetWidthHeight();
|
||||
virtual nsIntSize GetWidthHeight() override;
|
||||
|
||||
virtual already_AddRefed<nsICanvasRenderingContextInternal>
|
||||
CreateContext(CanvasContextType aContextType) override;
|
||||
|
||||
nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
|
||||
nsresult ParseParams(JSContext* aCx,
|
||||
const nsAString& aType,
|
||||
const JS::Value& aEncoderOptions,
|
||||
nsAString& aParams,
|
||||
bool* usingCustomParseOptions);
|
||||
nsresult ExtractData(nsAString& aType,
|
||||
const nsAString& aOptions,
|
||||
nsIInputStream** aStream);
|
||||
|
@ -307,13 +362,17 @@ protected:
|
|||
nsISupports** aResult);
|
||||
void CallPrintCallback();
|
||||
|
||||
CanvasContextType mCurrentContextType;
|
||||
AsyncCanvasRenderer* GetAsyncCanvasRenderer();
|
||||
|
||||
bool mResetLayer;
|
||||
nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
|
||||
nsRefPtr<PrintCallback> mPrintCallback;
|
||||
nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
|
||||
nsRefPtr<HTMLCanvasPrintState> mPrintState;
|
||||
nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners;
|
||||
nsRefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver;
|
||||
nsRefPtr<AsyncCanvasRenderer> mAsyncCanvasRenderer;
|
||||
nsRefPtr<OffscreenCanvas> mOffscreenCanvas;
|
||||
nsRefPtr<HTMLCanvasElementObserver> mContextObserver;
|
||||
|
||||
public:
|
||||
// Record whether this canvas should be write-only or not.
|
||||
|
|
|
@ -2014,7 +2014,7 @@ HTMLMediaElement::RemoveMediaElementFromURITable()
|
|||
}
|
||||
entry->mElements.RemoveElement(this);
|
||||
if (entry->mElements.IsEmpty()) {
|
||||
gElementTable->RemoveEntry(mLoadingSrc);
|
||||
gElementTable->RemoveEntry(entry);
|
||||
if (gElementTable->Count() == 0) {
|
||||
delete gElementTable;
|
||||
gElementTable = nullptr;
|
||||
|
|
|
@ -595,9 +595,7 @@ ProcessPriorityManagerImpl::ObserveContentParentDestroyed(nsISupports* aSubject)
|
|||
|
||||
mParticularManagers.Remove(childID);
|
||||
|
||||
if (mHighPriorityChildIDs.Contains(childID)) {
|
||||
mHighPriorityChildIDs.RemoveEntry(childID);
|
||||
}
|
||||
mHighPriorityChildIDs.RemoveEntry(childID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -454,7 +454,7 @@ void MediaCacheStream::BlockList::RemoveBlock(int32_t aBlock)
|
|||
mEntries.GetEntry(entry->mNextBlock)->mPrevBlock = entry->mPrevBlock;
|
||||
mEntries.GetEntry(entry->mPrevBlock)->mNextBlock = entry->mNextBlock;
|
||||
}
|
||||
mEntries.RemoveEntry(aBlock);
|
||||
mEntries.RemoveEntry(entry);
|
||||
--mCount;
|
||||
}
|
||||
|
||||
|
|
|
@ -567,7 +567,7 @@ nsresult ChannelMediaResource::OpenChannel(nsIStreamListener** aStreamListener)
|
|||
|
||||
rv = mChannel->AsyncOpen2(mListener);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
// Tell the media element that we are fetching data from a channel.
|
||||
MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
|
||||
NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/TaskQueue.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/Function.h"
|
||||
|
||||
#include "MediaResource.h"
|
||||
#include "TimeUnits.h"
|
||||
|
@ -21,6 +22,8 @@
|
|||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIConsoleService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -466,4 +469,78 @@ LogToBrowserConsole(const nsAString& aMsg)
|
|||
console->LogStringMessage(msg.get());
|
||||
}
|
||||
|
||||
bool
|
||||
ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs)
|
||||
{
|
||||
aOutCodecs.Clear();
|
||||
bool expectMoreTokens = false;
|
||||
nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& token = tokenizer.nextToken();
|
||||
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
|
||||
aOutCodecs.AppendElement(token);
|
||||
}
|
||||
if (expectMoreTokens) {
|
||||
// Last codec name was empty
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckContentType(const nsAString& aContentType,
|
||||
mozilla::Function<bool(const nsAString&)> aSubtypeFilter,
|
||||
mozilla::Function<bool(const nsAString&)> aCodecFilter)
|
||||
{
|
||||
nsContentTypeParser parser(aContentType);
|
||||
nsAutoString mimeType;
|
||||
nsresult rv = parser.GetType(mimeType);
|
||||
if (NS_FAILED(rv) || !aSubtypeFilter(mimeType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsString codecsStr;
|
||||
parser.GetParameter("codecs", codecsStr);
|
||||
nsTArray<nsString> codecs;
|
||||
if (!ParseCodecsString(codecsStr, codecs)) {
|
||||
return false;
|
||||
}
|
||||
for (const nsString& codec : codecs) {
|
||||
if (!aCodecFilter(codec)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IsH264ContentType(const nsAString& aContentType)
|
||||
{
|
||||
return CheckContentType(aContentType,
|
||||
[](const nsAString& type) {
|
||||
return type.EqualsLiteral("video/mp4");
|
||||
},
|
||||
[](const nsAString& codec) {
|
||||
int16_t profile = 0;
|
||||
int16_t level = 0;
|
||||
return ExtractH264CodecDetails(codec, profile, level);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool
|
||||
IsAACContentType(const nsAString& aContentType)
|
||||
{
|
||||
return CheckContentType(aContentType,
|
||||
[](const nsAString& type) {
|
||||
return type.EqualsLiteral("audio/mp4") ||
|
||||
type.EqualsLiteral("audio/x-m4a");
|
||||
},
|
||||
[](const nsAString& codec) {
|
||||
return codec.EqualsLiteral("mp4a.40.2") || // MPEG4 AAC-LC
|
||||
codec.EqualsLiteral("mp4a.40.5") || // MPEG4 HE-AAC
|
||||
codec.EqualsLiteral("mp4a.67"); // MPEG2 AAC-LC
|
||||
});
|
||||
}
|
||||
|
||||
} // end namespace mozilla
|
||||
|
|
|
@ -329,6 +329,15 @@ private:
|
|||
void
|
||||
LogToBrowserConsole(const nsAString& aMsg);
|
||||
|
||||
bool
|
||||
ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
|
||||
|
||||
bool
|
||||
IsH264ContentType(const nsAString& aContentType);
|
||||
|
||||
bool
|
||||
IsAACContentType(const nsAString& aContentType);
|
||||
|
||||
} // end namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "gmp-audio-decode.h"
|
||||
#include "gmp-video-decode.h"
|
||||
|
||||
#if defined(XP_WIN) || defined(XP_MACOSX)
|
||||
#define PRIMETIME_EME_SUPPORTED 1
|
||||
|
@ -297,52 +299,107 @@ MediaKeySystemAccess::GetKeySystemStatus(const nsAString& aKeySystem,
|
|||
}
|
||||
|
||||
static bool
|
||||
IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
|
||||
const nsAString& aKeySystem,
|
||||
const nsAString& aContentType)
|
||||
GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
|
||||
const nsAString& aKeySystem)
|
||||
{
|
||||
#ifdef MOZ_FMP4
|
||||
nsContentTypeParser parser(aContentType);
|
||||
nsAutoString mimeType;
|
||||
nsresult rv = parser.GetType(mimeType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
MOZ_ASSERT(HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
|
||||
return HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
|
||||
NS_LITERAL_CSTRING("aac"));
|
||||
}
|
||||
|
||||
if (!mimeType.EqualsLiteral("audio/mp4") &&
|
||||
!mimeType.EqualsLiteral("audio/x-m4a") &&
|
||||
!mimeType.EqualsLiteral("video/mp4")) {
|
||||
return false;
|
||||
}
|
||||
static bool
|
||||
GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
|
||||
const nsAString& aKeySystem)
|
||||
{
|
||||
MOZ_ASSERT(HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
|
||||
return HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
|
||||
NS_LITERAL_CSTRING("aac"));
|
||||
}
|
||||
|
||||
nsAutoString codecs;
|
||||
parser.GetParameter("codecs", codecs);
|
||||
|
||||
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
|
||||
bool hasAAC = false;
|
||||
bool hasH264 = false;
|
||||
bool hasMP3 = false;
|
||||
if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
|
||||
codecs,
|
||||
hasAAC,
|
||||
hasH264,
|
||||
hasMP3) ||
|
||||
hasMP3) {
|
||||
return false;
|
||||
}
|
||||
return (!hasAAC ||
|
||||
!HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
|
||||
NS_LITERAL_CSTRING("aac"))) &&
|
||||
(!hasH264 ||
|
||||
!HaveGMPFor(aGMPS,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
|
||||
NS_LITERAL_CSTRING("h264")));
|
||||
#else
|
||||
return false;
|
||||
// If this keysystem's CDM explicitly says it doesn't support decoding,
|
||||
// that means it's OK with passing the decrypted samples back to Gecko
|
||||
// for decoding. Note we special case Clearkey on Windows, where we need
|
||||
// to check for whether WMF is usable because the CDM uses that
|
||||
// to decode.
|
||||
static bool
|
||||
GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
|
||||
const nsAString& aKeySystem,
|
||||
const nsAString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(HaveGMPFor(aGMPService,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
|
||||
MOZ_ASSERT(IsH264ContentType(aContentType));
|
||||
return
|
||||
(!HaveGMPFor(aGMPService,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
|
||||
NS_LITERAL_CSTRING("h264"))
|
||||
#ifdef XP_WIN
|
||||
// Clearkey on Windows XP can't decode, but advertises that it can
|
||||
// in its GMP info file.
|
||||
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
|
||||
#endif
|
||||
) && MP4Decoder::CanHandleMediaType(aContentType);
|
||||
}
|
||||
|
||||
static bool
|
||||
GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
|
||||
const nsAString& aKeySystem,
|
||||
const nsAString& aContentType)
|
||||
{
|
||||
MOZ_ASSERT(HaveGMPFor(aGMPService,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
|
||||
MOZ_ASSERT(IsAACContentType(aContentType));
|
||||
return
|
||||
(!HaveGMPFor(aGMPService,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
|
||||
NS_LITERAL_CSTRING("aac"))
|
||||
#ifdef XP_WIN
|
||||
// Clearkey on Windows XP can't decode, but advertises that it can
|
||||
// in its GMP info file.
|
||||
|| (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
|
||||
#endif
|
||||
) && MP4Decoder::CanHandleMediaType(aContentType);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsSupported(mozIGeckoMediaPluginService* aGMPService,
|
||||
const nsAString& aKeySystem,
|
||||
const MediaKeySystemOptions& aConfig)
|
||||
{
|
||||
if (!aConfig.mInitDataType.EqualsLiteral("cenc")) {
|
||||
return false;
|
||||
}
|
||||
if (!aConfig.mAudioCapability.IsEmpty() ||
|
||||
!aConfig.mVideoCapability.IsEmpty()) {
|
||||
// Don't support any capabilities until we know we have a CDM with
|
||||
// capabilities...
|
||||
return false;
|
||||
}
|
||||
if (!aConfig.mAudioType.IsEmpty() &&
|
||||
(!IsAACContentType(aConfig.mAudioType) ||
|
||||
(!GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem) &&
|
||||
!GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aConfig.mAudioType)))) {
|
||||
return false;
|
||||
}
|
||||
if (!aConfig.mVideoType.IsEmpty() &&
|
||||
(!IsH264ContentType(aConfig.mVideoType) ||
|
||||
(!GMPDecryptsAndDecodesH264(aGMPService, aKeySystem) &&
|
||||
!GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aConfig.mVideoType)))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -356,32 +413,16 @@ MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
|
|||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aOptions.Length(); i++) {
|
||||
const MediaKeySystemOptions& options = aOptions[i];
|
||||
if (!options.mInitDataType.EqualsLiteral("cenc")) {
|
||||
continue;
|
||||
}
|
||||
if (!options.mAudioCapability.IsEmpty() ||
|
||||
!options.mVideoCapability.IsEmpty()) {
|
||||
// Don't support any capabilites until we know we have a CDM with
|
||||
// capabilities...
|
||||
continue;
|
||||
}
|
||||
if (!options.mAudioType.IsEmpty() &&
|
||||
!IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
|
||||
continue;
|
||||
}
|
||||
if (!options.mVideoType.IsEmpty() &&
|
||||
!IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
|
||||
continue;
|
||||
}
|
||||
if (!HaveGMPFor(mps,
|
||||
NS_ConvertUTF16toUTF8(aKeySystem),
|
||||
NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Our sandbox provides an origin specific unique identifier, and the
|
||||
// ability to persist data. We don't yet have a way to turn those off
|
||||
// and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
|
||||
// and stateful attributes here.
|
||||
|
||||
return true;
|
||||
for (const MediaKeySystemOptions& config : aOptions) {
|
||||
if (mozilla::dom::IsSupported(mps, aKeySystem, config)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#endif
|
||||
#include "mozilla/Logging.h"
|
||||
#include "nsMimeTypes.h"
|
||||
#include "nsContentTypeParser.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
|
@ -139,30 +140,45 @@ MP4Decoder::CanHandleMediaType(const nsACString& aType,
|
|||
|
||||
// Verify that all the codecs specifed are ones that we expect that
|
||||
// we can play.
|
||||
nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
|
||||
bool expectMoreTokens = false;
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
const nsSubstring& token = tokenizer.nextToken();
|
||||
expectMoreTokens = tokenizer.separatorAfterCurrentToken();
|
||||
if (IsSupportedAudioCodec(token,
|
||||
nsTArray<nsString> codecs;
|
||||
if (!ParseCodecsString(aCodecs, codecs)) {
|
||||
return false;
|
||||
}
|
||||
for (const nsString& codec : codecs) {
|
||||
if (IsSupportedAudioCodec(codec,
|
||||
aOutContainsAAC,
|
||||
aOutContainsMP3)) {
|
||||
continue;
|
||||
}
|
||||
if (IsSupportedH264Codec(token)) {
|
||||
if (IsSupportedH264Codec(codec)) {
|
||||
aOutContainsH264 = true;
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (expectMoreTokens) {
|
||||
// Last codec name was empty
|
||||
// Some unsupported codec.
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
MP4Decoder::CanHandleMediaType(const nsAString& aContentType)
|
||||
{
|
||||
nsContentTypeParser parser(aContentType);
|
||||
nsAutoString mimeType;
|
||||
nsresult rv = parser.GetType(mimeType);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
nsString codecs;
|
||||
parser.GetParameter("codecs", codecs);
|
||||
|
||||
bool ignoreAAC, ignoreH264, ignoreMP3;
|
||||
return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType),
|
||||
codecs,
|
||||
ignoreAAC, ignoreH264, ignoreMP3);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsFFmpegAvailable()
|
||||
{
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
bool& aOutContainsH264,
|
||||
bool& aOutContainsMP3);
|
||||
|
||||
static bool CanHandleMediaType(const nsAString& aMIMEType);
|
||||
|
||||
// Returns true if the MP4 backend is preffed on.
|
||||
static bool IsEnabled();
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ MediaSourceDecoder::Clone()
|
|||
MediaDecoderStateMachine*
|
||||
MediaSourceDecoder::CreateStateMachine()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
mDemuxer = new MediaSourceDemuxer();
|
||||
nsRefPtr<MediaFormatReader> reader = new MediaFormatReader(this, mDemuxer);
|
||||
return new MediaDecoderStateMachine(this, reader);
|
||||
|
@ -52,6 +53,7 @@ MediaSourceDecoder::CreateStateMachine()
|
|||
nsresult
|
||||
MediaSourceDecoder::Load(nsIStreamListener**, MediaDecoder*)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(!GetStateMachine());
|
||||
SetStateMachine(CreateStateMachine());
|
||||
if (!GetStateMachine()) {
|
||||
|
@ -133,6 +135,7 @@ MediaSourceDecoder::GetBuffered()
|
|||
void
|
||||
MediaSourceDecoder::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MSE_DEBUG("Shutdown");
|
||||
// Detach first so that TrackBuffers are unused on the main thread when
|
||||
// shut down on the decode task queue.
|
||||
|
@ -142,9 +145,6 @@ MediaSourceDecoder::Shutdown()
|
|||
mDemuxer = nullptr;
|
||||
|
||||
MediaDecoder::Shutdown();
|
||||
// Kick WaitForData out of its slumber.
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
/*static*/
|
||||
|
@ -171,10 +171,9 @@ MediaSourceDecoder::DetachMediaSource()
|
|||
void
|
||||
MediaSourceDecoder::Ended(bool aEnded)
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
static_cast<MediaSourceResource*>(GetResource())->SetEnded(aEnded);
|
||||
mEnded = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -183,7 +182,6 @@ MediaSourceDecoder::SetInitialDuration(int64_t aDuration)
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
// Only use the decoded duration if one wasn't already
|
||||
// set.
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
if (!mMediaSource || !IsNaN(ExplicitDuration())) {
|
||||
return;
|
||||
}
|
||||
|
@ -199,7 +197,6 @@ void
|
|||
MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalAction aAction)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
double oldDuration = ExplicitDuration();
|
||||
if (aDuration >= 0) {
|
||||
int64_t checkedDuration;
|
||||
|
@ -221,7 +218,7 @@ MediaSourceDecoder::SetMediaSourceDuration(double aDuration, MSRangeRemovalActio
|
|||
double
|
||||
MediaSourceDecoder::GetMediaSourceDuration()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return ExplicitDuration();
|
||||
}
|
||||
|
||||
|
@ -234,7 +231,7 @@ MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString)
|
|||
double
|
||||
MediaSourceDecoder::GetDuration()
|
||||
{
|
||||
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return ExplicitDuration();
|
||||
}
|
||||
|
||||
|
|
|
@ -1354,11 +1354,11 @@ TrackBuffersManager::ResolveProcessing(bool aResolveValue, const char* aName)
|
|||
}
|
||||
|
||||
void
|
||||
TrackBuffersManager::CheckSequenceDiscontinuity()
|
||||
TrackBuffersManager::CheckSequenceDiscontinuity(const TimeUnit& aPresentationTime)
|
||||
{
|
||||
if (mSourceBufferAttributes->GetAppendMode() == SourceBufferAppendMode::Sequence &&
|
||||
mGroupStartTimestamp.isSome()) {
|
||||
mTimestampOffset = mGroupStartTimestamp.ref();
|
||||
mTimestampOffset = mGroupStartTimestamp.ref() - aPresentationTime;
|
||||
mGroupEndTimestamp = mGroupStartTimestamp.ref();
|
||||
mVideoTracks.mNeedRandomAccessPoint = true;
|
||||
mAudioTracks.mNeedRandomAccessPoint = true;
|
||||
|
@ -1373,8 +1373,15 @@ TrackBuffersManager::ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData)
|
|||
return;
|
||||
}
|
||||
|
||||
// 1. If generate timestamps flag equals true
|
||||
// Let presentation timestamp equal 0.
|
||||
// Otherwise
|
||||
// Let presentation timestamp be a double precision floating point representation of the coded frame's presentation timestamp in seconds.
|
||||
TimeUnit presentationTimestamp = mSourceBufferAttributes->mGenerateTimestamps
|
||||
? TimeUnit() : TimeUnit::FromMicroseconds(aSamples[0]->mTime);
|
||||
|
||||
// 3. If mode equals "sequence" and group start timestamp is set, then run the following steps:
|
||||
CheckSequenceDiscontinuity();
|
||||
CheckSequenceDiscontinuity(presentationTimestamp);
|
||||
|
||||
// 5. Let track buffer equal the track buffer that the coded frame will be added to.
|
||||
auto& trackBuffer = aTrackData;
|
||||
|
@ -1482,7 +1489,9 @@ TrackBuffersManager::ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData)
|
|||
// Rather that restarting the process for the frame, we run the first
|
||||
// steps again instead.
|
||||
// 3. If mode equals "sequence" and group start timestamp is set, then run the following steps:
|
||||
CheckSequenceDiscontinuity();
|
||||
TimeUnit presentationTimestamp = mSourceBufferAttributes->mGenerateTimestamps
|
||||
? TimeUnit() : TimeUnit::FromMicroseconds(sample->mTime);
|
||||
CheckSequenceDiscontinuity(presentationTimestamp);
|
||||
|
||||
if (!sample->mKeyframe) {
|
||||
continue;
|
||||
|
|
|
@ -283,7 +283,7 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
void CheckSequenceDiscontinuity();
|
||||
void CheckSequenceDiscontinuity(const media::TimeUnit& aPresentationTime);
|
||||
void ProcessFrames(TrackBuffer& aSamples, TrackData& aTrackData);
|
||||
bool CheckNextInsertionIndex(TrackData& aTrackData,
|
||||
const media::TimeUnit& aSampleTime);
|
||||
|
|
|
@ -470,8 +470,6 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
|||
SetupMediaTracksInfo(serials);
|
||||
|
||||
if (HasAudio() || HasVideo()) {
|
||||
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
|
||||
|
||||
if (mInfo.mMetadataDuration.isNothing() &&
|
||||
!mDecoder->IsOggDecoderShutdown() &&
|
||||
mResource.GetLength() >= 0) {
|
||||
|
@ -481,11 +479,7 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
|||
|
||||
NS_ASSERTION(length > 0, "Must have a content length to get end time");
|
||||
|
||||
int64_t endTime = 0;
|
||||
{
|
||||
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
|
||||
endTime = RangeEndTime(length);
|
||||
}
|
||||
int64_t endTime = RangeEndTime(length);
|
||||
if (endTime != -1) {
|
||||
mInfo.mUnadjustedMetadataEndTime.emplace(TimeUnit::FromMicroseconds(endTime));
|
||||
LOG(LogLevel::Debug, ("Got Ogg duration from seeking to end %lld", endTime));
|
||||
|
|
|
@ -332,6 +332,11 @@ function SetupEME(test, token, params)
|
|||
});
|
||||
}
|
||||
|
||||
function streamType(type) {
|
||||
var x = test.tracks.find(o => o.name == type);
|
||||
return x ? x.type : undefined;
|
||||
}
|
||||
|
||||
// All 'initDataType's should be the same.
|
||||
// null indicates no 'encrypted' event received yet.
|
||||
var initDataType = null;
|
||||
|
@ -353,8 +358,8 @@ function SetupEME(test, token, params)
|
|||
var options = [
|
||||
{
|
||||
initDataType: ev.initDataType,
|
||||
videoType: test.type,
|
||||
audioType: test.type,
|
||||
videoType: streamType("video"),
|
||||
audioType: streamType("audio"),
|
||||
}
|
||||
];
|
||||
var p = navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options);
|
||||
|
|
|
@ -792,7 +792,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x300 audio&video tracks, each with its key",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -824,7 +823,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480@624kbps audio&video tracks, each with its key",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -856,7 +854,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480@959kbps audio&video tracks, each with its key",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -888,7 +885,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480 then 400x300, same key (1st) per track",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -921,7 +917,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480 then 400x300, same key (2nd) per track",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -954,7 +949,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480 with 1st keys then 400x300 with 2nd keys",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -988,7 +982,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x300 with 1st keys then 640x480 with 2nd keys",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1022,7 +1015,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480@959kbps with 1st keys then 640x480@624kbps with 2nd keys",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1056,7 +1048,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x480@624kbps with 1st keys then 640x480@959kbps with 2nd keys",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1090,7 +1081,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x300 with presentation size 533x300",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1122,7 +1112,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x300 as-is then 400x300 presented as 533x300",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1156,7 +1145,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x225",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1187,7 +1175,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x360",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1218,7 +1205,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"400x225 then 640x360",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1252,7 +1238,6 @@ var gEMETests = [
|
|||
},
|
||||
{
|
||||
name:"640x360 then 640x480",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
tracks: [
|
||||
{
|
||||
name:"audio",
|
||||
|
@ -1289,7 +1274,8 @@ var gEMETests = [
|
|||
var gEMENonMSEFailTests = [
|
||||
{
|
||||
name:"short-cenc.mp4",
|
||||
type:"video/mp4; codecs=\"avc1.64000d,mp4a.40.2\"",
|
||||
audioType:"audio/mp4; codecs=\"mp4a.40.2\"",
|
||||
videoType:"video/mp4; codecs=\"avc1.64000d\"",
|
||||
duration:0.47,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -69,6 +69,8 @@ function check_mp4(v, enabled) {
|
|||
check("audio/x-m4a; codecs=\"mp4a.40.2\"", "probably");
|
||||
check("audio/x-m4a; codecs=mp4a.40.2", "probably");
|
||||
|
||||
check("audio/mp4; codecs=\"mp4a.40.2,\"", ""); // Invalid codecs string
|
||||
|
||||
// HE-AAC v1
|
||||
check("audio/mp4; codecs=\"mp4a.40.5\"", "probably");
|
||||
check("audio/mp4; codecs=mp4a.40.5", "probably");
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
<script class="testbody" type="text/javascript">
|
||||
var manager = new MediaTestManager;
|
||||
|
||||
function DoSetMediaKeys(v)
|
||||
function DoSetMediaKeys(v, test)
|
||||
{
|
||||
var options = [{
|
||||
initDataType: "cenc",
|
||||
videoType: test.type,
|
||||
audioType: test.audioType,
|
||||
videoType: test.videoType,
|
||||
}];
|
||||
|
||||
return navigator.requestMediaKeySystemAccess("org.w3.clearkey", options)
|
||||
|
@ -68,7 +69,7 @@ function TestSetSrc(test, token)
|
|||
manager.finished(token);
|
||||
});
|
||||
|
||||
DoSetMediaKeys(v)
|
||||
DoSetMediaKeys(v, test)
|
||||
|
||||
.then(function() {
|
||||
v.src = test.name;
|
||||
|
|
|
@ -202,12 +202,23 @@ var tests = [
|
|||
shouldPass: false,
|
||||
},
|
||||
{
|
||||
name: 'MP4 video container with mp3 and h264 is unsupported',
|
||||
name: 'MP4 video container type with an audio codec is unsupported',
|
||||
keySystem: CLEARKEY_ID,
|
||||
options: [
|
||||
{
|
||||
initDataType: 'cenc',
|
||||
videoType: 'video/mp4; codecs="avc1.42E01E,mp3"',
|
||||
videoType: 'video/mp4; codecs="mp3"',
|
||||
}
|
||||
],
|
||||
shouldPass: false,
|
||||
},
|
||||
{
|
||||
name: 'MP4 audio container type with a video codec is unsupported',
|
||||
keySystem: CLEARKEY_ID,
|
||||
options: [
|
||||
{
|
||||
initDataType: 'cenc',
|
||||
audioType: 'audio/mp4; codecs="avc1.42E01E"',
|
||||
}
|
||||
],
|
||||
shouldPass: false,
|
||||
|
@ -218,7 +229,7 @@ var tests = [
|
|||
options: [
|
||||
{
|
||||
initDataType: 'cenc',
|
||||
audioType: 'video/mp4; codecs="avc1.42E01E"',
|
||||
videoType: 'video/mp4; codecs="avc1.42E01E"',
|
||||
}
|
||||
],
|
||||
shouldPass: true,
|
||||
|
@ -235,7 +246,7 @@ var tests = [
|
|||
shouldPass: false,
|
||||
},
|
||||
{
|
||||
name: 'MP4 video container with constrained baseline h.264 and AAC-LC',
|
||||
name: 'MP4 video container with both audio and video codec type in videoType',
|
||||
keySystem: CLEARKEY_ID,
|
||||
options: [
|
||||
{
|
||||
|
@ -243,7 +254,7 @@ var tests = [
|
|||
videoType: 'video/mp4; codecs="avc1.42E01E,mp4a.40.2"',
|
||||
}
|
||||
],
|
||||
shouldPass: true,
|
||||
shouldPass: false,
|
||||
},
|
||||
{
|
||||
name: 'MP4 audio and video type both specified',
|
||||
|
|
|
@ -26,6 +26,7 @@ skip-if = true
|
|||
[test_ril_worker_cf.js]
|
||||
[test_ril_worker_cellbroadcast_config.js]
|
||||
[test_ril_worker_cellbroadcast.js]
|
||||
skip-if = true # Bug 1207936
|
||||
[test_ril_worker_ruim.js]
|
||||
[test_ril_worker_cw.js]
|
||||
[test_ril_worker_clir.js]
|
||||
|
|
|
@ -872,6 +872,8 @@ var interfaceNamesInGlobalScope =
|
|||
"Notification",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"NotifyPaintEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "OffscreenCanvas", disabled: true},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"OfflineAudioCompletionEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -46,6 +46,13 @@ partial interface HTMLCanvasElement {
|
|||
CanvasCaptureMediaStream captureStream(optional double frameRate);
|
||||
};
|
||||
|
||||
// For OffscreenCanvas
|
||||
// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
|
||||
partial interface HTMLCanvasElement {
|
||||
[Pref="gfx.offscreencanvas.enabled", Throws]
|
||||
OffscreenCanvas transferControlToOffscreen();
|
||||
};
|
||||
|
||||
[ChromeOnly]
|
||||
interface MozCanvasPrintState
|
||||
{
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* For more information on this interface, please see
|
||||
* https://wiki.whatwg.org/wiki/OffscreenCanvas
|
||||
*
|
||||
* Current implementation focus on transfer canvas from main thread to worker.
|
||||
* So there are some spec doesn't implement, such as [Constructor], toBlob() and
|
||||
* transferToImageBitmap in OffscreenCanvas. Bug 1172796 will implement
|
||||
* remaining spec.
|
||||
*/
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
|
||||
interface OffscreenCanvas : EventTarget {
|
||||
[Pure, SetterThrows]
|
||||
attribute unsigned long width;
|
||||
[Pure, SetterThrows]
|
||||
attribute unsigned long height;
|
||||
|
||||
[Throws]
|
||||
nsISupports? getContext(DOMString contextId,
|
||||
optional any contextOptions = null);
|
||||
};
|
||||
|
||||
// OffscreenCanvas implements Transferable;
|
|
@ -45,24 +45,38 @@ dictionary WebGLContextAttributes {
|
|||
boolean failIfMajorPerformanceCaveat = false;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLBuffer {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLFramebuffer {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLProgram {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLRenderbuffer {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLShader {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLTexture {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLUniformLocation {
|
||||
};
|
||||
|
||||
|
@ -70,18 +84,24 @@ interface WebGLUniformLocation {
|
|||
interface WebGLVertexArrayObjectOES {
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLActiveInfo {
|
||||
readonly attribute GLint size;
|
||||
readonly attribute GLenum type;
|
||||
readonly attribute DOMString name;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLShaderPrecisionFormat {
|
||||
readonly attribute GLint rangeMin;
|
||||
readonly attribute GLint rangeMax;
|
||||
readonly attribute GLint precision;
|
||||
};
|
||||
|
||||
[Exposed=(Window,Worker),
|
||||
Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
|
||||
interface WebGLRenderingContext {
|
||||
|
||||
/* ClearBufferMask */
|
||||
|
@ -504,7 +524,7 @@ interface WebGLRenderingContext {
|
|||
const GLenum BROWSER_DEFAULT_WEBGL = 0x9244;
|
||||
|
||||
// The canvas might actually be null in some cases, apparently.
|
||||
readonly attribute HTMLCanvasElement? canvas;
|
||||
readonly attribute (HTMLCanvasElement or OffscreenCanvas)? canvas;
|
||||
readonly attribute GLsizei drawingBufferWidth;
|
||||
readonly attribute GLsizei drawingBufferHeight;
|
||||
|
||||
|
@ -766,6 +786,14 @@ interface WebGLRenderingContext {
|
|||
void viewport(GLint x, GLint y, GLsizei width, GLsizei height);
|
||||
};
|
||||
|
||||
// For OffscreenCanvas
|
||||
// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
|
||||
[Exposed=(Window,Worker)]
|
||||
partial interface WebGLRenderingContext {
|
||||
[Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
|
||||
void commit();
|
||||
};
|
||||
|
||||
/*[Constructor(DOMString type, optional WebGLContextEventInit eventInit)]
|
||||
interface WebGLContextEvent : Event {
|
||||
readonly attribute DOMString statusMessage;
|
||||
|
|
|
@ -344,6 +344,7 @@ WEBIDL_FILES = [
|
|||
'OfflineAudioCompletionEvent.webidl',
|
||||
'OfflineAudioContext.webidl',
|
||||
'OfflineResourceList.webidl',
|
||||
'OffscreenCanvas.webidl',
|
||||
'OscillatorNode.webidl',
|
||||
'PaintRequest.webidl',
|
||||
'PaintRequestList.webidl',
|
||||
|
|
|
@ -170,6 +170,7 @@ static_assert(MAX_WORKERS_PER_DOMAIN >= 1,
|
|||
#define PREF_INTERCEPTION_OPAQUE_ENABLED "dom.serviceWorkers.interception.opaque.enabled"
|
||||
#define PREF_PUSH_ENABLED "dom.push.enabled"
|
||||
#define PREF_REQUESTCONTEXT_ENABLED "dom.requestcontext.enabled"
|
||||
#define PREF_OFFSCREENCANVAS_ENABLED "gfx.offscreencanvas.enabled"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -1943,6 +1944,10 @@ RuntimeService::Init()
|
|||
WorkerPrefChanged,
|
||||
PREF_REQUESTCONTEXT_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_REQUESTCONTEXT))) ||
|
||||
NS_FAILED(Preferences::RegisterCallbackAndCall(
|
||||
WorkerPrefChanged,
|
||||
PREF_OFFSCREENCANVAS_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_OFFSCREENCANVAS))) ||
|
||||
NS_FAILED(Preferences::RegisterCallback(LoadRuntimeOptions,
|
||||
PREF_JS_OPTIONS_PREFIX,
|
||||
nullptr)) ||
|
||||
|
@ -2182,6 +2187,10 @@ RuntimeService::Cleanup()
|
|||
WorkerPrefChanged,
|
||||
PREF_REQUESTCONTEXT_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_REQUESTCONTEXT))) ||
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
PREF_OFFSCREENCANVAS_ENABLED,
|
||||
reinterpret_cast<void *>(WORKERPREF_OFFSCREENCANVAS))) ||
|
||||
#if DUMP_CONTROLLED_BY_PREF
|
||||
NS_FAILED(Preferences::UnregisterCallback(
|
||||
WorkerPrefChanged,
|
||||
|
@ -2732,6 +2741,7 @@ RuntimeService::WorkerPrefChanged(const char* aPrefName, void* aClosure)
|
|||
case WORKERPREF_SERVICEWORKERS_TESTING:
|
||||
case WORKERPREF_PUSH:
|
||||
case WORKERPREF_REQUESTCONTEXT:
|
||||
case WORKERPREF_OFFSCREENCANVAS:
|
||||
sDefaultPreferences[key] = Preferences::GetBool(aPrefName, false);
|
||||
break;
|
||||
|
||||
|
|
|
@ -2726,9 +2726,7 @@ ServiceWorkerManager::MaybeStopControlling(nsIDocument* aDoc)
|
|||
StopControllingADocument(registration);
|
||||
}
|
||||
|
||||
if (mAllDocuments.Contains(aDoc)) {
|
||||
mAllDocuments.RemoveEntry(aDoc);
|
||||
}
|
||||
mAllDocuments.RemoveEntry(aDoc);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -1313,6 +1313,13 @@ public:
|
|||
return mPreferences[WORKERPREF_REQUESTCONTEXT];
|
||||
}
|
||||
|
||||
bool
|
||||
OffscreenCanvasEnabled() const
|
||||
{
|
||||
AssertIsOnWorkerThread();
|
||||
return mPreferences[WORKERPREF_OFFSCREENCANVAS];
|
||||
}
|
||||
|
||||
bool
|
||||
OnLine() const
|
||||
{
|
||||
|
|
|
@ -209,6 +209,7 @@ enum WorkerPreference
|
|||
WORKERPREF_PERFORMANCE_LOGGING_ENABLED, // dom.performance.enable_user_timing_logging
|
||||
WORKERPREF_PUSH, // dom.push.enabled
|
||||
WORKERPREF_REQUESTCONTEXT, // dom.requestcontext.enabled
|
||||
WORKERPREF_OFFSCREENCANVAS, // gfx.offscreencanvas.enabled
|
||||
WORKERPREF_COUNT
|
||||
};
|
||||
|
||||
|
|
|
@ -155,6 +155,8 @@ var interfaceNamesInGlobalScope =
|
|||
"MessagePort",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Notification",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "OffscreenCanvas", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"Performance",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
@ -189,6 +191,26 @@ var interfaceNamesInGlobalScope =
|
|||
"URL",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"URLSearchParams",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLActiveInfo", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLBuffer", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLFramebuffer", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLProgram", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLRenderbuffer", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLRenderingContext", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLShader", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLShaderPrecisionFormat", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLTexture", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{ name: "WebGLUniformLocation", disabled: true },
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"WebSocket",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
|
|
@ -2650,6 +2650,7 @@ GLContext::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
|
|||
}
|
||||
|
||||
GLuint tempFB = 0;
|
||||
GLuint tempTex = 0;
|
||||
|
||||
{
|
||||
ScopedBindFramebuffer autoFB(this);
|
||||
|
@ -2681,6 +2682,24 @@ GLContext::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
|
|||
MOZ_ASSERT(status == LOCAL_GL_FRAMEBUFFER_COMPLETE);
|
||||
}
|
||||
|
||||
if (src->NeedsIndirectReads()) {
|
||||
fGenTextures(1, &tempTex);
|
||||
{
|
||||
ScopedBindTexture autoTex(this, tempTex);
|
||||
|
||||
GLenum format = src->mHasAlpha ? LOCAL_GL_RGBA
|
||||
: LOCAL_GL_RGB;
|
||||
auto width = src->mSize.width;
|
||||
auto height = src->mSize.height;
|
||||
fCopyTexImage2D(LOCAL_GL_TEXTURE_2D, 0, format, 0, 0, width,
|
||||
height, 0);
|
||||
}
|
||||
|
||||
fFramebufferTexture2D(LOCAL_GL_FRAMEBUFFER,
|
||||
LOCAL_GL_COLOR_ATTACHMENT0,
|
||||
LOCAL_GL_TEXTURE_2D, tempTex, 0);
|
||||
}
|
||||
|
||||
ReadPixelsIntoDataSurface(this, dest);
|
||||
|
||||
src->ProducerReadRelease();
|
||||
|
@ -2689,6 +2708,10 @@ GLContext::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
|
|||
if (tempFB)
|
||||
fDeleteFramebuffers(1, &tempFB);
|
||||
|
||||
if (tempTex) {
|
||||
fDeleteTextures(1, &tempTex);
|
||||
}
|
||||
|
||||
if (needsSwap) {
|
||||
src->UnlockProd();
|
||||
if (prev)
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "GLLibraryEGL.h"
|
||||
|
||||
#include "gfxCrashReporterUtils.h"
|
||||
#include "gfxUtils.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
|
@ -14,13 +15,17 @@
|
|||
#ifdef XP_WIN
|
||||
#include "nsWindowsHelpers.h"
|
||||
#endif
|
||||
#include "OGLShaderProgram.h"
|
||||
#include "prenv.h"
|
||||
#include "GLContext.h"
|
||||
#include "GLContextProvider.h"
|
||||
#include "gfxPrefs.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
||||
StaticMutex GLLibraryEGL::sMutex;
|
||||
GLLibraryEGL sEGLLibrary;
|
||||
#ifdef MOZ_B2G
|
||||
ThreadLocal<EGLContext> GLLibraryEGL::sCurrentContext;
|
||||
|
@ -128,7 +133,9 @@ static bool
|
|||
IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
|
||||
{
|
||||
int32_t angleSupport;
|
||||
gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &angleSupport);
|
||||
gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
|
||||
nsIGfxInfo::FEATURE_WEBGL_ANGLE,
|
||||
&angleSupport);
|
||||
return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
|
||||
}
|
||||
|
||||
|
@ -145,6 +152,32 @@ GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
|
|||
return display;
|
||||
}
|
||||
|
||||
bool
|
||||
GLLibraryEGL::ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
StaticMutexAutoUnlock lock(sMutex);
|
||||
if (!mReadbackGL) {
|
||||
mReadbackGL = gl::GLContextProvider::CreateHeadless(gl::CreateContextFlags::NONE);
|
||||
}
|
||||
|
||||
ScopedTexture destTex(mReadbackGL);
|
||||
const GLuint target = LOCAL_GL_TEXTURE_EXTERNAL;
|
||||
ScopedBindTexture autoTex(mReadbackGL, destTex.Texture(), target);
|
||||
mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
|
||||
mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
|
||||
mReadbackGL->fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
|
||||
mReadbackGL->fEGLImageTargetTexture2D(target, image);
|
||||
|
||||
ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(target,
|
||||
out_surface->GetFormat());
|
||||
int shaderConfig = config.mFeatures;
|
||||
mReadbackGL->ReadTexImageHelper()->ReadTexImage(out_surface, 0, target,
|
||||
out_surface->GetSize(), shaderConfig);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
GLLibraryEGL::EnsureInitialized(bool forceAccel)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#endif
|
||||
|
||||
#include "GLLibraryLoader.h"
|
||||
#include "mozilla/StaticMutex.h"
|
||||
#include "mozilla/ThreadLocal.h"
|
||||
#include "nsIFile.h"
|
||||
#include "GeckoProfiler.h"
|
||||
|
@ -52,6 +53,11 @@
|
|||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
}
|
||||
|
||||
namespace gl {
|
||||
|
||||
#undef BEFORE_GL_CALL
|
||||
|
@ -94,6 +100,8 @@ namespace gl {
|
|||
#define AFTER_GL_CALL
|
||||
#endif
|
||||
|
||||
class GLContext;
|
||||
|
||||
class GLLibraryEGL
|
||||
{
|
||||
public:
|
||||
|
@ -478,6 +486,8 @@ public:
|
|||
return IsExtensionSupported(EXT_create_context_robustness);
|
||||
}
|
||||
|
||||
bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);
|
||||
|
||||
bool EnsureInitialized(bool forceAccel = false);
|
||||
|
||||
void DumpEGLConfig(EGLConfig cfg);
|
||||
|
@ -603,9 +613,11 @@ private:
|
|||
bool mInitialized;
|
||||
PRLibrary* mEGLLibrary;
|
||||
EGLDisplay mEGLDisplay;
|
||||
RefPtr<GLContext> mReadbackGL;
|
||||
|
||||
bool mIsANGLE;
|
||||
bool mIsWARP;
|
||||
static StaticMutex sMutex;
|
||||
};
|
||||
|
||||
extern GLLibraryEGL sEGLLibrary;
|
||||
|
|
|
@ -214,7 +214,7 @@ GetActualReadFormats(GLContext* gl,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
SwapRAndBComponents(DataSourceSurface* surf)
|
||||
{
|
||||
DataSourceSurface::MappedSurface map;
|
||||
|
@ -533,8 +533,8 @@ ReadPixelsIntoDataSurface(GLContext* gl, DataSourceSurface* dest)
|
|||
#endif
|
||||
}
|
||||
|
||||
static already_AddRefed<DataSourceSurface>
|
||||
YInvertImageSurface(DataSourceSurface* aSurf)
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
YInvertImageSurface(gfx::DataSourceSurface* aSurf)
|
||||
{
|
||||
RefPtr<DataSourceSurface> temp =
|
||||
Factory::CreateDataSourceSurfaceWithStride(aSurf->GetSize(),
|
||||
|
@ -560,8 +560,8 @@ YInvertImageSurface(DataSourceSurface* aSurf)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
dt->SetTransform(Matrix::Translation(0.0, aSurf->GetSize().height) *
|
||||
Matrix::Scaling(1.0, -1.0));
|
||||
dt->SetTransform(Matrix::Scaling(1.0, -1.0) *
|
||||
Matrix::Translation(0.0, aSurf->GetSize().height));
|
||||
Rect rect(0, 0, aSurf->GetSize().width, aSurf->GetSize().height);
|
||||
dt->DrawSurface(aSurf, rect, rect, DrawSurfaceOptions(),
|
||||
DrawOptions(1.0, CompositionOp::OP_SOURCE, AntialiasMode::NONE));
|
||||
|
@ -614,8 +614,7 @@ ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, SurfaceFormat aFo
|
|||
|
||||
#define CLEANUP_IF_GLERROR_OCCURRED(x) \
|
||||
if (DidGLErrorOccur(x)) { \
|
||||
isurf = nullptr; \
|
||||
break; \
|
||||
return false; \
|
||||
}
|
||||
|
||||
already_AddRefed<DataSourceSurface>
|
||||
|
@ -624,6 +623,31 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
|
|||
const gfx::IntSize& aSize,
|
||||
/* ShaderConfigOGL.mFeature */ int aConfig,
|
||||
bool aYInvert)
|
||||
{
|
||||
/* Allocate resulting image surface */
|
||||
int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
|
||||
RefPtr<DataSourceSurface> isurf =
|
||||
Factory::CreateDataSourceSurfaceWithStride(aSize,
|
||||
SurfaceFormat::R8G8B8A8,
|
||||
stride);
|
||||
if (NS_WARN_IF(!isurf)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ReadTexImage(isurf, aTextureId, aTextureTarget, aSize, aConfig, aYInvert)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return isurf.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
GLReadTexImageHelper::ReadTexImage(DataSourceSurface* aDest,
|
||||
GLuint aTextureId,
|
||||
GLenum aTextureTarget,
|
||||
const gfx::IntSize& aSize,
|
||||
/* ShaderConfigOGL.mFeature */ int aConfig,
|
||||
bool aYInvert)
|
||||
{
|
||||
MOZ_ASSERT(aTextureTarget == LOCAL_GL_TEXTURE_2D ||
|
||||
aTextureTarget == LOCAL_GL_TEXTURE_EXTERNAL ||
|
||||
|
@ -631,16 +655,6 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
|
|||
|
||||
mGL->MakeCurrent();
|
||||
|
||||
/* Allocate resulting image surface */
|
||||
int32_t stride = aSize.width * BytesPerPixel(SurfaceFormat::R8G8B8A8);
|
||||
RefPtr<DataSourceSurface> isurf =
|
||||
Factory::CreateDataSourceSurfaceWithStride(aSize,
|
||||
SurfaceFormat::R8G8B8A8,
|
||||
stride);
|
||||
if (NS_WARN_IF(!isurf)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GLint oldrb, oldfb, oldprog, oldTexUnit, oldTex;
|
||||
GLuint rb, fb;
|
||||
|
||||
|
@ -737,7 +751,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
|
|||
CLEANUP_IF_GLERROR_OCCURRED("when drawing texture");
|
||||
|
||||
/* Read-back draw results */
|
||||
ReadPixelsIntoDataSurface(mGL, isurf);
|
||||
ReadPixelsIntoDataSurface(mGL, aDest);
|
||||
CLEANUP_IF_GLERROR_OCCURRED("when reading pixels into surface");
|
||||
} while (false);
|
||||
|
||||
|
@ -756,7 +770,7 @@ GLReadTexImageHelper::ReadTexImage(GLuint aTextureId,
|
|||
if (oldTexUnit != LOCAL_GL_TEXTURE0)
|
||||
mGL->fActiveTexture(oldTexUnit);
|
||||
|
||||
return isurf.forget();
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef CLEANUP_IF_GLERROR_OCCURRED
|
||||
|
|
|
@ -34,6 +34,12 @@ void ReadPixelsIntoDataSurface(GLContext* aGL,
|
|||
already_AddRefed<gfx::DataSourceSurface>
|
||||
ReadBackSurface(GLContext* gl, GLuint aTexture, bool aYInvert, gfx::SurfaceFormat aFormat);
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
YInvertImageSurface(gfx::DataSourceSurface* aSurf);
|
||||
|
||||
void
|
||||
SwapRAndBComponents(gfx::DataSourceSurface* surf);
|
||||
|
||||
class GLReadTexImageHelper final
|
||||
{
|
||||
// The GLContext is the sole owner of the GLBlitHelper.
|
||||
|
@ -65,12 +71,17 @@ public:
|
|||
* passed as int to eliminate including LayerManagerOGLProgram.h here.
|
||||
*/
|
||||
already_AddRefed<gfx::DataSourceSurface> ReadTexImage(GLuint aTextureId,
|
||||
GLenum aTextureTarget,
|
||||
const gfx::IntSize& aSize,
|
||||
/* ShaderProgramType */ int aShaderProgram,
|
||||
bool aYInvert = false);
|
||||
|
||||
GLenum aTextureTarget,
|
||||
const gfx::IntSize& aSize,
|
||||
/* ShaderProgramType */ int aShaderProgram,
|
||||
bool aYInvert = false);
|
||||
|
||||
bool ReadTexImage(gfx::DataSourceSurface* aDest,
|
||||
GLuint aTextureId,
|
||||
GLenum aTextureTarget,
|
||||
const gfx::IntSize& aSize,
|
||||
int aShaderProgram,
|
||||
bool aYInvert = false);
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
|
|
@ -10,18 +10,32 @@
|
|||
#include "GLContext.h"
|
||||
#include "GLBlitHelper.h"
|
||||
#include "GLReadTexImageHelper.h"
|
||||
#include "SharedSurfaceEGL.h"
|
||||
#include "SharedSurfaceGL.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "../layers/ipc/ShadowLayers.h"
|
||||
#include "mozilla/layers/CompositableForwarder.h"
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "SharedSurfaceANGLE.h" // for SurfaceFactory_ANGLEShareHandle
|
||||
#include "gfxWindowsPlatform.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
#include "SharedSurfaceGralloc.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
#include "SharedSurfaceIO.h"
|
||||
#endif
|
||||
#include "ScopedGLHelpers.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "../layers/ipc/ShadowLayers.h"
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
|
||||
#ifdef GL_PROVIDER_GLX
|
||||
#include "GLXLibrary.h"
|
||||
#include "SharedSurfaceGLX.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace gl {
|
||||
|
@ -51,6 +65,53 @@ GLScreenBuffer::Create(GLContext* gl,
|
|||
return Move(ret);
|
||||
}
|
||||
|
||||
/* static */ UniquePtr<SurfaceFactory>
|
||||
GLScreenBuffer::CreateFactory(GLContext* gl,
|
||||
const SurfaceCaps& caps,
|
||||
const RefPtr<layers::CompositableForwarder>& forwarder,
|
||||
const layers::TextureFlags& flags)
|
||||
{
|
||||
UniquePtr<SurfaceFactory> factory = nullptr;
|
||||
if (!gfxPrefs::WebGLForceLayersReadback()) {
|
||||
switch (forwarder->GetCompositorBackendType()) {
|
||||
case mozilla::layers::LayersBackend::LAYERS_OPENGL: {
|
||||
#if defined(XP_MACOSX)
|
||||
factory = SurfaceFactory_IOSurface::Create(gl, caps, forwarder, flags);
|
||||
#elif defined(MOZ_WIDGET_GONK)
|
||||
factory = MakeUnique<SurfaceFactory_Gralloc>(gl, caps, forwarder, flags);
|
||||
#elif defined(GL_PROVIDER_GLX)
|
||||
if (sGLXLibrary.UseSurfaceSharing())
|
||||
factory = SurfaceFactory_GLXDrawable::Create(gl, caps, forwarder, flags);
|
||||
#else
|
||||
if (gl->GetContextType() == GLContextType::EGL) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
factory = SurfaceFactory_EGLImage::Create(gl, caps, forwarder, flags);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case mozilla::layers::LayersBackend::LAYERS_D3D11: {
|
||||
#ifdef XP_WIN
|
||||
// Enable surface sharing only if ANGLE and compositing devices
|
||||
// are both WARP or both not WARP
|
||||
if (gl->IsANGLE() &&
|
||||
(gl->IsWARP() == gfxWindowsPlatform::GetPlatform()->IsWARP()) &&
|
||||
gfxWindowsPlatform::GetPlatform()->CompositorD3D11TextureSharingWorks())
|
||||
{
|
||||
factory = SurfaceFactory_ANGLEShareHandle::Create(gl, caps, forwarder, flags);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
GLScreenBuffer::GLScreenBuffer(GLContext* gl,
|
||||
const SurfaceCaps& caps,
|
||||
UniquePtr<SurfaceFactory> factory)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
class CompositableForwarder;
|
||||
class SharedSurfaceTextureClient;
|
||||
} // namespace layers
|
||||
|
||||
|
@ -133,6 +134,12 @@ public:
|
|||
const gfx::IntSize& size,
|
||||
const SurfaceCaps& caps);
|
||||
|
||||
static UniquePtr<SurfaceFactory>
|
||||
CreateFactory(GLContext* gl,
|
||||
const SurfaceCaps& caps,
|
||||
const RefPtr<layers::CompositableForwarder>& forwarder,
|
||||
const layers::TextureFlags& flags);
|
||||
|
||||
protected:
|
||||
GLContext* const mGL; // Owns us.
|
||||
public:
|
||||
|
|
|
@ -311,6 +311,7 @@ SurfaceFactory::SurfaceFactory(SharedSurfaceType type, GLContext* gl,
|
|||
, mAllocator(allocator)
|
||||
, mFlags(flags)
|
||||
, mFormats(gl->ChooseGLFormats(caps))
|
||||
, mMutex("SurfaceFactor::mMutex")
|
||||
{
|
||||
ChooseBufferBits(mCaps, &mDrawCaps, &mReadCaps);
|
||||
}
|
||||
|
@ -368,6 +369,7 @@ SurfaceFactory::StartRecycling(layers::SharedSurfaceTextureClient* tc)
|
|||
void
|
||||
SurfaceFactory::StopRecycling(layers::SharedSurfaceTextureClient* tc)
|
||||
{
|
||||
MutexAutoLock autoLock(mMutex);
|
||||
// Must clear before releasing ref.
|
||||
tc->ClearRecycleCallback();
|
||||
|
||||
|
@ -379,11 +381,8 @@ SurfaceFactory::StopRecycling(layers::SharedSurfaceTextureClient* tc)
|
|||
/*static*/ void
|
||||
SurfaceFactory::RecycleCallback(layers::TextureClient* rawTC, void* rawFactory)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
RefPtr<layers::SharedSurfaceTextureClient> tc;
|
||||
tc = static_cast<layers::SharedSurfaceTextureClient*>(rawTC);
|
||||
|
||||
SurfaceFactory* factory = static_cast<SurfaceFactory*>(rawFactory);
|
||||
|
||||
if (tc->mSurf->mCanRecycle) {
|
||||
|
@ -399,6 +398,7 @@ bool
|
|||
SurfaceFactory::Recycle(layers::SharedSurfaceTextureClient* texClient)
|
||||
{
|
||||
MOZ_ASSERT(texClient);
|
||||
MutexAutoLock autoLock(mMutex);
|
||||
|
||||
if (mRecycleFreePool.size() >= 2) {
|
||||
return false;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "mozilla/WeakPtr.h"
|
||||
#include "ScopedGLHelpers.h"
|
||||
|
@ -33,6 +34,7 @@ class nsIThread;
|
|||
|
||||
namespace mozilla {
|
||||
namespace gfx {
|
||||
class DataSourceSurface;
|
||||
class DrawTarget;
|
||||
} // namespace gfx
|
||||
|
||||
|
@ -199,6 +201,10 @@ public:
|
|||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) = 0;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -298,6 +304,7 @@ public:
|
|||
const RefPtr<layers::ISurfaceAllocator> mAllocator;
|
||||
const layers::TextureFlags mFlags;
|
||||
const GLFormats mFormats;
|
||||
Mutex mMutex;
|
||||
protected:
|
||||
SurfaceCaps mDrawCaps;
|
||||
SurfaceCaps mReadCaps;
|
||||
|
|
|
@ -273,6 +273,138 @@ SharedSurface_ANGLEShareHandle::ToSurfaceDescriptor(layers::SurfaceDescriptor* c
|
|||
return true;
|
||||
}
|
||||
|
||||
class ScopedLockTexture final
|
||||
{
|
||||
public:
|
||||
explicit ScopedLockTexture(ID3D11Texture2D* texture, bool* succeeded)
|
||||
: mIsLocked(false)
|
||||
, mTexture(texture)
|
||||
{
|
||||
MOZ_ASSERT(mTexture);
|
||||
MOZ_ASSERT(succeeded);
|
||||
*succeeded = false;
|
||||
|
||||
HRESULT hr;
|
||||
mTexture->QueryInterface((IDXGIKeyedMutex**)byRef(mMutex));
|
||||
if (mMutex) {
|
||||
hr = mMutex->AcquireSync(0, 10000);
|
||||
if (hr == WAIT_TIMEOUT) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to lock the texture");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
|
||||
device->GetImmediateContext(byRef(mDeviceContext));
|
||||
|
||||
mTexture->GetDesc(&mDesc);
|
||||
mDesc.BindFlags = 0;
|
||||
mDesc.Usage = D3D11_USAGE_STAGING;
|
||||
mDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
|
||||
mDesc.MiscFlags = 0;
|
||||
|
||||
hr = device->CreateTexture2D(&mDesc, nullptr, byRef(mCopiedTexture));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDeviceContext->CopyResource(mCopiedTexture, mTexture);
|
||||
|
||||
hr = mDeviceContext->Map(mCopiedTexture, 0, D3D11_MAP_READ, 0, &mSubresource);
|
||||
if (FAILED(hr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
*succeeded = true;
|
||||
mIsLocked = true;
|
||||
}
|
||||
|
||||
~ScopedLockTexture()
|
||||
{
|
||||
mDeviceContext->Unmap(mCopiedTexture, 0);
|
||||
if (mMutex) {
|
||||
HRESULT hr = mMutex->ReleaseSync(0);
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Failed to unlock the texture");
|
||||
}
|
||||
}
|
||||
mIsLocked = false;
|
||||
}
|
||||
|
||||
bool mIsLocked;
|
||||
RefPtr<ID3D11Texture2D> mTexture;
|
||||
RefPtr<ID3D11Texture2D> mCopiedTexture;
|
||||
RefPtr<IDXGIKeyedMutex> mMutex;
|
||||
RefPtr<ID3D11DeviceContext> mDeviceContext;
|
||||
D3D11_TEXTURE2D_DESC mDesc;
|
||||
D3D11_MAPPED_SUBRESOURCE mSubresource;
|
||||
};
|
||||
|
||||
bool
|
||||
SharedSurface_ANGLEShareHandle::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
MOZ_ASSERT(out_surface);
|
||||
RefPtr<ID3D11Texture2D> tex;
|
||||
ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
|
||||
HRESULT hr = device->OpenSharedResource(mShareHandle,
|
||||
__uuidof(ID3D11Texture2D),
|
||||
(void**)(ID3D11Texture2D**)byRef(tex));
|
||||
|
||||
if (FAILED(hr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool succeeded = false;
|
||||
ScopedLockTexture scopedLock(tex, &succeeded);
|
||||
if (!succeeded) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* data = reinterpret_cast<uint8_t*>(scopedLock.mSubresource.pData);
|
||||
uint32_t srcStride = scopedLock.mSubresource.RowPitch;
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (map.GetStride() == srcStride) {
|
||||
memcpy(map.GetData(), data, out_surface->GetSize().height * map.GetStride());
|
||||
} else {
|
||||
const uint8_t bytesPerPixel = BytesPerPixel(out_surface->GetFormat());
|
||||
for (int32_t i = 0; i < out_surface->GetSize().height; i++) {
|
||||
memcpy(map.GetData() + i * map.GetStride(),
|
||||
data + i * srcStride,
|
||||
bytesPerPixel * out_surface->GetSize().width);
|
||||
}
|
||||
}
|
||||
|
||||
DXGI_FORMAT srcFormat = scopedLock.mDesc.Format;
|
||||
MOZ_ASSERT(srcFormat == DXGI_FORMAT_B8G8R8A8_UNORM ||
|
||||
srcFormat == DXGI_FORMAT_B8G8R8X8_UNORM ||
|
||||
srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM);
|
||||
bool isSrcRGB = srcFormat == DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
gfx::SurfaceFormat destFormat = out_surface->GetFormat();
|
||||
MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
|
||||
destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::B8G8R8A8);
|
||||
bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::R8G8B8A8;
|
||||
|
||||
if (isSrcRGB != isDestRGB) {
|
||||
SwapRAndBComponents(out_surface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Factory
|
||||
|
||||
|
|
|
@ -81,6 +81,8 @@ public:
|
|||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include "GLLibraryEGL.h"
|
||||
#include "GLReadTexImageHelper.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h" // for SurfaceDescriptor, etc
|
||||
#include "ScopedGLHelpers.h"
|
||||
#include "SharedSurface.h"
|
||||
#include "TextureGarbageBin.h"
|
||||
|
||||
|
@ -213,6 +212,14 @@ SharedSurface_EGLImage::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_EGLImage::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
MOZ_ASSERT(out_surface);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
return sEGLLibrary.ReadbackEGLImage(mImage, out_surface);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*static*/ UniquePtr<SurfaceFactory_EGLImage>
|
||||
|
|
|
@ -79,6 +79,8 @@ public:
|
|||
void AcquireConsumerTexture(GLContext* consGL, GLuint* out_texture, GLuint* out_target);
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "GLContextProvider.h"
|
||||
#include "GLContextGLX.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "mozilla/gfx/SourceSurfaceCairo.h"
|
||||
#include "mozilla/layers/LayersSurfaces.h"
|
||||
#include "mozilla/layers/ShadowLayerUtilsX11.h"
|
||||
#include "mozilla/layers/ISurfaceAllocator.h"
|
||||
|
@ -83,6 +84,38 @@ SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_GLXDrawable::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
MOZ_ASSERT(out_surface);
|
||||
RefPtr<gfx::DataSourceSurface> dataSurf =
|
||||
new gfx::DataSourceSurfaceCairo(mXlibSurface->CairoSurface());
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap mapSrc(dataSurf, gfx::DataSourceSurface::READ);
|
||||
if (!mapSrc.IsMapped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap mapDest(out_surface, gfx::DataSourceSurface::WRITE);
|
||||
if (!mapDest.IsMapped()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mapDest.GetStride() == mapSrc.GetStride()) {
|
||||
memcpy(mapDest.GetData(),
|
||||
mapSrc.GetData(),
|
||||
out_surface->GetSize().height * mapDest.GetStride());
|
||||
} else {
|
||||
for (int32_t i = 0; i < dataSurf->GetSize().height; i++) {
|
||||
memcpy(mapDest.GetData() + i * mapDest.GetStride(),
|
||||
mapSrc.GetData() + i * mapSrc.GetStride(),
|
||||
std::min(mapSrc.GetStride(), mapDest.GetStride()));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
UniquePtr<SurfaceFactory_GLXDrawable>
|
||||
SurfaceFactory_GLXDrawable::Create(GLContext* prodGL,
|
||||
|
|
|
@ -32,6 +32,8 @@ public:
|
|||
virtual void UnlockProdImpl() override;
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
|
||||
private:
|
||||
SharedSurface_GLXDrawable(GLContext* gl,
|
||||
const gfx::IntSize& size,
|
||||
|
|
|
@ -283,5 +283,58 @@ SharedSurface_Gralloc::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_
|
|||
return mTextureClient->ToSurfaceDescriptor(*out_descriptor);
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_Gralloc::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
MOZ_ASSERT(out_surface);
|
||||
sp<GraphicBuffer> buffer = mTextureClient->GetGraphicBuffer();
|
||||
|
||||
const uint8_t* grallocData = nullptr;
|
||||
auto result = buffer->lock(
|
||||
GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_NEVER,
|
||||
const_cast<void**>(reinterpret_cast<const void**>(&grallocData))
|
||||
);
|
||||
|
||||
if (result == BAD_VALUE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
buffer->unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t stride = buffer->getStride() * android::bytesPerPixel(buffer->getPixelFormat());
|
||||
uint32_t height = buffer->getHeight();
|
||||
uint32_t width = buffer->getWidth();
|
||||
for (uint32_t i = 0; i < height; i++) {
|
||||
memcpy(map.GetData() + i * map.GetStride(),
|
||||
grallocData + i * stride, width * 4);
|
||||
}
|
||||
|
||||
buffer->unlock();
|
||||
|
||||
android::PixelFormat srcFormat = buffer->getPixelFormat();
|
||||
MOZ_ASSERT(srcFormat == PIXEL_FORMAT_RGBA_8888 ||
|
||||
srcFormat == PIXEL_FORMAT_BGRA_8888 ||
|
||||
srcFormat == PIXEL_FORMAT_RGBX_8888);
|
||||
bool isSrcRGB = srcFormat == PIXEL_FORMAT_RGBA_8888 ||
|
||||
srcFormat == PIXEL_FORMAT_RGBX_8888;
|
||||
|
||||
gfx::SurfaceFormat destFormat = out_surface->GetFormat();
|
||||
MOZ_ASSERT(destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::R8G8B8A8 ||
|
||||
destFormat == gfx::SurfaceFormat::B8G8R8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::B8G8R8A8);
|
||||
bool isDestRGB = destFormat == gfx::SurfaceFormat::R8G8B8X8 ||
|
||||
destFormat == gfx::SurfaceFormat::R8G8B8A8;
|
||||
|
||||
if (isSrcRGB != isDestRGB) {
|
||||
SwapRAndBComponents(out_surface);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -75,6 +75,8 @@ public:
|
|||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
|
||||
};
|
||||
|
||||
class SurfaceFactory_Gralloc
|
||||
|
|
|
@ -182,6 +182,31 @@ SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const ou
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SharedSurface_IOSurface::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
|
||||
{
|
||||
MOZ_ASSERT(out_surface);
|
||||
mIOSurf->Lock();
|
||||
size_t bytesPerRow = mIOSurf->GetBytesPerRow();
|
||||
size_t ioWidth = mIOSurf->GetDevicePixelWidth();
|
||||
size_t ioHeight = mIOSurf->GetDevicePixelHeight();
|
||||
|
||||
const unsigned char* ioData = (unsigned char*)mIOSurf->GetBaseAddress();
|
||||
gfx::DataSourceSurface::ScopedMap map(out_surface, gfx::DataSourceSurface::WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
mIOSurf->Unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ioHeight; i++) {
|
||||
memcpy(map.GetData() + i * map.GetStride(),
|
||||
ioData + i * bytesPerRow, ioWidth * 4);
|
||||
}
|
||||
|
||||
mIOSurf->Unlock();
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// SurfaceFactory_IOSurface
|
||||
|
||||
|
|
|
@ -68,6 +68,8 @@ public:
|
|||
}
|
||||
|
||||
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;
|
||||
|
||||
virtual bool ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface) override;
|
||||
};
|
||||
|
||||
class SurfaceFactory_IOSurface : public SurfaceFactory
|
||||
|
|
|
@ -0,0 +1,279 @@
|
|||
/* -*- 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 "AsyncCanvasRenderer.h"
|
||||
|
||||
#include "gfxUtils.h"
|
||||
#include "GLContext.h"
|
||||
#include "GLReadTexImageHelper.h"
|
||||
#include "GLScreenBuffer.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "mozilla/layers/CanvasClient.h"
|
||||
#include "mozilla/layers/TextureClient.h"
|
||||
#include "mozilla/layers/TextureClientSharedSurface.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
AsyncCanvasRenderer::AsyncCanvasRenderer()
|
||||
: mHTMLCanvasElement(nullptr)
|
||||
, mContext(nullptr)
|
||||
, mGLContext(nullptr)
|
||||
, mIsAlphaPremultiplied(true)
|
||||
, mWidth(0)
|
||||
, mHeight(0)
|
||||
, mCanvasClientAsyncID(0)
|
||||
, mCanvasClient(nullptr)
|
||||
, mMutex("AsyncCanvasRenderer::mMutex")
|
||||
{
|
||||
MOZ_COUNT_CTOR(AsyncCanvasRenderer);
|
||||
}
|
||||
|
||||
AsyncCanvasRenderer::~AsyncCanvasRenderer()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AsyncCanvasRenderer);
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::NotifyElementAboutAttributesChanged()
|
||||
{
|
||||
class Runnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer) {
|
||||
dom::HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(mRenderer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new Runnable(this);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch a runnable to the main-thread.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::NotifyElementAboutInvalidation()
|
||||
{
|
||||
class Runnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit Runnable(AsyncCanvasRenderer* aRenderer)
|
||||
: mRenderer(aRenderer)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
if (mRenderer) {
|
||||
dom::HTMLCanvasElement::InvalidateFromAsyncCanvasRenderer(mRenderer);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Revoke()
|
||||
{
|
||||
mRenderer = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<AsyncCanvasRenderer> mRenderer;
|
||||
};
|
||||
|
||||
nsRefPtr<nsRunnable> runnable = new Runnable(this);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch a runnable to the main-thread.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::SetCanvasClient(CanvasClient* aClient)
|
||||
{
|
||||
mCanvasClient = aClient;
|
||||
if (aClient) {
|
||||
mCanvasClientAsyncID = aClient->GetAsyncID();
|
||||
} else {
|
||||
mCanvasClientAsyncID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::SetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mActiveThread = NS_GetCurrentThread();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::ResetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mActiveThread = nullptr;
|
||||
}
|
||||
|
||||
already_AddRefed<nsIThread>
|
||||
AsyncCanvasRenderer::GetActiveThread()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsCOMPtr<nsIThread> result = mActiveThread;
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
void
|
||||
AsyncCanvasRenderer::CopyFromTextureClient(TextureClient* aTextureClient)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
RefPtr<BufferTextureClient> buffer = static_cast<BufferTextureClient*>(aTextureClient);
|
||||
if (!buffer->Lock(layers::OpenMode::OPEN_READ)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const gfx::IntSize& size = aTextureClient->GetSize();
|
||||
// This buffer would be used later for content rendering. So we choose
|
||||
// B8G8R8A8 format here.
|
||||
const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
|
||||
// Avoid to create buffer every time.
|
||||
if (!mSurfaceForBasic ||
|
||||
size != mSurfaceForBasic->GetSize() ||
|
||||
format != mSurfaceForBasic->GetFormat())
|
||||
{
|
||||
uint32_t stride = gfx::GetAlignedStride<8>(size.width * BytesPerPixel(format));
|
||||
mSurfaceForBasic = gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
|
||||
}
|
||||
|
||||
const uint8_t* lockedBytes = buffer->GetLockedData();
|
||||
gfx::DataSourceSurface::ScopedMap map(mSurfaceForBasic,
|
||||
gfx::DataSourceSurface::MapType::WRITE);
|
||||
if (!map.IsMapped()) {
|
||||
buffer->Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(map.GetData(), lockedBytes, map.GetStride() * mSurfaceForBasic->GetSize().height);
|
||||
buffer->Unlock();
|
||||
|
||||
if (mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8A8 ||
|
||||
mSurfaceForBasic->GetFormat() == gfx::SurfaceFormat::R8G8B8X8) {
|
||||
gl::SwapRAndBComponents(mSurfaceForBasic);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
AsyncCanvasRenderer::UpdateTarget()
|
||||
{
|
||||
if (!mGLContext) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gl::SharedSurface* frontbuffer = nullptr;
|
||||
gl::GLScreenBuffer* screen = mGLContext->Screen();
|
||||
const auto& front = screen->Front();
|
||||
if (front) {
|
||||
frontbuffer = front->Surf();
|
||||
}
|
||||
|
||||
if (!frontbuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (frontbuffer->mType == gl::SharedSurfaceType::Basic) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const gfx::IntSize& size = frontbuffer->mSize;
|
||||
// This buffer would be used later for content rendering. So we choose
|
||||
// B8G8R8A8 format here.
|
||||
const gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
|
||||
uint32_t stride = gfx::GetAlignedStride<8>(size.width * BytesPerPixel(format));
|
||||
RefPtr<gfx::DataSourceSurface> surface =
|
||||
gfx::Factory::CreateDataSourceSurfaceWithStride(size, format, stride);
|
||||
|
||||
|
||||
if (NS_WARN_IF(!surface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!frontbuffer->ReadbackBySharedHandle(surface)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool needsPremult = frontbuffer->mHasAlpha && !mIsAlphaPremultiplied;
|
||||
if (needsPremult) {
|
||||
gfxUtils::PremultiplyDataSurface(surface, surface);
|
||||
}
|
||||
|
||||
return surface.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<gfx::DataSourceSurface>
|
||||
AsyncCanvasRenderer::GetSurface()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MutexAutoLock lock(mMutex);
|
||||
if (mSurfaceForBasic) {
|
||||
// Since SourceSurface isn't thread-safe, we need copy to a new SourceSurface.
|
||||
RefPtr<gfx::DataSourceSurface> result =
|
||||
gfx::Factory::CreateDataSourceSurfaceWithStride(mSurfaceForBasic->GetSize(),
|
||||
mSurfaceForBasic->GetFormat(),
|
||||
mSurfaceForBasic->Stride());
|
||||
|
||||
gfx::DataSourceSurface::ScopedMap srcMap(mSurfaceForBasic, gfx::DataSourceSurface::READ);
|
||||
gfx::DataSourceSurface::ScopedMap dstMap(result, gfx::DataSourceSurface::WRITE);
|
||||
|
||||
if (NS_WARN_IF(!srcMap.IsMapped()) ||
|
||||
NS_WARN_IF(!dstMap.IsMapped())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(dstMap.GetData(),
|
||||
srcMap.GetData(),
|
||||
srcMap.GetStride() * mSurfaceForBasic->GetSize().height);
|
||||
return result.forget();
|
||||
} else {
|
||||
return UpdateTarget();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
AsyncCanvasRenderer::GetInputStream(const char *aMimeType,
|
||||
const char16_t *aEncoderOptions,
|
||||
nsIInputStream **aStream)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
RefPtr<gfx::DataSourceSurface> surface = GetSurface();
|
||||
if (!surface) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Handle y flip.
|
||||
RefPtr<gfx::DataSourceSurface> dataSurf = gl::YInvertImageSurface(surface);
|
||||
|
||||
return gfxUtils::GetInputStream(dataSurf, false, aMimeType, aEncoderOptions, aStream);
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче