diff --git a/b2g/config/mozconfigs/linux32_gecko/debug b/b2g/config/mozconfigs/linux32_gecko/debug index 9ba6ac90f90d..756288753dbf 100644 --- a/b2g/config/mozconfigs/linux32_gecko/debug +++ b/b2g/config/mozconfigs/linux32_gecko/debug @@ -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 diff --git a/b2g/config/mozconfigs/linux32_gecko/nightly b/b2g/config/mozconfigs/linux32_gecko/nightly index 251ab2ccd064..42dcc47a45f7 100644 --- a/b2g/config/mozconfigs/linux32_gecko/nightly +++ b/b2g/config/mozconfigs/linux32_gecko/nightly @@ -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 diff --git a/b2g/config/mozconfigs/linux64_gecko/debug b/b2g/config/mozconfigs/linux64_gecko/debug index 71f4ec3ab0ba..4943f410b841 100644 --- a/b2g/config/mozconfigs/linux64_gecko/debug +++ b/b2g/config/mozconfigs/linux64_gecko/debug @@ -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 diff --git a/b2g/config/mozconfigs/linux64_gecko/nightly b/b2g/config/mozconfigs/linux64_gecko/nightly index 8ac486ed7210..5f08a54eedd6 100644 --- a/b2g/config/mozconfigs/linux64_gecko/nightly +++ b/b2g/config/mozconfigs/linux64_gecko/nightly @@ -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 diff --git a/b2g/dev/config/mozconfigs/linux64/mulet b/b2g/dev/config/mozconfigs/linux64/mulet index f2b8977b0de6..4aaa6b78ddbe 100644 --- a/b2g/dev/config/mozconfigs/linux64/mulet +++ b/b2g/dev/config/mozconfigs/linux64/mulet @@ -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. diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index c8111d9e7c13..afbdb9491c7f 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -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] diff --git a/browser/base/content/test/social/browser_social_multiworker.js b/browser/base/content/test/social/browser_social_multiworker.js index 602c2d1f24ba..b839f3e8e7ec 100644 --- a/browser/base/content/test/social/browser_social_multiworker.js +++ b/browser/base/content/test/social/browser_social_multiworker.js @@ -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) { diff --git a/browser/config/mozconfigs/linux32/debug-asan b/browser/config/mozconfigs/linux32/debug-asan index 7f5642c421cf..61f48d8b6085 100644 --- a/browser/config/mozconfigs/linux32/debug-asan +++ b/browser/config/mozconfigs/linux32/debug-asan @@ -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 diff --git a/browser/config/mozconfigs/linux32/nightly-asan b/browser/config/mozconfigs/linux32/nightly-asan index 8254e7a6c483..6bb94e802aca 100644 --- a/browser/config/mozconfigs/linux32/nightly-asan +++ b/browser/config/mozconfigs/linux32/nightly-asan @@ -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 diff --git a/browser/config/mozconfigs/linux32/valgrind b/browser/config/mozconfigs/linux32/valgrind index 8a33228bede4..c8fb4f574e6d 100644 --- a/browser/config/mozconfigs/linux32/valgrind +++ b/browser/config/mozconfigs/linux32/valgrind @@ -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 diff --git a/browser/config/mozconfigs/linux64/debug-asan b/browser/config/mozconfigs/linux64/debug-asan index 1d53a7fd0c2f..81fbf833ebf6 100644 --- a/browser/config/mozconfigs/linux64/debug-asan +++ b/browser/config/mozconfigs/linux64/debug-asan @@ -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 diff --git a/browser/config/mozconfigs/linux64/debug-static-analysis-clang b/browser/config/mozconfigs/linux64/debug-static-analysis-clang index 17c8e266a265..37a5bb104432 100644 --- a/browser/config/mozconfigs/linux64/debug-static-analysis-clang +++ b/browser/config/mozconfigs/linux64/debug-static-analysis-clang @@ -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" diff --git a/browser/config/mozconfigs/linux64/hazards b/browser/config/mozconfigs/linux64/hazards index 68f924d358d0..6c5e19373284 100644 --- a/browser/config/mozconfigs/linux64/hazards +++ b/browser/config/mozconfigs/linux64/hazards @@ -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" diff --git a/browser/config/mozconfigs/linux64/nightly-asan b/browser/config/mozconfigs/linux64/nightly-asan index d183b7c9bd96..d86e6b844e74 100644 --- a/browser/config/mozconfigs/linux64/nightly-asan +++ b/browser/config/mozconfigs/linux64/nightly-asan @@ -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 diff --git a/browser/config/mozconfigs/linux64/valgrind b/browser/config/mozconfigs/linux64/valgrind index 7c010b25e42f..2efc6e3bbc93 100644 --- a/browser/config/mozconfigs/linux64/valgrind +++ b/browser/config/mozconfigs/linux64/valgrind @@ -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" diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index ed83214006ae..c142003a8952 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -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] diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 index cb47cf749a96..e246e8b26bbe 100644 --- a/build/autoconf/compiler-opts.m4 +++ b/build/autoconf/compiler-opts.m4 @@ -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. diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp index d3887496ab62..4b60c56ee7c6 100644 --- a/build/clang-plugin/clang-plugin.cpp +++ b/build/clang-plugin/clang-plugin.cpp @@ -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; diff --git a/build/sanitizers/lsan_suppressions.txt b/build/sanitizers/lsan_suppressions.txt index 78865b3049a1..41f6e53612ed 100644 --- a/build/sanitizers/lsan_suppressions.txt +++ b/build/sanitizers/lsan_suppressions.txt @@ -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. diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk new file mode 100644 index 000000000000..fd92daf3fa4d --- /dev/null +++ b/build/unix/mozconfig.gtk @@ -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 + + + + /usr/share/X11/fonts + +EOF + +else + ac_add_options --enable-default-toolkit=cairo-gtk2 +fi diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux index 1f124d97a69d..dd198d02b350 100644 --- a/build/unix/mozconfig.linux +++ b/build/unix/mozconfig.linux @@ -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" diff --git a/build/unix/mozconfig.linux32 b/build/unix/mozconfig.linux32 index 5198e37683b0..309767751216 100644 --- a/build/unix/mozconfig.linux32 +++ b/build/unix/mozconfig.linux32 @@ -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 diff --git a/build/valgrind/mach_commands.py b/build/valgrind/mach_commands.py index b9038a2d8ef2..a2af75d11a8d 100644 --- a/build/valgrind/mach_commands.py +++ b/build/valgrind/mach_commands.py @@ -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]} diff --git a/dom/base/FileList.cpp b/dom/base/FileList.cpp index 8a90c026ea92..642b167f982d 100644 --- a/dom/base/FileList.cpp +++ b/dom/base/FileList.cpp @@ -21,30 +21,6 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(FileList) NS_IMPL_CYCLE_COLLECTING_RELEASE(FileList) -/* static */ already_AddRefed -FileList::Create(nsISupports* aParent, FileListClonedData* aData) -{ - MOZ_ASSERT(aData); - - nsRefPtr fileList = new FileList(aParent); - - const nsTArray>& blobImpls = aData->BlobImpls(); - for (uint32_t i = 0; i < blobImpls.Length(); ++i) { - const nsRefPtr& blobImpl = blobImpls[i]; - MOZ_ASSERT(blobImpl); - MOZ_ASSERT(blobImpl->IsFile()); - - nsRefPtr 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 aGivenProto) { @@ -67,19 +43,5 @@ FileList::Item(uint32_t aIndex, nsISupports** aFile) return NS_OK; } -already_AddRefed -FileList::CreateClonedData() const -{ - nsTArray> blobImpls; - for (uint32_t i = 0; i < mFiles.Length(); ++i) { - blobImpls.AppendElement(mFiles[i]->Impl()); - } - - nsRefPtr data = new FileListClonedData(blobImpls); - return data.forget(); -} - -NS_IMPL_ISUPPORTS0(FileListClonedData) - } // namespace dom } // namespace mozilla diff --git a/dom/base/FileList.h b/dom/base/FileList.h index 9193c64d271d..772c6d7966de 100644 --- a/dom/base/FileList.h +++ b/dom/base/FileList.h @@ -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>& aBlobImpls) - : mBlobImpls(aBlobImpls) - {} - - const nsTArray>& BlobImpls() const - { - return mBlobImpls; - } - -private: - ~FileListClonedData() - {} - - const nsTArray> mBlobImpls; -}; - class FileList final : public nsIDOMFileList, public nsWrapperCache { @@ -50,9 +31,6 @@ public: : mParent(aParent) {} - static already_AddRefed - Create(nsISupports* aParent, FileListClonedData* aData); - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; @@ -114,9 +92,6 @@ public: return mFiles.Length(); } - // Useful for cloning - already_AddRefed CreateClonedData() const; - private: ~FileList() {} diff --git a/dom/base/PostMessageEvent.cpp b/dom/base/PostMessageEvent.cpp index 9f0a1a56875b..53f6fa3ad779 100644 --- a/dom/base/PostMessageEvent.cpp +++ b/dom/base/PostMessageEvent.cpp @@ -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), diff --git a/dom/base/StructuredCloneHelper.cpp b/dom/base/StructuredCloneHelper.cpp index 15e49afc5a26..f7b93f5e48c6 100644 --- a/dom/base/StructuredCloneHelper.cpp +++ b/dom/base/StructuredCloneHelper.cpp @@ -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 aValue) +{ + return Write(aCx, aValue, JS::UndefinedHandleValue); +} + bool StructuredCloneHelper::Write(JSContext* aCx, JS::Handle aValue, @@ -227,7 +236,36 @@ StructuredCloneHelper::Read(nsISupports* aParent, mozilla::AutoRestore 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>& aBlobImpls, + JS::MutableHandle 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 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 = mBlobImplArray[aIndex]; - BlobImpl* blobImpl; - if (JS_ReadBytes(aReader, &blobImpl, sizeof(blobImpl))) { - MOZ_ASSERT(blobImpl); - - // nsRefPtr 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 val(aCx); - { - nsRefPtr blob = Blob::Create(mParent, blobImpl); - if (!ToJSValue(aCx, blob, &val)) { - return nullptr; - } + // nsRefPtr 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 val(aCx); + { + nsRefPtr 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 val(aCx); + { + nsRefPtr 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 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 val(aCx); - { - nsRefPtr 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 = mBlobImplArray[index]; + MOZ_ASSERT(blobImpl->IsFile()); + + nsRefPtr 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 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 = - 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 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]); diff --git a/dom/base/StructuredCloneHelper.h b/dom/base/StructuredCloneHelper.h index 1141f08318b9..c0b6ca4c3c59 100644 --- a/dom/base/StructuredCloneHelper.h +++ b/dom/base/StructuredCloneHelper.h @@ -76,6 +76,18 @@ public: bool Read(JSContext* aCx, JS::MutableHandle 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 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 aValue); + bool Write(JSContext* aCx, JS::Handle aValue, JS::Handle aTransfer); @@ -118,9 +134,22 @@ public: JSContext* aCx, JS::MutableHandle aValue); + bool ReadFromBuffer(nsISupports* aParent, + JSContext* aCx, + uint64_t* aBuffer, + size_t aBufferLength, + nsTArray>& aBlobImpls, + JS::MutableHandle aValue); + + const nsTArray>& ClonedBlobImpls() const + { + MOZ_ASSERT(mBuffer, "Write() has never been called."); + return mBlobImplArray; + } + nsTArray>& 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> mSupportsArray; + nsTArray> 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. diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index dffaceabbcf1..f0b1e7787bde 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1250,21 +1250,11 @@ nsGlobalWindow::Init() sWindowsById = new WindowByIdTable(); } -static PLDHashOperator -DisconnectEventTargetObjects(nsPtrHashKey* aKey, - void* aClosure) -{ - nsRefPtr 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 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 os = mozilla::services::GetObserverService(); @@ -13577,27 +13577,6 @@ nsGlobalWindow::DisableTimeChangeNotifications() mozilla::time::RemoveWindowListener(this); } -static PLDHashOperator -CollectSizeAndListenerCount( - nsPtrHashKey* aEntry, - void *arg) -{ - nsWindowSizes* windowSizes = static_cast(arg); - - DOMEventTargetHelper* et = aEntry->GetKey(); - - if (nsCOMPtr 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 >*> - (&mEventTargetObjects)->EnumerateEntries(CollectSizeAndListenerCount, - aWindowSizes); + + + for (auto iter = mEventTargetObjects.ConstIter(); !iter.Done(); iter.Next()) { + DOMEventTargetHelper* et = iter.Get()->GetKey(); + if (nsCOMPtr iSizeOf = do_QueryObject(et)) { + aWindowSizes->mDOMEventTargetsSize += + iSizeOf->SizeOfEventTargetIncludingThis(aWindowSizes->mMallocSizeOf); + } + if (EventListenerManager* elm = et->GetExistingListenerManager()) { + aWindowSizes->mDOMEventListenersCount += elm->ListenerCount(); + } + ++aWindowSizes->mDOMEventTargetsCount; + } } diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index a39b7229526e..9565fd85fcfc 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -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 diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index b20569a842da..8e62a3923ee7 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -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 base = xpc::UnwrapReflectorToISupports(obj); nsCOMPtr 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); } diff --git a/dom/base/test/iframe_cloning_fileList.html b/dom/base/test/iframe_postMessages.html similarity index 67% rename from dom/base/test/iframe_cloning_fileList.html rename to dom/base/test/iframe_postMessages.html index 68c67085d821..b34462f5e800 100644 --- a/dom/base/test/iframe_cloning_fileList.html +++ b/dom/base/test/iframe_postMessages.html @@ -3,7 +3,7 @@ diff --git a/dom/base/test/mochitest.ini b/dom/base/test/mochitest.ini index 9473eb0f82ec..b1cedc8b52db 100644 --- a/dom/base/test/mochitest.ini +++ b/dom/base/test/mochitest.ini @@ -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] diff --git a/dom/base/test/script_cloning_fileList.js b/dom/base/test/script_postmessages_fileList.js similarity index 100% rename from dom/base/test/script_cloning_fileList.js rename to dom/base/test/script_postmessages_fileList.js diff --git a/dom/base/test/test_cloning_fileList.html b/dom/base/test/test_cloning_fileList.html deleted file mode 100644 index 613dfc6198da..000000000000 --- a/dom/base/test/test_cloning_fileList.html +++ /dev/null @@ -1,85 +0,0 @@ - - - - Test for cloning FileList - - - - - -

- -

-
-
-
- - diff --git a/dom/base/test/test_postMessages.html b/dom/base/test/test_postMessages.html new file mode 100644 index 000000000000..89a8ed8ea794 --- /dev/null +++ b/dom/base/test/test_postMessages.html @@ -0,0 +1,295 @@ + + + + Test for postMessages cloning/transferring objects + + + + + + + + + diff --git a/dom/broadcastchannel/BroadcastChannel.cpp b/dom/broadcastchannel/BroadcastChannel.cpp index b3547da485c1..6e8701602bcd 100644 --- a/dom/broadcastchannel/BroadcastChannel.cpp +++ b/dom/broadcastchannel/BroadcastChannel.cpp @@ -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>& blobImpls = mData->mClosure.mBlobImpls; + const nsTArray>& blobImpls = mData->ClonedBlobImpls(); if (!blobImpls.IsEmpty()) { message.blobsChild().SetCapacity(blobImpls.Length()); @@ -455,12 +454,12 @@ BroadcastChannel::PostMessageInternal(JSContext* aCx, { nsRefPtr 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>& blobImpls = data->mClosure.mBlobImpls; + const nsTArray>& 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); diff --git a/dom/broadcastchannel/BroadcastChannelChild.cpp b/dom/broadcastchannel/BroadcastChannelChild.cpp index 3c4c4aac8443..76078bfde38b 100644 --- a/dom/broadcastchannel/BroadcastChannelChild.cpp +++ b/dom/broadcastchannel/BroadcastChannelChild.cpp @@ -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 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; } diff --git a/dom/canvas/test/webgl-mochitest/webgl-util.js b/dom/canvas/test/webgl-mochitest/webgl-util.js index e71d84cfe643..8206c6e2b771 100644 --- a/dom/canvas/test/webgl-mochitest/webgl-util.js +++ b/dom/canvas/test/webgl-mochitest/webgl-util.js @@ -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(); diff --git a/dom/events/DataContainerEvent.cpp b/dom/events/DataContainerEvent.cpp index 66d10be099f0..4878f467f336 100644 --- a/dom/events/DataContainerEvent.cpp +++ b/dom/events/DataContainerEvent.cpp @@ -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(aUserArg); - cb->NoteXPCOMChild(aDataItem); - - return PL_DHASH_NEXT; -} - } // namespace dom } // namespace mozilla diff --git a/dom/events/DataContainerEvent.h b/dom/events/DataContainerEvent.h index 51cbe2bcb4c5..15acfeb44b7d 100644 --- a/dom/events/DataContainerEvent.h +++ b/dom/events/DataContainerEvent.h @@ -51,9 +51,6 @@ protected: ~DataContainerEvent() {} private: - static PLDHashOperator - TraverseEntry(const nsAString& aKey, nsIVariant* aDataItem, void* aUserArg); - nsInterfaceHashtable mData; }; diff --git a/dom/html/HTMLCanvasElement.cpp b/dom/html/HTMLCanvasElement.cpp index 5d30e6b52211..4593ca2bf567 100644 --- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -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; } diff --git a/dom/html/HTMLFormControlsCollection.cpp b/dom/html/HTMLFormControlsCollection.cpp index 95576eaa53d8..f420a6f75519 100644 --- a/dom/html/HTMLFormControlsCollection.cpp +++ b/dom/html/HTMLFormControlsCollection.cpp @@ -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*>(aClosure)->AppendElement(aName); - return PL_DHASH_NEXT; -} - void HTMLFormControlsCollection::GetSupportedNames(unsigned aFlags, nsTArray& 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* diff --git a/dom/html/HTMLFormElement.cpp b/dom/html/HTMLFormElement.cpp index 3e8de5f26501..43eed3a91535 100644 --- a/dom/html/HTMLFormElement.cpp +++ b/dom/html/HTMLFormElement.cpp @@ -132,17 +132,6 @@ HTMLFormElement::~HTMLFormElement() // nsISupports -static PLDHashOperator -ElementTraverser(const nsAString& key, HTMLInputElement* element, - void* userArg) -{ - nsCycleCollectionTraversalCallback *cb = - static_cast(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, diff --git a/dom/html/HTMLMediaElement.cpp b/dom/html/HTMLMediaElement.cpp index aa5738059be0..5c990acd2917 100644 --- a/dom/html/HTMLMediaElement.cpp +++ b/dom/html/HTMLMediaElement.cpp @@ -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 tags; - bool error; -} MetadataIterCx; - -PLDHashOperator -HTMLMediaElement::BuildObjectFromTags(nsCStringHashKey::KeyType aKey, - nsCString aValue, - void* aUserArg) -{ - MetadataIterCx* args = static_cast(aUserArg); - - nsString wideValue = NS_ConvertUTF8toUTF16(aValue); - JS::Rooted 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 aRetval, @@ -1752,12 +1722,16 @@ HTMLMediaElement::MozGetMetadata(JSContext* cx, return; } if (mTags) { - MetadataIterCx iter = {cx, tags, false}; - mTags->EnumerateRead(BuildObjectFromTags, static_cast(&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 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; + } } } diff --git a/dom/html/HTMLPropertiesCollection.cpp b/dom/html/HTMLPropertiesCollection.cpp index 8a511534a2c1..19ecfa8ea7ea 100644 --- a/dom/html/HTMLPropertiesCollection.cpp +++ b/dom/html/HTMLPropertiesCollection.cpp @@ -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(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; } diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 9667202fa404..128b45324645 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -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. diff --git a/dom/media/AudioSegment.h b/dom/media/AudioSegment.h index 25c0057f1b7f..4b30209992cd 100644 --- a/dom/media/AudioSegment.h +++ b/dom/media/AudioSegment.h @@ -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(const_cast(mChannelData[aChannel])); + } bool IsMuted() const { return mVolume == 0.0f; } diff --git a/dom/media/AudioSink.cpp b/dom/media/AudioSink.cpp index 1b761b8c1fcb..1127d8bbae29 100644 --- a/dom/media/AudioSink.cpp +++ b/dom/media/AudioSink.cpp @@ -10,6 +10,7 @@ #include "VideoUtils.h" #include "mozilla/CheckedInt.h" +#include "mozilla/DebugOnly.h" namespace mozilla { @@ -28,6 +29,8 @@ AudioSink::AudioSink(MediaQueue& 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& aAudioQueue, { } +void +AudioSink::SetState(State aState) +{ + AssertOnAudioThread(); + mPendingState = Some(aState); +} + +void +AudioSink::DispatchTask(already_AddRefed&& event) +{ + DebugOnly 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 r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop); + DispatchTask(r.forget()); +} + +void +AudioSink::ScheduleNextLoopCrossThread() +{ + AssertNotOnAudioThread(); + nsRefPtr self = this; + nsCOMPtr 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 AudioSink::Init() { @@ -57,13 +102,7 @@ AudioSink::Init() return p; } - nsCOMPtr 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(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(UINT32_MAX, missingFrames.value()); - mWritten += PlaySilence(static_cast(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(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(UINT32_MAX, missingFrames.value()); + mWritten += PlaySilence(static_cast(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 diff --git a/dom/media/AudioSink.h b/dom/media/AudioSink.h index e14f09399112..7dcb76068ee9 100644 --- a/dom/media/AudioSink.h +++ b/dom/media/AudioSink.h @@ -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&& 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& mAudioQueue; mutable ReentrantMonitor mMonitor; + // There members are accessed on the audio thread only. + State mState; + Maybe mPendingState; + bool mAudioLoopScheduled; + // Thread for pushing audio onto the audio hardware. // The "audio push thread". nsCOMPtr mThread; diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index f6220cee495c..4a22c4e8579e 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -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); } diff --git a/dom/media/mediasource/TrackBuffersManager.cpp b/dom/media/mediasource/TrackBuffersManager.cpp index ab3404d8d2bf..1a20058c733c 100644 --- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -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); } } diff --git a/dom/media/mediasource/test/mochitest.ini b/dom/media/mediasource/test/mochitest.ini index e8c02548dd8a..9099bdddba05 100644 --- a/dom/media/mediasource/test/mochitest.ini +++ b/dom/media/mediasource/test/mochitest.ini @@ -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] diff --git a/dom/media/mediasource/test/test_SeekNoData_mp4.html b/dom/media/mediasource/test/test_SeekNoData_mp4.html new file mode 100644 index 000000000000..8bad3b0e86d9 --- /dev/null +++ b/dom/media/mediasource/test/test_SeekNoData_mp4.html @@ -0,0 +1,55 @@ + + + + MSE: basic functionality + + + + + +
+
+
+ + diff --git a/dom/media/test/crashtests/1012609.html b/dom/media/test/crashtests/1012609.html new file mode 100644 index 000000000000..1dad783a073c --- /dev/null +++ b/dom/media/test/crashtests/1012609.html @@ -0,0 +1,9 @@ + + diff --git a/dom/media/test/crashtests/1020205.html b/dom/media/test/crashtests/1020205.html new file mode 100644 index 000000000000..e9efc4096f49 --- /dev/null +++ b/dom/media/test/crashtests/1020205.html @@ -0,0 +1,22 @@ + + + + diff --git a/dom/media/test/crashtests/1041466.html b/dom/media/test/crashtests/1041466.html new file mode 100644 index 000000000000..0f064d186cdf --- /dev/null +++ b/dom/media/test/crashtests/1041466.html @@ -0,0 +1,21 @@ + + + diff --git a/dom/media/test/crashtests/1045650.html b/dom/media/test/crashtests/1045650.html new file mode 100644 index 000000000000..32acd2ce758d --- /dev/null +++ b/dom/media/test/crashtests/1045650.html @@ -0,0 +1,18 @@ + diff --git a/dom/media/test/crashtests/analyser-channels-1.html b/dom/media/test/crashtests/analyser-channels-1.html new file mode 100644 index 000000000000..2f3133cf13ec --- /dev/null +++ b/dom/media/test/crashtests/analyser-channels-1.html @@ -0,0 +1,16 @@ + + + diff --git a/dom/media/test/crashtests/crashtests.list b/dom/media/test/crashtests/crashtests.list index 69589149f7b1..17863201549f 100644 --- a/dom/media/test/crashtests/crashtests.list +++ b/dom/media/test/crashtests/crashtests.list @@ -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 diff --git a/dom/media/webaudio/AnalyserNode.cpp b/dom/media/webaudio/AnalyserNode.cpp index c66398d33c45..8c3642c97c7e 100644 --- a/dom/media/webaudio/AnalyserNode.cpp +++ b/dom/media/webaudio/AnalyserNode.cpp @@ -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(const_cast(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(const_cast(aOutput->mChannelData[channel])); - AudioBlockInPlaceScale(samples, aOutput->mVolume); - } - nsRefPtr transfer = new TransferBuffer(aStream, *aOutput); + nsRefPtr 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 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 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(aChunk.mChannelData[0]), chunkDuration); - for (uint32_t i = 1; i < channelCount; ++i) { - AudioBlockAddChannelWithScale(static_cast(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(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(chunk.mChannelData[0]) + readIndex; + AudioBufferCopyWithScale(channelData, scale, dataOut, copyLength); + } + for (uint32_t i = 1; i < channelCount; ++i) { + auto channelData = + static_cast(chunk.mChannelData[i]) + readIndex; + AudioBufferAddWithScale(channelData, scale, dataOut, copyLength); + } + } + + readChunk++; + writeIndex += copyLength; } } diff --git a/dom/media/webaudio/AnalyserNode.h b/dom/media/webaudio/AnalyserNode.h index 6915a17243f0..7fca5df6fe5c 100644 --- a/dom/media/webaudio/AnalyserNode.h +++ b/dom/media/webaudio/AnalyserNode.h @@ -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 mChunks; double mMinDecibels; double mMaxDecibels; double mSmoothingTimeConstant; - uint32_t mWriteIndex; - AlignedTArray mBuffer; + size_t mCurrentChunk = 0; AlignedTArray mOutputBuffer; }; diff --git a/dom/media/webaudio/AudioBufferSourceNode.cpp b/dom/media/webaudio/AudioBufferSourceNode.cpp index bacb6c6c7212..b3346542c1c0 100644 --- a/dom/media/webaudio/AudioBufferSourceNode.cpp +++ b/dom/media/webaudio/AudioBufferSourceNode.cpp @@ -207,7 +207,7 @@ public: uintptr_t aOffsetWithinBlock, uint32_t aNumberOfFrames) { for (uint32_t i = 0; i < aChannels; ++i) { - float* baseChannelData = static_cast(const_cast(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(const_cast(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(const_cast(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. diff --git a/dom/media/webaudio/AudioNodeEngine.cpp b/dom/media/webaudio/AudioNodeEngine.cpp index 57a9f71d98cb..6fd9cae776d2 100644 --- a/dom/media/webaudio/AudioNodeEngine.cpp +++ b/dom/media/webaudio/AudioNodeEngine.cpp @@ -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 = 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(const_cast(aChunk->mChannelData[i])) + aStart, - 0, aLength*sizeof(float)); + PodZero(aChunk->ChannelFloatsForWrite(i) + aStart, aLength); } } diff --git a/dom/media/webaudio/AudioNodeEngine.h b/dom/media/webaudio/AudioNodeEngine.h index 7b8d84a2cc0b..1fc1fb42fc38 100644 --- a/dom/media/webaudio/AudioNodeEngine.h +++ b/dom/media/webaudio/AudioNodeEngine.h @@ -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); diff --git a/dom/media/webaudio/AudioNodeExternalInputStream.cpp b/dom/media/webaudio/AudioNodeExternalInputStream.cpp index 84fdecbaeac0..9ba790487685 100644 --- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp +++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp @@ -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(const_cast(aBlock->mChannelData[c])) + aOffsetInBlock; + float* outputData = aBlock->ChannelFloatsForWrite(c) + aOffsetInBlock; if (channels[c]) { switch (aInput.mBufferFormat) { case AUDIO_FORMAT_FLOAT32: diff --git a/dom/media/webaudio/AudioNodeStream.cpp b/dom/media/webaudio/AudioNodeStream.cpp index ecbfbc4810ac..049f7aa9ddd6 100644 --- a/dom/media/webaudio/AudioNodeStream.cpp +++ b/dom/media/webaudio/AudioNodeStream.cpp @@ -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(channels[c]); - float* outputData = static_cast(const_cast(aBlock->mChannelData[c])); + float* outputData = aBlock->ChannelFloatsForWrite(c); if (inputData) { if (aInputIndex == 0) { AudioBlockCopyChannelWithScale(inputData, aChunk.mVolume, outputData); diff --git a/dom/media/webaudio/BiquadFilterNode.cpp b/dom/media/webaudio/BiquadFilterNode.cpp index b5bf83a70d75..12dfb62e53c6 100644 --- a/dom/media/webaudio/BiquadFilterNode.cpp +++ b/dom/media/webaudio/BiquadFilterNode.cpp @@ -206,7 +206,7 @@ public: SetParamsOnBiquad(mBiquads[i], aStream->SampleRate(), mType, freq, q, gain, detune); mBiquads[i].process(input, - static_cast(const_cast(aOutput->mChannelData[i])), + aOutput->ChannelFloatsForWrite(i), aInput.GetDuration()); } } diff --git a/dom/media/webaudio/ChannelMergerNode.cpp b/dom/media/webaudio/ChannelMergerNode.cpp index b095e7a81875..64defdb36c21 100644 --- a/dom/media/webaudio/ChannelMergerNode.cpp +++ b/dom/media/webaudio/ChannelMergerNode.cpp @@ -50,7 +50,7 @@ public: AudioBlockCopyChannelWithScale( static_cast(aInput[i].mChannelData[j]), aInput[i].mVolume, - static_cast(const_cast(aOutput[0].mChannelData[channelIndex]))); + aOutput[0].ChannelFloatsForWrite(channelIndex)); ++channelIndex; if (channelIndex >= channelCount) { return; diff --git a/dom/media/webaudio/ChannelSplitterNode.cpp b/dom/media/webaudio/ChannelSplitterNode.cpp index e15255bf57aa..2a9b2cff3c3d 100644 --- a/dom/media/webaudio/ChannelSplitterNode.cpp +++ b/dom/media/webaudio/ChannelSplitterNode.cpp @@ -38,7 +38,7 @@ public: AudioBlockCopyChannelWithScale( static_cast(aInput[0].mChannelData[i]), aInput[0].mVolume, - static_cast(const_cast(aOutput[i].mChannelData[0]))); + aOutput[i].ChannelFloatsForWrite(0)); } else { // Pad with silent channels if needed aOutput[i].SetNull(WEBAUDIO_BLOCK_SIZE); diff --git a/dom/media/webaudio/ConvolverNode.cpp b/dom/media/webaudio/ConvolverNode.cpp index 4aa5bd31d664..67e2cc22f45f 100644 --- a/dom/media/webaudio/ConvolverNode.cpp +++ b/dom/media/webaudio/ConvolverNode.cpp @@ -135,7 +135,7 @@ public: AllocateAudioBlock(numChannels, &input); for (uint32_t i = 0; i < numChannels; ++i) { const float* src = static_cast(aInput.mChannelData[i]); - float* dest = static_cast(const_cast(input.mChannelData[i])); + float* dest = input.ChannelFloatsForWrite(i); AudioBlockCopyChannelWithScale(src, aInput.mVolume, dest); } } diff --git a/dom/media/webaudio/DelayBuffer.cpp b/dom/media/webaudio/DelayBuffer.cpp index 1bf0a480aeee..1869c08f6994 100644 --- a/dom/media/webaudio/DelayBuffer.cpp +++ b/dom/media/webaudio/DelayBuffer.cpp @@ -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 - (const_cast(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 - (const_cast(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(mUpmixChannels[channel])[readOffset]; } } diff --git a/dom/media/webaudio/DelayBuffer.h b/dom/media/webaudio/DelayBuffer.h index 86ff03c055dd..6479ef443722 100644 --- a/dom/media/webaudio/DelayBuffer.h +++ b/dom/media/webaudio/DelayBuffer.h @@ -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(); diff --git a/dom/media/webaudio/GainNode.cpp b/dom/media/webaudio/GainNode.cpp index 207d6a136e06..f42636a8618d 100644 --- a/dom/media/webaudio/GainNode.cpp +++ b/dom/media/webaudio/GainNode.cpp @@ -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 (aInput.mChannelData[channel]); - float* buffer = static_cast (const_cast - (aOutput->mChannelData[channel])); + float* buffer = aOutput->ChannelFloatsForWrite(channel); AudioBlockCopyChannelWithScale(inputBuffer, computedGain, buffer); } } diff --git a/dom/media/webaudio/OscillatorNode.cpp b/dom/media/webaudio/OscillatorNode.cpp index 2faca4e9dac4..7d3156d3e2dd 100644 --- a/dom/media/webaudio/OscillatorNode.cpp +++ b/dom/media/webaudio/OscillatorNode.cpp @@ -308,8 +308,7 @@ public: } AllocateAudioBlock(1, aOutput); - float* output = static_cast( - const_cast(aOutput->mChannelData[0])); + float* output = aOutput->ChannelFloatsForWrite(0); uint32_t start, end; FillBounds(output, ticks, start, end); diff --git a/dom/media/webaudio/PanningUtils.h b/dom/media/webaudio/PanningUtils.h index 126bc898287c..dfe7a84afece 100644 --- a/dom/media/webaudio/PanningUtils.h +++ b/dom/media/webaudio/PanningUtils.h @@ -18,9 +18,9 @@ void GainMonoToStereo(const AudioChunk& aInput, AudioChunk* aOutput, T aGainL, T aGainR) { - float* outputL = static_cast(const_cast(aOutput->mChannelData[0])); - float* outputR = static_cast(const_cast(aOutput->mChannelData[1])); - const float* input = static_cast(const_cast(aInput.mChannelData[0])); + float* outputL = aOutput->ChannelFloatsForWrite(0); + float* outputR = aOutput->ChannelFloatsForWrite(1); + const float* input = static_cast(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(const_cast(aOutput->mChannelData[0])); - float* outputR = static_cast(const_cast(aOutput->mChannelData[1])); - const float* inputL = static_cast(const_cast(aInput.mChannelData[0])); - const float* inputR = static_cast(const_cast(aInput.mChannelData[1])); + float* outputL = aOutput->ChannelFloatsForWrite(0); + float* outputR = aOutput->ChannelFloatsForWrite(1); + const float* inputL = static_cast(aInput.mChannelData[0]); + const float* inputR = static_cast(aInput.mChannelData[1]); MOZ_ASSERT(aInput.ChannelCount() == 2); MOZ_ASSERT(aOutput->ChannelCount() == 2); diff --git a/dom/media/webaudio/StereoPannerNode.cpp b/dom/media/webaudio/StereoPannerNode.cpp index c00758b9a2d0..f405f0ad79bf 100644 --- a/dom/media/webaudio/StereoPannerNode.cpp +++ b/dom/media/webaudio/StereoPannerNode.cpp @@ -86,7 +86,7 @@ public: void SetToSilentStereoBlock(AudioChunk* aChunk) { for (uint32_t channel = 0; channel < 2; channel++) { - float* samples = static_cast(const_cast(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(aInput.mChannelData[0]); for (uint32_t channel = 0; channel < 2; channel++) { - float* output = static_cast(const_cast(aOutput->mChannelData[channel])); + float* output = aOutput->ChannelFloatsForWrite(channel); PodCopy(output, input, WEBAUDIO_BLOCK_SIZE); } } diff --git a/dom/media/webaudio/WaveShaperNode.cpp b/dom/media/webaudio/WaveShaperNode.cpp index 538e37e1b560..61769f442d6e 100644 --- a/dom/media/webaudio/WaveShaperNode.cpp +++ b/dom/media/webaudio/WaveShaperNode.cpp @@ -232,7 +232,7 @@ public: float* scaledSample = (float *)(aInput.mChannelData[i]); AudioBlockInPlaceScale(scaledSample, aInput.mVolume); const float* inputBuffer = static_cast(scaledSample); - float* outputBuffer = const_cast (static_cast(aOutput->mChannelData[i])); + float* outputBuffer = aOutput->ChannelFloatsForWrite(i); float* sampleBuffer; switch (mType) { diff --git a/dom/media/webaudio/test/mochitest.ini b/dom/media/webaudio/test/mochitest.ini index 89c45163ee9b..428e2b4a74df 100644 --- a/dom/media/webaudio/test/mochitest.ini +++ b/dom/media/webaudio/test/mochitest.ini @@ -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] diff --git a/dom/media/webaudio/test/test_analyserNodeWithGain.html b/dom/media/webaudio/test/test_analyserNodeWithGain.html new file mode 100644 index 000000000000..6cb059f23e04 --- /dev/null +++ b/dom/media/webaudio/test/test_analyserNodeWithGain.html @@ -0,0 +1,47 @@ + +Test effect of AnalyserNode on GainNode output + + + diff --git a/dom/security/moz.build b/dom/security/moz.build index b0c0f34aebe1..1577c17512b7 100644 --- a/dom/security/moz.build +++ b/dom/security/moz.build @@ -36,3 +36,7 @@ LOCAL_INCLUDES += [ '/caps', '/netwerk/base', ] + +if CONFIG['GNU_CC']: + CFLAGS += ['-Wshadow'] + CXXFLAGS += ['-Wshadow'] diff --git a/dom/workers/DataStore.cpp b/dom/workers/DataStore.cpp index 1b08bcb392b4..6ce1be706717 100644 --- a/dom/workers/DataStore.cpp +++ b/dom/workers/DataStore.cpp @@ -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 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& 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 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; diff --git a/dom/xbl/nsXBLDocumentInfo.cpp b/dom/xbl/nsXBLDocumentInfo.cpp index 645b1f9ea17e..f9560350416e 100644 --- a/dom/xbl/nsXBLDocumentInfo.cpp +++ b/dom/xbl/nsXBLDocumentInfo.cpp @@ -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(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(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 diff --git a/gfx/layers/ImageContainer.cpp b/gfx/layers/ImageContainer.cpp index d9b5fb79647c..84649184a4cc 100644 --- a/gfx/layers/ImageContainer.cpp +++ b/gfx/layers/ImageContainer.cpp @@ -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 dataSurf = surface->GetDataSurface(); + textureClient->UpdateFromSurface(dataSurf); + + textureClient->SyncWithObject(forwarder->GetSyncObject()); mTextureClients.Put(forwarder->GetSerial(), textureClient); return textureClient; diff --git a/gfx/layers/TextureDIB.cpp b/gfx/layers/TextureDIB.cpp index d12bb63241e6..e52617b7a851 100644 --- a/gfx/layers/TextureDIB.cpp +++ b/gfx/layers/TextureDIB.cpp @@ -56,6 +56,25 @@ TextureClientDIB::BorrowDrawTarget() return mDrawTarget; } +void +TextureClientDIB::UpdateFromSurface(gfx::DataSourceSurface* aSurface) +{ + MOZ_ASSERT(mIsLocked && IsAllocated()); + + nsRefPtr 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) diff --git a/gfx/layers/TextureDIB.h b/gfx/layers/TextureDIB.h index 59ff3a920f6a..b3e2c4f68ba6 100644 --- a/gfx/layers/TextureDIB.h +++ b/gfx/layers/TextureDIB.h @@ -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: diff --git a/gfx/layers/apz/util/APZCCallbackHelper.cpp b/gfx/layers/apz/util/APZCCallbackHelper.cpp index 812f11407152..3617bcaf7e0e 100644 --- a/gfx/layers/apz/util/APZCCallbackHelper.cpp +++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp @@ -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); diff --git a/gfx/layers/basic/TextureClientX11.cpp b/gfx/layers/basic/TextureClientX11.cpp index ff0eefdde84b..81d3ae92ea93 100644 --- a/gfx/layers/basic/TextureClientX11.cpp +++ b/gfx/layers/basic/TextureClientX11.cpp @@ -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()); +} \ No newline at end of file diff --git a/gfx/layers/basic/TextureClientX11.h b/gfx/layers/basic/TextureClientX11.h index 30b4d528c52e..e1afb8b0097d 100644 --- a/gfx/layers/basic/TextureClientX11.h +++ b/gfx/layers/basic/TextureClientX11.h @@ -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; } diff --git a/gfx/layers/client/TextureClient.cpp b/gfx/layers/client/TextureClient.cpp index 3eb2350cc44a..afa4f53af9a7 100644 --- a/gfx/layers/client/TextureClient.cpp +++ b/gfx/layers/client/TextureClient.cpp @@ -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 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) { diff --git a/gfx/layers/client/TextureClient.h b/gfx/layers/client/TextureClient.h index c0818129214b..6fb4bb38bfec 100644 --- a/gfx/layers/client/TextureClient.h +++ b/gfx/layers/client/TextureClient.h @@ -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; diff --git a/gfx/layers/d3d11/TextureD3D11.cpp b/gfx/layers/d3d11/TextureD3D11.cpp index 5b7b0edab485..59af9af04128 100644 --- a/gfx/layers/d3d11/TextureD3D11.cpp +++ b/gfx/layers/d3d11/TextureD3D11.cpp @@ -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 device; + mTexture->GetDevice(byRef(device)); + RefPtr 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 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); diff --git a/gfx/layers/d3d11/TextureD3D11.h b/gfx/layers/d3d11/TextureD3D11.h index c8e998c7bbbc..c6ca898c375d 100644 --- a/gfx/layers/d3d11/TextureD3D11.h +++ b/gfx/layers/d3d11/TextureD3D11.h @@ -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; diff --git a/gfx/layers/d3d9/TextureD3D9.cpp b/gfx/layers/d3d9/TextureD3D9.cpp index 3c6131a0dde5..b88ca6fd446d 100644 --- a/gfx/layers/d3d9/TextureD3D9.cpp +++ b/gfx/layers/d3d9/TextureD3D9.cpp @@ -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) { diff --git a/gfx/layers/d3d9/TextureD3D9.h b/gfx/layers/d3d9/TextureD3D9.h index 6278c40c2561..612e11cff643 100644 --- a/gfx/layers/d3d9/TextureD3D9.h +++ b/gfx/layers/d3d9/TextureD3D9.h @@ -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; diff --git a/gfx/layers/opengl/GrallocTextureClient.cpp b/gfx/layers/opengl/GrallocTextureClient.cpp index ca485f6f8c17..b340bc4608a5 100644 --- a/gfx/layers/opengl/GrallocTextureClient.cpp +++ b/gfx/layers/opengl/GrallocTextureClient.cpp @@ -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) diff --git a/gfx/layers/opengl/GrallocTextureClient.h b/gfx/layers/opengl/GrallocTextureClient.h index 0276b610c03a..a2ed914878b0 100644 --- a/gfx/layers/opengl/GrallocTextureClient.h +++ b/gfx/layers/opengl/GrallocTextureClient.h @@ -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; diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index 8126f677911a..1a2a4c8f01b6 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -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) diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index eb730b3a8cf0..3cfcc85dceb8 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -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(); diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index a552038d190b..eb0ef2de27fc 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -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& 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(); diff --git a/ipc/ipdl/ipdl/lower.py b/ipc/ipdl/ipdl/lower.py index 2c90eec21d4c..390a910fb6ac 100644 --- a/ipc/ipdl/ipdl/lower.py +++ b/ipc/ipdl/ipdl/lower.py @@ -2057,6 +2057,12 @@ def _generateCxxStruct(sd): # Struct() defctor = ConstructorDefn(ConstructorDecl(sd.name)) defctor.addstmt(StmtExpr(callinit)) + defctor.memberinits = [] + for f in sd.fields: + # Only generate default values for primitives. + if not (f.ipdltype.isCxx() and f.ipdltype.isAtom()): + continue + defctor.memberinits.append(ExprMemberInit(f.memberVar())) struct.addstmts([ defctor, Whitespace.NL ]) # Struct(const field1& _f1, ...) diff --git a/js/src/builtin/ReflectParse.cpp b/js/src/builtin/ReflectParse.cpp index e0b54ad92b42..537179a5ae37 100644 --- a/js/src/builtin/ReflectParse.cpp +++ b/js/src/builtin/ReflectParse.cpp @@ -47,7 +47,7 @@ enum AssignmentOperator { /* assign */ AOP_ASSIGN = 0, /* operator-assign */ - AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD, + AOP_PLUS, AOP_MINUS, AOP_STAR, AOP_DIV, AOP_MOD, AOP_POW, /* shift-assign */ AOP_LSH, AOP_RSH, AOP_URSH, /* binary */ @@ -66,7 +66,7 @@ enum BinaryOperator { /* shift */ BINOP_LSH, BINOP_RSH, BINOP_URSH, /* arithmetic */ - BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD, + BINOP_ADD, BINOP_SUB, BINOP_STAR, BINOP_DIV, BINOP_MOD, BINOP_POW, /* binary */ BINOP_BITOR, BINOP_BITXOR, BINOP_BITAND, /* misc */ @@ -113,6 +113,7 @@ static const char* const aopNames[] = { "*=", /* AOP_STAR */ "/=", /* AOP_DIV */ "%=", /* AOP_MOD */ + "**=", /* AOP_POW */ "<<=", /* AOP_LSH */ ">>=", /* AOP_RSH */ ">>>=", /* AOP_URSH */ @@ -138,6 +139,7 @@ static const char* const binopNames[] = { "*", /* BINOP_STAR */ "/", /* BINOP_DIV */ "%", /* BINOP_MOD */ + "**", /* BINOP_POW */ "|", /* BINOP_BITOR */ "^", /* BINOP_BITXOR */ "&", /* BINOP_BITAND */ @@ -1858,6 +1860,7 @@ class ASTSerializer bool statements(ParseNode* pn, NodeVector& elts); bool expressions(ParseNode* pn, NodeVector& elts); bool leftAssociate(ParseNode* pn, MutableHandleValue dst); + bool rightAssociate(ParseNode* pn, MutableHandleValue dst); bool functionArgs(ParseNode* pn, ParseNode* pnargs, ParseNode* pnbody, NodeVector& args, NodeVector& defaults, MutableHandleValue rest); @@ -1974,6 +1977,8 @@ ASTSerializer::aop(JSOp op) return AOP_DIV; case JSOP_MOD: return AOP_MOD; + case JSOP_POW: + return AOP_POW; case JSOP_LSH: return AOP_LSH; case JSOP_RSH: @@ -2052,6 +2057,8 @@ ASTSerializer::binop(ParseNodeKind kind, JSOp op) return BINOP_DIV; case PNK_MOD: return BINOP_MOD; + case PNK_POW: + return BINOP_POW; case PNK_BITOR: return BINOP_BITOR; case PNK_BITXOR: @@ -2786,6 +2793,49 @@ ASTSerializer::leftAssociate(ParseNode* pn, MutableHandleValue dst) return true; } +bool +ASTSerializer::rightAssociate(ParseNode* pn, MutableHandleValue dst) +{ + MOZ_ASSERT(pn->isArity(PN_LIST)); + MOZ_ASSERT(pn->pn_count >= 1); + + // First, we need to reverse the list, so that we can traverse it in the right order. + // It's OK to destructively reverse the list, because there are no other consumers. + + ParseNode* head = pn->pn_head; + ParseNode* prev = nullptr; + ParseNode* current = head; + ParseNode* next; + while (current != nullptr) { + next = current->pn_next; + current->pn_next = prev; + prev = current; + current = next; + } + + head = prev; + + RootedValue right(cx); + if (!expression(head, &right)) + return false; + for (ParseNode* next = head->pn_next; next; next = next->pn_next) { + RootedValue left(cx); + if (!expression(next, &left)) + return false; + + TokenPos subpos(pn->pn_pos.begin, next->pn_pos.end); + + BinaryOperator op = binop(pn->getKind(), pn->getOp()); + LOCAL_ASSERT(op > BINOP_ERR && op < BINOP_LIMIT); + + if (!builder.binaryExpression(op, left, right, &subpos, &right)) + return false; + } + + dst.set(right); + return true; +} + bool ASTSerializer::comprehensionBlock(ParseNode* pn, MutableHandleValue dst) { @@ -2974,6 +3024,7 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: { MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_left->pn_pos)); MOZ_ASSERT(pn->pn_pos.encloses(pn->pn_right->pn_pos)); @@ -3010,6 +3061,9 @@ ASTSerializer::expression(ParseNode* pn, MutableHandleValue dst) case PNK_INSTANCEOF: return leftAssociate(pn, dst); + case PNK_POW: + return rightAssociate(pn, dst); + case PNK_DELETENAME: case PNK_DELETEPROP: case PNK_DELETESUPERPROP: diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 35fc03faf7ee..8540ce603ae4 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -2039,6 +2039,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: MOZ_ASSERT(pn->isArity(PN_BINARY)); *answer = true; return true; @@ -2091,6 +2092,7 @@ BytecodeEmitter::checkSideEffects(ParseNode* pn, bool* answer) case PNK_STAR: case PNK_DIV: case PNK_MOD: + case PNK_POW: MOZ_ASSERT(pn->isArity(PN_LIST)); MOZ_ASSERT(pn->pn_count >= 2); *answer = true; @@ -7730,6 +7732,7 @@ BytecodeEmitter::emitTree(ParseNode* pn) case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: if (!emitAssignment(pn->pn_left, pn->getOp(), pn->pn_right)) return false; break; @@ -7779,6 +7782,20 @@ BytecodeEmitter::emitTree(ParseNode* pn) break; } + case PNK_POW: { + MOZ_ASSERT(pn->isArity(PN_LIST)); + /* Right-associative operator chain. */ + for (ParseNode* subexpr = pn->pn_head; subexpr; subexpr = subexpr->pn_next) { + if (!emitTree(subexpr)) + return false; + } + for (uint32_t i = 0; i < pn->pn_count - 1; i++) { + if (!emit1(JSOP_POW)) + return false; + } + break; + } + case PNK_TYPEOFNAME: ok = emitTypeof(pn, JSOP_TYPEOF); break; diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index be844ad8686f..1ba497518a83 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -363,6 +363,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_STAR: case PNK_DIV: case PNK_MOD: + case PNK_POW: case PNK_ASSIGN: case PNK_ADDASSIGN: case PNK_SUBASSIGN: @@ -375,6 +376,7 @@ ContainsHoistedDeclaration(ExclusiveContext* cx, ParseNode* node, bool* result) case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: case PNK_COMMA: case PNK_ARRAY: case PNK_OBJECT: @@ -530,6 +532,10 @@ FoldBinaryNumeric(ExclusiveContext* cx, JSOp op, ParseNode* pn1, ParseNode* pn2, } break; + case JSOP_POW: + d = ecmaPow(d, d2); + break; + default:; } @@ -604,19 +610,18 @@ condIf(const ParseNode* pn, ParseNodeKind kind) } static bool -Fold(ExclusiveContext* cx, ParseNode** pnp, - FullParseHandler& handler, const ReadOnlyCompileOptions& options, - bool inGenexpLambda, SyntacticContext sc); +Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda, + SyntacticContext sc); static bool -FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, - const ReadOnlyCompileOptions& options, bool inGenexpLambda) +FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) { MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; - if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) return false; // Constant-fold the entire |typeof| if given a constant with known type. @@ -633,7 +638,7 @@ FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, result = cx->names().function; if (result) { - handler.prepareNodeForMutation(node); + parser.prepareNodeForMutation(node); node->setKind(PNK_STRING); node->setArity(PN_NULLARY); @@ -645,14 +650,14 @@ FoldTypeOfExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, } static bool -FoldVoid(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, - const ReadOnlyCompileOptions& options, bool inGenexpLambda, SyntacticContext sc) +FoldVoid(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda, SyntacticContext sc) { MOZ_ASSERT(node->isKind(PNK_VOID)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; - if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) return false; if (sc == SyntacticContext::Condition) { @@ -664,7 +669,7 @@ FoldVoid(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, expr->isKind(PNK_NULL) || expr->isKind(PNK_FUNCTION)) { - handler.prepareNodeForMutation(node); + parser.prepareNodeForMutation(node); node->setKind(PNK_FALSE); node->setArity(PN_NULLARY); node->setOp(JSOP_FALSE); @@ -675,14 +680,14 @@ FoldVoid(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, } static bool -FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, - const ReadOnlyCompileOptions& options, bool inGenexpLambda) +FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) { MOZ_ASSERT(node->isKind(PNK_DELETEEXPR)); MOZ_ASSERT(node->isArity(PN_UNARY)); ParseNode*& expr = node->pn_kid; - if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) return false; // Expression deletion evaluates the expression, then evaluates to @@ -695,7 +700,7 @@ FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, expr->isKind(PNK_NULL) || expr->isKind(PNK_FUNCTION)) { - handler.prepareNodeForMutation(node); + parser.prepareNodeForMutation(node); node->setKind(PNK_TRUE); node->setArity(PN_NULLARY); node->setOp(JSOP_TRUE); @@ -705,15 +710,15 @@ FoldDeleteExpr(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, } static bool -FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, - const ReadOnlyCompileOptions& options, bool inGenexpLambda) +FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) { MOZ_ASSERT(node->isKind(PNK_DELETEELEM) || node->isKind(PNK_DELETESUPERELEM)); MOZ_ASSERT(node->isArity(PN_UNARY)); MOZ_ASSERT(node->pn_kid->isKind(PNK_ELEM) || node->pn_kid->isKind(PNK_SUPERELEM)); ParseNode*& expr = node->pn_kid; - if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) return false; // If we're deleting an element, but constant-folding converted our @@ -732,8 +737,8 @@ FoldDeleteElement(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handl } static bool -FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, FullParseHandler& handler, - const ReadOnlyCompileOptions& options, bool inGenexpLambda) +FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) { MOZ_ASSERT(node->isKind(PNK_DELETEPROP) || node->isKind(PNK_DELETESUPERPROP)); MOZ_ASSERT(node->isArity(PN_UNARY)); @@ -744,7 +749,7 @@ FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, FullParseHandler& hand ParseNodeKind oldKind = expr->getKind(); #endif - if (!Fold(cx, &expr, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) return false; MOZ_ASSERT(expr->isKind(oldKind), @@ -753,10 +758,100 @@ FoldDeleteProperty(ExclusiveContext* cx, ParseNode* node, FullParseHandler& hand return true; } +static bool +FoldNot(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) +{ + MOZ_ASSERT(node->isKind(PNK_NOT)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode*& expr = node->pn_kid; + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Condition)) + return false; + + if (expr->isKind(PNK_NUMBER)) { + double d = expr->pn_dval; + + parser.prepareNodeForMutation(node); + if (d == 0 || IsNaN(d)) { + node->setKind(PNK_TRUE); + node->setOp(JSOP_TRUE); + } else { + node->setKind(PNK_FALSE); + node->setOp(JSOP_FALSE); + } + node->setArity(PN_NULLARY); + } else if (expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) { + bool newval = !expr->isKind(PNK_TRUE); + + parser.prepareNodeForMutation(node); + node->setKind(newval ? PNK_TRUE : PNK_FALSE); + node->setArity(PN_NULLARY); + node->setOp(newval ? JSOP_TRUE : JSOP_FALSE); + } + + return true; +} + +static bool +FoldUnaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) +{ + MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG), + "need a different method for this node kind"); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode*& expr = node->pn_kid; + if (!Fold(cx, &expr, parser, inGenexpLambda, SyntacticContext::Other)) + return false; + + if (expr->isKind(PNK_NUMBER) || expr->isKind(PNK_TRUE) || expr->isKind(PNK_FALSE)) { + double d = expr->isKind(PNK_NUMBER) + ? expr->pn_dval + : double(expr->isKind(PNK_TRUE)); + + if (node->isKind(PNK_BITNOT)) + d = ~ToInt32(d); + else if (node->isKind(PNK_NEG)) + d = -d; + else + MOZ_ASSERT(node->isKind(PNK_POS)); // nothing to do + + parser.prepareNodeForMutation(node); + node->setKind(PNK_NUMBER); + node->setOp(JSOP_DOUBLE); + node->setArity(PN_NULLARY); + node->pn_dval = d; + } + + return true; +} + +static bool +FoldIncrementDecrement(ExclusiveContext* cx, ParseNode* node, Parser& parser, + bool inGenexpLambda) +{ + MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) || + node->isKind(PNK_POSTINCREMENT) || + node->isKind(PNK_PREDECREMENT) || + node->isKind(PNK_POSTDECREMENT)); + MOZ_ASSERT(node->isArity(PN_UNARY)); + + ParseNode*& target = node->pn_kid; + MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser::PermitAssignmentToFunctionCalls)); + + if (!Fold(cx, &target, parser, inGenexpLambda, SyntacticContext::Other)) + return false; + + MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser::PermitAssignmentToFunctionCalls)); + + return true; +} + + bool -Fold(ExclusiveContext* cx, ParseNode** pnp, - FullParseHandler& handler, const ReadOnlyCompileOptions& options, - bool inGenexpLambda, SyntacticContext sc) +Fold(ExclusiveContext* cx, ParseNode** pnp, Parser& parser, bool inGenexpLambda, + SyntacticContext sc) { JS_CHECK_RECURSION(cx, return false); @@ -809,10 +904,10 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, return true; case PNK_TYPEOFEXPR: - return FoldTypeOfExpr(cx, pn, handler, options, inGenexpLambda); + return FoldTypeOfExpr(cx, pn, parser, inGenexpLambda); case PNK_VOID: - return FoldVoid(cx, pn, handler, options, inGenexpLambda, sc); + return FoldVoid(cx, pn, parser, inGenexpLambda, sc); case PNK_DELETENAME: { MOZ_ASSERT(pn->isArity(PN_UNARY)); @@ -821,25 +916,31 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, } case PNK_DELETEEXPR: - return FoldDeleteExpr(cx, pn, handler, options, inGenexpLambda); + return FoldDeleteExpr(cx, pn, parser, inGenexpLambda); case PNK_DELETEELEM: case PNK_DELETESUPERELEM: - return FoldDeleteElement(cx, pn, handler, options, inGenexpLambda); + return FoldDeleteElement(cx, pn, parser, inGenexpLambda); case PNK_DELETEPROP: case PNK_DELETESUPERPROP: - return FoldDeleteProperty(cx, pn, handler, options, inGenexpLambda); + return FoldDeleteProperty(cx, pn, parser, inGenexpLambda); case PNK_NOT: + return FoldNot(cx, pn, parser, inGenexpLambda); + case PNK_BITNOT: - case PNK_THROW: case PNK_POS: case PNK_NEG: + return FoldUnaryArithmetic(cx, pn, parser, inGenexpLambda); + case PNK_PREINCREMENT: case PNK_POSTINCREMENT: case PNK_PREDECREMENT: case PNK_POSTDECREMENT: + return FoldIncrementDecrement(cx, pn, parser, inGenexpLambda); + + case PNK_THROW: case PNK_COMPUTED_NAME: case PNK_ARRAYPUSH: case PNK_SPREAD: @@ -858,6 +959,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: case PNK_ELEM: case PNK_SUPERELEM: case PNK_COLON: @@ -908,6 +1010,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, case PNK_STAR: case PNK_DIV: case PNK_MOD: + case PNK_POW: case PNK_COMMA: case PNK_NEW: case PNK_CALL: @@ -951,9 +1054,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, // Note: pn_body is nullptr for functions which are being lazily parsed. MOZ_ASSERT(pn->getKind() == PNK_FUNCTION); if (pn->pn_body) { - if (!Fold(cx, &pn->pn_body, handler, options, pn->pn_funbox->inGenexpLambda, + if (!Fold(cx, &pn->pn_body, parser, pn->pn_funbox->inGenexpLambda, SyntacticContext::Other)) + { return false; + } } break; @@ -970,7 +1075,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, listp = &(*listp)->pn_next; for (; *listp; listp = &(*listp)->pn_next) { - if (!Fold(cx, listp, handler, options, inGenexpLambda, kidsc)) + if (!Fold(cx, listp, parser, inGenexpLambda, kidsc)) return false; } @@ -986,16 +1091,16 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, case PN_TERNARY: /* Any kid may be null (e.g. for (;;)). */ if (pn->pn_kid1) { - if (!Fold(cx, &pn->pn_kid1, handler, options, inGenexpLambda, condIf(pn, PNK_IF))) + if (!Fold(cx, &pn->pn_kid1, parser, inGenexpLambda, condIf(pn, PNK_IF))) return false; } pn1 = pn->pn_kid1; if (pn->pn_kid2) { - if (!Fold(cx, &pn->pn_kid2, handler, options, inGenexpLambda, condIf(pn, PNK_FORHEAD))) + if (!Fold(cx, &pn->pn_kid2, parser, inGenexpLambda, condIf(pn, PNK_FORHEAD))) return false; if (pn->isKind(PNK_FORHEAD) && pn->pn_kid2->isKind(PNK_TRUE)) { - handler.freeTree(pn->pn_kid2); + parser.freeTree(pn->pn_kid2); pn->pn_kid2 = nullptr; } } @@ -1006,7 +1111,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, restartNode = &pn->pn_kid3; restartContext = SyntacticContext::Other; } else { - if (!Fold(cx, &pn->pn_kid3, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (!Fold(cx, &pn->pn_kid3, parser, inGenexpLambda, SyntacticContext::Other)) return false; } } @@ -1020,19 +1125,19 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, SyntacticContext kidsc = SyntacticContext::Other; if (sc == SyntacticContext::Condition) kidsc = sc; - if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, kidsc)) + if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda, kidsc)) return false; - if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, kidsc)) + if (!Fold(cx, &pn->pn_right, parser, inGenexpLambda, kidsc)) return false; } else { /* First kid may be null (for default case in switch). */ if (pn->pn_left) { - if (!Fold(cx, &pn->pn_left, handler, options, inGenexpLambda, condIf(pn, PNK_WHILE))) + if (!Fold(cx, &pn->pn_left, parser, inGenexpLambda, condIf(pn, PNK_WHILE))) return false; } /* Second kid may be null (for return in non-generator). */ if (pn->pn_right) { - if (!Fold(cx, &pn->pn_right, handler, options, inGenexpLambda, condIf(pn, PNK_DOWHILE))) + if (!Fold(cx, &pn->pn_right, parser, inGenexpLambda, condIf(pn, PNK_DOWHILE))) return false; } } @@ -1044,10 +1149,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, MOZ_ASSERT(!IsDeleteKind(pn->getKind()), "should have been handled above"); if (pn->pn_kid) { - SyntacticContext kidsc = pn->isKind(PNK_NOT) - ? SyntacticContext::Condition - : SyntacticContext::Other; - if (!Fold(cx, &pn->pn_kid, handler, options, inGenexpLambda, kidsc)) + if (!Fold(cx, &pn->pn_kid, parser, inGenexpLambda, SyntacticContext::Other)) return false; } pn1 = pn->pn_kid; @@ -1064,7 +1166,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, ParseNode** lhsp = &pn->pn_expr; while (*lhsp && (*lhsp)->isArity(PN_NAME) && !(*lhsp)->isUsed()) lhsp = &(*lhsp)->pn_expr; - if (*lhsp && !Fold(cx, lhsp, handler, options, inGenexpLambda, SyntacticContext::Other)) + if (*lhsp && !Fold(cx, lhsp, parser, inGenexpLambda, SyntacticContext::Other)) return false; pn1 = *lhsp; } @@ -1145,7 +1247,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null * kid or an empty statement for a child. */ - handler.prepareNodeForMutation(pn); + parser.prepareNodeForMutation(pn); pn->setKind(PNK_STATEMENTLIST); pn->setArity(PN_LIST); pn->makeEmpty(); @@ -1153,7 +1255,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, if (pn3 && pn3 != pn2) { if (restartNode && *restartNode == pn3) restartNode = nullptr; - handler.freeTree(pn3); + parser.freeTree(pn3); } break; @@ -1172,7 +1274,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, if ((t == Truthy) == pn->isKind(PNK_OR)) { for (pn2 = pn1->pn_next; pn2; pn2 = pn3) { pn3 = pn2->pn_next; - handler.freeTree(pn2); + parser.freeTree(pn2); --pn->pn_count; } pn1->pn_next = nullptr; @@ -1182,7 +1284,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, if (pn->pn_count == 1) break; *listp = pn1->pn_next; - handler.freeTree(pn1); + parser.freeTree(pn1); --pn->pn_count; } while ((pn1 = *listp) != nullptr); @@ -1214,7 +1316,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, while (pn2 && pn2->isKind(PNK_NUMBER)) { pn1->pn_dval += pn2->pn_dval; pn1->pn_next = pn2->pn_next; - handler.freeTree(pn2); + parser.freeTree(pn2); pn2 = pn1->pn_next; pn->pn_count--; folded = true; @@ -1258,7 +1360,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, if (!foldedStr) return false; pn1->pn_next = pn2->pn_next; - handler.freeTree(pn2); + parser.freeTree(pn2); pn2 = pn1->pn_next; pn->pn_count--; folded = true; @@ -1304,6 +1406,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, case PNK_URSH: case PNK_DIV: case PNK_MOD: + case PNK_POW: MOZ_ASSERT(pn->getArity() == PN_LIST); MOZ_ASSERT(pn->pn_count >= 2); for (pn2 = pn1; pn2; pn2 = pn2->pn_next) { @@ -1315,6 +1418,12 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, if (!pn2->isKind(PNK_NUMBER)) break; } + + // No constant-folding for (2**3**5), because (**) is right- + // associative. We would have to reverse the list. It's not worth it. + if (pn->getKind() == PNK_POW && pn->pn_count > 2) + break; + if (!pn2) { JSOp op = pn->getOp(); @@ -1333,63 +1442,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, case PNK_TYPEOFNAME: case PNK_TYPEOFEXPR: case PNK_VOID: - MOZ_CRASH("should have been fully handled above"); - case PNK_NOT: case PNK_BITNOT: case PNK_POS: case PNK_NEG: - if (pn1->isKind(PNK_NUMBER)) { - double d; - - /* Operate on one numeric constant. */ - d = pn1->pn_dval; - switch (pn->getKind()) { - case PNK_BITNOT: - d = ~ToInt32(d); - break; - - case PNK_NEG: - d = -d; - break; - - case PNK_POS: - break; - - case PNK_NOT: - if (d == 0 || IsNaN(d)) { - pn->setKind(PNK_TRUE); - pn->setOp(JSOP_TRUE); - } else { - pn->setKind(PNK_FALSE); - pn->setOp(JSOP_FALSE); - } - pn->setArity(PN_NULLARY); - /* FALL THROUGH */ - - default: - /* Return early to dodge the common PNK_NUMBER code. */ - goto restart; - } - pn->setKind(PNK_NUMBER); - pn->setOp(JSOP_DOUBLE); - pn->setArity(PN_NULLARY); - pn->pn_dval = d; - handler.freeTree(pn1); - } else if (pn1->isKind(PNK_TRUE) || pn1->isKind(PNK_FALSE)) { - if (pn->isKind(PNK_NOT)) { - ReplaceNode(pnp, pn1); - pn = pn1; - if (pn->isKind(PNK_TRUE)) { - pn->setKind(PNK_FALSE); - pn->setOp(JSOP_FALSE); - } else { - pn->setKind(PNK_TRUE); - pn->setOp(JSOP_TRUE); - } - } - } - break; + MOZ_CRASH("should have been fully handled above"); case PNK_ELEM: { // An indexed expression, pn1[pn2]. A few cases can be improved. @@ -1425,7 +1482,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, // Convert to a property access (like pn1.foo) which we optimize // better downstream. Don't bother with this for names which TI // considers to be indexes, to simplify downstream analysis. - ParseNode* expr = handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end); + ParseNode* expr = parser.handler.newPropertyAccess(pn->pn_left, name, pn->pn_pos.end); if (!expr) return false; expr->setInParens(pn->isInParens()); @@ -1437,11 +1494,11 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, // node with |"prop"| as its child so that our |pn| doesn't have a // necessarily-weird structure (say, by nulling out |pn->pn_left| // only) that would fail AST sanity assertions performed by - // |handler.freeTree(pn)|. + // |parser.freeTree(pn)|. pn->setKind(PNK_TYPEOFEXPR); pn->setArity(PN_UNARY); pn->pn_kid = pn2; - handler.freeTree(pn); + parser.freeTree(pn); pn = expr; } @@ -1461,7 +1518,7 @@ Fold(ExclusiveContext* cx, ParseNode** pnp, * a method list corrupts the method list. However, methods are M's in * statements of the form 'this.foo = M;', which we never fold, so we're okay. */ - handler.prepareNodeForMutation(pn); + parser.prepareNodeForMutation(pn); if (t == Truthy) { pn->setKind(PNK_TRUE); pn->setOp(JSOP_TRUE); @@ -1486,5 +1543,5 @@ frontend::FoldConstants(ExclusiveContext* cx, ParseNode** pnp, Parserpc->useAsmOrInsideUseAsm()) return true; - return Fold(cx, pnp, parser->handler, parser->options(), false, SyntacticContext::Other); + return Fold(cx, pnp, *parser, false, SyntacticContext::Other); } diff --git a/js/src/frontend/NameFunctions.cpp b/js/src/frontend/NameFunctions.cpp index 31c5474c2f6b..a8eb84f5c4f1 100644 --- a/js/src/frontend/NameFunctions.cpp +++ b/js/src/frontend/NameFunctions.cpp @@ -439,6 +439,7 @@ class NameResolver case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: case PNK_ELEM: case PNK_COLON: case PNK_CASE: @@ -659,6 +660,7 @@ class NameResolver case PNK_STAR: case PNK_DIV: case PNK_MOD: + case PNK_POW: case PNK_COMMA: case PNK_NEW: case PNK_CALL: diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index a4e3a87758b8..4d3219df87f1 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -272,6 +272,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_MULASSIGN: case PNK_DIVASSIGN: case PNK_MODASSIGN: + case PNK_POWASSIGN: // ...and a few others. case PNK_ELEM: case PNK_IMPORT_SPEC: @@ -478,6 +479,7 @@ PushNodeChildren(ParseNode* pn, NodeStack* stack) case PNK_STAR: case PNK_DIV: case PNK_MOD: + case PNK_POW: case PNK_COMMA: case PNK_NEW: case PNK_CALL: @@ -611,7 +613,18 @@ ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, Pars // processing such a tree, exactly implemented that way, would blow the // the stack. We use a list node that uses O(1) stack to represent // such operations: (+ a b c). - if (left->isKind(kind) && left->isOp(op) && (js_CodeSpec[op].format & JOF_LEFTASSOC)) { + // + // (**) is right-associative; per spec |a ** b ** c| parses as + // (** a (** b c)). But we treat this the same way, creating a list + // node: (** a b c). All consumers must understand that this must be + // processed with a right fold, whereas the list (+ a b c) must be + // processed with a left fold because (+) is left-associative. + // + if (left->isKind(kind) && + left->isOp(op) && + (js_CodeSpec[op].format & JOF_LEFTASSOC || + (kind == PNK_POW && !left->pn_parens))) + { ListNode* list = &left->as(); list->append(right); diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 740ec754abe4..9fa3593fa6ee 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -197,6 +197,7 @@ class UpvarCookie F(STAR) \ F(DIV) \ F(MOD) \ + F(POW) \ \ /* Assignment operators (= += -= etc.). */ \ /* ParseNode::isAssignment assumes all these are consecutive. */ \ @@ -211,7 +212,8 @@ class UpvarCookie F(URSHASSIGN) \ F(MULASSIGN) \ F(DIVASSIGN) \ - F(MODASSIGN) + F(MODASSIGN) \ + F(POWASSIGN) /* * Parsing builds a tree of nodes that directs code generation. This tree is @@ -230,9 +232,9 @@ enum ParseNodeKind #undef EMIT_ENUM PNK_LIMIT, /* domain size */ PNK_BINOP_FIRST = PNK_OR, - PNK_BINOP_LAST = PNK_MOD, + PNK_BINOP_LAST = PNK_POW, PNK_ASSIGNMENT_START = PNK_ASSIGN, - PNK_ASSIGNMENT_LAST = PNK_MODASSIGN + PNK_ASSIGNMENT_LAST = PNK_POWASSIGN }; inline bool @@ -369,31 +371,34 @@ IsDeleteKind(ParseNodeKind kind) * PNK_URSHASSIGN, * PNK_MULASSIGN, * PNK_DIVASSIGN, - * PNK_MODASSIGN + * PNK_MODASSIGN, + * PNK_POWASSIGN * PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr) * pn_kid1: cond, pn_kid2: then, pn_kid3: else - * PNK_OR binary pn_left: first in || chain, pn_right: rest of chain - * PNK_AND binary pn_left: first in && chain, pn_right: rest of chain - * PNK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr - * PNK_BITXOR binary pn_left: left-assoc ^ expr, pn_right: & expr - * PNK_BITAND binary pn_left: left-assoc & expr, pn_right: EQ expr - * - * PNK_EQ, binary pn_left: left-assoc EQ expr, pn_right: REL expr + * PNK_OR, list pn_head; list of pn_count subexpressions + * PNK_AND, All of these operators are left-associative except (**). + * PNK_BITOR, + * PNK_BITXOR, + * PNK_BITAND, + * PNK_EQ, * PNK_NE, * PNK_STRICTEQ, - * PNK_STRICTNE - * PNK_LT, binary pn_left: left-assoc REL expr, pn_right: SH expr + * PNK_STRICTNE, + * PNK_LT, * PNK_LE, * PNK_GT, - * PNK_GE - * PNK_LSH, binary pn_left: left-assoc SH expr, pn_right: ADD expr + * PNK_GE, + * PNK_LSH, * PNK_RSH, - * PNK_URSH - * PNK_ADD, binary pn_left: left-assoc ADD expr, pn_right: MUL expr - * PNK_SUB - * PNK_STAR, binary pn_left: left-assoc MUL expr, pn_right: UNARY expr - * PNK_DIV, pn_op: JSOP_MUL, JSOP_DIV, JSOP_MOD - * PNK_MOD + * PNK_URSH, + * PNK_ADD, + * PNK_SUB, + * PNK_STAR, + * PNK_DIV, + * PNK_MOD, + * PNK_POW (**) is right-associative, but forms a list + * nonetheless. Special hacks everywhere. + * * PNK_POS, unary pn_kid: UNARY expr * PNK_NEG * PNK_VOID, unary pn_kid: UNARY expr diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 0e229cec1ab4..29a231ddfdbf 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6351,7 +6351,8 @@ static const JSOp ParseNodeKindToJSOp[] = { JSOP_SUB, JSOP_MUL, JSOP_DIV, - JSOP_MOD + JSOP_MOD, + JSOP_POW }; static inline JSOp @@ -6392,10 +6393,11 @@ static const int PrecedenceTable[] = { 9, /* PNK_SUB */ 10, /* PNK_STAR */ 10, /* PNK_DIV */ - 10 /* PNK_MOD */ + 10, /* PNK_MOD */ + 11 /* PNK_POW */ }; -static const int PRECEDENCE_CLASSES = 10; +static const int PRECEDENCE_CLASSES = 11; static int Precedence(ParseNodeKind pnk) { @@ -6415,8 +6417,8 @@ MOZ_ALWAYS_INLINE typename ParseHandler::Node Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling, InvokedPrediction invoked) { - // Shift-reduce parser for the left-associative binary operator part of - // the JS syntax. + // Shift-reduce parser for the binary operator part of the JS expression + // syntax. // Conceptually there's just one stack, a stack of pairs (lhs, op). // It's implemented using two separate arrays, though. @@ -6448,10 +6450,9 @@ Parser::orExpr1(InHandling inHandling, YieldHandling yieldHandling // stack, reduce. This combines nodes on the stack until we form the // actual lhs of pnk. // - // The >= in this condition works because all the operators in question - // are left-associative; if any were not, the case where two operators - // have equal precedence would need to be handled specially, and the - // stack would need to be a Vector. + // The >= in this condition works because it is appendOrCreateList's + // job to decide if the operator in question is left- or + // right-associative, and build the corresponding tree. while (depth > 0 && Precedence(kindStack[depth - 1]) >= Precedence(pnk)) { depth--; ParseNodeKind combiningPnk = kindStack[depth]; @@ -6613,6 +6614,7 @@ Parser::assignExpr(InHandling inHandling, YieldHandling yieldHandl case TOK_MULASSIGN: kind = PNK_MULASSIGN; op = JSOP_MUL; break; case TOK_DIVASSIGN: kind = PNK_DIVASSIGN; op = JSOP_DIV; break; case TOK_MODASSIGN: kind = PNK_MODASSIGN; op = JSOP_MOD; break; + case TOK_POWASSIGN: kind = PNK_POWASSIGN; op = JSOP_POW; break; case TOK_ARROW: { // A line terminator between ArrowParameters and the => should trigger a SyntaxError. diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h index 9e5dff90a633..003b3831ee18 100644 --- a/js/src/frontend/Parser.h +++ b/js/src/frontend/Parser.h @@ -389,6 +389,9 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter /* State specific to the kind of parse being performed. */ ParseHandler handler; + void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); } + void freeTree(Node node) { handler.freeTree(node); } + private: bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber, va_list args); @@ -683,6 +686,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter ParseNodeKind headKind); bool checkForHeadConstInitializers(Node pn1); + public: enum FunctionCallBehavior { PermitAssignmentToFunctionCalls, ForbidAssignmentToFunctionCalls @@ -691,6 +695,7 @@ class Parser : private JS::AutoGCRooter, public StrictModeGetter bool isValidSimpleAssignmentTarget(Node node, FunctionCallBehavior behavior = ForbidAssignmentToFunctionCalls); + private: bool reportIfArgumentsEvalTarget(Node nameNode); bool reportIfNotValidSimpleAssignmentTarget(Node target, AssignmentFlavor flavor); diff --git a/js/src/frontend/SyntaxParseHandler.h b/js/src/frontend/SyntaxParseHandler.h index aec7002e6f76..65704a3c6ab1 100644 --- a/js/src/frontend/SyntaxParseHandler.h +++ b/js/src/frontend/SyntaxParseHandler.h @@ -173,6 +173,9 @@ class SyntaxParseHandler static Node null() { return NodeFailure; } + void prepareNodeForMutation(Node node) {} + void freeTree(Node node) {} + void trace(JSTracer* trc) {} Node newName(PropertyName* name, uint32_t blockid, const TokenPos& pos, ExclusiveContext* cx) { diff --git a/js/src/frontend/TokenKind.h b/js/src/frontend/TokenKind.h index 590667283662..83ee7717257c 100644 --- a/js/src/frontend/TokenKind.h +++ b/js/src/frontend/TokenKind.h @@ -122,7 +122,7 @@ * range-testing. \ */ \ /* \ - * Binary operators tokens, TOK_OR thru TOK_MOD. These must be in the same \ + * Binary operators tokens, TOK_OR thru TOK_POW. These must be in the same \ * order as F(OR) and friends in FOR_EACH_PARSE_NODE_KIND in ParseNode.h. \ */ \ macro(OR, "'||'") /* logical or */ \ @@ -163,7 +163,8 @@ macro(MUL, "'*'") \ macro(DIV, "'/'") \ macro(MOD, "'%'") \ - range(BINOP_LAST, MOD) \ + macro(POW, "'**'") \ + range(BINOP_LAST, POW) \ \ /* Unary operation tokens. */ \ macro(TYPEOF, "keyword 'typeof'") \ @@ -187,7 +188,8 @@ macro(MULASSIGN, "'*='") \ macro(DIVASSIGN, "'/='") \ macro(MODASSIGN, "'%='") \ - range(ASSIGNMENT_LAST, MODASSIGN) + macro(POWASSIGN, "'**='") \ + range(ASSIGNMENT_LAST, POWASSIGN) #define TOKEN_KIND_RANGE_EMIT_NONE(name, value) #define FOR_EACH_TOKEN_KIND(macro) \ diff --git a/js/src/frontend/TokenStream.cpp b/js/src/frontend/TokenStream.cpp index a1b3dae78383..c87aeb38b0a0 100644 --- a/js/src/frontend/TokenStream.cpp +++ b/js/src/frontend/TokenStream.cpp @@ -1509,7 +1509,10 @@ TokenStream::getTokenInternal(TokenKind* ttp, Modifier modifier) goto out; case '*': - tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL; + if (matchChar('*')) + tp->type = matchChar('=') ? TOK_POWASSIGN : TOK_POW; + else + tp->type = matchChar('=') ? TOK_MULASSIGN : TOK_MUL; goto out; case '/': diff --git a/js/src/jit/BaselineCompiler.cpp b/js/src/jit/BaselineCompiler.cpp index 0e38eba46603..031bf4d60c39 100644 --- a/js/src/jit/BaselineCompiler.cpp +++ b/js/src/jit/BaselineCompiler.cpp @@ -1598,6 +1598,12 @@ BaselineCompiler::emit_JSOP_MOD() return emitBinaryArith(); } +bool +BaselineCompiler::emit_JSOP_POW() +{ + return emitBinaryArith(); +} + bool BaselineCompiler::emitBinaryArith() { diff --git a/js/src/jit/BaselineCompiler.h b/js/src/jit/BaselineCompiler.h index c9da772f032d..66752f39d9eb 100644 --- a/js/src/jit/BaselineCompiler.h +++ b/js/src/jit/BaselineCompiler.h @@ -80,6 +80,7 @@ namespace jit { _(JSOP_MUL) \ _(JSOP_DIV) \ _(JSOP_MOD) \ + _(JSOP_POW) \ _(JSOP_LT) \ _(JSOP_LE) \ _(JSOP_GT) \ diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index d0dd8d50a6d3..db41f17d3318 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -1905,6 +1905,10 @@ DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallbac if (!ModValues(cx, &lhsCopy, &rhsCopy, ret)) return false; break; + case JSOP_POW: + if (!math_pow_handle(cx, lhsCopy, rhsCopy, ret)) + return false; + break; case JSOP_BITOR: { int32_t result; if (!BitOr(cx, lhs, rhs, &result)) @@ -2038,7 +2042,7 @@ DoBinaryArithFallback(JSContext* cx, BaselineFrame* frame, ICBinaryArith_Fallbac } } - if (lhs.isInt32() && rhs.isInt32()) { + if (lhs.isInt32() && rhs.isInt32() && op != JSOP_POW) { bool allowDouble = ret.isDouble(); if (allowDouble) stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32); @@ -10024,7 +10028,7 @@ ICCallStubCompiler::pushSpreadCallArguments(MacroAssembler& masm, masm.unboxObject(Address(masm.getStackPointer(), (isConstructing * sizeof(Value)) + STUB_FRAME_SIZE), startReg); masm.loadPtr(Address(startReg, NativeObject::offsetOfElements()), startReg); - + // Align the stack such that the JitFrameLayout is aligned on the // JitStackAlignment. if (isJitCall) { @@ -10274,18 +10278,18 @@ ICCall_Fallback::Compiler::generateStubCode(MacroAssembler& masm) // Use BaselineFrameReg instead of BaselineStackReg, because // BaselineFrameReg and BaselineStackReg hold the same value just after // calling enterStubFrame. - + // newTarget if (isConstructing_) masm.pushValue(Address(BaselineFrameReg, STUB_FRAME_SIZE)); - + // array uint32_t valueOffset = isConstructing_; masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE)); - + // this masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE)); - + // callee masm.pushValue(Address(BaselineFrameReg, valueOffset++ * sizeof(Value) + STUB_FRAME_SIZE)); diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 9d5dcfdbb11d..dab3f7da0667 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -1657,6 +1657,9 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_MOD: return jsop_binary(op); + case JSOP_POW: + return jsop_pow(); + case JSOP_POS: return jsop_pos(); @@ -4597,6 +4600,25 @@ IonBuilder::jsop_binary(JSOp op, MDefinition* left, MDefinition* right) return maybeInsertResume(); } +bool +IonBuilder::jsop_pow() +{ + MDefinition* exponent = current->pop(); + MDefinition* base = current->pop(); + + if (inlineMathPowHelper(base, exponent, MIRType_Double) == InliningStatus_Inlined) { + base->setImplicitlyUsedUnchecked(); + exponent->setImplicitlyUsedUnchecked(); + return true; + } + + // For now, use MIRType_Double, as a safe cover-all. See bug 1188079. + MPow* pow = MPow::New(alloc(), base, exponent, MIRType_Double); + current->add(pow); + current->push(pow); + return true; +} + bool IonBuilder::jsop_binary(JSOp op) { diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index f8f8613f73e2..7f03949a455b 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -624,6 +624,7 @@ class IonBuilder bool jsop_bitop(JSOp op); bool jsop_binary(JSOp op); bool jsop_binary(JSOp op, MDefinition* left, MDefinition* right); + bool jsop_pow(); bool jsop_pos(); bool jsop_neg(); bool jsop_setarg(uint32_t arg); @@ -756,6 +757,7 @@ class IonBuilder InliningStatus inlineMathHypot(CallInfo& callInfo); InliningStatus inlineMathMinMax(CallInfo& callInfo, bool max); InliningStatus inlineMathPow(CallInfo& callInfo); + InliningStatus inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType); InliningStatus inlineMathRandom(CallInfo& callInfo); InliningStatus inlineMathImul(CallInfo& callInfo); InliningStatus inlineMathFRound(CallInfo& callInfo); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index c5a6eeec8a4b..ecc11ef2be10 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -1347,17 +1347,11 @@ IonBuilder::inlineMathHypot(CallInfo& callInfo) } IonBuilder::InliningStatus -IonBuilder::inlineMathPow(CallInfo& callInfo) +IonBuilder::inlineMathPowHelper(MDefinition* lhs, MDefinition* rhs, MIRType outputType) { - if (callInfo.argc() != 2 || callInfo.constructing()) { - trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); - return InliningStatus_NotInlined; - } - // Typechecking. - MIRType baseType = callInfo.getArg(0)->type(); - MIRType powerType = callInfo.getArg(1)->type(); - MIRType outputType = getInlineReturnType(); + MIRType baseType = lhs->type(); + MIRType powerType = rhs->type(); if (outputType != MIRType_Int32 && outputType != MIRType_Double) return InliningStatus_NotInlined; @@ -1366,17 +1360,13 @@ IonBuilder::inlineMathPow(CallInfo& callInfo) if (!IsNumberType(powerType)) return InliningStatus_NotInlined; - callInfo.setImplicitlyUsedUnchecked(); - - MDefinition* base = callInfo.getArg(0); - MDefinition* power = callInfo.getArg(1); + MDefinition* base = lhs; + MDefinition* power = rhs; MDefinition* output = nullptr; // Optimize some constant powers. - if (callInfo.getArg(1)->isConstantValue() && - callInfo.getArg(1)->constantValue().isNumber()) - { - double pow = callInfo.getArg(1)->constantValue().toNumber(); + if (rhs->isConstantValue() && rhs->constantValue().isNumber()) { + double pow = rhs->constantValue().toNumber(); // Math.pow(x, 0.5) is a sqrt with edge-case detection. if (pow == 0.5) { @@ -1451,6 +1441,23 @@ IonBuilder::inlineMathPow(CallInfo& callInfo) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineMathPow(CallInfo& callInfo) +{ + if (callInfo.argc() != 2 || callInfo.constructing()) { + trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm); + return InliningStatus_NotInlined; + } + + IonBuilder::InliningStatus status = + inlineMathPowHelper(callInfo.getArg(0), callInfo.getArg(1), getInlineReturnType()); + + if (status == IonBuilder::InliningStatus_Inlined) + callInfo.setImplicitlyUsedUnchecked(); + + return status; +} + IonBuilder::InliningStatus IonBuilder::inlineMathRandom(CallInfo& callInfo) { diff --git a/js/src/tests/ecma_7/Math/Pow.js b/js/src/tests/ecma_7/Math/Pow.js new file mode 100644 index 000000000000..31c64770100f --- /dev/null +++ b/js/src/tests/ecma_7/Math/Pow.js @@ -0,0 +1,104 @@ +// |reftest| skip-if(!xulRuntime.shell) +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var BUGNUMBER = 1135708; +var summary = "Implement the exponentiation operator"; + +print(BUGNUMBER + ": " + summary); + +// Constant folding +assertEq(2 ** 2 ** 3, 256); +assertEq(1 ** 1 ** 4, 1); + +// No folding +var two = 2; +var three = 3; +var four = 4; +assertEq(two ** two ** three, 256); +assertEq(1 ** 1 ** four, 1); + +// Operator precedence +assertEq(2 ** 3 / 2 ** 3, 1); +assertEq(2 ** 3 * 2 ** 3, 64); +assertEq(2 ** 3 + 2 ** 3, 16); + +// With parentheses +assertEq((2 ** 3) ** 2, 64); +assertEq(2 ** (3 ** 2), 512); + +// Assignment operator +var x = 2; +assertEq(x **= 2 ** 3, 256); +assertEq(x, 256); + +// Loop to test baseline and ION +for (var i=0; i<10000; i++) { + assertEq((2 ** 3) ** 2, 64); + assertEq(2 ** (3 ** 2), 512); + var x = 2; + assertEq(x **= 2 ** 3, 256); + assertEq(x, 256); +} + +// Comments should not be confused with exp operator +var a, c, e; +a = c = e = 2; +assertEq(a**/**b**/c/**/**/**d**/e, 16); + +// Two stars separated should not parse as exp operator +assertThrows(function() { return Reflect.parse("2 * * 3"); }, SyntaxError); + +// Check if error propagation works +var thrower = { + get value() { + throw new Error(); + } +}; + +assertThrowsInstanceOf(function() { return thrower.value ** 2; }, Error); +assertThrowsInstanceOf(function() { return 2 ** thrower.value; }, Error); +assertThrowsInstanceOf(function() { return 2 ** thrower.value ** 2; }, Error); + +var convertibleToPrimitive = { + valueOf: function() { + throw new Error("oops"); + } +}; + +assertThrowsInstanceOf(function() { return convertibleToPrimitive ** 3; }, Error); +assertThrowsInstanceOf(function() { return 3 ** convertibleToPrimitive; }, Error); + +assertEq(NaN ** 2, NaN); +assertEq(2 ** NaN, NaN); +assertEq(2 ** "3", 8); +assertEq("2" ** 3, 8); + +// Reflect.parse generates a correct parse tree for simplest case +var parseTree = Reflect.parse("a ** b"); +assertEq(parseTree.body[0].type, "ExpressionStatement"); +assertEq(parseTree.body[0].expression.operator, "**"); +assertEq(parseTree.body[0].expression.left.name, "a"); +assertEq(parseTree.body[0].expression.right.name, "b"); + +// Reflect.parse generates a tree following the right-associativity rule +var parseTree = Reflect.parse("a ** b ** c"); +assertEq(parseTree.body[0].type, "ExpressionStatement"); +assertEq(parseTree.body[0].expression.left.name, "a"); +assertEq(parseTree.body[0].expression.right.operator, "**"); +assertEq(parseTree.body[0].expression.right.left.name, "b"); +assertEq(parseTree.body[0].expression.right.right.name, "c"); + + +function assertTrue(v) { + assertEq(v, true); +} + +function assertFalse(v) { + assertEq(v, false); +} + +if (typeof reportCompare === "function") + reportCompare(true, true); diff --git a/js/src/tests/ecma_7/Math/shell.js b/js/src/tests/ecma_7/Math/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 682e12417194..ec6de065bf05 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -2052,7 +2052,6 @@ CASE(EnableInterruptsPseudoOpcode) CASE(JSOP_NOP) CASE(JSOP_UNUSED2) CASE(JSOP_BACKPATCH) -CASE(JSOP_UNUSED150) CASE(JSOP_UNUSED161) CASE(JSOP_UNUSED162) CASE(JSOP_UNUSED163) @@ -2655,6 +2654,17 @@ CASE(JSOP_MOD) } END_CASE(JSOP_MOD) +CASE(JSOP_POW) +{ + ReservedRooted lval(&rootValue0, REGS.sp[-2]); + ReservedRooted rval(&rootValue1, REGS.sp[-1]); + MutableHandleValue res = REGS.stackHandleAt(-2); + if (!math_pow_handle(cx, lval, rval, res)) + goto error; + REGS.sp--; +} +END_CASE(JSOP_POW) + CASE(JSOP_NOT) { bool cond = ToBoolean(REGS.stackHandleAt(-1)); diff --git a/js/src/vm/Opcodes.h b/js/src/vm/Opcodes.h index bcbbacd586e3..67372674d57a 100644 --- a/js/src/vm/Opcodes.h +++ b/js/src/vm/Opcodes.h @@ -1533,7 +1533,15 @@ * Stack: => */ \ macro(JSOP_BACKPATCH, 149,"backpatch", NULL, 5, 0, 0, JOF_JUMP) \ - macro(JSOP_UNUSED150, 150,"unused150", NULL, 1, 0, 0, JOF_BYTE) \ + /* + * Pops the top two values 'lval' and 'rval' from the stack, then pushes + * the result of 'Math.pow(lval, rval)'. + * Category: Operators + * Type: Arithmetic Operators + * Operands: + * Stack: lval, rval => (lval ** rval) + */ \ + macro(JSOP_POW, 150, "pow", "**", 1, 2, 1, JOF_BYTE|JOF_ARITH) \ \ /* * Pops the top of stack value as 'v', sets pending exception as 'v', diff --git a/js/src/vm/Xdr.h b/js/src/vm/Xdr.h index 9f243f746e9c..07d0c8631e3b 100644 --- a/js/src/vm/Xdr.h +++ b/js/src/vm/Xdr.h @@ -29,7 +29,7 @@ namespace js { * * https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode */ -static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 298; +static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 299; static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND); diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 1b3d74834c23..b0aa59d89794 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -986,7 +986,7 @@ PresShell::Init(nsIDocument* aDocument, mZoomConstraintsClient->Init(this, mDocument); #ifndef MOZ_WIDGET_ANDROID // Fennec will need some work to use this code; see bug 1180267. - if (gfxPrefs::MetaViewportEnabled()) { + if (gfxPrefs::MetaViewportEnabled() || gfxPrefs::APZAllowZooming()) { mMobileViewportManager = new MobileViewportManager(this, mDocument); } #endif diff --git a/layout/generic/nsBlockReflowContext.cpp b/layout/generic/nsBlockReflowContext.cpp index c98982e25d27..16ddc9f1c5e6 100644 --- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -164,7 +164,8 @@ nsBlockReflowContext::ComputeCollapsedBStartMargin(const nsHTMLReflowState& aRS, availSpace); // Record that we're being optimistic by assuming the kid // has no clearance - if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE) { + if (kid->StyleDisplay()->mBreakType != NS_STYLE_CLEAR_NONE || + !nsBlockFrame::BlockCanIntersectFloats(kid)) { *aMayNeedRetry = true; } if (ComputeCollapsedBStartMargin(innerReflowState, aMargin, diff --git a/layout/reftests/floats/478834-1-ref.html b/layout/reftests/floats/478834-1-ref.html new file mode 100644 index 000000000000..22cc4548e5bf --- /dev/null +++ b/layout/reftests/floats/478834-1-ref.html @@ -0,0 +1,26 @@ + + + + Bug 478834 Testcase + + + +
+
+
+ + +
+ x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x +
+
+
+ + diff --git a/layout/reftests/floats/478834-1.html b/layout/reftests/floats/478834-1.html new file mode 100644 index 000000000000..c38663ec23aa --- /dev/null +++ b/layout/reftests/floats/478834-1.html @@ -0,0 +1,26 @@ + + + + Bug 478834 Testcase + + + +
+
+
+ + +
+ x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x + x x x x x x x x x x x x x x x x x x x x x x +
+
+
+ + diff --git a/layout/reftests/floats/reftest.list b/layout/reftests/floats/reftest.list index 14bea856466e..06aa7f9b1ba0 100644 --- a/layout/reftests/floats/reftest.list +++ b/layout/reftests/floats/reftest.list @@ -16,6 +16,7 @@ fails == 345369-2.html 345369-2-ref.html == 345369-4.html 345369-4-ref.html == 345369-5.html 345369-5-ref.html == 429974-1.html 429974-1-ref.html +== 478834-1.html 478834-1-ref.html == 546048-1.html 546048-1-ref.html == 775350-1.html 775350-1-ref.html == 1114329.html 1114329-ref.html diff --git a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html index ab9d96fdbbed..0d7099e0e177 100644 --- a/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html +++ b/layout/reftests/w3c-css/submitted/css21/replaced-sizing/replaced-elements-all-auto-ref.html @@ -3,9 +3,6 @@ CSS Reference: CSS 2.1 replaced element sizing - - -