merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2015-07-28 16:28:02 +02:00
Родитель 815a154fae 0a02d60651
Коммит c86c8718cf
150 изменённых файлов: 2303 добавлений и 1106 удалений

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

@ -4,8 +4,6 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
. "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux32"
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-signmar

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

@ -5,8 +5,6 @@ MOZ_AUTOMATION_SDK=0
. "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux32"
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-signmar

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

@ -4,8 +4,6 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
. "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux"
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-signmar

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

@ -5,8 +5,6 @@ MOZ_AUTOMATION_SDK=0
. "$topsrcdir/b2g/config/mozconfigs/common"
. "$topsrcdir/build/unix/mozconfig.linux"
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-signmar

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

@ -4,7 +4,6 @@ MOZ_AUTOMATION_UPDATE_PACKAGING=0
MOZ_AUTOMATION_SDK=0
. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-application=b2g/dev
# Include Firefox OS fonts.

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

@ -112,7 +112,7 @@
#endif
#ifdef MOZ_GTK3
@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
#endif
[browser]

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

@ -53,7 +53,8 @@ var tests = {
}
waitForCondition(function() messageReceived == Social.providers.length,
next, "received messages from all workers");
next, "received messages from all workers",
/* increase timeout because shutting down a child process is slow */ 60);
},
testMultipleWorkerEnabling: function(next) {

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

@ -10,6 +10,9 @@ ac_add_options --enable-valgrind
. $topsrcdir/build/unix/mozconfig.asan
export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

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

@ -9,6 +9,9 @@ ac_add_options --enable-valgrind
. $topsrcdir/build/unix/mozconfig.asan
export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

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

@ -1,7 +1,5 @@
. $topsrcdir/browser/config/mozconfigs/linux32/nightly
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-valgrind
ac_add_options --disable-jemalloc
ac_add_options --disable-install-strip

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

@ -8,10 +8,11 @@ ac_add_options --enable-optimize="-O1"
# ASan specific options on Linux
ac_add_options --enable-valgrind
ac_add_options --enable-default-toolkit=cairo-gtk2
. $topsrcdir/build/unix/mozconfig.asan
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

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

@ -7,8 +7,6 @@ MOZ_AUTOMATION_L10N_CHECK=0
ac_add_options --enable-debug
ac_add_options --enable-dmd
ac_add_options --enable-default-toolkit=cairo-gtk2
# Use Clang as specified in manifest
export CC="$topsrcdir/clang/bin/clang"
export CXX="$topsrcdir/clang/bin/clang++"
@ -19,4 +17,7 @@ ac_add_options --enable-clang-plugin
# Avoid dependency on libstdc++ 4.7
ac_add_options --enable-stdcxx-compat
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
. "$topsrcdir/build/mozconfig.common.override"

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

@ -11,8 +11,6 @@
ac_add_options --enable-elf-hack
ac_add_options --enable-stdcxx-compat
ac_add_options --enable-default-toolkit=cairo-gtk2
# The objdir must be at a known location so its path can be stripped from the
# filenames stored by the analysis
mk_add_options MOZ_OBJDIR=obj-analyzed
@ -28,4 +26,7 @@ CFLAGS="$CFLAGS -Wno-attributes"
CPPFLAGS="$CPPFLAGS -Wno-attributes"
CXXFLAGS="$CXXFLAGS -Wno-attributes"
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
. "$topsrcdir/build/mozconfig.common.override"

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

@ -7,10 +7,11 @@ ac_add_options --enable-optimize="-O2 -gline-tables-only"
# ASan specific options on Linux
ac_add_options --enable-valgrind
ac_add_options --enable-default-toolkit=cairo-gtk2
. $topsrcdir/build/unix/mozconfig.asan
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
. $topsrcdir/build/unix/mozconfig.gtk
# Package js shell.
export MOZ_PACKAGE_JSSHELL=1

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

@ -1,13 +1,9 @@
. $topsrcdir/browser/config/mozconfigs/linux64/nightly
ac_add_options --enable-default-toolkit=cairo-gtk2
ac_add_options --enable-valgrind
ac_add_options --disable-jemalloc
ac_add_options --disable-install-strip
ac_add_options --enable-default-toolkit=cairo-gtk2
# Include the override mozconfig again (even though the above includes it)
# since it's supposed to override everything.
. "$topsrcdir/build/mozconfig.common.override"

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

@ -130,7 +130,7 @@
#endif
#ifdef MOZ_GTK3
@BINPATH@/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
@BINPATH@/@DLL_PREFIX@mozgtk2@DLL_SUFFIX@
@BINPATH@/gtk2/@DLL_PREFIX@mozgtk@DLL_SUFFIX@
#endif
[browser]

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

@ -358,6 +358,20 @@ fi
AC_SUBST(MOZ_PROGRAM_LDFLAGS)
dnl ASan assumes no symbols are being interposed, and when that happens,
dnl it's not happy with it. Unconveniently, since Firefox is exporting
dnl libffi symbols and Gtk+3 pulls system libffi via libwayland-client,
dnl system libffi interposes libffi symbols that ASan assumes are in
dnl libxul, so it barfs about buffer overflows.
dnl Using -Wl,-Bsymbolic ensures no exported symbol can be interposed.
if test -n "$GCC_USE_GNU_LD"; then
case "$LDFLAGS" in
*-fsanitize=address*)
LDFLAGS="$LDFLAGS -Wl,-Bsymbolic"
;;
esac
fi
])
dnl GCC and clang will fail if given an unknown warning option like -Wfoobar.

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

@ -805,7 +805,11 @@ void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag, QualTyp
}
bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const {
#if CLANG_VERSION_FULL >= 306
if (const TagDecl *D = T->getAsTagDecl()) {
#else
if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) {
#endif
return MozChecker::hasCustomAnnotation(D, Spelling);
}
return false;

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

@ -41,6 +41,13 @@ leak:nr_reg_set
# Additional WebRTC leak suppressions added for Mochitest 3.
leak:mozilla::TransportLayerDtls::Setup
# Bug 1187424 - DesktopApplication does not free any of its string members.
leak:webrtc::DesktopApplication::
# Bug 1187518 - SCTP leaks in child process while running WebRTC tests.
leak:recv_function_udp
###
### Many leaks only affect some test suites. The suite annotations are not checked.
###
@ -85,6 +92,22 @@ leak:_PR_Getfd
# Bug 1028483 - The XML parser sometimes leaks an object. bc3
leak:processInternalEntity
# Bug 1187421 - With e10s, NSS does not always free the error stack. m1.
leak:nss_ClearErrorStack
# Bug 1090570 - IPC Transport lifetime is not always managed correctly.
leak:mozilla::ipc::OpenDescriptor
leak:IPC::Channel::ChannelImpl::OutputQueuePush
leak:IPC::Channel::Channel
leak:base::MessagePumpLibevent::WatchFileDescriptor
# Bug 1122045 - Leaks in MessageLoop::MessageLoop()
leak:MessageLoop::MessageLoop
# This may not actually be related to MessageLoop.
leak:base::WaitableEvent::TimedWait
leak:MessageLoop::PostTask_Helper
###
### Leaks with system libraries in their stacks. These show up across a number of tests.
### Better symbols and disabling fast stackwalking may help diagnose these.

48
build/unix/mozconfig.gtk Normal file
Просмотреть файл

@ -0,0 +1,48 @@
# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it.
if [ -d "$topsrcdir/gtk3" ]; then
if [ -z "$PKG_CONFIG_LIBDIR" ]; then
echo PKG_CONFIG_LIBDIR must be set >&2
exit 1
fi
export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3"
export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig"
export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}"
# Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib ${LDFLAGS}"
mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
ac_add_options --enable-default-toolkit=cairo-gtk3
# Set things up to use Gtk+3 from the tooltool package
mk_add_options "export FONTCONFIG_PATH=$topsrcdir/gtk3/usr/local/etc/fonts"
mk_add_options "export PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc"
mk_add_options "export PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib"
mk_add_options "export GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache"
mk_add_options "export GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders"
mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
# pango expects absolute paths in pango.modules, and topsrcdir may vary...
LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
PANGO_SYSCONFDIR=$topsrcdir/gtk3/usr/local/etc \
PANGO_LIBDIR=$topsrcdir/gtk3/usr/local/lib \
$topsrcdir/gtk3/usr/local/bin/pango-querymodules > $topsrcdir/gtk3/usr/local/etc/pango/pango.modules
# same with gdb-pixbuf and loaders.cache
LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib \
GDK_PIXBUF_MODULE_FILE=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \
GDK_PIXBUF_MODULEDIR=$topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \
$topsrcdir/gtk3/usr/local/bin/gdk-pixbuf-query-loaders > $topsrcdir/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
# mock build environment doesn't have fonts in /usr/share/fonts, but
# has some in /usr/share/X11/fonts. Add this directory to the
# fontconfig configuration without changing the gtk3 tooltool package.
cat << EOF > $topsrcdir/gtk3/usr/local/etc/fonts/local.conf
<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<dir>/usr/share/X11/fonts</dir>
</fontconfig>
EOF
else
ac_add_options --enable-default-toolkit=cairo-gtk2
fi

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

@ -29,17 +29,9 @@ ac_add_options --enable-elf-hack
# Avoid dependency on libstdc++ 4.7
ac_add_options --enable-stdcxx-compat
# $topsrcdir/gtk3 comes from tooltool, when the tooltool manifest contains it.
if [ -d "$topsrcdir/gtk3" ]; then
# PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
export PKG_CONFIG_SYSROOT_DIR="$topsrcdir/gtk3"
export PKG_CONFIG_PATH="$topsrcdir/gtk3/usr/local/lib/pkgconfig"
export PATH="$topsrcdir/gtk3/usr/local/bin:${PATH}"
# Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages.
LDFLAGS="-L$topsrcdir/gtk3/usr/local/lib"
mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/gtk3/usr/local/lib"
ac_add_options --enable-default-toolkit=cairo-gtk3
fi
# PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32
export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig
export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
. "$topsrcdir/build/unix/mozconfig.gtk"

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

@ -1,5 +1,7 @@
. "$topsrcdir/build/unix/mozconfig.linux"
export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
if test `uname -m` = "x86_64"; then
# -march=pentiumpro is what our 32-bit native toolchain defaults to
CC="$CC -m32 -march=pentiumpro"
@ -7,5 +9,4 @@ if test `uname -m` = "x86_64"; then
ac_add_options --target=i686-pc-linux
ac_add_options --host=i686-pc-linux
ac_add_options --x-libraries=/usr/lib
export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig
fi

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

@ -92,6 +92,8 @@ class MachCommands(MachCommandBase):
env['MOZ_CRASHREPORTER_NO_REPORT'] = '1'
env['XPCOM_DEBUG_BREAK'] = 'warn'
env.update(self.extra_environment_variables)
outputHandler = OutputHandler()
kp_kwargs = {'processOutputLine': [outputHandler]}

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

@ -21,30 +21,6 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList)
/* static */ already_AddRefed<FileList>
FileList::Create(nsISupports* aParent, FileListClonedData* aData)
{
MOZ_ASSERT(aData);
nsRefPtr<FileList> fileList = new FileList(aParent);
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = aData->BlobImpls();
for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
const nsRefPtr<BlobImpl>& blobImpl = blobImpls[i];
MOZ_ASSERT(blobImpl);
MOZ_ASSERT(blobImpl->IsFile());
nsRefPtr<File> file = File::Create(aParent, blobImpl);
MOZ_ASSERT(file);
if (NS_WARN_IF(!fileList->Append(file))) {
return nullptr;
}
}
return fileList.forget();
}
JSObject*
FileList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
@ -67,19 +43,5 @@ FileList::Item(uint32_t aIndex, nsISupports** aFile)
return NS_OK;
}
already_AddRefed<FileListClonedData>
FileList::CreateClonedData() const
{
nsTArray<nsRefPtr<BlobImpl>> blobImpls;
for (uint32_t i = 0; i < mFiles.Length(); ++i) {
blobImpls.AppendElement(mFiles[i]->Impl());
}
nsRefPtr<FileListClonedData> data = new FileListClonedData(blobImpls);
return data.forget();
}
NS_IMPL_ISUPPORTS0(FileListClonedData)
} // namespace dom
} // namespace mozilla

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

@ -7,6 +7,8 @@
#ifndef mozilla_dom_FileList_h
#define mozilla_dom_FileList_h
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMFileList.h"
#include "nsWrapperCache.h"
@ -16,27 +18,6 @@ namespace dom {
class BlobImpls;
class File;
class FileListClonedData final : public nsISupports
{
public:
NS_DECL_THREADSAFE_ISUPPORTS
explicit FileListClonedData(const nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls)
: mBlobImpls(aBlobImpls)
{}
const nsTArray<nsRefPtr<BlobImpl>>& BlobImpls() const
{
return mBlobImpls;
}
private:
~FileListClonedData()
{}
const nsTArray<nsRefPtr<BlobImpl>> mBlobImpls;
};
class FileList final : public nsIDOMFileList,
public nsWrapperCache
{
@ -50,9 +31,6 @@ public:
: mParent(aParent)
{}
static already_AddRefed<FileList>
Create(nsISupports* aParent, FileListClonedData* aData);
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
@ -114,9 +92,6 @@ public:
return mFiles.Length();
}
// Useful for cloning
already_AddRefed<FileListClonedData> CreateClonedData() const;
private:
~FileList() {}

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

@ -28,7 +28,8 @@ PostMessageEvent::PostMessageEvent(nsGlobalWindow* aSource,
nsGlobalWindow* aTargetWindow,
nsIPrincipal* aProvidedPrincipal,
bool aTrustedCaller)
: mSource(aSource),
: StructuredCloneHelper(CloningSupported, TransferringSupported),
mSource(aSource),
mCallerOrigin(aCallerOrigin),
mTargetWindow(aTargetWindow),
mProvidedPrincipal(aProvidedPrincipal),

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

@ -199,8 +199,10 @@ StructuredCloneHelperInternal::FreeTransferCallback(uint32_t aTag,
// StructuredCloneHelper class
StructuredCloneHelper::StructuredCloneHelper(uint32_t aFlags)
: mFlags(aFlags)
StructuredCloneHelper::StructuredCloneHelper(CloningSupport aSupportsCloning,
TransferringSupport aSupportsTransferring)
: mSupportsCloning(aSupportsCloning == CloningSupported)
, mSupportsTransferring(aSupportsTransferring == TransferringSupported)
, mParent(nullptr)
{}
@ -209,6 +211,13 @@ StructuredCloneHelper::~StructuredCloneHelper()
Shutdown();
}
bool
StructuredCloneHelper::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue)
{
return Write(aCx, aValue, JS::UndefinedHandleValue);
}
bool
StructuredCloneHelper::Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
@ -227,7 +236,36 @@ StructuredCloneHelper::Read(nsISupports* aParent,
mozilla::AutoRestore<nsISupports*> guard(mParent);
mParent = aParent;
return StructuredCloneHelperInternal::Read(aCx, aValue);
bool ok = StructuredCloneHelperInternal::Read(aCx, aValue);
mBlobImplArray.Clear();
return ok;
}
bool
StructuredCloneHelper::ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls,
JS::MutableHandle<JS::Value> aValue)
{
MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write().");
MOZ_ASSERT(mBlobImplArray.IsEmpty());
MOZ_ASSERT(aBuffer);
MOZ_ASSERT_IF(!mSupportsCloning, aBlobImpls.IsEmpty());
mozilla::AutoRestore<nsISupports*> guard(mParent);
mParent = aParent;
mBlobImplArray.AppendElements(aBlobImpls);
bool ok = JS_ReadStructuredClone(aCx, aBuffer, aBufferLength,
JS_STRUCTURED_CLONE_VERSION, aValue,
&gCallbacks, this);
mBlobImplArray.Clear();
return ok;
}
JSObject*
@ -236,54 +274,59 @@ StructuredCloneHelper::ReadCallback(JSContext* aCx,
uint32_t aTag,
uint32_t aIndex)
{
MOZ_ASSERT(mSupportsCloning);
if (aTag == SCTAG_DOM_BLOB) {
MOZ_ASSERT(!(mFlags & eBlobNotSupported));
MOZ_ASSERT(aIndex < mBlobImplArray.Length());
nsRefPtr<BlobImpl> blobImpl = mBlobImplArray[aIndex];
BlobImpl* blobImpl;
if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) {
MOZ_ASSERT(blobImpl);
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> val(aCx);
{
nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
if (!ToJSValue(aCx, blob, &val)) {
return nullptr;
}
// nsRefPtr<File> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> val(aCx);
{
nsRefPtr<Blob> blob = Blob::Create(mParent, blobImpl);
if (!ToJSValue(aCx, blob, &val)) {
return nullptr;
}
return &val.toObject();
}
return &val.toObject();
}
if (aTag == SCTAG_DOM_FILELIST) {
MOZ_ASSERT(!(mFlags & eFileListNotSupported));
JS::Rooted<JS::Value> val(aCx);
{
nsRefPtr<FileList> fileList = new FileList(mParent);
FileListClonedData* fileListClonedData;
if (JS_ReadBytes(aReader, &fileListClonedData,
sizeof(fileListClonedData))) {
MOZ_ASSERT(fileListClonedData);
// |aIndex| is the number of BlobImpls to use from |offset|.
uint32_t tag, offset;
if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
return nullptr;
}
MOZ_ASSERT(tag == 0);
// nsRefPtr<FileList> needs to go out of scope before toObjectOrNull() is
// called because the static analysis thinks dereferencing XPCOM objects
// can GC (because in some cases it can!), and a return statement with a
// JSObject* type means that JSObject* is on the stack as a raw pointer
// while destructors are running.
JS::Rooted<JS::Value> val(aCx);
{
nsRefPtr<FileList> fileList =
FileList::Create(mParent, fileListClonedData);
if (!fileList || !ToJSValue(aCx, fileList, &val)) {
for (uint32_t i = 0; i < aIndex; ++i) {
uint32_t index = offset + i;
MOZ_ASSERT(index < mBlobImplArray.Length());
nsRefPtr<BlobImpl> blobImpl = mBlobImplArray[index];
MOZ_ASSERT(blobImpl->IsFile());
nsRefPtr<File> file = File::Create(mParent, blobImpl);
if (!fileList->Append(file)) {
return nullptr;
}
}
return &val.toObject();
if (!ToJSValue(aCx, fileList, &val)) {
return nullptr;
}
}
return &val.toObject();
}
return NS_DOMReadStructuredClone(aCx, aReader, aTag, aIndex, nullptr);
@ -294,27 +337,43 @@ StructuredCloneHelper::WriteCallback(JSContext* aCx,
JSStructuredCloneWriter* aWriter,
JS::Handle<JSObject*> aObj)
{
if (!mSupportsCloning) {
return false;
}
// See if this is a File/Blob object.
if (!(mFlags & eBlobNotSupported)) {
{
Blob* blob = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob))) {
BlobImpl* blobImpl = blob->Impl();
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB, 0) &&
JS_WriteBytes(aWriter, &blobImpl, sizeof(blobImpl)) &&
StoreISupports(blobImpl);
if (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
mBlobImplArray.Length())) {
mBlobImplArray.AppendElement(blobImpl);
return true;
}
return false;
}
}
if (!(mFlags & eFileListNotSupported)) {
{
FileList* fileList = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
nsRefPtr<FileListClonedData> fileListClonedData =
fileList->CreateClonedData();
MOZ_ASSERT(fileListClonedData);
FileListClonedData* ptr = fileListClonedData.get();
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST, 0) &&
JS_WriteBytes(aWriter, &ptr, sizeof(ptr)) &&
StoreISupports(fileListClonedData);
// A FileList is serialized writing the X number of elements and the offset
// from mBlobImplArray. The Read will take X elements from mBlobImplArray
// starting from the offset.
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
fileList->Length()) ||
!JS_WriteUint32Pair(aWriter, 0,
mBlobImplArray.Length())) {
return false;
}
for (uint32_t i = 0; i < fileList->Length(); ++i) {
mBlobImplArray.AppendElement(fileList->Item(i)->Impl());
}
return true;
}
}
@ -329,9 +388,9 @@ StructuredCloneHelper::ReadTransferCallback(JSContext* aCx,
uint64_t aExtraData,
JS::MutableHandleObject aReturnObject)
{
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
MOZ_ASSERT(mSupportsTransferring);
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
// This can be null.
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mParent);
@ -370,7 +429,11 @@ StructuredCloneHelper::WriteTransferCallback(JSContext* aCx,
void** aContent,
uint64_t* aExtraData)
{
if (!(mFlags & eMessagePortNotSupported)) {
if (!mSupportsTransferring) {
return false;
}
{
MessagePortBase* port = nullptr;
nsresult rv = UNWRAP_OBJECT(MessagePort, aObj, port);
if (NS_SUCCEEDED(rv)) {
@ -406,8 +469,9 @@ StructuredCloneHelper::FreeTransferCallback(uint32_t aTag,
void* aContent,
uint64_t aExtraData)
{
MOZ_ASSERT(mSupportsTransferring);
if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
MOZ_ASSERT(!aContent);
MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
MessagePort::ForceClose(mPortIdentifiers[aExtraData]);

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

@ -76,6 +76,18 @@ public:
bool Read(JSContext* aCx,
JS::MutableHandle<JS::Value> aValue);
uint64_t* BufferData() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->data();
}
size_t BufferSize() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBuffer->nbytes();
}
protected:
nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
@ -84,32 +96,36 @@ protected:
#endif
};
class BlobImpl;
class MessagePortBase;
class MessagePortIdentifier;
class StructuredCloneHelper : public StructuredCloneHelperInternal
{
public:
enum StructuredCloneHelperFlags {
eAll = 0,
// Disable the cloning of blobs. If a blob is part of the cloning value,
// an exception will be thrown.
eBlobNotSupported = 1 << 0,
// Disable the cloning of FileLists. If a FileList is part of the cloning
// value, an exception will be thrown.
eFileListNotSupported = 1 << 1,
// MessagePort can just be transfered. Using this flag we do not support
// the transfering.
eMessagePortNotSupported = 1 << 2,
enum CloningSupport
{
CloningSupported,
CloningNotSupported
};
// aFlags is a bitmap of StructuredCloneHelperFlags.
explicit StructuredCloneHelper(uint32_t aFlags = eAll);
enum TransferringSupport
{
TransferringSupported,
TransferringNotSupported
};
// If cloning is supported, this object will clone objects such as Blobs,
// FileList, ImageData, etc.
// If transferring is supported, we will transfer MessagePorts and in the
// future other transferrable objects.
explicit StructuredCloneHelper(CloningSupport aSupportsCloning,
TransferringSupport aSupportsTransferring);
virtual ~StructuredCloneHelper();
bool Write(JSContext* aCx,
JS::Handle<JS::Value> aValue);
bool Write(JSContext* aCx,
JS::Handle<JS::Value> aValue,
JS::Handle<JS::Value> aTransfer);
@ -118,9 +134,22 @@ public:
JSContext* aCx,
JS::MutableHandle<JS::Value> aValue);
bool ReadFromBuffer(nsISupports* aParent,
JSContext* aCx,
uint64_t* aBuffer,
size_t aBufferLength,
nsTArray<nsRefPtr<BlobImpl>>& aBlobImpls,
JS::MutableHandle<JS::Value> aValue);
const nsTArray<nsRefPtr<BlobImpl>>& ClonedBlobImpls() const
{
MOZ_ASSERT(mBuffer, "Write() has never been called.");
return mBlobImplArray;
}
nsTArray<nsRefPtr<MessagePortBase>>& GetTransferredPorts()
{
MOZ_ASSERT(!(mFlags & eMessagePortNotSupported));
MOZ_ASSERT(mSupportsTransferring);
return mTransferredPorts;
}
@ -154,19 +183,12 @@ public:
void* aContent,
uint64_t aExtraData) override;
private:
bool StoreISupports(nsISupports* aSupports)
{
MOZ_ASSERT(aSupports);
mSupportsArray.AppendElement(aSupports);
return true;
}
// This is our bitmap.
uint32_t mFlags;
bool mSupportsCloning;
bool mSupportsTransferring;
// Useful for the structured clone algorithm:
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
nsTArray<nsRefPtr<BlobImpl>> mBlobImplArray;
// This raw pointer is set and unset into the ::Read(). It's always null
// outside that method. For this reason it's a raw pointer.

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

@ -1250,21 +1250,11 @@ nsGlobalWindow::Init()
sWindowsById = new WindowByIdTable();
}
static PLDHashOperator
DisconnectEventTargetObjects(nsPtrHashKey<DOMEventTargetHelper>* aKey,
void* aClosure)
{
nsRefPtr<DOMEventTargetHelper> target = aKey->GetKey();
target->DisconnectFromOwner();
return PL_DHASH_NEXT;
}
nsGlobalWindow::~nsGlobalWindow()
{
AssertIsOnMainThread();
mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
mEventTargetObjects.Clear();
DisconnectEventTargetObjects();
// We have to check if sWindowsById isn't null because ::Shutdown might have
// been called.
@ -1375,6 +1365,17 @@ nsGlobalWindow::RemoveEventTargetObject(DOMEventTargetHelper* aObject)
mEventTargetObjects.RemoveEntry(aObject);
}
void
nsGlobalWindow::DisconnectEventTargetObjects()
{
for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done();
iter.Next()) {
nsRefPtr<DOMEventTargetHelper> target = iter.Get()->GetKey();
target->DisconnectFromOwner();
}
mEventTargetObjects.Clear();
}
// static
void
nsGlobalWindow::ShutDown()
@ -1434,8 +1435,7 @@ nsGlobalWindow::CleanUp()
StartDying();
mEventTargetObjects.EnumerateEntries(DisconnectEventTargetObjects, nullptr);
mEventTargetObjects.Clear();
DisconnectEventTargetObjects();
if (mObserver) {
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
@ -13577,27 +13577,6 @@ nsGlobalWindow::DisableTimeChangeNotifications()
mozilla::time::RemoveWindowListener(this);
}
static PLDHashOperator
CollectSizeAndListenerCount(
nsPtrHashKey<DOMEventTargetHelper>* aEntry,
void *arg)
{
nsWindowSizes* windowSizes = static_cast<nsWindowSizes*>(arg);
DOMEventTargetHelper* et = aEntry->GetKey();
if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
windowSizes->mDOMEventTargetsSize +=
iSizeOf->SizeOfEventTargetIncludingThis(windowSizes->mMallocSizeOf);
}
if (EventListenerManager* elm = et->GetExistingListenerManager()) {
windowSizes->mDOMEventListenersCount += elm->ListenerCount();
}
return PL_DHASH_NEXT;
}
void
nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
{
@ -13631,10 +13610,19 @@ nsGlobalWindow::AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const
aWindowSizes->mDOMEventTargetsSize +=
mEventTargetObjects.SizeOfExcludingThis(nullptr,
aWindowSizes->mMallocSizeOf);
aWindowSizes->mDOMEventTargetsCount +=
const_cast<nsTHashtable<nsPtrHashKey<DOMEventTargetHelper> >*>
(&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount,
aWindowSizes);
for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) {
DOMEventTargetHelper* et = iter.Get()->GetKey();
if (nsCOMPtr<nsISizeOfEventTarget> iSizeOf = do_QueryObject(et)) {
aWindowSizes->mDOMEventTargetsSize +=
iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf);
}
if (EventListenerManager* elm = et->GetExistingListenerManager()) {
aWindowSizes->mDOMEventListenersCount += elm->ListenerCount();
}
++aWindowSizes->mDOMEventTargetsCount;
}
}

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

@ -1617,6 +1617,8 @@ private:
// Fire the JS engine's onNewGlobalObject hook. Only used on inner windows.
void FireOnNewGlobalObject();
void DisconnectEventTargetObjects();
protected:
// When adding new member variables, be careful not to create cycles
// through JavaScript. If there is any chance that a member variable

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

@ -2493,7 +2493,13 @@ NS_DOMReadStructuredClone(JSContext* cx,
{
if (tag == SCTAG_DOM_IMAGEDATA) {
return ReadStructuredCloneImageData(cx, reader);
} else if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
}
if (tag == SCTAG_DOM_WEBCRYPTO_KEY) {
if (!NS_IsMainThread()) {
return nullptr;
}
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
if (!global) {
return nullptr;
@ -2510,9 +2516,15 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
}
return result;
} else if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
}
if (tag == SCTAG_DOM_NULL_PRINCIPAL ||
tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
tag == SCTAG_DOM_CONTENT_PRINCIPAL) {
if (!NS_IsMainThread()) {
return nullptr;
}
mozilla::ipc::PrincipalInfo info;
if (tag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
info = mozilla::ipc::SystemPrincipalInfo();
@ -2550,8 +2562,14 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
return result.toObjectOrNull();
} else if (tag == SCTAG_DOM_NFC_NDEF) {
}
#ifdef MOZ_NFC
if (tag == SCTAG_DOM_NFC_NDEF) {
if (!NS_IsMainThread()) {
return nullptr;
}
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
if (!global) {
return nullptr;
@ -2565,13 +2583,15 @@ NS_DOMReadStructuredClone(JSContext* cx,
ndefRecord->WrapObject(cx, nullptr) : nullptr;
}
return result;
#else
return nullptr;
#endif
}
#endif
if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
#ifdef MOZ_WEBRTC
if (tag == SCTAG_DOM_RTC_CERTIFICATE) {
if (!NS_IsMainThread()) {
return nullptr;
}
nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(cx));
if (!global) {
return nullptr;
@ -2588,10 +2608,8 @@ NS_DOMReadStructuredClone(JSContext* cx,
}
}
return result;
#else
return nullptr;
#endif
}
#endif
// Don't know what this is. Bail.
xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
@ -2613,6 +2631,7 @@ NS_DOMWriteStructuredClone(JSContext* cx,
// Handle Key cloning
CryptoKey* key;
if (NS_SUCCEEDED(UNWRAP_OBJECT(CryptoKey, obj, key))) {
MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
return JS_WriteUint32Pair(writer, SCTAG_DOM_WEBCRYPTO_KEY, 0) &&
key->WriteStructuredClone(writer);
}
@ -2621,12 +2640,13 @@ NS_DOMWriteStructuredClone(JSContext* cx,
// Handle WebRTC Certificate cloning
RTCCertificate* cert;
if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCCertificate, obj, cert))) {
MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
return JS_WriteUint32Pair(writer, SCTAG_DOM_RTC_CERTIFICATE, 0) &&
cert->WriteStructuredClone(writer);
}
#endif
if (xpc::IsReflector(obj)) {
if (NS_IsMainThread() && xpc::IsReflector(obj)) {
nsCOMPtr<nsISupports> base = xpc::UnwrapReflectorToISupports(obj);
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(base);
if (principal) {
@ -2654,6 +2674,7 @@ NS_DOMWriteStructuredClone(JSContext* cx,
#ifdef MOZ_NFC
MozNDEFRecord* ndefRecord;
if (NS_SUCCEEDED(UNWRAP_OBJECT(MozNDEFRecord, obj, ndefRecord))) {
MOZ_ASSERT(NS_IsMainThread(), "This object should not be exposed outside the main-thread.");
return JS_WriteUint32Pair(writer, SCTAG_DOM_NFC_NDEF, 0) &&
ndefRecord->WriteStructuredClone(cx, writer);
}

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

@ -3,7 +3,7 @@
<body>
<script>
onmessage = function(e) {
parent.postMessage(e.data, '*');
parent.postMessage(e.data, '*', e.ports);
}
</script>
</body>

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

@ -242,6 +242,8 @@ support-files =
file_webaudioLoop2.html
referrer_helper.js
referrer_testserver.sjs
script_postmessages_fileList.js
iframe_postMessages.html
[test_anonymousContent_api.html]
[test_anonymousContent_append_after_reflow.html]
@ -805,5 +807,4 @@ skip-if = buildapp == 'mulet' || buildapp == 'b2g'
[test_nonascii_blob_url.html]
[test_window_element_enumeration.html]
[test_referrer_redirect.html]
[test_cloning_fileList.html]
support-files = script_cloning_fileList.js iframe_cloning_fileList.html
[test_postMessages.html]

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

@ -1,85 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for cloning FileList</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<p id="display">
<input id="fileList" type="file"></input>
</p>
<pre id="test">
<script class="testbody" type="text/javascript">
var iframeScriptURL;
var url = SimpleTest.getTestFileURL("script_cloning_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
script.addMessageListener("file.opened", onOpened);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
// Just a simple test
var domFile = fileList.files[0];
is(domFile.name, "prefs.js", "fileName should be prefs.js");
var ifr = document.createElement('iframe');
ifr.src = iframeScriptURL;
ifr.onload = function() {
ifr.contentWindow.postMessage(fileList.files, "*");
}
document.body.appendChild(ifr);
}
function runTest() {
script.sendAsyncMessage("file.open");
}
onmessage = function(e) {
var fileList = document.getElementById('fileList');
ok(true, "Message received");
ok(e.data instanceof FileList, "The object is a FileList");
ok(e.data != fileList.files, "The object has been cloned!");
is(e.data.length, fileList.files.length, "The length matches");
is(e.data.length, 1, "1 element found!");
is(e.data[0].name, "prefs.js", "fileName should be prefs.js");
next();
}
var tests = [
function() {
// Same origin
iframeScriptURL = 'iframe_cloning_fileList.html';
runTest();
},
function() {
// Cross Origin
iframeScriptURL = 'http://example.com/tests/dom/base/test/iframe_cloning_fileList.html';
runTest();
}
];
function next() {
if (!tests.length) {
script.destroy();
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
next();
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,295 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for postMessages cloning/transferring objects</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<input id="fileList" type="file"></input>
<script type="application/javascript;version=1.7">
function getType(a) {
if (a === null || a === undefined)
return 'null';
if (Array.isArray(a))
return 'array';
if (typeof a == 'object')
return 'object';
return 'primitive';
}
function compare(a, b) {
is (getType(a), getType(b), 'Type matches');
var type = getType(a);
if (type == 'array') {
is (a.length, b.length, 'Array.length matches');
for (var i = 0; i < a.length; ++i) {
compare(a[i], b[i]);
}
return;
}
if (type == 'object') {
ok (a !== b, 'They should not match');
var aProps = [];
for (var p in a) aProps.push(p);
var bProps = [];
for (var p in b) bProps.push(p);
is (aProps.length, bProps.length, 'Props match');
is (aProps.sort().toSource(), bProps.sort().toSource(), 'Props match - using toSource()');
for (var p in a) {
compare(a[p], b[p]);
}
return;
}
if (type != 'null') {
is (a.toSource(), b.toSource(), 'Matching using toSource()');
}
}
var clonableObjects = [
'hello world',
123,
null,
true,
new Date(),
[ 1, 'test', true, new Date() ],
{ a: true, b: null, c: new Date(), d: [ true, false, {} ] },
new Blob([123], { type: 'plain/text' }),
new ImageData(2, 2),
];
function create_fileList() {
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetFileArray([message.file]);
// Just a simple test
var domFile = fileList.files[0];
is(domFile.name, "prefs.js", "fileName should be prefs.js");
clonableObjects.push(fileList.files);
script.destroy();
next();
}
script.addMessageListener("file.opened", onOpened);
script.sendAsyncMessage("file.open");
}
function runTests(obj) {
ok(('clonableObjects' in obj) &&
('transferableObjects' in obj) &&
(obj.clonableObjects || obj.transferableObjects), "We must run some test!");
// cloning tests
new Promise(function(resolve, reject) {
if (!obj.clonableObjects) {
resolve();
return;
}
var clonableObjectsId = 0;
function runClonableTest() {
if (clonableObjectsId >= clonableObjects.length) {
resolve();
return;
}
var object = clonableObjects[clonableObjectsId++];
obj.send(object, []).then(function(received) {
compare(received.data, object);
runClonableTest();
});
}
runClonableTest();
})
// transfering tests
.then(function() {
if (!obj.transferableObjects) {
return;
}
// MessagePort
return new Promise(function(r, rr) {
var mc = new MessageChannel();
obj.send(42, [mc.port1]).then(function(received) {
ok(received.ports.length, 1, "MessagePort has been transferred");
mc.port2.postMessage("hello world");
received.ports[0].onmessage = function(e) {
ok(e.data, "hello world", "Ports are connected!");
r();
}
});
});
})
// done.
.then(function() {
obj.finished();
});
}
// PostMessage to the same window.
function test_windowToWindow() {
info("Testing window to window");
var resolve;
onmessage = function(e) {
if (!resolve) {
ok(false, "Unexpected message!");
return;
}
let tmp = resolve;
resolve = null;
tmp({ data: e.data, ports: e.ports });
}
runTests({
clonableObjects: true,
transferableObjects: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
postMessage(what, '*', ports);
});
},
finished: function() {
onmessage = null;
next();
}
});
}
// PostMessage to iframe
function test_windowToIframe() {
info("Testing window to iframe");
test_windowToIframeURL('iframe_postMessages.html');
}
// PostMessage to cross-origin iframe
function test_windowToCrossOriginIframe() {
info("Testing window to cross-origin iframe");
test_windowToIframeURL('http://example.com/tests/dom/base/test/iframe_postMessages.html');
}
// iframe helper class
function test_windowToIframeURL(url) {
var resolve;
onmessage = function(e) {
if (!resolve) {
ok(false, "Unexpected message!");
return;
}
let tmp = resolve;
resolve = null;
tmp({ data: e.data, ports: e.ports });
}
var ifr = document.createElement('iframe');
ifr.src = url;
ifr.onload = function() {
runTests({
clonableObjects: true,
transferableObjects: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
ifr.contentWindow.postMessage(what, '*', ports);
});
},
finished: function() {
document.body.removeChild(ifr);
onmessage = null;
next();
}
});
}
document.body.appendChild(ifr);
}
function test_broadcastChannel() {
info("Testing broadcastChannel");
var bc1 = new BroadcastChannel('postMessagesTest');
var bc2 = new BroadcastChannel('postMessagesTest');
var resolve;
bc2.onmessage = function(e) {
if (!resolve) {
ok(false, "Unexpected message!");
return;
}
let tmp = resolve;
resolve = null;
tmp({ data: e.data, ports: [] });
}
runTests({
clonableObjects: true,
transferableObjects: false,
send: function(what, ports) {
is(ports.length, 0, "No ports for this test!");
return new Promise(function(r, rr) {
resolve = r;
bc1.postMessage(what);
});
},
finished: function() {
onmessage = null;
next();
}
});
}
var tests = [
create_fileList,
test_windowToWindow,
test_windowToIframe,
test_windowToCrossOriginIframe,
test_broadcastChannel,
];
function next() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
SpecialPowers.pushPrefEnv({"set": [["dom.messageChannel.enabled", true]]}, next);
</script>
</body>
</html>

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

@ -8,7 +8,8 @@
#include "BroadcastChannelChild.h"
#include "mozilla/dom/BroadcastChannelBinding.h"
#include "mozilla/dom/Navigator.h"
#include "mozilla/dom/StructuredCloneUtils.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/StructuredCloneHelper.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundChild.h"
@ -30,20 +31,18 @@ namespace dom {
using namespace workers;
class BroadcastChannelMessage final
class BroadcastChannelMessage final : public StructuredCloneHelper
{
public:
NS_INLINE_DECL_REFCOUNTING(BroadcastChannelMessage)
JSAutoStructuredCloneBuffer mBuffer;
StructuredCloneClosure mClosure;
BroadcastChannelMessage()
{ }
: StructuredCloneHelper(CloningSupported, TransferringNotSupported)
{}
private:
~BroadcastChannelMessage()
{ }
{}
};
namespace {
@ -166,13 +165,13 @@ public:
ClonedMessageData message;
SerializedStructuredCloneBuffer& buffer = message.data();
buffer.data = mData->mBuffer.data();
buffer.dataLength = mData->mBuffer.nbytes();
buffer.data = mData->BufferData();
buffer.dataLength = mData->BufferSize();
PBackgroundChild* backgroundManager = mActor->Manager();
MOZ_ASSERT(backgroundManager);
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->mClosure.mBlobImpls;
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = mData->ClonedBlobImpls();
if (!blobImpls.IsEmpty()) {
message.blobsChild().SetCapacity(blobImpls.Length());
@ -455,12 +454,12 @@ BroadcastChannel::PostMessageInternal(JSContext* aCx,
{
nsRefPtr<BroadcastChannelMessage> data = new BroadcastChannelMessage();
if (!WriteStructuredClone(aCx, aMessage, data->mBuffer, data->mClosure)) {
if (!data->Write(aCx, aMessage)) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return;
}
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->mClosure.mBlobImpls;
const nsTArray<nsRefPtr<BlobImpl>>& blobImpls = data->ClonedBlobImpls();
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
if (!blobImpls[i]->MayBeClonedToOtherThreads()) {
aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);

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

@ -11,7 +11,7 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/MessageEvent.h"
#include "mozilla/dom/MessageEventBinding.h"
#include "mozilla/dom/StructuredCloneUtils.h"
#include "mozilla/dom/StructuredCloneHelper.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/ScriptSettings.h"
@ -86,15 +86,15 @@ BroadcastChannelChild::RecvNotify(const ClonedMessageData& aData)
}
JSContext* cx = jsapi.cx();
const SerializedStructuredCloneBuffer& buffer = aData.data();
StructuredCloneData cloneData;
cloneData.mData = buffer.data;
cloneData.mDataLength = buffer.dataLength;
cloneData.mClosure.mBlobImpls.SwapElements(blobs);
StructuredCloneHelper cloneHelper(StructuredCloneHelper::CloningSupported,
StructuredCloneHelper::TransferringNotSupported);
JS::Rooted<JS::Value> value(cx, JS::NullValue());
if (cloneData.mDataLength && !ReadStructuredClone(cx, cloneData, &value)) {
if (buffer.dataLength &&
!cloneHelper.ReadFromBuffer(mBC->GetParentObject(), cx,
buffer.data, buffer.dataLength, blobs,
&value)) {
JS_ClearPendingException(cx);
return false;
}

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

@ -73,12 +73,6 @@ WebGLUtil = (function() {
gl = canvas.getContext('webgl2');
} catch(e) {}
if (!gl) {
try {
gl = canvas.getContext('experimental-webgl2');
} catch(e) {}
}
if (!gl) {
todo(false, 'WebGL2 is not supported');
onFinished();

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

@ -28,11 +28,11 @@ DataContainerEvent::DataContainerEvent(EventTarget* aOwner,
NS_IMPL_CYCLE_COLLECTION_CLASS(DataContainerEvent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DataContainerEvent, Event)
tmp->mData.Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mData)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DataContainerEvent, Event)
tmp->mData.EnumerateRead(TraverseEntry, &cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mData)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(DataContainerEvent, Event)
@ -81,18 +81,6 @@ DataContainerEvent::SetData(JSContext* aCx, const nsAString& aKey,
aRv = SetData(aKey, val);
}
PLDHashOperator
DataContainerEvent::TraverseEntry(const nsAString& aKey,
nsIVariant* aDataItem,
void* aUserArg)
{
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
cb->NoteXPCOMChild(aDataItem);
return PL_DHASH_NEXT;
}
} // namespace dom
} // namespace mozilla

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

@ -51,9 +51,6 @@ protected:
~DataContainerEvent() {}
private:
static PLDHashOperator
TraverseEntry(const nsAString& aKey, nsIVariant* aDataItem, void* aUserArg);
nsInterfaceHashtable<nsStringHashKey, nsIVariant> mData;
};

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

@ -737,7 +737,7 @@ GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
#endif
if (WebGL2Context::IsSupported()) {
if (str.EqualsLiteral("experimental-webgl2")) {
if (str.EqualsLiteral("webgl2")) {
*out_type = CanvasContextType::WebGL2;
return true;
}

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

@ -389,15 +389,6 @@ HTMLFormControlsCollection::NamedGetter(const nsAString& aName,
MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
}
static PLDHashOperator
CollectNames(const nsAString& aName,
nsISupports* /* unused */,
void* aClosure)
{
static_cast<nsTArray<nsString>*>(aClosure)->AppendElement(aName);
return PL_DHASH_NEXT;
}
void
HTMLFormControlsCollection::GetSupportedNames(unsigned aFlags,
nsTArray<nsString>& aNames)
@ -410,7 +401,9 @@ HTMLFormControlsCollection::GetSupportedNames(unsigned aFlags,
// Just enumerate mNameLookupTable. This won't guarantee order, but
// that's OK, because the HTML5 spec doesn't define an order for
// this enumeration.
mNameLookupTable.EnumerateRead(CollectNames, &aNames);
for (auto iter = mNameLookupTable.Iter(); !iter.Done(); iter.Next()) {
aNames.AppendElement(iter.Key());
}
}
/* virtual */ JSObject*

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

@ -132,17 +132,6 @@ HTMLFormElement::~HTMLFormElement()
// nsISupports
static PLDHashOperator
ElementTraverser(const nsAString& key, HTMLInputElement* element,
void* userArg)
{
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(userArg);
cb->NoteXPCOMChild(ToSupports(element));
return PL_DHASH_NEXT;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFormElement,
@ -150,7 +139,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLFormElement,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
tmp->mSelectedRadioButtons.EnumerateRead(ElementTraverser, &cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedRadioButtons)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLFormElement,

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

@ -1706,36 +1706,6 @@ NS_IMETHODIMP HTMLMediaElement::SetVolume(double aVolume)
return rv.StealNSResult();
}
// Helper struct with arguments for our hash iterator.
typedef struct MOZ_STACK_CLASS {
JSContext* cx;
JS::Handle<JSObject*> tags;
bool error;
} MetadataIterCx;
PLDHashOperator
HTMLMediaElement::BuildObjectFromTags(nsCStringHashKey::KeyType aKey,
nsCString aValue,
void* aUserArg)
{
MetadataIterCx* args = static_cast<MetadataIterCx*>(aUserArg);
nsString wideValue = NS_ConvertUTF8toUTF16(aValue);
JS::Rooted<JSString*> string(args->cx, JS_NewUCStringCopyZ(args->cx, wideValue.Data()));
if (!string) {
NS_WARNING("Failed to perform string copy");
args->error = true;
return PL_DHASH_STOP;
}
if (!JS_DefineProperty(args->cx, args->tags, aKey.Data(), string, JSPROP_ENUMERATE)) {
NS_WARNING("Failed to set metadata property");
args->error = true;
return PL_DHASH_STOP;
}
return PL_DHASH_NEXT;
}
void
HTMLMediaElement::MozGetMetadata(JSContext* cx,
JS::MutableHandle<JSObject*> aRetval,
@ -1752,12 +1722,16 @@ HTMLMediaElement::MozGetMetadata(JSContext* cx,
return;
}
if (mTags) {
MetadataIterCx iter = {cx, tags, false};
mTags->EnumerateRead(BuildObjectFromTags, static_cast<void*>(&iter));
if (iter.error) {
NS_WARNING("couldn't create metadata object!");
aRv.Throw(NS_ERROR_FAILURE);
return;
for (auto iter = mTags->ConstIter(); !iter.Done(); iter.Next()) {
nsString wideValue = NS_ConvertUTF8toUTF16(iter.UserData());
JS::Rooted<JSString*> string(cx,
JS_NewUCStringCopyZ(cx, wideValue.Data()));
if (!string || !JS_DefineProperty(cx, tags, iter.Key().Data(), string,
JSPROP_ENUMERATE)) {
NS_WARNING("couldn't create metadata object!");
aRv.Throw(NS_ERROR_FAILURE);
return;
}
}
}

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

@ -73,14 +73,6 @@ NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLPropertiesCollection)
NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLPropertiesCollection)
static PLDHashOperator
SetPropertyListDocument(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
{
aEntry->SetDocument(static_cast<nsIDocument*>(aData));
return PL_DHASH_NEXT;
}
void
HTMLPropertiesCollection::SetDocument(nsIDocument* aDocument) {
if (mDoc) {
@ -90,7 +82,9 @@ HTMLPropertiesCollection::SetDocument(nsIDocument* aDocument) {
if (mDoc) {
mDoc->AddMutationObserver(this);
}
mNamedItemEntries.EnumerateRead(SetPropertyListDocument, aDocument);
for (auto iter = mNamedItemEntries.Iter(); !iter.Done(); iter.Next()) {
iter.UserData()->SetDocument(aDocument);
}
mIsDirty = true;
}
@ -191,13 +185,6 @@ HTMLPropertiesCollection::ContentRemoved(nsIDocument *aDocument,
mIsDirty = true;
}
static PLDHashOperator
MarkDirty(const nsAString& aKey, PropertyNodeList* aEntry, void* aData)
{
aEntry->SetDirty();
return PL_DHASH_NEXT;
}
void
HTMLPropertiesCollection::EnsureFresh()
{
@ -209,7 +196,9 @@ HTMLPropertiesCollection::EnsureFresh()
mProperties.Clear();
mNames->Clear();
// We don't clear NamedItemEntries because the PropertyNodeLists must be live.
mNamedItemEntries.EnumerateRead(MarkDirty, nullptr);
for (auto iter = mNamedItemEntries.Iter(); !iter.Done(); iter.Next()) {
iter.UserData()->SetDirty();
}
if (!mRoot->HasAttr(kNameSpaceID_None, nsGkAtoms::itemscope)) {
return;
}

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

@ -1909,7 +1909,7 @@ ContentChild::ActorDestroy(ActorDestroyReason why)
QuickExit();
}
#ifndef DEBUG
#if !defined(DEBUG) && !defined(MOZ_ASAN)
// In release builds, there's no point in the content process
// going through the full XPCOM shutdown path, because it doesn't
// keep persistent state.

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

@ -148,7 +148,14 @@ struct AudioChunk {
return true;
}
int ChannelCount() const { return mChannelData.Length(); }
size_t ChannelCount() const { return mChannelData.Length(); }
float* ChannelFloatsForWrite(size_t aChannel)
{
MOZ_ASSERT(mBufferFormat == AUDIO_FORMAT_FLOAT32);
MOZ_ASSERT(!mBuffer->IsShared());
return static_cast<float*>(const_cast<void*>(mChannelData[aChannel]));
}
bool IsMuted() const { return mVolume == 0.0f; }

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

@ -10,6 +10,7 @@
#include "VideoUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
namespace mozilla {
@ -28,6 +29,8 @@ AudioSink::AudioSink(MediaQueue<AudioData>& aAudioQueue,
dom::AudioChannel aChannel)
: mAudioQueue(aAudioQueue)
, mMonitor("AudioSink::mMonitor")
, mState(AUDIOSINK_STATE_INIT)
, mAudioLoopScheduled(false)
, mStartTime(aStartTime)
, mWritten(0)
, mLastGoodPosition(0)
@ -44,6 +47,48 @@ AudioSink::AudioSink(MediaQueue<AudioData>& aAudioQueue,
{
}
void
AudioSink::SetState(State aState)
{
AssertOnAudioThread();
mPendingState = Some(aState);
}
void
AudioSink::DispatchTask(already_AddRefed<nsIRunnable>&& event)
{
DebugOnly<nsresult> rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL);
// There isn't much we can do if Dispatch() fails.
// Just assert it to keep things simple.
MOZ_ASSERT(NS_SUCCEEDED(rv));
}
void
AudioSink::ScheduleNextLoop()
{
AssertOnAudioThread();
if (mAudioLoopScheduled) {
return;
}
mAudioLoopScheduled = true;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
DispatchTask(r.forget());
}
void
AudioSink::ScheduleNextLoopCrossThread()
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () {
// Do nothing if there is already a pending task waiting for its turn.
if (!self->mAudioLoopScheduled) {
self->AudioLoop();
}
});
DispatchTask(r.forget());
}
nsRefPtr<GenericPromise>
AudioSink::Init()
{
@ -57,13 +102,7 @@ AudioSink::Init()
return p;
}
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL);
if (NS_FAILED(rv)) {
mEndPromise.Reject(rv, __func__);
return p;
}
ScheduleNextLoopCrossThread();
return p;
}
@ -99,7 +138,7 @@ AudioSink::Shutdown()
if (mAudioStream) {
mAudioStream->Cancel();
}
GetReentrantMonitor().NotifyAll();
ScheduleNextLoopCrossThread();
// Exit the monitor so audio loop can enter the monitor and finish its job.
ReentrantMonitorAutoExit exit(GetReentrantMonitor());
@ -109,6 +148,12 @@ AudioSink::Shutdown()
mAudioStream->Shutdown();
mAudioStream = nullptr;
}
// Should've reached the final state after shutdown.
MOZ_ASSERT(mState == AUDIOSINK_STATE_SHUTDOWN ||
mState == AUDIOSINK_STATE_ERROR);
// Should have no pending state change.
MOZ_ASSERT(mPendingState.isNothing());
}
void
@ -141,73 +186,13 @@ AudioSink::SetPlaying(bool aPlaying)
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
mPlaying = aPlaying;
GetReentrantMonitor().NotifyAll();
ScheduleNextLoopCrossThread();
}
void
AudioSink::NotifyData()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
GetReentrantMonitor().NotifyAll();
}
void
AudioSink::AudioLoop()
{
AssertOnAudioThread();
SINK_LOG("AudioLoop started");
nsresult rv = InitializeAudioStream();
if (NS_FAILED(rv)) {
NS_WARNING("Initializing AudioStream failed.");
mEndPromise.Reject(rv, __func__);
return;
}
while (1) {
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
WaitForAudioToPlay();
if (!IsPlaybackContinuing()) {
break;
}
}
// See if there's a gap in the audio. If there is, push silence into the
// audio hardware, so we can play across the gap.
// Calculate the timestamp of the next chunk of audio in numbers of
// samples.
NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
// Calculate the number of frames that have been pushed onto the audio hardware.
CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
static_cast<int64_t>(mWritten);
CheckedInt64 missingFrames = sampleTime - playedFrames;
if (!missingFrames.isValid() || !sampleTime.isValid()) {
NS_WARNING("Int overflow adding in AudioLoop");
break;
}
if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
// The next audio chunk begins some time after the end of the last chunk
// we pushed to the audio hardware. We must push silence into the audio
// hardware so that the next audio chunk begins playback at the correct
// time.
missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
} else {
mWritten += PlayFromAudioQueue();
}
}
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
if (!mStopAudioThread && mPlaying) {
Drain();
}
SINK_LOG("AudioLoop complete");
Cleanup();
SINK_LOG("AudioLoop exit");
ScheduleNextLoopCrossThread();
}
nsresult
@ -261,18 +246,19 @@ AudioSink::ExpectMoreAudioData()
return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
}
void
AudioSink::WaitForAudioToPlay()
bool
AudioSink::WaitingForAudioToPlay()
{
// Wait while we're not playing, and we're not shutting down, or we're
// Return true if we're not playing, and we're not shutting down, or we're
// playing and we've got no audio to play.
AssertCurrentThreadInMonitor();
while (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
if (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
if (!mPlaying && !mAudioStream->IsPaused()) {
mAudioStream->Pause();
}
GetReentrantMonitor().Wait();
return true;
}
return false;
}
bool
@ -294,6 +280,119 @@ AudioSink::IsPlaybackContinuing()
return true;
}
void
AudioSink::AudioLoop()
{
AssertOnAudioThread();
mAudioLoopScheduled = false;
switch (mState) {
case AUDIOSINK_STATE_INIT: {
SINK_LOG("AudioLoop started");
nsresult rv = InitializeAudioStream();
if (NS_FAILED(rv)) {
NS_WARNING("Initializing AudioStream failed.");
mEndPromise.Reject(rv, __func__);
SetState(AUDIOSINK_STATE_ERROR);
break;
}
SetState(AUDIOSINK_STATE_PLAYING);
break;
}
case AUDIOSINK_STATE_PLAYING: {
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
if (WaitingForAudioToPlay()) {
// NotifyData() will schedule next loop.
break;
}
if (!IsPlaybackContinuing()) {
SetState(AUDIOSINK_STATE_COMPLETE);
break;
}
}
if (!PlayAudio()) {
SetState(AUDIOSINK_STATE_COMPLETE);
break;
}
// Schedule next loop to play next sample.
ScheduleNextLoop();
break;
}
case AUDIOSINK_STATE_COMPLETE: {
FinishAudioLoop();
SetState(AUDIOSINK_STATE_SHUTDOWN);
break;
}
case AUDIOSINK_STATE_SHUTDOWN:
break;
case AUDIOSINK_STATE_ERROR:
break;
} // end of switch
// We want mState to stay stable during AudioLoop to keep things simple.
// Therefore, we only do state transition at the end of AudioLoop.
if (mPendingState.isSome()) {
MOZ_ASSERT(mState != mPendingState.ref());
SINK_LOG("change mState, %d -> %d", mState, mPendingState.ref());
mState = mPendingState.ref();
mPendingState.reset();
// Schedule next loop when state changes.
ScheduleNextLoop();
}
}
bool
AudioSink::PlayAudio()
{
// See if there's a gap in the audio. If there is, push silence into the
// audio hardware, so we can play across the gap.
// Calculate the timestamp of the next chunk of audio in numbers of
// samples.
NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
// Calculate the number of frames that have been pushed onto the audio hardware.
CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) +
static_cast<int64_t>(mWritten);
CheckedInt64 missingFrames = sampleTime - playedFrames;
if (!missingFrames.isValid() || !sampleTime.isValid()) {
NS_WARNING("Int overflow adding in AudioLoop");
return false;
}
if (missingFrames.value() > AUDIO_FUZZ_FRAMES) {
// The next audio chunk begins some time after the end of the last chunk
// we pushed to the audio hardware. We must push silence into the audio
// hardware so that the next audio chunk begins playback at the correct
// time.
missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value());
mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value()));
} else {
mWritten += PlayFromAudioQueue();
}
return true;
}
void
AudioSink::FinishAudioLoop()
{
ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
if (!mStopAudioThread && mPlaying) {
Drain();
}
SINK_LOG("AudioLoop complete");
Cleanup();
SINK_LOG("AudioLoop exit");
}
uint32_t
AudioSink::PlaySilence(uint32_t aFrames)
{
@ -411,4 +510,10 @@ AudioSink::AssertOnAudioThread()
MOZ_ASSERT(NS_GetCurrentThread() == mThread);
}
void
AudioSink::AssertNotOnAudioThread()
{
MOZ_ASSERT(NS_GetCurrentThread() != mThread);
}
} // namespace mozilla

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

@ -12,6 +12,7 @@
#include "mozilla/dom/AudioChannelBinding.h"
#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"
#include "mozilla/MozPromise.h"
#include "mozilla/ReentrantMonitor.h"
@ -57,8 +58,21 @@ public:
void NotifyData();
private:
enum State {
AUDIOSINK_STATE_INIT,
AUDIOSINK_STATE_PLAYING,
AUDIOSINK_STATE_COMPLETE,
AUDIOSINK_STATE_SHUTDOWN,
AUDIOSINK_STATE_ERROR
};
~AudioSink() {}
void DispatchTask(already_AddRefed<nsIRunnable>&& event);
void SetState(State aState);
void ScheduleNextLoop();
void ScheduleNextLoopCrossThread();
// The main loop for the audio thread. Sent to the thread as
// an nsRunnableMethod. This continually does blocking writes to
// to audio stream to play audio data.
@ -73,14 +87,20 @@ private:
bool ExpectMoreAudioData();
// Wait on the decoder monitor until playback is ready or the sink is told to shut down.
void WaitForAudioToPlay();
// Return true if playback is not ready and the sink is not told to shut down.
bool WaitingForAudioToPlay();
// Check if the sink has been told to shut down, resuming mAudioStream if
// not. Returns true if processing should continue, false if AudioLoop
// should shutdown.
bool IsPlaybackContinuing();
// Write audio samples or silence to the audio hardware.
// Return false if any error. Called on the audio thread.
bool PlayAudio();
void FinishAudioLoop();
// Write aFrames of audio frames of silence to the audio hardware. Returns
// the number of frames actually written. The write size is capped at
// SILENCE_BYTES_CHUNK (32kB), so must be called in a loop to write the
@ -114,10 +134,16 @@ private:
}
void AssertOnAudioThread();
void AssertNotOnAudioThread();
MediaQueue<AudioData>& mAudioQueue;
mutable ReentrantMonitor mMonitor;
// There members are accessed on the audio thread only.
State mState;
Maybe<State> mPendingState;
bool mAudioLoopScheduled;
// Thread for pushing audio onto the audio hardware.
// The "audio push thread".
nsCOMPtr<nsIThread> mThread;

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

@ -504,11 +504,15 @@ void
MediaFormatReader::DisableHardwareAcceleration()
{
MOZ_ASSERT(OnTaskQueue());
if (HasVideo() && mSharedDecoderManager) {
mSharedDecoderManager->DisableHardwareAcceleration();
if (!mSharedDecoderManager->Recreate(mInfo.mVideo)) {
mVideo.mError = true;
if (HasVideo()) {
mPlatform->DisableHardwareAcceleration();
Flush(TrackInfo::kVideoTrack);
mVideo.mDecoder->Shutdown();
mVideo.mDecoder = nullptr;
if (!EnsureDecodersSetup()) {
LOG("Unable to re-create decoder, aborting");
NotifyError(TrackInfo::kVideoTrack);
return;
}
ScheduleUpdate(TrackInfo::kVideoTrack);
}

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

@ -1142,7 +1142,7 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
const auto& track = mVideoTracks.mBuffers.LastElement();
MOZ_ASSERT(track.IsEmpty() || track[0]->mKeyframe);
for (uint32_t i = 1; i < track.Length(); i++) {
MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode < track[i]->mTimecode) ||
MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode <= track[i]->mTimecode) ||
track[i]->mKeyframe);
}
}
@ -1156,7 +1156,7 @@ TrackBuffersManager::CompleteCodedFrameProcessing()
const auto& track = mAudioTracks.mBuffers.LastElement();
MOZ_ASSERT(track.IsEmpty() || track[0]->mKeyframe);
for (uint32_t i = 1; i < track.Length(); i++) {
MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode < track[i]->mTimecode) ||
MOZ_ASSERT((track[i-1]->mTrackInfo->GetID() == track[i]->mTrackInfo->GetID() && track[i-1]->mTimecode <= track[i]->mTimecode) ||
track[i]->mKeyframe);
}
}

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

@ -81,6 +81,8 @@ skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac"))
skip-if = true # bug 1182946
[test_SeekableBeforeEndOfStreamSplit_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_SeekNoData_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_SeekTwice_mp4.html]
skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
[test_SetModeThrows.html]

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

@ -0,0 +1,55 @@
<!DOCTYPE HTML>
<html>
<head>
<title>MSE: basic functionality</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="mediasource.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
// Avoid making trouble for people who fix rounding bugs.
function fuzzyEquals(a, b) {
return Math.abs(a - b) < 0.01;
}
runWithMSE(function(ms, el) {
el.controls = true;
once(ms, 'sourceopen').then(function() {
ok(true, "Receive a sourceopen event");
var audiosb = ms.addSourceBuffer("audio/mp4");
var videosb = ms.addSourceBuffer("video/mp4");
fetchAndLoad(audiosb, 'bipbop/bipbop_audio', ['init'], '.mp4')
.then(fetchAndLoad.bind(null, videosb, 'bipbop/bipbop_video', ['init'], '.mp4'))
.then(once.bind(null, el, 'loadedmetadata'))
.then(function() {
var p = once(el, 'seeking');
el.play();
el.currentTime = 5;
return p;
})
.then(function() {
ok(true, "Got seeking event");
var promises = [];
promises.push(once(el, 'seeked'));
promises.push(fetchAndLoad(audiosb, 'bipbop/bipbop_audio', range(5, 9), '.m4s'));
promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(6, 10), '.m4s'));
return Promise.all(promises);
})
.then(function() {
ok(true, "Got seeked event");
ok(el.currentTime >= 5, "Time >= 5");
once(el, 'ended').then(SimpleTest.finish.bind(SimpleTest));
ms.endOfStream();
});
});
});
</script>
</pre>
</body>
</html>

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

@ -0,0 +1,9 @@
<script>
try{var r0=new AudioContext();}catch(e){}
try{var r32=r0.createOscillator();}catch(e){}
try{var r58=r0.createPeriodicWave(new Float32Array(1997),new Float32Array(1997));}catch(e){}
try{r32.start(0);}catch(e){}
try{r32.setPeriodicWave(r58);}catch(e){}
try{r32.frequency.value=-1;}catch(e){}
</script>

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

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html class="reftest-wait">
<script>
var context = new window.AudioContext();
var source = context.createBufferSource();
source.connect(context.destination);
source.playbackRate.value = 0.01;
source.buffer = context.createBuffer(2, 32, context.sampleRate);
source.start(0);
setTimeout(
function() {
source.buffer = context.createBuffer(1, 10, context.sampleRate);
source.playbackRate.value = 1;
source.onended =
function() {
document.documentElement.removeAttribute("class");
};
}, 0);
</script>
</html>

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

@ -0,0 +1,21 @@
<html>
<script>
try{var Context1= new (window.webkitAudioContext || window.AudioContext)()}catch(e){}
try{var Delay0=Context1.createDelay();}catch(e){}
try{var ScriptProcessor0=Context1.createScriptProcessor(512,26,7);}catch(e){}
try{var ChannelSplitter0=Context1.createChannelSplitter(91);}catch(e){}
try{var Gain1=Context1.createGain();}catch(e){}
try{var WaveShaper0=Context1.createWaveShaper();}catch(e){}
try{var Analyser1=Context1.createAnalyser();}catch(e){}
try{Gain1.connect(Delay0);}catch(e){}
try{Analyser1.connect(BiquadFilter0);}catch(e){}
try{Gain1.connect(Context1.destination);}catch(e){}
try{WaveShaper0.connect(ScriptProcessor0);}catch(e){}
try{ChannelSplitter0.connect(BiquadFilter1);}catch(e){}
try{Delay0.connect(Gain1);}catch(e){}
try{ScriptProcessor0.connect(Context1.destination);}catch(e){}
try{WaveShaper0.connect(Gain1);}catch(e){}
try{WaveShaper0.connect(ChannelSplitter0);}catch(e){}
try{ScriptProcessor0.connect(WaveShaper0);}catch(e){}
</script>
</html>

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

@ -0,0 +1,18 @@
<html><body><script>
var r0=new AudioContext();
var splitter=r0.createChannelSplitter();
var delay=r0.createDelay();
var scriptp=r0.createScriptProcessor();
var cmerger=r0.createChannelMerger();
var gain=r0.createGain();
splitter.connect(delay,2);
delay.connect(scriptp);
scriptp.connect(cmerger);
cmerger.connect(splitter);
gain.connect(gain);
gain.connect(cmerger);
</script></body></html>

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

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html class="reftest-wait">
<script>
var context = new window.OfflineAudioContext(1, 256, 48000);
var analyser = context.createAnalyser();
analyser.channelCount = 2;
analyser.channelCountMode = "explicit";
analyser.fftSize = 32;
var source = context.createOscillator();
source.connect(analyser);
source.start(0);
context.startRendering().
then(function() {
document.documentElement.removeAttribute("class");
});
</script>

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

@ -67,8 +67,13 @@ load 952756.html
load 966636.html
load 986901.html
load 990794.html
load 1012609.html
load 1015662.html
load 1020205.html
load 1041466.html
load 1045650.html
skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
load analyser-channels-1.html
load buffer-source-ended-1.html
load doppler-1.html
HTTP load media-element-source-seek-1.html

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

@ -12,6 +12,14 @@
#include "mozilla/PodOperations.h"
namespace mozilla {
static const uint32_t MAX_FFT_SIZE = 32768;
static const size_t CHUNK_COUNT = MAX_FFT_SIZE >> WEBAUDIO_BLOCK_SIZE_BITS;
static_assert(MAX_FFT_SIZE == CHUNK_COUNT * WEBAUDIO_BLOCK_SIZE,
"MAX_FFT_SIZE must be a multiple of WEBAUDIO_BLOCK_SIZE");
static_assert((CHUNK_COUNT & (CHUNK_COUNT - 1)) == 0,
"CHUNK_COUNT must be power of 2 for remainder behavior");
namespace dom {
NS_IMPL_ISUPPORTS_INHERITED0(AnalyserNode, AudioNode)
@ -57,20 +65,7 @@ public:
{
*aOutput = aInput;
// If the input is silent, we sill need to send a silent buffer
if (aOutput->IsNull()) {
AllocateAudioBlock(1, aOutput);
float* samples =
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
PodZero(samples, WEBAUDIO_BLOCK_SIZE);
}
uint32_t channelCount = aOutput->mChannelData.Length();
for (uint32_t channel = 0; channel < channelCount; ++channel) {
float* samples =
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
AudioBlockInPlaceScale(samples, aOutput->mVolume);
}
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, *aOutput);
nsRefPtr<TransferBuffer> transfer = new TransferBuffer(aStream, aInput);
NS_DispatchToMainThread(transfer);
}
@ -89,10 +84,15 @@ AnalyserNode::AnalyserNode(AudioContext* aContext)
, mMinDecibels(-100.)
, mMaxDecibels(-30.)
, mSmoothingTimeConstant(.8)
, mWriteIndex(0)
{
mStream = aContext->Graph()->CreateAudioNodeStream(new AnalyserNodeEngine(this),
MediaStreamGraph::INTERNAL_STREAM);
// Enough chunks must be recorded to handle the case of fftSize being
// increased to maximum immediately before getFloatTimeDomainData() is
// called, for example.
(void)mChunks.SetLength(CHUNK_COUNT, fallible);
AllocateBuffer();
}
@ -101,7 +101,7 @@ AnalyserNode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
{
size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
amount += mAnalysisBlock.SizeOfExcludingThis(aMallocSizeOf);
amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
amount += mChunks.SizeOfExcludingThis(aMallocSizeOf);
amount += mOutputBuffer.SizeOfExcludingThis(aMallocSizeOf);
return amount;
}
@ -123,7 +123,7 @@ AnalyserNode::SetFftSize(uint32_t aValue, ErrorResult& aRv)
{
// Disallow values that are not a power of 2 and outside the [32,32768] range
if (aValue < 32 ||
aValue > 32768 ||
aValue > MAX_FFT_SIZE ||
(aValue & (aValue - 1)) != 0) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
@ -212,11 +212,9 @@ AnalyserNode::GetFloatTimeDomainData(const Float32Array& aArray)
aArray.ComputeLengthAndData();
float* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());
size_t length = std::min(aArray.Length(), FftSize());
for (size_t i = 0; i < length; ++i) {
buffer[i] = mBuffer[(i + mWriteIndex) % mBuffer.Length()];;
}
GetTimeDomainData(buffer, length);
}
void
@ -224,11 +222,18 @@ AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray)
{
aArray.ComputeLengthAndData();
unsigned char* buffer = aArray.Data();
size_t length = std::min(size_t(aArray.Length()), mBuffer.Length());
size_t length = std::min(aArray.Length(), FftSize());
AlignedTArray<float> tmpBuffer;
if (!tmpBuffer.SetLength(length, fallible)) {
return;
}
GetTimeDomainData(tmpBuffer.Elements(), length);
unsigned char* buffer = aArray.Data();
for (size_t i = 0; i < length; ++i) {
const float value = mBuffer[(i + mWriteIndex) % mBuffer.Length()];
const float value = tmpBuffer[i];
// scale the value to the range of [0, UCHAR_MAX]
const float scaled = std::max(0.0f, std::min(float(UCHAR_MAX),
128.0f * (value + 1.0f)));
@ -239,25 +244,19 @@ AnalyserNode::GetByteTimeDomainData(const Uint8Array& aArray)
bool
AnalyserNode::FFTAnalysis()
{
float* inputBuffer;
AlignedTArray<float> tmpBuffer;
if (mWriteIndex == 0) {
inputBuffer = mBuffer.Elements();
} else {
if (!tmpBuffer.SetLength(FftSize(), fallible)) {
return false;
}
inputBuffer = tmpBuffer.Elements();
memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex));
memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex);
size_t fftSize = FftSize();
if (!tmpBuffer.SetLength(fftSize, fallible)) {
return false;
}
ApplyBlackmanWindow(inputBuffer, FftSize());
float* inputBuffer = tmpBuffer.Elements();
GetTimeDomainData(inputBuffer, fftSize);
ApplyBlackmanWindow(inputBuffer, fftSize);
mAnalysisBlock.PerformFFT(inputBuffer);
// Normalize so than an input sine wave at 0dBfs registers as 0dBfs (undo FFT scaling factor).
const double magnitudeScale = 1.0 / FftSize();
const double magnitudeScale = 1.0 / fftSize;
for (uint32_t i = 0; i < mOutputBuffer.Length(); ++i) {
double scalarMagnitude = NS_hypot(mAnalysisBlock.RealData(i),
@ -289,13 +288,7 @@ bool
AnalyserNode::AllocateBuffer()
{
bool result = true;
if (mBuffer.Length() != FftSize()) {
if (!mBuffer.SetLength(FftSize(), fallible)) {
return false;
}
memset(mBuffer.Elements(), 0, sizeof(float) * FftSize());
mWriteIndex = 0;
if (mOutputBuffer.Length() != FrequencyBinCount()) {
if (!mOutputBuffer.SetLength(FrequencyBinCount(), fallible)) {
return false;
}
@ -307,31 +300,57 @@ AnalyserNode::AllocateBuffer()
void
AnalyserNode::AppendChunk(const AudioChunk& aChunk)
{
const uint32_t bufferSize = mBuffer.Length();
const uint32_t channelCount = aChunk.mChannelData.Length();
uint32_t chunkDuration = aChunk.mDuration;
MOZ_ASSERT((bufferSize & (bufferSize - 1)) == 0); // Must be a power of two!
MOZ_ASSERT(channelCount > 0);
MOZ_ASSERT(chunkDuration == WEBAUDIO_BLOCK_SIZE);
if (chunkDuration > bufferSize) {
// Copy a maximum bufferSize samples.
chunkDuration = bufferSize;
if (mChunks.Length() == 0) {
return;
}
PodCopy(mBuffer.Elements() + mWriteIndex, static_cast<const float*>(aChunk.mChannelData[0]), chunkDuration);
for (uint32_t i = 1; i < channelCount; ++i) {
AudioBlockAddChannelWithScale(static_cast<const float*>(aChunk.mChannelData[i]), 1.0f,
mBuffer.Elements() + mWriteIndex);
++mCurrentChunk;
mChunks[mCurrentChunk & (CHUNK_COUNT - 1)] = aChunk;
}
// Reads into aData the oldest aLength samples of the fftSize most recent
// samples.
void
AnalyserNode::GetTimeDomainData(float* aData, size_t aLength)
{
size_t fftSize = FftSize();
MOZ_ASSERT(aLength <= fftSize);
if (mChunks.Length() == 0) {
PodZero(aData, aLength);
return;
}
if (channelCount > 1) {
AudioBlockInPlaceScale(mBuffer.Elements() + mWriteIndex,
1.0f / aChunk.mChannelData.Length());
}
mWriteIndex += chunkDuration;
MOZ_ASSERT(mWriteIndex <= bufferSize);
if (mWriteIndex >= bufferSize) {
mWriteIndex = 0;
size_t readChunk =
mCurrentChunk - ((fftSize - 1) >> WEBAUDIO_BLOCK_SIZE_BITS);
size_t readIndex = (0 - fftSize) & (WEBAUDIO_BLOCK_SIZE - 1);
MOZ_ASSERT(readIndex == 0 || readIndex + fftSize == WEBAUDIO_BLOCK_SIZE);
for (size_t writeIndex = 0; writeIndex < aLength; ) {
const AudioChunk& chunk = mChunks[readChunk & (CHUNK_COUNT - 1)];
const size_t channelCount = chunk.mChannelData.Length();
size_t copyLength =
std::min<size_t>(aLength - writeIndex, WEBAUDIO_BLOCK_SIZE);
float* dataOut = &aData[writeIndex];
if (channelCount == 0) {
PodZero(dataOut, copyLength);
} else {
float scale = chunk.mVolume / channelCount;
{ // channel 0
auto channelData =
static_cast<const float*>(chunk.mChannelData[0]) + readIndex;
AudioBufferCopyWithScale(channelData, scale, dataOut, copyLength);
}
for (uint32_t i = 1; i < channelCount; ++i) {
auto channelData =
static_cast<const float*>(chunk.mChannelData[i]) + readIndex;
AudioBufferAddWithScale(channelData, scale, dataOut, copyLength);
}
}
readChunk++;
writeIndex += copyLength;
}
}

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

@ -71,14 +71,15 @@ private:
bool AllocateBuffer();
bool FFTAnalysis();
void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize);
void GetTimeDomainData(float* aData, size_t aLength);
private:
FFTBlock mAnalysisBlock;
nsTArray<AudioChunk> mChunks;
double mMinDecibels;
double mMaxDecibels;
double mSmoothingTimeConstant;
uint32_t mWriteIndex;
AlignedTArray<float> mBuffer;
size_t mCurrentChunk = 0;
AlignedTArray<float> mOutputBuffer;
};

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

@ -207,7 +207,7 @@ public:
uintptr_t aOffsetWithinBlock,
uint32_t aNumberOfFrames) {
for (uint32_t i = 0; i < aChannels; ++i) {
float* baseChannelData = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i]));
float* baseChannelData = aOutput->ChannelFloatsForWrite(i);
memcpy(baseChannelData + aOffsetWithinBlock,
mBuffer->GetData(i) + mBufferPosition,
aNumberOfFrames * sizeof(float));
@ -268,8 +268,7 @@ public:
uint32_t outSamples = availableInOutputBuffer;
float* outputData =
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
*aOffsetWithinBlock;
aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
WebAudioUtils::SpeexResamplerProcess(resampler, i,
inputData, &inSamples,
@ -294,8 +293,7 @@ public:
uint32_t inSamples = mRemainingResamplerTail;
uint32_t outSamples = availableInOutputBuffer;
float* outputData =
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])) +
*aOffsetWithinBlock;
aOutput->ChannelFloatsForWrite(i) + *aOffsetWithinBlock;
// AudioDataValue* for aIn selects the function that does not try to
// copy and format-convert input data.

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

@ -15,6 +15,15 @@ namespace mozilla {
void
AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk)
{
if (aChunk->mBuffer && !aChunk->mBuffer->IsShared() &&
aChunk->ChannelCount() == aChannelCount) {
MOZ_ASSERT(aChunk->mBufferFormat == AUDIO_FORMAT_FLOAT32);
MOZ_ASSERT(aChunk->mDuration == WEBAUDIO_BLOCK_SIZE);
// No need to allocate again.
aChunk->mVolume = 1.0f;
return;
}
CheckedInt<size_t> size = WEBAUDIO_BLOCK_SIZE;
size *= aChannelCount;
size *= sizeof(float);
@ -42,9 +51,9 @@ WriteZeroesToAudioBlock(AudioChunk* aChunk, uint32_t aStart, uint32_t aLength)
MOZ_ASSERT(!aChunk->IsNull(), "You should pass a non-null chunk");
if (aLength == 0)
return;
for (uint32_t i = 0; i < aChunk->mChannelData.Length(); ++i) {
memset(static_cast<float*>(const_cast<void*>(aChunk->mChannelData[i])) + aStart,
0, aLength*sizeof(float));
PodZero(aChunk->ChannelFloatsForWrite(i) + aStart, aLength);
}
}

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

@ -114,8 +114,8 @@ private:
};
/**
* Allocates an AudioChunk with fresh buffers of WEBAUDIO_BLOCK_SIZE float samples.
* AudioChunk::mChannelData's entries can be cast to float* for writing.
* Allocates, if necessary, aChannelCount buffers of WEBAUDIO_BLOCK_SIZE float
* samples for writing to an AudioChunk.
*/
void AllocateAudioBlock(uint32_t aChannelCount, AudioChunk* aChunk);

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

@ -49,8 +49,7 @@ CopyChunkToBlock(const AudioChunk& aInput, AudioChunk *aBlock,
uint32_t duration = aInput.GetDuration();
for (uint32_t c = 0; c < blockChannels; ++c) {
float* outputData =
static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c])) + aOffsetInBlock;
float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock;
if (channels[c]) {
switch (aInput.mBufferFormat) {
case AUDIO_FORMAT_FLOAT32:

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

@ -411,7 +411,7 @@ AudioNodeStream::AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aC
for (uint32_t c = 0; c < channels.Length(); ++c) {
const float* inputData = static_cast<const float*>(channels[c]);
float* outputData = static_cast<float*>(const_cast<void*>(aBlock->mChannelData[c]));
float* outputData = aBlock->ChannelFloatsForWrite(c);
if (inputData) {
if (aInputIndex == 0) {
AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData);

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

@ -206,7 +206,7 @@ public:
SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune);
mBiquads[i].process(input,
static_cast<float*>(const_cast<void*>(aOutput->mChannelData[i])),
aOutput->ChannelFloatsForWrite(i),
aInput.GetDuration());
}
}

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

@ -50,7 +50,7 @@ public:
AudioBlockCopyChannelWithScale(
static_cast<const float*>(aInput[i].mChannelData[j]),
aInput[i].mVolume,
static_cast<float*>(const_cast<void*>(aOutput[0].mChannelData[channelIndex])));
aOutput[0].ChannelFloatsForWrite(channelIndex));
++channelIndex;
if (channelIndex >= channelCount) {
return;

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

@ -38,7 +38,7 @@ public:
AudioBlockCopyChannelWithScale(
static_cast<const float*>(aInput[0].mChannelData[i]),
aInput[0].mVolume,
static_cast<float*>(const_cast<void*>(aOutput[i].mChannelData[0])));
aOutput[i].ChannelFloatsForWrite(0));
} else {
// Pad with silent channels if needed
aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE);

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

@ -135,7 +135,7 @@ public:
AllocateAudioBlock(numChannels, &input);
for (uint32_t i = 0; i < numChannels; ++i) {
const float* src = static_cast<const float*>(aInput.mChannelData[i]);
float* dest = static_cast<float*>(const_cast<void*>(input.mChannelData[i]));
float* dest = input.ChannelFloatsForWrite(i);
AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest);
}
}

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

@ -97,12 +97,11 @@ DelayBuffer::Read(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
void
DelayBuffer::ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk, uint32_t aChannel,
AudioChunk* aOutputChunk, uint32_t aChannel,
ChannelInterpretation aChannelInterpretation)
{
if (!mChunks.Length()) {
float* outputChannel = static_cast<float*>
(const_cast<void*>(aOutputChunk->mChannelData[aChannel]));
float* outputChannel = aOutputChunk->ChannelFloatsForWrite(aChannel);
PodZero(outputChannel, WEBAUDIO_BLOCK_SIZE);
return;
}
@ -113,7 +112,7 @@ DelayBuffer::ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
void
DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk,
AudioChunk* aOutputChunk,
uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
ChannelInterpretation aChannelInterpretation)
{
@ -125,11 +124,9 @@ DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
mLastReadChunk = -1; // invalidate cache
}
float* const* outputChannels = reinterpret_cast<float* const*>
(const_cast<void* const*>(aOutputChunk->mChannelData.Elements()));
for (uint32_t channel = aFirstChannel;
channel < readChannelsEnd; ++channel) {
PodZero(outputChannels[channel], WEBAUDIO_BLOCK_SIZE);
PodZero(aOutputChunk->ChannelFloatsForWrite(channel), WEBAUDIO_BLOCK_SIZE);
}
for (unsigned i = 0; i < WEBAUDIO_BLOCK_SIZE; ++i) {
@ -158,7 +155,7 @@ DelayBuffer::ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
double multiplier = interpolationFactor * mChunks[readChunk].mVolume;
for (uint32_t channel = aFirstChannel;
channel < readChannelsEnd; ++channel) {
outputChannels[channel][i] += multiplier *
aOutputChunk->ChannelFloatsForWrite(channel)[i] += multiplier *
static_cast<const float*>(mUpmixChannels[channel])[readOffset];
}
}

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

@ -56,7 +56,7 @@ public:
// channels. aOutputChunk must have already been allocated with at least as
// many channels as were in any of the blocks passed to Write().
void ReadChannel(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk, uint32_t aChannel,
AudioChunk* aOutputChunk, uint32_t aChannel,
ChannelInterpretation aChannelInterpretation);
// Advance the buffer pointer
@ -80,7 +80,7 @@ public:
private:
void ReadChannels(const double aPerFrameDelays[WEBAUDIO_BLOCK_SIZE],
const AudioChunk* aOutputChunk,
AudioChunk* aOutputChunk,
uint32_t aFirstChannel, uint32_t aNumChannelsToRead,
ChannelInterpretation aChannelInterpretation);
bool EnsureBuffer();

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

@ -95,8 +95,7 @@ public:
// Apply the gain to the output buffer
for (size_t channel = 0; channel < aOutput->mChannelData.Length(); ++channel) {
const float* inputBuffer = static_cast<const float*> (aInput.mChannelData[channel]);
float* buffer = static_cast<float*> (const_cast<void*>
(aOutput->mChannelData[channel]));
float* buffer = aOutput->ChannelFloatsForWrite(channel);
AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer);
}
}

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

@ -308,8 +308,7 @@ public:
}
AllocateAudioBlock(1, aOutput);
float* output = static_cast<float*>(
const_cast<void*>(aOutput->mChannelData[0]));
float* output = aOutput->ChannelFloatsForWrite(0);
uint32_t start, end;
FillBounds(output, ticks, start, end);

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

@ -18,9 +18,9 @@ void
GainMonoToStereo(const AudioChunk& aInput, AudioChunk* aOutput,
T aGainL, T aGainR)
{
float* outputL = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
float* outputR = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[1]));
const float* input = static_cast<float*>(const_cast<void*>(aInput.mChannelData[0]));
float* outputL = aOutput->ChannelFloatsForWrite(0);
float* outputR = aOutput->ChannelFloatsForWrite(1);
const float* input = static_cast<const float*>(aInput.mChannelData[0]);
MOZ_ASSERT(aInput.ChannelCount() == 1);
MOZ_ASSERT(aOutput->ChannelCount() == 2);
@ -35,10 +35,10 @@ void
GainStereoToStereo(const AudioChunk& aInput, AudioChunk* aOutput,
T aGainL, T aGainR, U aOnLeft)
{
float* outputL = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[0]));
float* outputR = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[1]));
const float* inputL = static_cast<float*>(const_cast<void*>(aInput.mChannelData[0]));
const float* inputR = static_cast<float*>(const_cast<void*>(aInput.mChannelData[1]));
float* outputL = aOutput->ChannelFloatsForWrite(0);
float* outputR = aOutput->ChannelFloatsForWrite(1);
const float* inputL = static_cast<const float*>(aInput.mChannelData[0]);
const float* inputR = static_cast<const float*>(aInput.mChannelData[1]);
MOZ_ASSERT(aInput.ChannelCount() == 2);
MOZ_ASSERT(aOutput->ChannelCount() == 2);

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

@ -86,7 +86,7 @@ public:
void SetToSilentStereoBlock(AudioChunk* aChunk)
{
for (uint32_t channel = 0; channel < 2; channel++) {
float* samples = static_cast<float*>(const_cast<void*>(aChunk->mChannelData[channel]));
float* samples = aChunk->ChannelFloatsForWrite(channel);
for (uint32_t i = 0; i < WEBAUDIO_BLOCK_SIZE; i++) {
samples[i] = 0.f;
}
@ -102,7 +102,7 @@ public:
AllocateAudioBlock(2, aOutput);
const float* input = static_cast<const float*>(aInput.mChannelData[0]);
for (uint32_t channel = 0; channel < 2; channel++) {
float* output = static_cast<float*>(const_cast<void*>(aOutput->mChannelData[channel]));
float* output = aOutput->ChannelFloatsForWrite(channel);
PodCopy(output, input, WEBAUDIO_BLOCK_SIZE);
}
}

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

@ -232,7 +232,7 @@ public:
float* scaledSample = (float *)(aInput.mChannelData[i]);
AudioBlockInPlaceScale(scaledSample, aInput.mVolume);
const float* inputBuffer = static_cast<const float*>(scaledSample);
float* outputBuffer = const_cast<float*> (static_cast<const float*>(aOutput->mChannelData[i]));
float* outputBuffer = aOutput->ChannelFloatsForWrite(i);
float* sampleBuffer;
switch (mType) {

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

@ -31,6 +31,7 @@ support-files =
[test_analyserScale.html]
[test_analyserNodeOutput.html]
[test_analyserNodePassThrough.html]
[test_analyserNodeWithGain.html]
[test_AudioBuffer.html]
[test_audioBufferSourceNode.html]
[test_audioBufferSourceNodeEnded.html]

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

@ -0,0 +1,47 @@
<!DOCTYPE html>
<title>Test effect of AnalyserNode on GainNode output</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
async_test(function(t) {
// fftSize <= 128 so that the time domain data is full of input after
// notification that the first block has been processed.
const fftSize = 32;
var context = new AudioContext();
var analyser1 = context.createAnalyser();
analyser1.fftSize = fftSize;
var analyser2 = context.createAnalyser();
analyser2.fftSize = fftSize;
var gain = context.createGain();
gain.gain.value = 2.0;
gain.connect(analyser1);
gain.connect(analyser2);
// Create a DC input to make getFloatTimeDomainData() output consistent at
// any time.
var buffer = context.createBuffer(1, 1, context.sampleRate);
buffer.getChannelData(0)[0] = 1.0 / gain.gain.value;
var source = context.createBufferSource();
source.buffer = buffer;
source.loop = true;
source.connect(gain);
source.start();
// Waiting for an ended event ensures that the AnalyserNode has received the
// signal.
var timer = context.createBufferSource();
timer.buffer = buffer;
timer.onended = t.step_func_done(function() {
var data = new Float32Array(1);
analyser1.getFloatTimeDomainData(data);
assert_equals(data[0], 1.0, "analyser1 time domain data");
analyser2.getFloatTimeDomainData(data);
assert_equals(data[0], 1.0, "analyser2 time domain data");
});
timer.start()
});
</script>

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

@ -36,3 +36,7 @@ LOCAL_INCLUDES += [
'/caps',
'/netwerk/base',
]
if CONFIG['GNU_CC']:
CFLAGS += ['-Wshadow']
CXXFLAGS += ['-Wshadow']

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

@ -16,6 +16,7 @@
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseWorkerProxy.h"
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/StructuredCloneHelper.h"
#include "mozilla/ErrorResult.h"
#include "WorkerPrivate.h"
@ -207,8 +208,8 @@ protected:
// A DataStoreRunnable to run DataStore::Put(...) on the main thread.
class DataStorePutRunnable final : public DataStoreProxyRunnable
, public StructuredCloneHelper
{
JSAutoStructuredCloneBuffer mObjBuffer;
const StringOrUnsignedLong& mId;
const nsString mRevisionId;
ErrorResult& mRv;
@ -223,6 +224,7 @@ public:
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
, StructuredCloneHelper(CloningNotSupported, TransferringNotSupported)
, mId(aId)
, mRevisionId(aRevisionId)
, mRv(aRv)
@ -231,7 +233,7 @@ public:
aWorkerPrivate->AssertIsOnWorkerThread();
// This needs to be structured cloned while it's still on the worker thread.
if (!mObjBuffer.write(aCx, aObj)) {
if (!Write(aCx, aObj)) {
JS_ClearPendingException(aCx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
@ -252,7 +254,7 @@ protected:
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {
if (!Read(mBackingStore->GetParentObject(), cx, &value)) {
JS_ClearPendingException(cx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return true;
@ -270,8 +272,8 @@ protected:
// A DataStoreRunnable to run DataStore::Add(...) on the main thread.
class DataStoreAddRunnable final : public DataStoreProxyRunnable
, public StructuredCloneHelper
{
JSAutoStructuredCloneBuffer mObjBuffer;
const Optional<StringOrUnsignedLong>& mId;
const nsString mRevisionId;
ErrorResult& mRv;
@ -286,6 +288,7 @@ public:
const nsAString& aRevisionId,
ErrorResult& aRv)
: DataStoreProxyRunnable(aWorkerPrivate, aBackingStore, aWorkerPromise)
, StructuredCloneHelper(CloningNotSupported, TransferringNotSupported)
, mId(aId)
, mRevisionId(aRevisionId)
, mRv(aRv)
@ -294,7 +297,7 @@ public:
aWorkerPrivate->AssertIsOnWorkerThread();
// This needs to be structured cloned while it's still on the worker thread.
if (!mObjBuffer.write(aCx, aObj)) {
if (!Write(aCx, aObj)) {
JS_ClearPendingException(aCx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
}
@ -315,7 +318,7 @@ protected:
JSContext* cx = jsapi.cx();
JS::Rooted<JS::Value> value(cx);
if (!mObjBuffer.read(cx, &value)) {
if (!Read(mBackingStore->GetParentObject(), cx, &value)) {
JS_ClearPendingException(cx);
mRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
return true;

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

@ -39,42 +39,14 @@ using namespace mozilla::dom;
static const char kXBLCachePrefix[] = "xblcache";
/* Implementation file */
static PLDHashOperator
TraverseProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
{
nsCycleCollectionTraversalCallback *cb =
static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
aProto->Traverse(*cb);
return PL_DHASH_NEXT;
}
static PLDHashOperator
UnlinkProto(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
{
aProto->Unlink();
return PL_DHASH_NEXT;
}
struct ProtoTracer
{
const TraceCallbacks &mCallbacks;
void *mClosure;
};
static PLDHashOperator
TraceProtos(const nsACString &aKey, nsXBLPrototypeBinding *aProto, void* aClosure)
{
ProtoTracer* closure = static_cast<ProtoTracer*>(aClosure);
aProto->Trace(closure->mCallbacks, closure->mClosure);
return PL_DHASH_NEXT;
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXBLDocumentInfo)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXBLDocumentInfo)
if (tmp->mBindingTable) {
tmp->mBindingTable->EnumerateRead(UnlinkProto, nullptr);
for (auto iter = tmp->mBindingTable->ConstIter();
!iter.Done(); iter.Next()) {
iter.UserData()->Unlink();
}
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -86,14 +58,19 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo)
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
if (tmp->mBindingTable) {
tmp->mBindingTable->EnumerateRead(TraverseProtos, &cb);
for (auto iter = tmp->mBindingTable->ConstIter();
!iter.Done(); iter.Next()) {
iter.UserData()->Traverse(cb);
}
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
if (tmp->mBindingTable) {
ProtoTracer closure = { aCallbacks, aClosure };
tmp->mBindingTable->EnumerateRead(TraceProtos, &closure);
for (auto iter = tmp->mBindingTable->ConstIter();
!iter.Done(); iter.Next()) {
iter.UserData()->Trace(aCallbacks, aClosure);
}
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END

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

@ -639,20 +639,17 @@ CairoImage::GetTextureClient(CompositableClient *aClient)
if (!textureClient) {
return nullptr;
}
MOZ_ASSERT(textureClient->CanExposeDrawTarget());
if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
return nullptr;
}
TextureClientAutoUnlock autoUnolck(textureClient);
{
// We must not keep a reference to the DrawTarget after it has been unlocked.
DrawTarget* dt = textureClient->BorrowDrawTarget();
if (!dt) {
return nullptr;
}
dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
}
TextureClientAutoUnlock autoUnlock(textureClient);
RefPtr<DataSourceSurface> dataSurf = surface->GetDataSurface();
textureClient->UpdateFromSurface(dataSurf);
textureClient->SyncWithObject(forwarder->GetSyncObject());
mTextureClients.Put(forwarder->GetSerial(), textureClient);
return textureClient;

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

@ -56,6 +56,25 @@ TextureClientDIB::BorrowDrawTarget()
return mDrawTarget;
}
void
TextureClientDIB::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
MOZ_ASSERT(mIsLocked && IsAllocated());
nsRefPtr<gfxImageSurface> imgSurf = mSurface->GetAsImageSurface();
DataSourceSurface::MappedSurface sourceMap;
aSurface->Map(DataSourceSurface::READ, &sourceMap);
for (int y = 0; y < aSurface->GetSize().height; y++) {
memcpy(imgSurf->Data() + imgSurf->Stride() * y,
sourceMap.mData + sourceMap.mStride * y,
aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
}
aSurface->Unmap();
}
TextureClientMemoryDIB::TextureClientMemoryDIB(ISurfaceAllocator* aAllocator,
gfx::SurfaceFormat aFormat,
TextureFlags aFlags)

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

@ -34,6 +34,8 @@ public:
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual bool HasInternalBuffer() const override { return true; }
protected:

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

@ -7,6 +7,7 @@
#include "ContentHelper.h"
#include "gfxPlatform.h" // For gfxPlatform::UseTiling
#include "gfxPrefs.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/layers/LayerTransactionChild.h"
@ -194,34 +195,41 @@ APZCCallbackHelper::UpdateRootFrame(FrameMetrics& aMetrics)
MOZ_ASSERT(aMetrics.GetUseDisplayPortMargins());
float presShellResolution = nsLayoutUtils::GetResolution(shell);
if (gfxPrefs::APZAllowZooming()) {
// If zooming is disabled then we don't really want to let APZ fiddle
// with these things. In theory setting the resolution here should be a
// no-op, but setting the SPCSPS is bad because it can cause a stale value
// to be returned by window.innerWidth/innerHeight (see bug 1187792).
// If the pres shell resolution has changed on the content side side
// the time this repaint request was fired, consider this request out of date
// and drop it; setting a zoom based on the out-of-date resolution can have
// the effect of getting us stuck with the stale resolution.
if (presShellResolution != aMetrics.GetPresShellResolution()) {
return;
float presShellResolution = nsLayoutUtils::GetResolution(shell);
// If the pres shell resolution has changed on the content side side
// the time this repaint request was fired, consider this request out of date
// and drop it; setting a zoom based on the out-of-date resolution can have
// the effect of getting us stuck with the stale resolution.
if (presShellResolution != aMetrics.GetPresShellResolution()) {
return;
}
// Set the scroll port size, which determines the scroll range. For example if
// a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
// be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
// overscroll). Note that if the content here was zoomed to 2x, the document would
// be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
// scroll range would be 900. Therefore this calculation depends on the zoom applied
// to the content relative to the container.
// Note that this needs to happen before scrolling the frame (in UpdateFrameCommon),
// otherwise the scroll position may get clamped incorrectly.
CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, scrollPort);
// The pres shell resolution is updated by the the async zoom since the
// last paint.
presShellResolution = aMetrics.GetPresShellResolution()
* aMetrics.GetAsyncZoom().scale;
nsLayoutUtils::SetResolutionAndScaleTo(shell, presShellResolution);
}
// Set the scroll port size, which determines the scroll range. For example if
// a 500-pixel document is shown in a 100-pixel frame, the scroll port length would
// be 100, and gecko would limit the maximum scroll offset to 400 (so as to prevent
// overscroll). Note that if the content here was zoomed to 2x, the document would
// be 1000 pixels long but the frame would still be 100 pixels, and so the maximum
// scroll range would be 900. Therefore this calculation depends on the zoom applied
// to the content relative to the container.
// Note that this needs to happen before scrolling the frame (in UpdateFrameCommon),
// otherwise the scroll position may get clamped incorrectly.
CSSSize scrollPort = aMetrics.CalculateCompositedSizeInCssPixels();
nsLayoutUtils::SetScrollPositionClampingScrollPortSize(shell, scrollPort);
// The pres shell resolution is updated by the the async zoom since the
// last paint.
presShellResolution = aMetrics.GetPresShellResolution()
* aMetrics.GetAsyncZoom().scale;
nsLayoutUtils::SetResolutionAndScaleTo(shell, presShellResolution);
// Do this as late as possible since scrolling can flush layout. It also
// adjusts the display port margins, so do it before we set those.
ScrollFrame(content, aMetrics);

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

@ -152,3 +152,18 @@ TextureClientX11::BorrowDrawTarget()
return mDrawTarget;
}
void
TextureClientX11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
MOZ_ASSERT(CanExposeDrawTarget());
DrawTarget* dt = BorrowDrawTarget();
if (!dt) {
gfxCriticalError() << "Failed to borrow drawtarget for TextureClientX11::UpdateFromSurface";
return;
}
dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
}

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

@ -43,6 +43,8 @@ class TextureClientX11 : public TextureClient
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
virtual bool HasInternalBuffer() const override { return false; }

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

@ -353,7 +353,8 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
aMoz2DBackend == gfx::BackendType::CAIRO &&
aAllocator->IsSameProcess() &&
aSize.width <= maxTextureSize &&
aSize.height <= maxTextureSize) {
aSize.height <= maxTextureSize &&
NS_IsMainThread()) {
if (gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
texture = new TextureClientD3D9(aAllocator, aFormat, aTextureFlags);
}
@ -361,7 +362,8 @@ TextureClient::CreateForDrawing(ISurfaceAllocator* aAllocator,
if (!texture && aFormat == SurfaceFormat::B8G8R8X8 &&
aAllocator->IsSameProcess() &&
aMoz2DBackend == gfx::BackendType::CAIRO) {
aMoz2DBackend == gfx::BackendType::CAIRO &&
NS_IsMainThread()) {
if (aAllocator->IsSameProcess()) {
texture = new TextureClientMemoryDIB(aAllocator, aFormat, aTextureFlags);
} else {
@ -801,6 +803,7 @@ gfx::DrawTarget*
BufferTextureClient::BorrowDrawTarget()
{
MOZ_ASSERT(IsValid());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only");
if (!mLocked) {
return nullptr;
@ -826,6 +829,33 @@ BufferTextureClient::BorrowDrawTarget()
return mDrawTarget;
}
void
BufferTextureClient::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
RefPtr<DataSourceSurface> surface = serializer.GetAsSurface();
if (surface->GetSize() != aSurface->GetSize() || surface->GetFormat() != aSurface->GetFormat()) {
gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << surface->GetSize() << " " << surface->GetFormat() << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
return;
}
DataSourceSurface::MappedSurface sourceMap;
DataSourceSurface::MappedSurface destMap;
aSurface->Map(DataSourceSurface::READ, &sourceMap);
surface->Map(DataSourceSurface::WRITE, &destMap);
for (int y = 0; y < aSurface->GetSize().height; y++) {
memcpy(destMap.mData + destMap.mStride * y,
sourceMap.mData + sourceMap.mStride * y,
aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
}
aSurface->Unmap();
surface->Unmap();
}
bool
BufferTextureClient::Lock(OpenMode aMode)
{

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

@ -243,6 +243,7 @@ public:
/**
* Returns a DrawTarget to draw into the TextureClient.
* This function should never be called when not on the main thread!
*
* This must never be called on a TextureClient that is not sucessfully locked.
* When called several times within one Lock/Unlock pair, this method should
@ -268,6 +269,12 @@ public:
*/
virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; }
/**
* This function can be used to update the contents of the TextureClient
* off the main thread.
*/
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) { MOZ_CRASH(); }
// TextureClients that can expose a DrawTarget should override this method.
virtual gfx::SurfaceFormat GetFormat() const
{
@ -586,6 +593,8 @@ public:
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual bool AllocateForSurface(gfx::IntSize aSize,
TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;

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

@ -244,7 +244,8 @@ TextureClientD3D11::CreateSimilar(TextureFlags aFlags,
void
TextureClientD3D11::SyncWithObject(SyncObject* aSyncObject)
{
if (!aSyncObject) {
if (!aSyncObject || !NS_IsMainThread()) {
// When off the main thread we sync using a keyed mutex per texture.
return;
}
@ -278,33 +279,35 @@ TextureClientD3D11::Lock(OpenMode aMode)
return false;
}
// Make sure that successful write-lock means we will have a DrawTarget to
// write into.
if (aMode & OpenMode::OPEN_WRITE) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
if (NS_IsMainThread()) {
// Make sure that successful write-lock means we will have a DrawTarget to
// write into.
if (aMode & OpenMode::OPEN_WRITE) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
}
}
if (mNeedsClear) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
if (mNeedsClear) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
mNeedsClear = false;
}
mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
mNeedsClear = false;
}
if (mNeedsClearWhite) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
if (mNeedsClearWhite) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
mNeedsClearWhite = false;
}
mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
mNeedsClearWhite = false;
}
return true;
@ -327,7 +330,7 @@ TextureClientD3D11::Unlock()
mDrawTarget->Flush();
}
if (mReadbackSink && mTexture10) {
if (NS_IsMainThread() && mReadbackSink && mTexture10) {
ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
D3D10_TEXTURE2D_DESC desc;
@ -363,6 +366,7 @@ DrawTarget*
TextureClientD3D11::BorrowDrawTarget()
{
MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :(");
MOZ_ASSERT(NS_IsMainThread());
if (!mIsLocked || (!mTexture && !mTexture10)) {
gfxCriticalError() << "Attempted to borrow a DrawTarget without locking the texture.";
@ -387,6 +391,53 @@ TextureClientD3D11::BorrowDrawTarget()
return mDrawTarget;
}
void
TextureClientD3D11::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
DataSourceSurface::MappedSurface sourceMap;
aSurface->Map(DataSourceSurface::READ, &sourceMap);
if (mDrawTarget) {
// Ensure unflushed work from our outstanding drawtarget won't override this
// update later.
mDrawTarget->Flush();
}
if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format!";
return;
}
if (mTexture) {
RefPtr<ID3D11Device> device;
mTexture->GetDevice(byRef(device));
RefPtr<ID3D11DeviceContext> ctx;
device->GetImmediateContext(byRef(ctx));
D3D11_BOX box;
box.front = 0;
box.back = 1;
box.top = box.left = 0;
box.right = aSurface->GetSize().width;
box.bottom = aSurface->GetSize().height;
ctx->UpdateSubresource(mTexture, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
} else {
RefPtr<ID3D10Device> device;
mTexture10->GetDevice(byRef(device));
D3D10_BOX box;
box.front = 0;
box.back = 1;
box.top = box.left = 0;
box.right = aSurface->GetSize().width;
box.bottom = aSurface->GetSize().height;
device->UpdateSubresource(mTexture10, 0, &box, sourceMap.mData, sourceMap.mStride, 0);
}
aSurface->Unmap();
}
static const GUID sD3D11TextureUsage =
{ 0xd89275b0, 0x6c7d, 0x4038, { 0xb5, 0xfa, 0x4d, 0x87, 0x16, 0xd5, 0xcc, 0x4e } };
@ -447,8 +498,11 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag
}
gfxWindowsPlatform* windowsPlatform = gfxWindowsPlatform::GetPlatform();
ID3D11Device* d3d11device = windowsPlatform->GetD3D11ContentDevice();
bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1;
ID3D11Device* d3d11device = windowsPlatform->GetD3D11DeviceForCurrentThread();
// When we're not on the main thread we're not going to be using Direct2D
// to access the contents of this texture client so we will always use D3D11.
bool haveD3d11Backend = windowsPlatform->GetContentBackend() == BackendType::DIRECT2D1_1 || !NS_IsMainThread();
if (haveD3d11Backend) {
MOZ_ASSERT(d3d11device != nullptr);
@ -459,6 +513,11 @@ TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlag
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
if (!NS_IsMainThread()) {
// On the main thread we use the syncobject to handle synchronization.
newDesc.MiscFlags = D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX;
}
hr = d3d11device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
if (FAILED(hr)) {
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize))) << "[D3D11] 2 CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);

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

@ -64,6 +64,8 @@ public:
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual bool AllocateForSurface(gfx::IntSize aSize,
TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;

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

@ -498,35 +498,6 @@ TextureClientD3D9::Lock(OpenMode aMode)
mIsLocked = true;
// Make sure that successful write-lock means we will have a DrawTarget to
// write into.
if (aMode & OpenMode::OPEN_WRITE) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
}
if (mNeedsClear) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
mNeedsClear = false;
}
if (mNeedsClearWhite) {
mDrawTarget = BorrowDrawTarget();
if (!mDrawTarget) {
Unlock();
return false;
}
mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
mNeedsClearWhite = false;
}
return true;
}
@ -601,9 +572,51 @@ TextureClientD3D9::BorrowDrawTarget()
mLockRect = true;
}
if (mNeedsClear) {
mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
mNeedsClear = false;
}
if (mNeedsClearWhite) {
mDrawTarget->FillRect(Rect(0, 0, GetSize().width, GetSize().height), ColorPattern(Color(1.0, 1.0, 1.0, 1.0)));
mNeedsClearWhite = false;
}
return mDrawTarget;
}
void
TextureClientD3D9::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
MOZ_ASSERT(mIsLocked && mD3D9Surface);
// gfxWindowsSurface don't support transparency so we can't use the d3d9
// windows surface optimization.
// Instead we have to use a gfxImageSurface and fallback for font drawing.
D3DLOCKED_RECT rect;
HRESULT hr = mD3D9Surface->LockRect(&rect, nullptr, 0);
if (FAILED(hr) || !rect.pBits) {
gfxCriticalError() << "Failed to lock rect borrowing the target in D3D9 " << hexa(hr);
return;
}
if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << mFormat << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
return;
}
DataSourceSurface::MappedSurface sourceMap;
aSurface->Map(DataSourceSurface::READ, &sourceMap);
for (int y = 0; y < aSurface->GetSize().height; y++) {
memcpy((uint8_t*)rect.pBits + rect.Pitch * y,
sourceMap.mData + sourceMap.mStride * y,
aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
}
aSurface->Unmap();
mD3D9Surface->UnlockRect();
}
bool
TextureClientD3D9::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
{

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

@ -202,6 +202,8 @@ public:
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual bool AllocateForSurface(gfx::IntSize aSize,
TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;

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

@ -214,6 +214,40 @@ GrallocTextureClientOGL::BorrowDrawTarget()
return mDrawTarget;
}
void
GrallocTextureClientOGL::UpdateFromSurface(gfx::DataSourceSurface* aSurface)
{
MOZ_ASSERT(IsValid());
MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :(");
if (!IsValid() || !IsAllocated() || !mMappedBuffer) {
return;
}
gfx::SurfaceFormat format = SurfaceFormatForPixelFormat(mGraphicBuffer->getPixelFormat());
if (mSize != aSurface->GetSize() || mFormat != aSurface->GetFormat()) {
gfxCriticalError() << "Attempt to update texture client from a surface with a different size or format! This: " << mSize << " " << format << " Other: " << aSurface->GetSize() << " " << aSurface->GetFormat();
return;
}
long pixelStride = mGraphicBuffer->getStride();
long byteStride = pixelStride * BytesPerPixel(format);
DataSourceSurface::MappedSurface sourceMap;
aSurface->Map(DataSourceSurface::READ, &sourceMap);
uint8_t* buffer = GetBuffer();
for (int y = 0; y < aSurface->GetSize().height; y++) {
memcpy(buffer + byteStride * y,
sourceMap.mData + sourceMap.mStride * y,
aSurface->GetSize().width * BytesPerPixel(aSurface->GetFormat()));
}
aSurface->Unmap();
}
bool
GrallocTextureClientOGL::AllocateForSurface(gfx::IntSize aSize,
TextureAllocationFlags)

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

@ -82,6 +82,8 @@ public:
virtual gfx::DrawTarget* BorrowDrawTarget() override;
virtual void UpdateFromSurface(gfx::DataSourceSurface* aSurface) override;
virtual bool AllocateForSurface(gfx::IntSize aSize,
TextureAllocationFlags aFlags = ALLOC_DEFAULT) override;

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

@ -557,6 +557,7 @@ gfxWindowsPlatform::UpdateRenderMode()
imgLoader::Singleton()->ClearCache(true);
imgLoader::Singleton()->ClearCache(false);
gfxAlphaBoxBlur::ShutdownBlurCache();
Factory::SetDirect3D11Device(nullptr);
didReset = true;
@ -1588,6 +1589,15 @@ gfxWindowsPlatform::GetD3D11ImageBridgeDevice()
return mD3D11ImageBridgeDevice;
}
ID3D11Device*
gfxWindowsPlatform::GetD3D11DeviceForCurrentThread()
{
if (NS_IsMainThread()) {
return GetD3D11ContentDevice();
} else {
return GetD3D11ImageBridgeDevice();
}
}
ReadbackManagerD3D11*
gfxWindowsPlatform::GetReadbackManager()
@ -1900,15 +1910,7 @@ bool DoesD3D11TextureSharingWorkInternal(ID3D11Device *device, DXGI_FORMAT forma
bool DoesD3D11TextureSharingWork(ID3D11Device *device)
{
static bool checked;
static bool result;
if (checked)
return result;
checked = true;
result = DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
return result;
return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_B8G8R8A8_UNORM, D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE);
}
bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device)

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

@ -244,6 +244,7 @@ public:
ID3D10Device1 *GetD3D10Device() { return mD3D10Device; }
ID3D11Device *GetD3D11Device();
ID3D11Device *GetD3D11ContentDevice();
ID3D11Device* GetD3D11DeviceForCurrentThread();
// Device to be used on the ImageBridge thread
ID3D11Device *GetD3D11ImageBridgeDevice();

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

@ -125,7 +125,8 @@ GeckoChildProcessHost::~GeckoChildProcessHost()
SharedMemoryBasic::CleanupForPid(mChildProcessHandle);
#endif
ProcessWatcher::EnsureProcessTerminated(mChildProcessHandle
#if defined(NS_BUILD_REFCNT_LOGGING)
#if defined(NS_BUILD_REFCNT_LOGGING) || defined(MOZ_ASAN)
// If we're doing leak logging, shutdown can be slow.
, false // don't "force"
#endif
);
@ -600,32 +601,19 @@ GeckoChildProcessHost::PerformAsyncLaunchInternal(std::vector<std::string>& aExt
path += "/lib";
# endif // MOZ_WIDGET_ANDROID
const char *ld_library_path = PR_GetEnv("LD_LIBRARY_PATH");
nsCString new_ld_lib_path;
if (ld_library_path && *ld_library_path) {
new_ld_lib_path.Assign(path.get());
new_ld_lib_path.Append(':');
new_ld_lib_path.Append(ld_library_path);
newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
} else {
newEnvVars["LD_LIBRARY_PATH"] = path.get();
}
nsCString new_ld_lib_path(path.get());
# if (MOZ_WIDGET_GTK == 3)
if (mProcessType == GeckoProcessType_Plugin) {
const char *ld_preload = PR_GetEnv("LD_PRELOAD");
nsCString new_ld_preload;
new_ld_preload.Assign(path.get());
new_ld_preload.AppendLiteral("/" DLL_PREFIX "mozgtk2" DLL_SUFFIX);
if (ld_preload && *ld_preload) {
new_ld_preload.AppendLiteral(":");
new_ld_preload.Append(ld_preload);
}
newEnvVars["LD_PRELOAD"] = new_ld_preload.get();
new_ld_lib_path.Append("/gtk2:");
new_ld_lib_path.Append(path.get());
}
# endif // MOZ_WIDGET_GTK
#endif
if (ld_library_path && *ld_library_path) {
new_ld_lib_path.Append(':');
new_ld_lib_path.Append(ld_library_path);
}
newEnvVars["LD_LIBRARY_PATH"] = new_ld_lib_path.get();
# elif OS_MACOSX
newEnvVars["DYLD_LIBRARY_PATH"] = path.get();

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