зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
c86c8718cf
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче