Merge m-c to fx-team on a CLOSED TREE

This commit is contained in:
Carsten "Tomcat" Book 2015-10-05 13:54:17 +02:00
Родитель 7d7128055f 131b0cd1dd
Коммит 15c18ce28f
144 изменённых файлов: 4497 добавлений и 1053 удалений

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

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

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