diff --git a/aclocal.m4 b/aclocal.m4 index 64598d518b..40a4fa904b 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -33,6 +33,7 @@ builtin(include, build/autoconf/ffi.m4)dnl builtin(include, build/autoconf/clang-plugin.m4)dnl builtin(include, build/autoconf/alloc.m4)dnl builtin(include, build/autoconf/jemalloc.m4)dnl +builtin(include, build/autoconf/rust.m4)dnl MOZ_PROG_CHECKMSYS() diff --git a/build/autoconf/config.status.m4 b/build/autoconf/config.status.m4 index 93348b4be9..a3e9d4fedd 100644 --- a/build/autoconf/config.status.m4 +++ b/build/autoconf/config.status.m4 @@ -222,3 +222,15 @@ MOZ_RUN_CONFIG_STATUS()], define([AC_CONFIG_HEADER], [m4_fatal([Use CONFIGURE_DEFINE_FILES in moz.build files to produce header files.]) ]) + +define([MOZ_BUILD_BACKEND], +[ +BUILD_BACKENDS="RecursiveMake" + +MOZ_ARG_ENABLE_STRING(build-backend, +[ --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake} + Enable additional build backends], +[ BUILD_BACKENDS="RecursiveMake `echo $enableval | sed 's/,/ /g'`"]) + +AC_SUBST_LIST([BUILD_BACKENDS]) +]) diff --git a/build/autoconf/rust.m4 b/build/autoconf/rust.m4 new file mode 100644 index 0000000000..90b44c8110 --- /dev/null +++ b/build/autoconf/rust.m4 @@ -0,0 +1,35 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +AC_DEFUN([MOZ_RUST_SUPPORT], [ + MOZ_PATH_PROG(RUSTC, rustc) + if test -n "$RUSTC"; then + AC_MSG_CHECKING([rustc version]) + RUSTC_VERSION=`$RUSTC --version | cut -d ' ' -f 2` + # Parse out semversion elements. + _RUSTC_MAJOR_VERSION=`echo ${RUSTC_VERSION} | cut -d . -f 1` + _RUSTC_MINOR_VERSION=`echo ${RUSTC_VERSION} | cut -d . -f 2` + _RUSTC_EXTRA_VERSION=`echo ${RUSTC_VERSION} | cut -d . -f 3 | cut -d + -f 1` + _RUSTC_PATCH_VERSION=`echo ${_RUSTC_EXTRA_VERSION} | cut -d '-' -f 1` + AC_MSG_RESULT([$RUSTC_VERSION (v${_RUSTC_MAJOR_VERSION}.${_RUSTC_MINOR_VERSION}.${_RUSTC_PATCH_VERSION})]) + fi + MOZ_ARG_ENABLE_BOOL([rust], + [ --enable-rust Include Rust language sources], + [MOZ_RUST=1], + [MOZ_RUST= ]) + if test -z "$RUSTC" -a -n "$MOZ_RUST"; then + AC_MSG_ERROR([Rust compiler not found. + To compile rust language sources, you must have 'rustc' in your path. + See http://www.rust-lang.org/ for more information.]) + fi + if test -n "$MOZ_RUST" && test -z "$_RUSTC_MAJOR_VERSION" -o \ + "$_RUSTC_MAJOR_VERSION" -lt 1; then + AC_MSG_ERROR([Rust compiler ${RUSTC_VERSION} is too old. + To compile Rust language sources please install at least + version 1.0 of the 'rustc' toolchain and make sure it is + first in your path. + You can verify this by typing 'rustc --version'.]) + fi + AC_SUBST(MOZ_RUST) +]) diff --git a/configure.in b/configure.in index ee59dc1465..8e098b0557 100644 --- a/configure.in +++ b/configure.in @@ -137,6 +137,8 @@ MOZ_BUILD_ROOT=`pwd -W 2>/dev/null || pwd` MOZ_PYTHON +MOZ_BUILD_BACKEND + MOZ_DEFAULT_COMPILER COMPILE_ENVIRONMENT=1 @@ -424,6 +426,8 @@ MOZ_TOOL_VARIABLES MOZ_CHECK_COMPILER_WRAPPER +MOZ_RUST_SUPPORT + dnl ======================================================== dnl Check for MacOS deployment target version dnl ======================================================== @@ -448,6 +452,9 @@ esac AC_SUBST(MACOSX_DEPLOYMENT_TARGET) +AC_PROG_CPP +AC_PROG_CXXCPP + dnl ======================================================== dnl Special win32 checks dnl ======================================================== @@ -647,8 +654,6 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) fi CFLAGS="$CFLAGS -D_HAS_EXCEPTIONS=0" CXXFLAGS="$CXXFLAGS -D_HAS_EXCEPTIONS=0" - - MOZ_FIND_WINSDK_VERSION else # Check w32api version _W32API_MAJOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $1 }'` @@ -700,10 +705,9 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) # strsafe.h on mingw uses macros for function deprecation that pollutes namespace # causing problems with local implementations with the same name. AC_DEFINE(STRSAFE_NO_DEPRECATE) - - MOZ_WINSDK_MAXVER=0x06030000 fi # !GNU_CC + MOZ_FIND_WINSDK_VERSION AC_DEFINE_UNQUOTED(WINVER,0x$WINVER) AC_DEFINE_UNQUOTED(_WIN32_WINNT,0x$WINVER) # Require OS features provided by IE 6.0 SP2 (XP SP2) @@ -727,9 +731,6 @@ See https://developer.mozilla.org/en/Windows_Build_Prerequisites.]) ;; esac -AC_PROG_CPP -AC_PROG_CXXCPP - if test -n "$_WIN32_MSVC"; then SKIP_PATH_CHECKS=1 SKIP_COMPILER_CHECKS=1 @@ -1648,46 +1649,6 @@ if test "$GCC_MAJOR_VERSION" -ge "6" ; then CXXFLAGS="$CXXFLAGS -fno-delete-null-pointer-checks -fno-lifetime-dse -fno-schedule-insns2" fi -dnl gcc can come with its own linker so it is better to use the pass-thru calls -dnl MKSHLIB_FORCE_ALL is used to force the linker to include all object -dnl files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker to -dnl normal behavior. -dnl ======================================================== -MKSHLIB_FORCE_ALL= -MKSHLIB_UNFORCE_ALL= - -if test "$COMPILE_ENVIRONMENT"; then -if test "$GNU_CC"; then - AC_MSG_CHECKING(whether ld has archive extraction flags) - AC_CACHE_VAL(ac_cv_mkshlib_force_and_unforce, - [_SAVE_LDFLAGS=$LDFLAGS; _SAVE_LIBS=$LIBS - ac_cv_mkshlib_force_and_unforce="no" - exec 3<&0 <= $GST_VERSION + gstreamer-app-$GST_API_VERSION + gstreamer-plugins-base-$GST_API_VERSION, + [_HAVE_GSTREAMER=1], + [_HAVE_GSTREAMER=]) + if test -z "$_HAVE_GSTREAMER"; then + AC_MSG_ERROR([gstreamer and gstreamer-plugins-base development packages are needed to build gstreamer backend. Install them or disable gstreamer support with --disable-gstreamer]) + fi + + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" + AC_TRY_LINK(,[return 0;],_HAVE_LIBGSTVIDEO=1,_HAVE_LIBGSTVIDEO=) + if test -n "$_HAVE_LIBGSTVIDEO" ; then + GSTREAMER_LIBS="$GSTREAMER_LIBS -lgstvideo-$GST_API_VERSION" + else + AC_MSG_ERROR([gstreamer-plugins-base found, but no libgstvideo. Something has gone terribly wrong. Try reinstalling gstreamer-plugins-base; failing that, disable the gstreamer backend with --disable-gstreamer.]) + fi + LDFLAGS=$_SAVE_LDFLAGS +fi + +AC_SUBST(MOZ_GSTREAMER) +AC_SUBST(GST_API_VERSION) + +if test -n "$MOZ_GSTREAMER"; then + AC_DEFINE(MOZ_GSTREAMER) + AC_DEFINE_UNQUOTED(GST_API_VERSION, "$GST_API_VERSION") +fi + + dnl ======================================================== dnl Permissions System dnl ======================================================== @@ -8498,8 +8493,6 @@ AC_SUBST(MOZ_CHROME_FILE_FORMAT) AC_SUBST(WRAP_LDFLAGS) AC_SUBST(MKSHLIB) AC_SUBST(MKCSHLIB) -AC_SUBST(MKSHLIB_FORCE_ALL) -AC_SUBST(MKSHLIB_UNFORCE_ALL) AC_SUBST(DSO_CFLAGS) AC_SUBST(DSO_PIC_CFLAGS) AC_SUBST(DSO_LDOPTS) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 547848c454..9f4ccc8f73 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -1350,6 +1350,8 @@ public: * are not converted into newlines. Only textnodes and cdata nodes are * added to the result. * + * @see nsLayoutUtils::GetFrameTextContent + * * @param aNode Node to get textual contents of. * @param aDeep If true child elements of aNode are recursivly descended * into to find text children. diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index 57ecf006a2..f9010aaf04 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -4705,8 +4705,7 @@ CanvasRenderingContext2D::DrawDirectlyToCanvas( auto result = image.mImgContainer-> Draw(context, scaledImageSize, ImageRegion::Create(gfxRect(src.x, src.y, src.width, src.height)), - image.mWhichFrame, GraphicsFilter::FILTER_GOOD, - Some(svgContext), modifiedFlags); + image.mWhichFrame, Filter::GOOD, Some(svgContext), modifiedFlags); if (result != DrawResult::SUCCESS) { NS_WARNING("imgIContainer::Draw failed"); diff --git a/dom/canvas/nsICanvasRenderingContextInternal.h b/dom/canvas/nsICanvasRenderingContextInternal.h index 8849756816..e209a2d70d 100644 --- a/dom/canvas/nsICanvasRenderingContextInternal.h +++ b/dom/canvas/nsICanvasRenderingContextInternal.h @@ -12,7 +12,6 @@ #include "nsIDocShell.h" #include "nsRefreshDriver.h" #include "mozilla/dom/HTMLCanvasElement.h" -#include "GraphicsFilter.h" #include "mozilla/RefPtr.h" #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \ diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl index 8feb82f08d..68ad43ae75 100644 --- a/dom/ipc/PBrowser.ipdl +++ b/dom/ipc/PBrowser.ipdl @@ -60,6 +60,7 @@ using mozilla::CommandInt from "mozilla/EventForwards.h"; using mozilla::Modifiers from "mozilla/EventForwards.h"; using mozilla::layers::GeckoContentController::APZStateChange from "mozilla/layers/GeckoContentController.h"; using mozilla::WritingMode from "mozilla/WritingModes.h"; +using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h"; using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h"; using nsIWidget::TouchPointerState from "nsIWidget.h"; using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h"; @@ -537,6 +538,9 @@ parent: prio(high) sync DispatchMouseEvent(WidgetMouseEvent event); prio(high) sync DispatchKeyboardEvent(WidgetKeyboardEvent event); + // Start an APZ drag on a scrollbar + async StartScrollbarDrag(AsyncDragMetrics aDragMetrics); + InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action, nsCString visualData, uint32_t width, uint32_t height, uint32_t stride, uint8_t format, int32_t dragAreaX, int32_t dragAreaY); @@ -619,7 +623,7 @@ child: * When two consecutive mouse move events would be added to the message queue, * they are 'compressed' by dumping the oldest one. */ - RealMouseMoveEvent(WidgetMouseEvent event) compress; + RealMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId) compress; /** * Mouse move events with |reason == eSynthesized| are sent via a separate * message because they do not generate DOM 'mousemove' events, and the @@ -627,8 +631,8 @@ child: * |reason == eReal| event being dropped in favour of an |eSynthesized| * event, and thus a DOM 'mousemove' event to be lost. */ - SynthMouseMoveEvent(WidgetMouseEvent event); - RealMouseButtonEvent(WidgetMouseEvent event); + SynthMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); + RealMouseButtonEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding); MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId); RealTouchEvent(WidgetTouchEvent aEvent, diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 589eb7b997..7797d96a63 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -33,6 +33,7 @@ #include "mozilla/layers/CompositorChild.h" #include "mozilla/layers/DoubleTapToZoom.h" #include "mozilla/layers/ImageBridgeChild.h" +#include "mozilla/layers/InputAPZContext.h" #include "mozilla/layers/ShadowLayers.h" #include "mozilla/layout/RenderFrameChild.h" #include "mozilla/LookAndFeel.h" @@ -1899,20 +1900,29 @@ TabChild::RecvMouseEvent(const nsString& aType, } bool -TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& event) +TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { - return RecvRealMouseButtonEvent(event); + return RecvRealMouseButtonEvent(event, aGuid, aInputBlockId); } bool -TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& event) +TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { - return RecvRealMouseButtonEvent(event); + return RecvRealMouseButtonEvent(event, aGuid, aInputBlockId); } bool -TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& event) +TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) { + nsEventStatus unused; + InputAPZContext context(aGuid, aInputBlockId, unused); + WidgetMouseEvent localEvent(event); localEvent.widget = mPuppetWidget; APZCCallbackHelper::DispatchWidgetEvent(localEvent); diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 89b91923fd..968dbb890b 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -331,9 +331,15 @@ public: const int32_t& aClickCount, const int32_t& aModifiers, const bool& aIgnoreRootScrollFrame) override; - virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& event) override; - virtual bool RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& event) override; - virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& event) override; + virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + virtual bool RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; + virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& event, + const ScrollableLayerGuid& aGuid, + const uint64_t& aInputBlockId) override; virtual bool RecvRealDragEvent(const WidgetDragEvent& aEvent, const uint32_t& aDragAction, const uint32_t& aDropEffect) override; diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index ce2a89cc63..1008f5da7d 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -28,6 +28,7 @@ #include "mozilla/IMEStateManager.h" #include "mozilla/ipc/DocumentRendererParent.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layers/AsyncDragMetrics.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/InputAPZContext.h" #include "mozilla/layout/RenderFrameParent.h" @@ -1437,14 +1438,19 @@ bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event) } } + ScrollableLayerGuid guid; + uint64_t blockId; + ApzAwareEventRoutingToChild(&guid, &blockId, nullptr); + if (eMouseMove == event.mMessage) { if (event.reason == WidgetMouseEvent::eSynthesized) { - return SendSynthMouseMoveEvent(event); + return SendSynthMouseMoveEvent(event, guid, blockId); } else { - return SendRealMouseMoveEvent(event); + return SendRealMouseMoveEvent(event, guid, blockId); } } - return SendRealMouseButtonEvent(event); + + return SendRealMouseButtonEvent(event, guid, blockId); } LayoutDeviceToCSSScale @@ -2913,6 +2919,15 @@ TabParent::RecvSetTargetAPZC(const uint64_t& aInputBlockId, return true; } +bool +TabParent::RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) +{ + if (RenderFrameParent* rfp = GetRenderFrame()) { + rfp->StartScrollbarDrag(aDragMetrics); + } + return true; +} + bool TabParent::RecvSetAllowedTouchBehavior(const uint64_t& aInputBlockId, nsTArray&& aFlags) diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index 16b4ed1b5b..640ea5877c 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -43,6 +43,7 @@ class CpowHolder; } // namespace jsipc namespace layers { +class AsyncDragMetrics; struct FrameMetrics; struct TextureFactoryIdentifier; } // namespace layers @@ -81,6 +82,7 @@ class TabParent final : public PBrowserParent , public nsAPostRefreshObserver { typedef mozilla::dom::ClonedMessageData ClonedMessageData; + typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; virtual ~TabParent(); @@ -239,6 +241,7 @@ public: virtual bool RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override; virtual bool RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override; virtual bool RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override; + virtual bool RecvStartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override; virtual PColorPickerParent* AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) override; diff --git a/dom/media/MP3FrameParser.h b/dom/media/MP3FrameParser.h index 154bff21e8..f0489db784 100644 --- a/dom/media/MP3FrameParser.h +++ b/dom/media/MP3FrameParser.h @@ -11,6 +11,7 @@ #include "mozilla/Mutex.h" #include "nsString.h" +#include "Intervals.h" namespace mozilla { @@ -213,6 +214,24 @@ private: }; +class NotifyDataArrivedFilter { +public: + media::IntervalSet NotifyDataArrived(uint32_t aLength, int64_t aOffset) { + media::Interval interval(aOffset, aOffset + aLength); + media::IntervalSet newIntervals(interval); + newIntervals -= mIntervals; + mIntervals += interval; + return newIntervals; + } + + const media::IntervalSet& GetIntervals() { + return mIntervals; + } + +private: + media::IntervalSet mIntervals; +}; + } // namespace mozilla #endif diff --git a/dom/media/MediaDecoder.cpp b/dom/media/MediaDecoder.cpp index d7f487c6ae..a0233c6deb 100644 --- a/dom/media/MediaDecoder.cpp +++ b/dom/media/MediaDecoder.cpp @@ -1478,6 +1478,14 @@ MediaDecoder::IsRtspEnabled() } #endif +#ifdef MOZ_GSTREAMER +bool +MediaDecoder::IsGStreamerEnabled() +{ + return Preferences::GetBool("media.gstreamer.enabled"); +} +#endif + #ifdef MOZ_OMX_DECODER bool MediaDecoder::IsOmxEnabled() diff --git a/dom/media/MediaDecoder.h b/dom/media/MediaDecoder.h index 6a3d3f9674..3cf0f5e6e2 100644 --- a/dom/media/MediaDecoder.h +++ b/dom/media/MediaDecoder.h @@ -710,6 +710,10 @@ private: static bool IsRtspEnabled(); #endif +#ifdef MOZ_GSTREAMER + static bool IsGStreamerEnabled(); +#endif + #ifdef MOZ_OMX_DECODER static bool IsOmxEnabled(); static bool IsOmxAsyncEnabled(); diff --git a/dom/media/apple/AppleMP3Reader.cpp b/dom/media/apple/AppleMP3Reader.cpp index 5eb68fa11f..488fe29d8b 100644 --- a/dom/media/apple/AppleMP3Reader.cpp +++ b/dom/media/apple/AppleMP3Reader.cpp @@ -520,20 +520,23 @@ AppleMP3Reader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) return; } - nsRefPtr bytes = - mDecoder->GetResource()->MediaReadAt(aOffset, aLength); - NS_ENSURE_TRUE_VOID(bytes); - mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset); - if (!mMP3FrameParser.IsMP3()) { - return; - } + IntervalSet intervals = mFilter.NotifyDataArrived(aLength, aOffset); + for (const auto& interval : intervals) { + nsRefPtr bytes = + mResource.MediaReadAt(interval.mStart, interval.Length()); + NS_ENSURE_TRUE_VOID(bytes); + mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart); + if (!mMP3FrameParser.IsMP3()) { + return; + } - uint64_t duration = mMP3FrameParser.GetDuration(); - if (duration != mDuration) { - LOGD("Updating media duration to %lluus\n", duration); - MOZ_ASSERT(mDecoder); - mDuration = duration; - mDecoder->DispatchUpdateEstimatedMediaDuration(duration); + uint64_t duration = mMP3FrameParser.GetDuration(); + if (duration != mDuration) { + LOGD("Updating media duration to %lluus\n", duration); + MOZ_ASSERT(mDecoder); + mDuration = duration; + mDecoder->DispatchUpdateEstimatedMediaDuration(duration); + } } } diff --git a/dom/media/apple/AppleMP3Reader.h b/dom/media/apple/AppleMP3Reader.h index 54dc0c585f..c5e704ac9c 100644 --- a/dom/media/apple/AppleMP3Reader.h +++ b/dom/media/apple/AppleMP3Reader.h @@ -82,6 +82,7 @@ private: MP3FrameParser mMP3FrameParser; MediaResourceIndex mResource; + NotifyDataArrivedFilter mFilter; }; } // namespace mozilla diff --git a/dom/media/directshow/DirectShowReader.cpp b/dom/media/directshow/DirectShowReader.cpp index 28c8afdf4f..1270e92c66 100644 --- a/dom/media/directshow/DirectShowReader.cpp +++ b/dom/media/directshow/DirectShowReader.cpp @@ -408,18 +408,22 @@ DirectShowReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) return; } - nsRefPtr bytes = mDecoder->GetResource()->MediaReadAt(aOffset, aLength); - NS_ENSURE_TRUE_VOID(bytes); - mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset); - if (!mMP3FrameParser.IsMP3()) { - return; - } + IntervalSet intervals = mFilter.NotifyDataArrived(aLength, aOffset); + for (const auto& interval : intervals) { + nsRefPtr bytes = + mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length()); + NS_ENSURE_TRUE_VOID(bytes); + mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart); + if (!mMP3FrameParser.IsMP3()) { + return; + } - int64_t duration = mMP3FrameParser.GetDuration(); - if (duration != mDuration) { - MOZ_ASSERT(mDecoder); - mDuration = duration; - mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration); + int64_t duration = mMP3FrameParser.GetDuration(); + if (duration != mDuration) { + MOZ_ASSERT(mDecoder); + mDuration = duration; + mDecoder->DispatchUpdateEstimatedMediaDuration(mDuration); + } } } diff --git a/dom/media/directshow/DirectShowReader.h b/dom/media/directshow/DirectShowReader.h index cc51c844ad..b95b029650 100644 --- a/dom/media/directshow/DirectShowReader.h +++ b/dom/media/directshow/DirectShowReader.h @@ -110,6 +110,8 @@ private: // Duration of the stream, in microseconds. int64_t mDuration; + + NotifyDataArrivedFilter mFilter; }; } // namespace mozilla diff --git a/dom/media/gmp/GMPServiceChild.cpp b/dom/media/gmp/GMPServiceChild.cpp index f23a89c107..d790a3027e 100644 --- a/dom/media/gmp/GMPServiceChild.cpp +++ b/dom/media/gmp/GMPServiceChild.cpp @@ -283,20 +283,16 @@ GMPServiceChild::GetBridgedGMPContentParent(ProcessId aOtherPid, mContentParents.Get(aOtherPid, aGMPContentParent); } -static PLDHashOperator -FindAndRemoveGMPContentParent(const uint64_t& aKey, - nsRefPtr& aData, - void* aUserArg) -{ - return aData == aUserArg ? - (PLDHashOperator)(PL_DHASH_STOP | PL_DHASH_REMOVE) : - PL_DHASH_NEXT; -} - void GMPServiceChild::RemoveGMPContentParent(GMPContentParent* aGMPContentParent) { - mContentParents.Enumerate(FindAndRemoveGMPContentParent, aGMPContentParent); + for (auto iter = mContentParents.Iter(); !iter.Done(); iter.Next()) { + nsRefPtr& parent = iter.Data(); + if (parent == aGMPContentParent) { + iter.Remove(); + break; + } + } } static PLDHashOperator diff --git a/dom/media/gstreamer/GStreamerAllocator.cpp b/dom/media/gstreamer/GStreamerAllocator.cpp new file mode 100644 index 0000000000..6a2a6ed1a2 --- /dev/null +++ b/dom/media/gstreamer/GStreamerAllocator.cpp @@ -0,0 +1,201 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "GStreamerAllocator.h" + +#include +#include + +#include "GStreamerLoader.h" + +using namespace mozilla::layers; + +namespace mozilla { + +typedef struct +{ + GstAllocator parent; + GStreamerReader *reader; +} MozGfxMemoryAllocator; + +typedef struct +{ + GstAllocatorClass parent; +} MozGfxMemoryAllocatorClass; + +typedef struct +{ + GstMemory memory; + PlanarYCbCrImage* image; + guint8* data; +} MozGfxMemory; + +typedef struct +{ + GstMeta meta; +} MozGfxMeta; + +typedef struct +{ + GstVideoBufferPoolClass parent_class; +} MozGfxBufferPoolClass; + +typedef struct +{ + GstVideoBufferPool pool; +} MozGfxBufferPool; + +// working around GTK+ bug https://bugzilla.gnome.org/show_bug.cgi?id=723899 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +G_DEFINE_TYPE(MozGfxMemoryAllocator, moz_gfx_memory_allocator, GST_TYPE_ALLOCATOR); +G_DEFINE_TYPE(MozGfxBufferPool, moz_gfx_buffer_pool, GST_TYPE_VIDEO_BUFFER_POOL); +#pragma GCC diagnostic pop + +void +moz_gfx_memory_reset(MozGfxMemory *mem) +{ + if (mem->image) + mem->image->Release(); + + ImageContainer* container = ((MozGfxMemoryAllocator*) mem->memory.allocator)->reader->GetImageContainer(); + mem->image = reinterpret_cast(container->CreateImage(ImageFormat::PLANAR_YCBCR).take()); + mem->data = mem->image->AllocateAndGetNewBuffer(mem->memory.size); +} + +static GstMemory* +moz_gfx_memory_allocator_alloc(GstAllocator* aAllocator, gsize aSize, + GstAllocationParams* aParams) +{ + MozGfxMemory* mem = g_slice_new (MozGfxMemory); + gsize maxsize = aSize + aParams->prefix + aParams->padding; + gst_memory_init(GST_MEMORY_CAST (mem), + (GstMemoryFlags)aParams->flags, + aAllocator, NULL, maxsize, aParams->align, + aParams->prefix, aSize); + mem->image = NULL; + moz_gfx_memory_reset(mem); + + return (GstMemory *) mem; +} + +static void +moz_gfx_memory_allocator_free (GstAllocator * allocator, GstMemory * gmem) +{ + MozGfxMemory *mem = (MozGfxMemory *) gmem; + + if (mem->memory.parent) + goto sub_mem; + + if (mem->image) + mem->image->Release(); + +sub_mem: + g_slice_free (MozGfxMemory, mem); +} + +static gpointer +moz_gfx_memory_map (MozGfxMemory * mem, gsize maxsize, GstMapFlags flags) +{ + // check that the allocation didn't fail + if (mem->data == nullptr) + return nullptr; + + return mem->data + mem->memory.offset; +} + +static gboolean +moz_gfx_memory_unmap (MozGfxMemory * mem) +{ + return TRUE; +} + +static MozGfxMemory * +moz_gfx_memory_share (MozGfxMemory * mem, gssize offset, gsize size) +{ + MozGfxMemory *sub; + GstMemory *parent; + + /* find the real parent */ + if ((parent = mem->memory.parent) == NULL) + parent = (GstMemory *) mem; + + if (size == (gsize) -1) + size = mem->memory.size - offset; + + /* the shared memory is always readonly */ + sub = g_slice_new (MozGfxMemory); + + gst_memory_init (GST_MEMORY_CAST (sub), + (GstMemoryFlags) (GST_MINI_OBJECT_FLAGS (parent) | GST_MINI_OBJECT_FLAG_LOCK_READONLY), + mem->memory.allocator, &mem->memory, mem->memory.maxsize, mem->memory.align, + mem->memory.offset + offset, size); + + sub->image = mem->image; + sub->data = mem->data; + + return sub; +} + +static void +moz_gfx_memory_allocator_class_init (MozGfxMemoryAllocatorClass * klass) +{ + GstAllocatorClass *allocator_class; + + allocator_class = (GstAllocatorClass *) klass; + + allocator_class->alloc = moz_gfx_memory_allocator_alloc; + allocator_class->free = moz_gfx_memory_allocator_free; +} + +static void +moz_gfx_memory_allocator_init (MozGfxMemoryAllocator * allocator) +{ + GstAllocator *alloc = GST_ALLOCATOR_CAST (allocator); + + alloc->mem_type = "moz-gfx-image"; + alloc->mem_map = (GstMemoryMapFunction) moz_gfx_memory_map; + alloc->mem_unmap = (GstMemoryUnmapFunction) moz_gfx_memory_unmap; + alloc->mem_share = (GstMemoryShareFunction) moz_gfx_memory_share; + /* fallback copy and is_span */ +} + +void +moz_gfx_memory_allocator_set_reader(GstAllocator* aAllocator, GStreamerReader* aReader) +{ + MozGfxMemoryAllocator *allocator = (MozGfxMemoryAllocator *) aAllocator; + allocator->reader = aReader; +} + +nsRefPtr +moz_gfx_memory_get_image(GstMemory *aMemory) +{ + NS_ASSERTION(GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(aMemory->allocator), "Should be a gfx image"); + + return ((MozGfxMemory *) aMemory)->image; +} + +void +moz_gfx_buffer_pool_reset_buffer (GstBufferPool* aPool, GstBuffer* aBuffer) +{ + GstMemory* mem = gst_buffer_peek_memory(aBuffer, 0); + + NS_ASSERTION(GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(mem->allocator), "Should be a gfx image"); + moz_gfx_memory_reset((MozGfxMemory *) mem); + GST_BUFFER_POOL_CLASS(moz_gfx_buffer_pool_parent_class)->reset_buffer(aPool, aBuffer); +} + +static void +moz_gfx_buffer_pool_class_init (MozGfxBufferPoolClass * klass) +{ + GstBufferPoolClass *pool_class = (GstBufferPoolClass *) klass; + pool_class->reset_buffer = moz_gfx_buffer_pool_reset_buffer; +} + +static void +moz_gfx_buffer_pool_init (MozGfxBufferPool * pool) +{ +} + +} // namespace mozilla diff --git a/dom/media/gstreamer/GStreamerAllocator.h b/dom/media/gstreamer/GStreamerAllocator.h new file mode 100644 index 0000000000..9098c2ca4c --- /dev/null +++ b/dom/media/gstreamer/GStreamerAllocator.h @@ -0,0 +1,25 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(GStreamerAllocator_h_) +#define GStreamerAllocator_h_ + +#include "GStreamerReader.h" + +#define GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR (moz_gfx_memory_allocator_get_type()) +#define GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR)) +#define GST_TYPE_MOZ_GFX_BUFFER_POOL (moz_gfx_buffer_pool_get_type()) +#define GST_IS_MOZ_GFX_BUFFER_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_GFX_BUFFER_POOL)) + +namespace mozilla { + +GType moz_gfx_memory_allocator_get_type(); +void moz_gfx_memory_allocator_set_reader(GstAllocator *aAllocator, GStreamerReader* aReader); +nsRefPtr moz_gfx_memory_get_image(GstMemory *aMemory); + +GType moz_gfx_buffer_pool_get_type(); + +} // namespace mozilla + +#endif diff --git a/dom/media/gstreamer/GStreamerDecoder.cpp b/dom/media/gstreamer/GStreamerDecoder.cpp new file mode 100644 index 0000000000..197c3bd8f2 --- /dev/null +++ b/dom/media/gstreamer/GStreamerDecoder.cpp @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "MediaDecoderStateMachine.h" +#include "GStreamerReader.h" +#include "GStreamerDecoder.h" +#include "GStreamerFormatHelper.h" + +namespace mozilla { + +MediaDecoderStateMachine* GStreamerDecoder::CreateStateMachine() +{ + return new MediaDecoderStateMachine(this, new GStreamerReader(this)); +} + +bool +GStreamerDecoder::CanHandleMediaType(const nsACString& aMIMEType, + const nsAString* aCodecs) +{ + return MediaDecoder::IsGStreamerEnabled() && + GStreamerFormatHelper::Instance()->CanHandleMediaType(aMIMEType, aCodecs); +} + +} // namespace mozilla + diff --git a/dom/media/gstreamer/GStreamerDecoder.h b/dom/media/gstreamer/GStreamerDecoder.h new file mode 100644 index 0000000000..d0df3ee89a --- /dev/null +++ b/dom/media/gstreamer/GStreamerDecoder.h @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(GStreamerDecoder_h_) +#define GStreamerDecoder_h_ + +#include "MediaDecoder.h" +#include "nsXPCOMStrings.h" + +namespace mozilla { + +class GStreamerDecoder : public MediaDecoder +{ +public: + virtual MediaDecoder* Clone() { return new GStreamerDecoder(); } + virtual MediaDecoderStateMachine* CreateStateMachine(); + static bool CanHandleMediaType(const nsACString& aMIMEType, const nsAString* aCodecs); +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/gstreamer/GStreamerFormatHelper.cpp b/dom/media/gstreamer/GStreamerFormatHelper.cpp new file mode 100644 index 0000000000..6b1d280057 --- /dev/null +++ b/dom/media/gstreamer/GStreamerFormatHelper.cpp @@ -0,0 +1,373 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GStreamerFormatHelper.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsString.h" +#include "GStreamerLoader.h" +#include "mozilla/Logging.h" +#include "mozilla/Preferences.h" + +#define ENTRY_FORMAT(entry) entry[0] +#define ENTRY_CAPS(entry) entry[1] + +namespace mozilla { + +extern PRLogModuleInfo* gMediaDecoderLog; +#define LOG(msg, ...) \ + MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("GStreamerFormatHelper " msg, ##__VA_ARGS__)) + +GStreamerFormatHelper* GStreamerFormatHelper::gInstance = nullptr; +bool GStreamerFormatHelper::sLoadOK = false; + +GStreamerFormatHelper* GStreamerFormatHelper::Instance() { + if (!gInstance) { + if ((sLoadOK = load_gstreamer())) { + gst_init(nullptr, nullptr); + } + + gInstance = new GStreamerFormatHelper(); + } + + return gInstance; +} + +void GStreamerFormatHelper::Shutdown() { + delete gInstance; + gInstance = nullptr; +} + +static char const *const sContainers[][2] = { + {"video/mp4", "video/quicktime"}, + {"video/x-m4v", "video/quicktime"}, + {"video/quicktime", "video/quicktime"}, + {"audio/mp4", "audio/x-m4a"}, + {"audio/x-m4a", "audio/x-m4a"}, + {"audio/mpeg", "audio/mpeg, mpegversion=(int)1"}, + {"audio/mp3", "audio/mpeg, mpegversion=(int)1"}, +}; + +static char const *const sCodecs[9][2] = { + {"avc1.42E01E", "video/x-h264"}, + {"avc1.42001E", "video/x-h264"}, + {"avc1.58A01E", "video/x-h264"}, + {"avc1.4D401E", "video/x-h264"}, + {"avc1.64001E", "video/x-h264"}, + {"avc1.64001F", "video/x-h264"}, + {"mp4v.20.3", "video/3gpp"}, + {"mp4a.40.2", "audio/mpeg, mpegversion=(int)4"}, + {"mp3", "audio/mpeg, mpegversion=(int)1"}, +}; + +static char const * const sDefaultCodecCaps[][2] = { + {"video/mp4", "video/x-h264"}, + {"video/x-m4v", "video/x-h264"}, + {"video/quicktime", "video/x-h264"}, + {"audio/mp4", "audio/mpeg, mpegversion=(int)4"}, + {"audio/x-m4a", "audio/mpeg, mpegversion=(int)4"}, + {"audio/mp3", "audio/mpeg, layer=(int)3"}, + {"audio/mpeg", "audio/mpeg, layer=(int)3"} +}; + +static char const * const sPluginBlacklist[] = { + "flump3dec", + "h264parse", +}; + +GStreamerFormatHelper::GStreamerFormatHelper() + : mFactories(nullptr), + mCookie(static_cast(-1)) +{ + if (!sLoadOK) { + return; + } + + mSupportedContainerCaps = gst_caps_new_empty(); + for (unsigned int i = 0; i < G_N_ELEMENTS(sContainers); i++) { + const char* capsString = sContainers[i][1]; + GstCaps* caps = gst_caps_from_string(capsString); + gst_caps_append(mSupportedContainerCaps, caps); + } + + mSupportedCodecCaps = gst_caps_new_empty(); + for (unsigned int i = 0; i < G_N_ELEMENTS(sCodecs); i++) { + const char* capsString = sCodecs[i][1]; + GstCaps* caps = gst_caps_from_string(capsString); + gst_caps_append(mSupportedCodecCaps, caps); + } +} + +GStreamerFormatHelper::~GStreamerFormatHelper() { + if (!sLoadOK) { + return; + } + + gst_caps_unref(mSupportedContainerCaps); + gst_caps_unref(mSupportedCodecCaps); + + if (mFactories) + g_list_free(mFactories); +} + +static GstCaps * +GetContainerCapsFromMIMEType(const char *aType) { + /* convert aMIMEType to gst container caps */ + const char* capsString = nullptr; + for (uint32_t i = 0; i < G_N_ELEMENTS(sContainers); i++) { + if (!strcmp(ENTRY_FORMAT(sContainers[i]), aType)) { + capsString = ENTRY_CAPS(sContainers[i]); + break; + } + } + + if (!capsString) { + /* we couldn't find any matching caps */ + return nullptr; + } + + return gst_caps_from_string(capsString); +} + +static GstCaps * +GetDefaultCapsFromMIMEType(const char *aType) { + GstCaps *caps = GetContainerCapsFromMIMEType(aType); + + for (uint32_t i = 0; i < G_N_ELEMENTS(sDefaultCodecCaps); i++) { + if (!strcmp(sDefaultCodecCaps[i][0], aType)) { + GstCaps *tmp = gst_caps_from_string(sDefaultCodecCaps[i][1]); + + gst_caps_append(caps, tmp); + return caps; + } + } + + return nullptr; +} + +bool GStreamerFormatHelper::CanHandleMediaType(const nsACString& aMIMEType, + const nsAString* aCodecs) { + if (!sLoadOK) { + return false; + } + + const char *type; + NS_CStringGetData(aMIMEType, &type, nullptr); + + GstCaps *caps; + if (aCodecs && !aCodecs->IsEmpty()) { + caps = ConvertFormatsToCaps(type, aCodecs); + } else { + // Get a minimal set of codec caps for this MIME type we should support so + // that we don't overreport MIME types we are able to play. + caps = GetDefaultCapsFromMIMEType(type); + } + + if (!caps) { + return false; + } + + bool ret = HaveElementsToProcessCaps(caps); + gst_caps_unref(caps); + + return ret; +} + +GstCaps* GStreamerFormatHelper::ConvertFormatsToCaps(const char* aMIMEType, + const nsAString* aCodecs) { + NS_ASSERTION(sLoadOK, "GStreamer library not linked"); + + unsigned int i; + + GstCaps *caps = GetContainerCapsFromMIMEType(aMIMEType); + if (!caps) { + return nullptr; + } + + /* container only */ + if (!aCodecs) { + return caps; + } + + nsCharSeparatedTokenizer tokenizer(*aCodecs, ','); + while (tokenizer.hasMoreTokens()) { + const nsSubstring& codec = tokenizer.nextToken(); + const char *capsString = nullptr; + + for (i = 0; i < G_N_ELEMENTS(sCodecs); i++) { + if (codec.EqualsASCII(ENTRY_FORMAT(sCodecs[i]))) { + capsString = ENTRY_CAPS(sCodecs[i]); + break; + } + } + + if (!capsString) { + gst_caps_unref(caps); + return nullptr; + } + + GstCaps* tmp = gst_caps_from_string(capsString); + /* appends and frees tmp */ + gst_caps_append(caps, tmp); + } + + return caps; +} + +/* static */ bool +GStreamerFormatHelper::IsBlacklistEnabled() +{ + static bool sBlacklistEnabled; + static bool sBlacklistEnabledCached = false; + + if (!sBlacklistEnabledCached) { + Preferences::AddBoolVarCache(&sBlacklistEnabled, + "media.gstreamer.enable-blacklist", true); + sBlacklistEnabledCached = true; + } + + return sBlacklistEnabled; +} + +/* static */ bool +GStreamerFormatHelper::IsPluginFeatureBlacklisted(GstPluginFeature *aFeature) +{ + if (!IsBlacklistEnabled()) { + return false; + } + + const gchar *factoryName = + gst_plugin_feature_get_name(aFeature); + + for (unsigned int i = 0; i < G_N_ELEMENTS(sPluginBlacklist); i++) { + if (!strcmp(factoryName, sPluginBlacklist[i])) { + LOG("rejecting disabled plugin %s", factoryName); + return true; + } + } + + return false; +} + +static gboolean FactoryFilter(GstPluginFeature *aFeature, gpointer) +{ + if (!GST_IS_ELEMENT_FACTORY(aFeature)) { + return FALSE; + } + + const gchar *className = + gst_element_factory_get_klass(GST_ELEMENT_FACTORY_CAST(aFeature)); + + // NB: We skip filtering parsers here, because adding them to + // the list can give false decoder positives to canPlayType(). + if (!strstr(className, "Decoder") && !strstr(className, "Demux")) { + return FALSE; + } + + return + gst_plugin_feature_get_rank(aFeature) >= GST_RANK_MARGINAL && + !GStreamerFormatHelper::IsPluginFeatureBlacklisted(aFeature); +} + +/** + * Returns true if any |aFactory| caps intersect with |aCaps| + */ +static bool SupportsCaps(GstElementFactory *aFactory, GstCaps *aCaps) +{ + for (const GList *iter = gst_element_factory_get_static_pad_templates(aFactory); iter; iter = iter->next) { + GstStaticPadTemplate *templ = static_cast(iter->data); + + if (templ->direction == GST_PAD_SRC) { + continue; + } + + GstCaps *caps = gst_static_caps_get(&templ->static_caps); + if (!caps) { + continue; + } + + bool supported = gst_caps_can_intersect(caps, aCaps); + + gst_caps_unref(caps); + + if (supported) { + return true; + } + } + + return false; +} + +bool GStreamerFormatHelper::HaveElementsToProcessCaps(GstCaps* aCaps) { + NS_ASSERTION(sLoadOK, "GStreamer library not linked"); + + GList* factories = GetFactories(); + + /* here aCaps contains [containerCaps, [codecCaps1, [codecCaps2, ...]]] so process + * caps structures individually as we want one element for _each_ + * structure */ + for (unsigned int i = 0; i < gst_caps_get_size(aCaps); i++) { + GstStructure* s = gst_caps_get_structure(aCaps, i); + GstCaps* caps = gst_caps_new_full(gst_structure_copy(s), nullptr); + + bool found = false; + for (GList *elem = factories; elem; elem = elem->next) { + if (SupportsCaps(GST_ELEMENT_FACTORY_CAST(elem->data), caps)) { + found = true; + break; + } + } + + gst_caps_unref(caps); + + if (!found) { + return false; + } + } + + return true; +} + +bool GStreamerFormatHelper::CanHandleContainerCaps(GstCaps* aCaps) +{ + NS_ASSERTION(sLoadOK, "GStreamer library not linked"); + + return gst_caps_can_intersect(aCaps, mSupportedContainerCaps); +} + +bool GStreamerFormatHelper::CanHandleCodecCaps(GstCaps* aCaps) +{ + NS_ASSERTION(sLoadOK, "GStreamer library not linked"); + + return gst_caps_can_intersect(aCaps, mSupportedCodecCaps); +} + +GList* GStreamerFormatHelper::GetFactories() { + NS_ASSERTION(sLoadOK, "GStreamer library not linked"); + +#if GST_VERSION_MAJOR >= 1 + uint32_t cookie = gst_registry_get_feature_list_cookie(gst_registry_get()); +#else + uint32_t cookie = gst_default_registry_get_feature_list_cookie(); +#endif + if (cookie != mCookie) { + g_list_free(mFactories); +#if GST_VERSION_MAJOR >= 1 + mFactories = + gst_registry_feature_filter(gst_registry_get(), + (GstPluginFeatureFilter)FactoryFilter, + false, nullptr); +#else + mFactories = + gst_default_registry_feature_filter((GstPluginFeatureFilter)FactoryFilter, + false, nullptr); +#endif + mCookie = cookie; + } + + return mFactories; +} + +} // namespace mozilla diff --git a/dom/media/gstreamer/GStreamerFormatHelper.h b/dom/media/gstreamer/GStreamerFormatHelper.h new file mode 100644 index 0000000000..55ddcfb523 --- /dev/null +++ b/dom/media/gstreamer/GStreamerFormatHelper.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(GStreamerFormatHelper_h_) +#define GStreamerFormatHelper_h_ + +#include +#include +#include "nsXPCOMStrings.h" + +namespace mozilla { + +class GStreamerFormatHelper { + /* This class can be used to query the GStreamer registry for the required + * demuxers/decoders from nsHTMLMediaElement::CanPlayType. + * It implements looking at the GstRegistry to check if elements to + * demux/decode the formats passed to CanPlayType() are actually installed. + */ + public: + static GStreamerFormatHelper* Instance(); + ~GStreamerFormatHelper(); + + bool CanHandleMediaType(const nsACString& aMIMEType, + const nsAString* aCodecs); + + bool CanHandleContainerCaps(GstCaps* aCaps); + bool CanHandleCodecCaps(GstCaps* aCaps); + + static bool IsBlacklistEnabled(); + static bool IsPluginFeatureBlacklisted(GstPluginFeature *aFeature); + + static GstCaps* ConvertFormatsToCaps(const char* aMIMEType, + const nsAString* aCodecs); + + static void Shutdown(); + + private: + GStreamerFormatHelper(); + char* const *CodecListFromCaps(GstCaps* aCaps); + bool HaveElementsToProcessCaps(GstCaps* aCaps); + GList* GetFactories(); + + static GStreamerFormatHelper* gInstance; + + /* table to convert from container MIME types to GStreamer caps */ + static char const *const mContainers[6][2]; + + /* table to convert from codec MIME types to GStreamer caps */ + static char const *const mCodecs[9][2]; + + /* + * True iff we were able to find the proper GStreamer libs and the functions + * we need. + */ + static bool sLoadOK; + + /* whitelist of supported container/codec gst caps */ + GstCaps* mSupportedContainerCaps; + GstCaps* mSupportedCodecCaps; + + /* list of GStreamer element factories + * Element factories are the basic types retrieved from the GStreamer + * registry, they describe all plugins and elements that GStreamer can + * create. + * This means that element factories are useful for automated element + * instancing, such as what autopluggers do, + * and for creating lists of available elements. */ + GList* mFactories; + + /* Storage for the default registrys feature list cookie. + * It changes every time a feature is added to or removed from the + * GStreamer registry. */ + uint32_t mCookie; +}; + +} //namespace mozilla + +#endif diff --git a/dom/media/gstreamer/GStreamerFunctionList.h b/dom/media/gstreamer/GStreamerFunctionList.h new file mode 100644 index 0000000000..75f352ec0d --- /dev/null +++ b/dom/media/gstreamer/GStreamerFunctionList.h @@ -0,0 +1,173 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __APPLE__ + +/* + * List of symbol names we need to dlsym from the gstreamer library. + */ +GST_FUNC(LIBGSTAPP, gst_app_sink_get_type) +GST_FUNC(LIBGSTAPP, gst_app_sink_set_callbacks) +GST_FUNC(LIBGSTAPP, gst_app_src_end_of_stream) +GST_FUNC(LIBGSTAPP, gst_app_src_get_size) +GST_FUNC(LIBGSTAPP, gst_app_src_get_type) +GST_FUNC(LIBGSTAPP, gst_app_src_push_buffer) +GST_FUNC(LIBGSTAPP, gst_app_src_set_callbacks) +GST_FUNC(LIBGSTAPP, gst_app_src_set_caps) +GST_FUNC(LIBGSTAPP, gst_app_src_set_size) +GST_FUNC(LIBGSTAPP, gst_app_src_set_stream_type) +GST_FUNC(LIBGSTREAMER, gst_bin_get_by_name) +GST_FUNC(LIBGSTREAMER, gst_bin_get_type) +GST_FUNC(LIBGSTREAMER, gst_bin_iterate_recurse) +GST_FUNC(LIBGSTREAMER, gst_buffer_get_type) +GST_FUNC(LIBGSTREAMER, gst_buffer_new) +GST_FUNC(LIBGSTREAMER, gst_bus_set_sync_handler) +GST_FUNC(LIBGSTREAMER, gst_bus_timed_pop_filtered) +GST_FUNC(LIBGSTREAMER, gst_caps_append) +GST_FUNC(LIBGSTREAMER, gst_caps_can_intersect) +GST_FUNC(LIBGSTREAMER, gst_caps_from_string) +GST_FUNC(LIBGSTREAMER, gst_caps_get_size) +GST_FUNC(LIBGSTREAMER, gst_caps_get_structure) +GST_FUNC(LIBGSTREAMER, gst_caps_new_any) +GST_FUNC(LIBGSTREAMER, gst_caps_new_empty) +GST_FUNC(LIBGSTREAMER, gst_caps_new_full) +GST_FUNC(LIBGSTREAMER, gst_caps_new_simple) +GST_FUNC(LIBGSTREAMER, gst_caps_set_simple) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_static_pad_templates) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_type) +GST_FUNC(LIBGSTREAMER, gst_element_factory_make) +GST_FUNC(LIBGSTREAMER, gst_element_get_factory) +GST_FUNC(LIBGSTREAMER, gst_element_get_static_pad) +GST_FUNC(LIBGSTREAMER, gst_element_get_type) +GST_FUNC(LIBGSTREAMER, gst_element_query_convert) +GST_FUNC(LIBGSTREAMER, gst_element_query_duration) +GST_FUNC(LIBGSTREAMER, gst_element_seek_simple) +GST_FUNC(LIBGSTREAMER, gst_element_set_state) +GST_FUNC(LIBGSTREAMER, gst_flow_get_name) +GST_FUNC(LIBGSTREAMER, gst_init) +GST_FUNC(LIBGSTREAMER, gst_init_check) +GST_FUNC(LIBGSTREAMER, gst_iterator_free) +GST_FUNC(LIBGSTREAMER, gst_iterator_next) +GST_FUNC(LIBGSTREAMER, gst_message_parse_error) +GST_FUNC(LIBGSTREAMER, gst_message_type_get_name) +GST_FUNC(LIBGSTREAMER, gst_mini_object_ref) +GST_FUNC(LIBGSTREAMER, gst_mini_object_unref) +GST_FUNC(LIBGSTREAMER, gst_object_get_name) +GST_FUNC(LIBGSTREAMER, gst_object_get_parent) +GST_FUNC(LIBGSTREAMER, gst_object_unref) +GST_FUNC(LIBGSTREAMER, gst_pad_get_element_private) +GST_FUNC(LIBGSTREAMER, gst_pad_set_element_private) +GST_FUNC(LIBGSTREAMER, gst_parse_bin_from_description) +GST_FUNC(LIBGSTREAMER, gst_pipeline_get_bus) +GST_FUNC(LIBGSTREAMER, gst_pipeline_get_type) +GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_rank) +GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_type) +GST_FUNC(LIBGSTREAMER, gst_registry_feature_filter) +GST_FUNC(LIBGSTREAMER, gst_registry_get_feature_list_cookie) +GST_FUNC(LIBGSTREAMER, gst_segment_init) +GST_FUNC(LIBGSTREAMER, gst_segment_to_stream_time) +GST_FUNC(LIBGSTREAMER, gst_static_caps_get) +GST_FUNC(LIBGSTREAMER, gst_structure_copy) +GST_FUNC(LIBGSTREAMER, gst_structure_get_fraction) +GST_FUNC(LIBGSTREAMER, gst_structure_get_int) +GST_FUNC(LIBGSTREAMER, gst_structure_get_value) +GST_FUNC(LIBGSTREAMER, gst_structure_new) +GST_FUNC(LIBGSTREAMER, gst_util_uint64_scale) + +#if GST_VERSION_MAJOR == 0 +GST_FUNC(LIBGSTAPP, gst_app_sink_pull_buffer) +GST_FUNC(LIBGSTREAMER, gst_buffer_copy_metadata) +GST_FUNC(LIBGSTREAMER, gst_buffer_new_and_alloc) +GST_FUNC(LIBGSTREAMER, gst_caps_unref) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_klass) +GST_FUNC(LIBGSTREAMER, gst_element_get_pad) +GST_FUNC(LIBGSTREAMER, gst_event_parse_new_segment) +GST_FUNC(LIBGSTREAMER, gst_mini_object_get_type) +GST_FUNC(LIBGSTREAMER, gst_mini_object_new) +GST_FUNC(LIBGSTREAMER, gst_pad_add_event_probe) +GST_FUNC(LIBGSTREAMER, gst_pad_alloc_buffer) +GST_FUNC(LIBGSTREAMER, gst_pad_get_negotiated_caps) +GST_FUNC(LIBGSTREAMER, gst_pad_set_bufferalloc_function) +GST_FUNC(LIBGSTREAMER, gst_plugin_feature_get_name) +GST_FUNC(LIBGSTREAMER, gst_registry_get_default) +GST_FUNC(LIBGSTREAMER, gst_segment_set_newsegment) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_height) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_offset) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_component_width) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_pixel_stride) +GST_FUNC(LIBGSTVIDEO, gst_video_format_get_row_stride) +GST_FUNC(LIBGSTVIDEO, gst_video_format_parse_caps) +GST_FUNC(LIBGSTVIDEO, gst_video_parse_caps_pixel_aspect_ratio) +#else + +GST_FUNC(LIBGSTAPP, gst_app_sink_pull_sample) +GST_FUNC(LIBGSTREAMER, _gst_caps_any) +GST_FUNC(LIBGSTREAMER, gst_allocator_get_type) +GST_FUNC(LIBGSTREAMER, gst_buffer_copy_into) +GST_FUNC(LIBGSTREAMER, gst_buffer_extract) +GST_FUNC(LIBGSTREAMER, gst_buffer_get_meta) +GST_FUNC(LIBGSTREAMER, gst_buffer_get_size) +GST_FUNC(LIBGSTREAMER, gst_buffer_map) +GST_FUNC(LIBGSTREAMER, gst_buffer_new_allocate) +GST_FUNC(LIBGSTREAMER, gst_buffer_n_memory) +GST_FUNC(LIBGSTREAMER, gst_buffer_peek_memory) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_acquire_buffer) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_config_set_allocator) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_config_set_params) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_get_config) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_get_type) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_is_active) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_set_active) +GST_FUNC(LIBGSTREAMER, gst_buffer_pool_set_config) +GST_FUNC(LIBGSTREAMER, gst_buffer_set_size) +GST_FUNC(LIBGSTREAMER, gst_buffer_unmap) +GST_FUNC(LIBGSTREAMER, gst_element_factory_get_metadata) +GST_FUNC(LIBGSTREAMER, gst_event_parse_segment) +GST_FUNC(LIBGSTREAMER, gst_event_type_get_name) +GST_FUNC(LIBGSTREAMER, gst_memory_init) +GST_FUNC(LIBGSTREAMER, gst_memory_map) +GST_FUNC(LIBGSTREAMER, gst_memory_unmap) +GST_FUNC(LIBGSTREAMER, gst_object_get_type) +GST_FUNC(LIBGSTREAMER, gst_pad_add_probe) +GST_FUNC(LIBGSTREAMER, gst_pad_get_current_caps) +GST_FUNC(LIBGSTREAMER, gst_pad_probe_info_get_query) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_meta) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_param) +GST_FUNC(LIBGSTREAMER, gst_query_add_allocation_pool) +GST_FUNC(LIBGSTREAMER, gst_query_parse_allocation) +GST_FUNC(LIBGSTREAMER, gst_registry_get) +GST_FUNC(LIBGSTREAMER, gst_sample_get_buffer) +GST_FUNC(LIBGSTREAMER, gst_segment_copy_into) +GST_FUNC(LIBGSTREAMER, gst_structure_free) +GST_FUNC(LIBGSTVIDEO, gst_buffer_pool_config_get_video_alignment) +GST_FUNC(LIBGSTVIDEO, gst_buffer_pool_has_option) +GST_FUNC(LIBGSTVIDEO, gst_video_buffer_pool_get_type) +GST_FUNC(LIBGSTVIDEO, gst_video_frame_map) +GST_FUNC(LIBGSTVIDEO, gst_video_frame_unmap) +GST_FUNC(LIBGSTVIDEO, gst_video_info_align) +GST_FUNC(LIBGSTVIDEO, gst_video_info_from_caps) +GST_FUNC(LIBGSTVIDEO, gst_video_info_init) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_api_get_type) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_map) +GST_FUNC(LIBGSTVIDEO, gst_video_meta_unmap) + +#endif + +/* + * Functions that have been defined in the header file. We replace them so that + * they don't try to use the global gstreamer functions. + */ +#ifdef REPLACE_FUNC +REPLACE_FUNC(gst_buffer_ref); +REPLACE_FUNC(gst_buffer_unref); +REPLACE_FUNC(gst_message_unref); + +#if GST_VERSION_MAJOR == 1 +REPLACE_FUNC(gst_caps_unref); +REPLACE_FUNC(gst_sample_unref); +#endif +#endif + +#endif // !defined(__APPLE__) diff --git a/dom/media/gstreamer/GStreamerLoader.cpp b/dom/media/gstreamer/GStreamerLoader.cpp new file mode 100644 index 0000000000..8182f689f4 --- /dev/null +++ b/dom/media/gstreamer/GStreamerLoader.cpp @@ -0,0 +1,145 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include + +#include "nsDebug.h" + +#include "GStreamerLoader.h" + +#define LIBGSTREAMER 0 +#define LIBGSTAPP 1 +#define LIBGSTVIDEO 2 + +#ifdef __OpenBSD__ +#define LIB_GST_SUFFIX ".so" +#else +#define LIB_GST_SUFFIX ".so.0" +#endif + +namespace mozilla { + +/* + * Declare our function pointers using the types from the global gstreamer + * definitions. + */ +#define GST_FUNC(_, func) typeof(::func)* func; +#define REPLACE_FUNC(func) GST_FUNC(-1, func) +#include "GStreamerFunctionList.h" +#undef GST_FUNC +#undef REPLACE_FUNC + +/* + * Redefinitions of functions that have been defined in the gstreamer headers to + * stop them calling the gstreamer functions in global scope. + */ +GstBuffer * gst_buffer_ref_impl(GstBuffer *buf); +void gst_buffer_unref_impl(GstBuffer *buf); +void gst_message_unref_impl(GstMessage *msg); +void gst_caps_unref_impl(GstCaps *caps); + +#if GST_VERSION_MAJOR == 1 +void gst_sample_unref_impl(GstSample *sample); +#endif + +bool +load_gstreamer() +{ +#ifdef __APPLE__ + return true; +#endif + static bool loaded = false; + + if (loaded) { + return true; + } + + void *gstreamerLib = nullptr; + guint major = 0; + guint minor = 0; + guint micro, nano; + + typedef typeof(::gst_version) VersionFuncType; + if (VersionFuncType *versionFunc = (VersionFuncType*)dlsym(RTLD_DEFAULT, "gst_version")) { + versionFunc(&major, &minor, µ, &nano); + } + + if (major == GST_VERSION_MAJOR && minor == GST_VERSION_MINOR) { + gstreamerLib = RTLD_DEFAULT; + } else { + gstreamerLib = dlopen("libgstreamer-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL); + } + + void *handles[3] = { + gstreamerLib, + dlopen("libgstapp-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL), + dlopen("libgstvideo-" GST_API_VERSION LIB_GST_SUFFIX, RTLD_NOW | RTLD_LOCAL) + }; + + for (size_t i = 0; i < sizeof(handles) / sizeof(handles[0]); i++) { + if (!handles[i]) { + NS_WARNING("Couldn't link gstreamer libraries"); + goto fail; + } + } + +#define GST_FUNC(lib, symbol) \ + if (!(symbol = (typeof(symbol))dlsym(handles[lib], #symbol))) { \ + NS_WARNING("Couldn't link symbol " #symbol); \ + goto fail; \ + } +#define REPLACE_FUNC(symbol) symbol = symbol##_impl; +#include "GStreamerFunctionList.h" +#undef GST_FUNC +#undef REPLACE_FUNC + + loaded = true; + return true; + +fail: + + for (size_t i = 0; i < sizeof(handles) / sizeof(handles[0]); i++) { + if (handles[i] && handles[i] != RTLD_DEFAULT) { + dlclose(handles[i]); + } + } + + return false; +} + +GstBuffer * +gst_buffer_ref_impl(GstBuffer *buf) +{ + return (GstBuffer *)gst_mini_object_ref(GST_MINI_OBJECT_CAST(buf)); +} + +void +gst_buffer_unref_impl(GstBuffer *buf) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(buf)); +} + +void +gst_message_unref_impl(GstMessage *msg) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(msg)); +} + +#if GST_VERSION_MAJOR == 1 +void +gst_sample_unref_impl(GstSample *sample) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(sample)); +} +#endif + +void +gst_caps_unref_impl(GstCaps *caps) +{ + gst_mini_object_unref(GST_MINI_OBJECT_CAST(caps)); +} + +} diff --git a/dom/media/gstreamer/GStreamerLoader.h b/dom/media/gstreamer/GStreamerLoader.h new file mode 100644 index 0000000000..cd7fe6d1e8 --- /dev/null +++ b/dom/media/gstreamer/GStreamerLoader.h @@ -0,0 +1,53 @@ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GStreamerLoader_h_ +#define GStreamerLoader_h_ + +#include +#include +#include +#include +#include +#include +// This include trips -Wreserved-user-defined-literal on clang. Ignoring it +// trips -Wpragmas on GCC (unknown warning), but ignoring that trips +// -Wunknown-pragmas on clang (unknown pragma). +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wreserved-user-defined-literal" +#include +#pragma GCC diagnostic pop + +#if GST_VERSION_MAJOR == 1 +#include +#include +#endif + +namespace mozilla { + +/* + * dlopens the required libraries and dlsyms the functions we need. + * Returns true on success, false otherwise. + */ +bool load_gstreamer(); + +/* + * Declare our extern function pointers using the types from the global + * gstreamer definitions. + */ +#define GST_FUNC(_, func) extern typeof(::func)* func; +#define REPLACE_FUNC(func) GST_FUNC(-1, func) +#include "GStreamerFunctionList.h" +#undef GST_FUNC +#undef REPLACE_FUNC + +} + +#undef GST_CAPS_ANY +#define GST_CAPS_ANY (*_gst_caps_any) + +#endif // GStreamerLoader_h_ diff --git a/dom/media/gstreamer/GStreamerMozVideoBuffer.cpp b/dom/media/gstreamer/GStreamerMozVideoBuffer.cpp new file mode 100644 index 0000000000..05666024f0 --- /dev/null +++ b/dom/media/gstreamer/GStreamerMozVideoBuffer.cpp @@ -0,0 +1,123 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include "GStreamerReader.h" +#include "GStreamerMozVideoBuffer.h" +#include "ImageContainer.h" + +namespace mozilla { + +static GstMozVideoBuffer *gst_moz_video_buffer_copy(GstMozVideoBuffer* self); +static void gst_moz_video_buffer_finalize(GstMozVideoBuffer* self); + +// working around GTK+ bug https://bugzilla.gnome.org/show_bug.cgi?id=723899 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-function" +G_DEFINE_TYPE(GstMozVideoBuffer, gst_moz_video_buffer, GST_TYPE_BUFFER); +#pragma GCC diagnostic pop + +static void +gst_moz_video_buffer_class_init(GstMozVideoBufferClass* klass) +{ + g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass)); + + GstMiniObjectClass *mo_class = GST_MINI_OBJECT_CLASS(klass); + + mo_class->copy =(GstMiniObjectCopyFunction)gst_moz_video_buffer_copy; + mo_class->finalize =(GstMiniObjectFinalizeFunction)gst_moz_video_buffer_finalize; +} + +static void +gst_moz_video_buffer_init(GstMozVideoBuffer* self) +{ + g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self)); +} + +static void +gst_moz_video_buffer_finalize(GstMozVideoBuffer* self) +{ + g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self)); + + if(self->data) + g_boxed_free(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data); + + GST_MINI_OBJECT_CLASS(gst_moz_video_buffer_parent_class)->finalize(GST_MINI_OBJECT(self)); +} + +static GstMozVideoBuffer* +gst_moz_video_buffer_copy(GstMozVideoBuffer* self) +{ + GstMozVideoBuffer* copy; + + g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), nullptr); + + copy = gst_moz_video_buffer_new(); + + /* we simply copy everything from our parent */ + GST_BUFFER_DATA(GST_BUFFER_CAST(copy)) = + (guint8*)g_memdup(GST_BUFFER_DATA(GST_BUFFER_CAST(self)), GST_BUFFER_SIZE(GST_BUFFER_CAST(self))); + + /* make sure it gets freed(even if the parent is subclassed, we return a + normal buffer) */ + GST_BUFFER_MALLOCDATA(GST_BUFFER_CAST(copy)) = GST_BUFFER_DATA(GST_BUFFER_CAST(copy)); + GST_BUFFER_SIZE(GST_BUFFER_CAST(copy)) = GST_BUFFER_SIZE(GST_BUFFER_CAST(self)); + + /* copy metadata */ + gst_buffer_copy_metadata(GST_BUFFER_CAST(copy), + GST_BUFFER_CAST(self), + (GstBufferCopyFlags)GST_BUFFER_COPY_ALL); + /* copy videobuffer */ + if(self->data) + copy->data = (GstMozVideoBufferData*)g_boxed_copy(GST_TYPE_MOZ_VIDEO_BUFFER_DATA, self->data); + + return copy; +} + +GstMozVideoBuffer* +gst_moz_video_buffer_new(void) +{ + GstMozVideoBuffer *self; + + self =(GstMozVideoBuffer*)gst_mini_object_new(GST_TYPE_MOZ_VIDEO_BUFFER); + self->data = nullptr; + + return self; +} + +void +gst_moz_video_buffer_set_data(GstMozVideoBuffer* self, GstMozVideoBufferData* data) +{ + g_return_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self)); + + self->data = data; +} + +GstMozVideoBufferData* +gst_moz_video_buffer_get_data(const GstMozVideoBuffer* self) +{ + g_return_val_if_fail(GST_IS_MOZ_VIDEO_BUFFER(self), nullptr); + + return self->data; +} + +GType +gst_moz_video_buffer_data_get_type(void) +{ + static volatile gsize g_define_type_id__volatile = 0; + + if(g_once_init_enter(&g_define_type_id__volatile)) { + GType g_define_type_id = + g_boxed_type_register_static(g_intern_static_string("GstMozVideoBufferData"), + (GBoxedCopyFunc)GstMozVideoBufferData::Copy, + (GBoxedFreeFunc)GstMozVideoBufferData::Free); + g_once_init_leave(&g_define_type_id__volatile, g_define_type_id); + } + + return g_define_type_id__volatile; +} + +} diff --git a/dom/media/gstreamer/GStreamerMozVideoBuffer.h b/dom/media/gstreamer/GStreamerMozVideoBuffer.h new file mode 100644 index 0000000000..e9295771ed --- /dev/null +++ b/dom/media/gstreamer/GStreamerMozVideoBuffer.h @@ -0,0 +1,61 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef __GST_MOZ_VIDEO_BUFFER_H__ +#define __GST_MOZ_VIDEO_BUFFER_H__ + +#include +#include "GStreamerLoader.h" +#include "MediaDecoderReader.h" + +namespace mozilla { + +#define GST_TYPE_MOZ_VIDEO_BUFFER_DATA (gst_moz_video_buffer_data_get_type()) +#define GST_IS_MOZ_VIDEO_BUFFER_DATA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER_DATA)) + +#define GST_TYPE_MOZ_VIDEO_BUFFER (gst_moz_video_buffer_get_type()) +#define GST_IS_MOZ_VIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MOZ_VIDEO_BUFFER)) +#define GST_IS_MOZ_VIDEO_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MOZ_VIDEO_BUFFER)) +#define GST_MOZ_VIDEO_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MOZ_VIDEO_BUFFER, GstMozVideoBuffer)) + +typedef struct _GstMozVideoBuffer GstMozVideoBuffer; +typedef struct _GstMozVideoBufferClass GstMozVideoBufferClass; + +class GstMozVideoBufferData; + +struct _GstMozVideoBuffer { + GstBuffer buffer; + GstMozVideoBufferData* data; +}; + +struct _GstMozVideoBufferClass { + GstBufferClass buffer_class; +}; + +GType gst_moz_video_buffer_get_type(void); +GstMozVideoBuffer* gst_moz_video_buffer_new(void); +void gst_moz_video_buffer_set_data(GstMozVideoBuffer* buf, GstMozVideoBufferData* data); +GstMozVideoBufferData* gst_moz_video_buffer_get_data(const GstMozVideoBuffer* buf); + +class GstMozVideoBufferData { + public: + explicit GstMozVideoBufferData(layers::PlanarYCbCrImage* aImage) : mImage(aImage) {} + + static void* Copy(void* aData) { + return new GstMozVideoBufferData(reinterpret_cast(aData)->mImage); + } + + static void Free(void* aData) { + delete reinterpret_cast(aData); + } + + nsRefPtr mImage; +}; + +GType gst_moz_video_buffer_data_get_type (void); + +} // namespace mozilla + +#endif /* __GST_MOZ_VIDEO_BUFFER_H__ */ + diff --git a/dom/media/gstreamer/GStreamerReader-0.10.cpp b/dom/media/gstreamer/GStreamerReader-0.10.cpp new file mode 100644 index 0000000000..3624d27b0f --- /dev/null +++ b/dom/media/gstreamer/GStreamerReader-0.10.cpp @@ -0,0 +1,204 @@ +#include "nsError.h" +#include "MediaDecoderStateMachine.h" +#include "AbstractMediaDecoder.h" +#include "MediaResource.h" +#include "GStreamerReader.h" +#include "GStreamerMozVideoBuffer.h" +#include "GStreamerFormatHelper.h" +#include "VideoUtils.h" +#include "mozilla/Endian.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; +using mozilla::layers::PlanarYCbCrImage; +using mozilla::layers::ImageContainer; + +GstFlowReturn GStreamerReader::AllocateVideoBufferCb(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf) +{ + GStreamerReader* reader = reinterpret_cast(gst_pad_get_element_private(aPad)); + return reader->AllocateVideoBuffer(aPad, aOffset, aSize, aCaps, aBuf); +} + +GstFlowReturn GStreamerReader::AllocateVideoBuffer(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf) +{ + nsRefPtr image; + return AllocateVideoBufferFull(aPad, aOffset, aSize, aCaps, aBuf, image); +} + +GstFlowReturn GStreamerReader::AllocateVideoBufferFull(GstPad* aPad, + guint64 aOffset, + guint aSize, + GstCaps* aCaps, + GstBuffer** aBuf, + nsRefPtr& aImage) +{ + /* allocate an image using the container */ + ImageContainer* container = mDecoder->GetImageContainer(); + if (container == nullptr) { + return GST_FLOW_ERROR; + } + nsRefPtr image = + container->CreateImage(ImageFormat::PLANAR_YCBCR).downcast(); + + /* prepare a GstBuffer pointing to the underlying PlanarYCbCrImage buffer */ + GstBuffer* buf = GST_BUFFER(gst_moz_video_buffer_new()); + GST_BUFFER_SIZE(buf) = aSize; + /* allocate the actual YUV buffer */ + GST_BUFFER_DATA(buf) = image->AllocateAndGetNewBuffer(aSize); + + aImage = image; + + /* create a GstMozVideoBufferData to hold the image */ + GstMozVideoBufferData* bufferdata = new GstMozVideoBufferData(image); + + /* Attach bufferdata to our GstMozVideoBuffer, it will take care to free it */ + gst_moz_video_buffer_set_data(GST_MOZ_VIDEO_BUFFER(buf), bufferdata); + + *aBuf = buf; + return GST_FLOW_OK; +} + +gboolean GStreamerReader::EventProbe(GstPad* aPad, GstEvent* aEvent) +{ + GstElement* parent = GST_ELEMENT(gst_pad_get_parent(aPad)); + switch(GST_EVENT_TYPE(aEvent)) { + case GST_EVENT_NEWSEGMENT: + { + gboolean update; + gdouble rate; + GstFormat format; + gint64 start, stop, position; + GstSegment* segment; + + /* Store the segments so we can convert timestamps to stream time, which + * is what the upper layers sync on. + */ + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + gst_event_parse_new_segment(aEvent, &update, &rate, &format, + &start, &stop, &position); + if (parent == GST_ELEMENT(mVideoAppSink)) + segment = &mVideoSegment; + else + segment = &mAudioSegment; + gst_segment_set_newsegment(segment, update, rate, format, + start, stop, position); + break; + } + case GST_EVENT_FLUSH_STOP: + /* Reset on seeks */ + ResetDecode(); + break; + default: + break; + } + gst_object_unref(parent); + + return TRUE; +} + +gboolean GStreamerReader::EventProbeCb(GstPad* aPad, + GstEvent* aEvent, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + return reader->EventProbe(aPad, aEvent); +} + +nsRefPtr GStreamerReader::GetImageFromBuffer(GstBuffer* aBuffer) +{ + if (!GST_IS_MOZ_VIDEO_BUFFER (aBuffer)) + return nullptr; + + nsRefPtr image; + GstMozVideoBufferData* bufferdata = reinterpret_cast(gst_moz_video_buffer_get_data(GST_MOZ_VIDEO_BUFFER(aBuffer))); + image = bufferdata->mImage; + + PlanarYCbCrImage::Data data; + data.mPicX = data.mPicY = 0; + data.mPicSize = gfx::IntSize(mPicture.width, mPicture.height); + data.mStereoMode = StereoMode::MONO; + + data.mYChannel = GST_BUFFER_DATA(aBuffer); + data.mYStride = gst_video_format_get_row_stride(mFormat, 0, mPicture.width); + data.mYSize = gfx::IntSize(gst_video_format_get_component_width(mFormat, 0, mPicture.width), + gst_video_format_get_component_height(mFormat, 0, mPicture.height)); + data.mYSkip = 0; + data.mCbCrStride = gst_video_format_get_row_stride(mFormat, 1, mPicture.width); + data.mCbCrSize = gfx::IntSize(gst_video_format_get_component_width(mFormat, 1, mPicture.width), + gst_video_format_get_component_height(mFormat, 1, mPicture.height)); + data.mCbChannel = data.mYChannel + gst_video_format_get_component_offset(mFormat, 1, + mPicture.width, mPicture.height); + data.mCrChannel = data.mYChannel + gst_video_format_get_component_offset(mFormat, 2, + mPicture.width, mPicture.height); + data.mCbSkip = 0; + data.mCrSkip = 0; + + image->SetDataNoCopy(data); + + return image; +} + +void GStreamerReader::CopyIntoImageBuffer(GstBuffer* aBuffer, + GstBuffer** aOutBuffer, + nsRefPtr &aImage) +{ + AllocateVideoBufferFull(nullptr, GST_BUFFER_OFFSET(aBuffer), + GST_BUFFER_SIZE(aBuffer), nullptr, aOutBuffer, aImage); + + gst_buffer_copy_metadata(*aOutBuffer, aBuffer, (GstBufferCopyFlags)GST_BUFFER_COPY_ALL); + memcpy(GST_BUFFER_DATA(*aOutBuffer), GST_BUFFER_DATA(aBuffer), GST_BUFFER_SIZE(*aOutBuffer)); + + aImage = GetImageFromBuffer(*aOutBuffer); +} + +GstCaps* GStreamerReader::BuildAudioSinkCaps() +{ + GstCaps* caps; + +#if MOZ_LITTLE_ENDIAN + int endianness = 1234; +#else + int endianness = 4321; +#endif + + gint width; + +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 + caps = gst_caps_from_string("audio/x-raw-float, channels={1,2}"); + width = 32; +#else /* !MOZ_SAMPLE_TYPE_FLOAT32 */ + caps = gst_caps_from_string("audio/x-raw-int, channels={1,2}"); + width = 16; +#endif + + gst_caps_set_simple(caps, + "width", G_TYPE_INT, width, + "endianness", G_TYPE_INT, endianness, + nullptr); + + return caps; +} + +void GStreamerReader::InstallPadCallbacks() +{ + GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); + gst_pad_add_event_probe(sinkpad, + G_CALLBACK(&GStreamerReader::EventProbeCb), this); + + gst_pad_set_bufferalloc_function(sinkpad, GStreamerReader::AllocateVideoBufferCb); + gst_pad_set_element_private(sinkpad, this); + gst_object_unref(sinkpad); + + sinkpad = gst_element_get_static_pad(GST_ELEMENT(mAudioAppSink), "sink"); + gst_pad_add_event_probe(sinkpad, + G_CALLBACK(&GStreamerReader::EventProbeCb), this); + gst_object_unref(sinkpad); +} diff --git a/dom/media/gstreamer/GStreamerReader.cpp b/dom/media/gstreamer/GStreamerReader.cpp new file mode 100644 index 0000000000..3514c97b72 --- /dev/null +++ b/dom/media/gstreamer/GStreamerReader.cpp @@ -0,0 +1,1502 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsError.h" +#include "nsMimeTypes.h" +#include "MediaDecoderStateMachine.h" +#include "AbstractMediaDecoder.h" +#include "GStreamerReader.h" +#if GST_VERSION_MAJOR >= 1 +#include "GStreamerAllocator.h" +#endif +#include "GStreamerFormatHelper.h" +#include "VideoUtils.h" +#include "mozilla/Endian.h" +#include "mozilla/Preferences.h" +#include "mozilla/unused.h" +#include "GStreamerLoader.h" +#include "gfx2DGlue.h" + +namespace mozilla { + +using namespace gfx; +using namespace layers; +using namespace media; + +// Un-comment to enable logging of seek bisections. +//#define SEEK_LOGGING + +extern PRLogModuleInfo* gMediaDecoderLog; +#define LOG(type, msg, ...) \ + MOZ_LOG(gMediaDecoderLog, type, ("GStreamerReader(%p) " msg, this, ##__VA_ARGS__)) + +#if DEBUG +static const unsigned int MAX_CHANNELS = 4; +#endif +// Let the demuxer work in pull mode for short files. This used to be a micro +// optimization to have more accurate durations for ogg files in mochitests. +// Since as of today we aren't using gstreamer to demux ogg, and having demuxers +// work in pull mode over http makes them slower (since they really assume +// near-zero latency in pull mode) set the constant to 0 for now, which +// effectively disables it. +static const int SHORT_FILE_SIZE = 0; +// The default resource->Read() size when working in push mode +static const int DEFAULT_SOURCE_READ_SIZE = 50 * 1024; + +typedef enum { + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9), + GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10) +} PlayFlags; + +GStreamerReader::GStreamerReader(AbstractMediaDecoder* aDecoder) + : MediaDecoderReader(aDecoder), + mMP3FrameParser(aDecoder->GetResource()->GetLength()), + mDataOffset(0), + mUseParserDuration(false), + mLastParserDuration(-1), +#if GST_VERSION_MAJOR >= 1 + mAllocator(nullptr), + mBufferPool(nullptr), +#endif + mPlayBin(nullptr), + mBus(nullptr), + mSource(nullptr), + mVideoSink(nullptr), + mVideoAppSink(nullptr), + mAudioSink(nullptr), + mAudioAppSink(nullptr), + mFormat(GST_VIDEO_FORMAT_UNKNOWN), + mVideoSinkBufferCount(0), + mAudioSinkBufferCount(0), + mGstThreadsMonitor("media.gst.threads"), + mReachedAudioEos(false), + mReachedVideoEos(false), +#if GST_VERSION_MAJOR >= 1 + mConfigureAlignment(true), +#endif + fpsNum(0), + fpsDen(0), + mResource(aDecoder->GetResource()) +{ + MOZ_COUNT_CTOR(GStreamerReader); + + mSrcCallbacks.need_data = GStreamerReader::NeedDataCb; + mSrcCallbacks.enough_data = GStreamerReader::EnoughDataCb; + mSrcCallbacks.seek_data = GStreamerReader::SeekDataCb; + + mSinkCallbacks.eos = GStreamerReader::EosCb; + mSinkCallbacks.new_preroll = GStreamerReader::NewPrerollCb; +#if GST_VERSION_MAJOR >= 1 + mSinkCallbacks.new_sample = GStreamerReader::NewBufferCb; +#else + mSinkCallbacks.new_buffer = GStreamerReader::NewBufferCb; + mSinkCallbacks.new_buffer_list = nullptr; +#endif + + gst_segment_init(&mVideoSegment, GST_FORMAT_UNDEFINED); + gst_segment_init(&mAudioSegment, GST_FORMAT_UNDEFINED); +} + +GStreamerReader::~GStreamerReader() +{ + MOZ_COUNT_DTOR(GStreamerReader); + NS_ASSERTION(!mPlayBin, "No Shutdown() after Init()"); +} + +nsresult GStreamerReader::Init(MediaDecoderReader* aCloneDonor) +{ + GStreamerFormatHelper::Instance(); + +#if GST_VERSION_MAJOR >= 1 + mAllocator = static_cast(g_object_new(GST_TYPE_MOZ_GFX_MEMORY_ALLOCATOR, nullptr)); + moz_gfx_memory_allocator_set_reader(mAllocator, this); + + mBufferPool = static_cast(g_object_new(GST_TYPE_MOZ_GFX_BUFFER_POOL, nullptr)); +#endif + +#if GST_VERSION_MAJOR >= 1 + mPlayBin = gst_element_factory_make("playbin", nullptr); +#else + mPlayBin = gst_element_factory_make("playbin2", nullptr); +#endif + if (!mPlayBin) { + LOG(LogLevel::Error, "couldn't create playbin"); + return NS_ERROR_FAILURE; + } + g_object_set(mPlayBin, "buffer-size", 0, nullptr); + mBus = gst_pipeline_get_bus(GST_PIPELINE(mPlayBin)); + + mVideoSink = gst_parse_bin_from_description("capsfilter name=filter ! " + "appsink name=videosink sync=false max-buffers=1 " +#if GST_VERSION_MAJOR >= 1 + "caps=video/x-raw,format=I420" +#else + "caps=video/x-raw-yuv,format=(fourcc)I420" +#endif + , TRUE, nullptr); + mVideoAppSink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(mVideoSink), + "videosink")); + mAudioSink = gst_parse_bin_from_description("capsfilter name=filter ! " + "appsink name=audiosink sync=false max-buffers=1", TRUE, nullptr); + mAudioAppSink = GST_APP_SINK(gst_bin_get_by_name(GST_BIN(mAudioSink), + "audiosink")); + GstCaps* caps = BuildAudioSinkCaps(); + g_object_set(mAudioAppSink, "caps", caps, nullptr); + gst_caps_unref(caps); + + gst_app_sink_set_callbacks(mVideoAppSink, &mSinkCallbacks, + (gpointer) this, nullptr); + gst_app_sink_set_callbacks(mAudioAppSink, &mSinkCallbacks, + (gpointer) this, nullptr); + InstallPadCallbacks(); + + g_object_set(mPlayBin, "uri", "appsrc://", + "video-sink", mVideoSink, + "audio-sink", mAudioSink, + nullptr); + + g_signal_connect(G_OBJECT(mPlayBin), "notify::source", + G_CALLBACK(GStreamerReader::PlayBinSourceSetupCb), this); + g_signal_connect(G_OBJECT(mPlayBin), "element-added", + G_CALLBACK(GStreamerReader::PlayElementAddedCb), this); + + g_signal_connect(G_OBJECT(mPlayBin), "element-added", + G_CALLBACK(GStreamerReader::ElementAddedCb), this); + + return NS_OK; +} + +nsRefPtr +GStreamerReader::Shutdown() +{ + ResetDecode(); + + if (mPlayBin) { + gst_app_src_end_of_stream(mSource); + if (mSource) + gst_object_unref(mSource); + gst_element_set_state(mPlayBin, GST_STATE_NULL); + gst_object_unref(mPlayBin); + mPlayBin = nullptr; + mVideoSink = nullptr; + mVideoAppSink = nullptr; + mAudioSink = nullptr; + mAudioAppSink = nullptr; + gst_object_unref(mBus); + mBus = nullptr; +#if GST_VERSION_MAJOR >= 1 + g_object_unref(mAllocator); + g_object_unref(mBufferPool); +#endif + } + + return MediaDecoderReader::Shutdown(); +} + +GstBusSyncReply +GStreamerReader::ErrorCb(GstBus *aBus, GstMessage *aMessage, gpointer aUserData) +{ + return static_cast(aUserData)->Error(aBus, aMessage); +} + +GstBusSyncReply +GStreamerReader::Error(GstBus *aBus, GstMessage *aMessage) +{ + if (GST_MESSAGE_TYPE(aMessage) == GST_MESSAGE_ERROR) { + Eos(); + } + + return GST_BUS_PASS; +} + +void GStreamerReader::ElementAddedCb(GstBin *aPlayBin, + GstElement *aElement, + gpointer aUserData) +{ + GstElementFactory *factory = gst_element_get_factory(aElement); + + if (!factory) + return; + + const gchar *name = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + + if (name && !strcmp(name, "uridecodebin")) { + g_signal_connect(G_OBJECT(aElement), "autoplug-sort", + G_CALLBACK(GStreamerReader::ElementFilterCb), aUserData); + } +} + +GValueArray *GStreamerReader::ElementFilterCb(GstURIDecodeBin *aBin, + GstPad *aPad, + GstCaps *aCaps, + GValueArray *aFactories, + gpointer aUserData) +{ + return ((GStreamerReader*)aUserData)->ElementFilter(aBin, aPad, aCaps, aFactories); +} + +GValueArray *GStreamerReader::ElementFilter(GstURIDecodeBin *aBin, + GstPad *aPad, + GstCaps *aCaps, + GValueArray *aFactories) +{ + GValueArray *filtered = g_value_array_new(aFactories->n_values); + + for (unsigned int i = 0; i < aFactories->n_values; i++) { + GValue *value = &aFactories->values[i]; + GstPluginFeature *factory = GST_PLUGIN_FEATURE(g_value_peek_pointer(value)); + + if (!GStreamerFormatHelper::IsPluginFeatureBlacklisted(factory)) { + g_value_array_append(filtered, value); + } + } + + return filtered; +} + +void GStreamerReader::PlayBinSourceSetupCb(GstElement* aPlayBin, + GParamSpec* pspec, + gpointer aUserData) +{ + GstElement *source; + GStreamerReader* reader = reinterpret_cast(aUserData); + + g_object_get(aPlayBin, "source", &source, nullptr); + reader->PlayBinSourceSetup(GST_APP_SRC(source)); +} + +void GStreamerReader::PlayBinSourceSetup(GstAppSrc* aSource) +{ + mSource = GST_APP_SRC(aSource); + gst_app_src_set_callbacks(mSource, &mSrcCallbacks, (gpointer) this, nullptr); + + /* do a short read to trigger a network request so that GetLength() below + * returns something meaningful and not -1 + */ + char buf[512]; + unsigned int size = 0; + mResource.Read(buf, sizeof(buf), &size); + mResource.Seek(SEEK_SET, 0); + + /* now we should have a length */ + int64_t resourceLength = GetDataLength(); + gst_app_src_set_size(mSource, resourceLength); + if (mResource.GetResource()->IsDataCachedToEndOfResource(0) || + (resourceLength != -1 && resourceLength <= SHORT_FILE_SIZE)) { + /* let the demuxer work in pull mode for local files (or very short files) + * so that we get optimal seeking accuracy/performance + */ + LOG(LogLevel::Debug, "configuring random access, len %lld", resourceLength); + gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_RANDOM_ACCESS); + } else { + /* make the demuxer work in push mode so that seeking is kept to a minimum + */ + LOG(LogLevel::Debug, "configuring push mode, len %lld", resourceLength); + gst_app_src_set_stream_type(mSource, GST_APP_STREAM_TYPE_SEEKABLE); + } + + // Set the source MIME type to stop typefind trying every. single. format. + GstCaps *caps = + GStreamerFormatHelper::ConvertFormatsToCaps(mDecoder->GetResource()->GetContentType().get(), + nullptr); + + gst_app_src_set_caps(aSource, caps); + gst_caps_unref(caps); +} + +/** + * If this stream is an MP3, we want to parse the headers to estimate the + * stream duration. + */ +nsresult GStreamerReader::ParseMP3Headers() +{ + const uint32_t MAX_READ_BYTES = 4096; + + uint64_t offset = 0; + char bytes[MAX_READ_BYTES]; + uint32_t bytesRead; + do { + nsresult rv = mResource.ReadAt(offset, bytes, MAX_READ_BYTES, &bytesRead); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(bytesRead, NS_ERROR_FAILURE); + + mMP3FrameParser.Parse(reinterpret_cast(bytes), bytesRead, offset); + offset += bytesRead; + } while (!mMP3FrameParser.ParsedHeaders()); + + if (mMP3FrameParser.IsMP3()) { + mLastParserDuration = mMP3FrameParser.GetDuration(); + mDataOffset = mMP3FrameParser.GetMP3Offset(); + + // Update GStreamer's stream length in case we found any ID3 headers to + // ignore. + gst_app_src_set_size(mSource, GetDataLength()); + } + + return NS_OK; +} + +int64_t +GStreamerReader::GetDataLength() +{ + int64_t streamLen = mResource.GetLength(); + + if (streamLen < 0) { + return streamLen; + } + + return streamLen - mDataOffset; +} + +nsresult GStreamerReader::ReadMetadata(MediaInfo* aInfo, + MetadataTags** aTags) +{ + MOZ_ASSERT(OnTaskQueue()); + nsresult ret = NS_OK; + + /* + * Parse MP3 headers before we kick off the GStreamer pipeline otherwise there + * might be concurrent stream operations happening on both decoding and gstreamer + * threads which will screw the GStreamer state machine. + */ + LOG(LogLevel::Debug, "content-type: %s %s", + mDecoder->GetResource()->GetContentType().get(), + mDecoder->GetResource()->GetContentURL().get()); + bool isMP3 = mDecoder->GetResource()->GetContentType().EqualsASCII(AUDIO_MP3); + if (isMP3) { + ParseMP3Headers(); + } + + + /* We do 3 attempts here: decoding audio and video, decoding video only, + * decoding audio only. This allows us to play streams that have one broken + * stream but that are otherwise decodeable. + */ + guint flags[3] = {GST_PLAY_FLAG_VIDEO|GST_PLAY_FLAG_AUDIO, + static_cast(~GST_PLAY_FLAG_AUDIO), static_cast(~GST_PLAY_FLAG_VIDEO)}; + guint default_flags, current_flags; + g_object_get(mPlayBin, "flags", &default_flags, nullptr); + + GstMessage* message = nullptr; + for (unsigned int i = 0; i < G_N_ELEMENTS(flags); i++) { + current_flags = default_flags & flags[i]; + g_object_set(G_OBJECT(mPlayBin), "flags", current_flags, nullptr); + + /* reset filter caps to ANY */ + GstCaps* caps = gst_caps_new_any(); + GstElement* filter = gst_bin_get_by_name(GST_BIN(mAudioSink), "filter"); + g_object_set(filter, "caps", caps, nullptr); + gst_object_unref(filter); + + filter = gst_bin_get_by_name(GST_BIN(mVideoSink), "filter"); + g_object_set(filter, "caps", caps, nullptr); + gst_object_unref(filter); + gst_caps_unref(caps); + filter = nullptr; + + if (!(current_flags & GST_PLAY_FLAG_AUDIO)) + filter = gst_bin_get_by_name(GST_BIN(mAudioSink), "filter"); + else if (!(current_flags & GST_PLAY_FLAG_VIDEO)) + filter = gst_bin_get_by_name(GST_BIN(mVideoSink), "filter"); + + if (filter) { + /* Little trick: set the target caps to "skip" so that playbin2 fails to + * find a decoder for the stream we want to skip. + */ + GstCaps* filterCaps = gst_caps_new_simple ("skip", nullptr, nullptr); + g_object_set(filter, "caps", filterCaps, nullptr); + gst_caps_unref(filterCaps); + gst_object_unref(filter); + } + + LOG(LogLevel::Debug, "starting metadata pipeline"); + if (gst_element_set_state(mPlayBin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { + LOG(LogLevel::Debug, "metadata pipeline state change failed"); + ret = NS_ERROR_FAILURE; + continue; + } + + /* Wait for ASYNC_DONE, which is emitted when the pipeline is built, + * prerolled and ready to play. Also watch for errors. + */ + message = gst_bus_timed_pop_filtered(mBus, GST_CLOCK_TIME_NONE, + (GstMessageType)(GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR | GST_MESSAGE_EOS)); + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ASYNC_DONE) { + LOG(LogLevel::Debug, "read metadata pipeline prerolled"); + gst_message_unref(message); + ret = NS_OK; + break; + } else { + LOG(LogLevel::Debug, "read metadata pipeline failed to preroll: %s", + gst_message_type_get_name (GST_MESSAGE_TYPE (message))); + + if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) { + GError* error; + gchar* debug; + gst_message_parse_error(message, &error, &debug); + LOG(LogLevel::Error, "read metadata error: %s: %s", error->message, debug); + g_error_free(error); + g_free(debug); + } + /* Unexpected stream close/EOS or other error. We'll give up if all + * streams are in error/eos. */ + gst_element_set_state(mPlayBin, GST_STATE_NULL); + gst_message_unref(message); + ret = NS_ERROR_FAILURE; + } + } + + if (NS_SUCCEEDED(ret)) + ret = CheckSupportedFormats(); + + if (NS_FAILED(ret)) + /* we couldn't get this to play */ + return ret; + + /* report the duration */ + gint64 duration; + + if (isMP3 && mMP3FrameParser.IsMP3()) { + // The MP3FrameParser has reported a duration; use that over the gstreamer + // reported duration for inter-platform consistency. + mUseParserDuration = true; + mLastParserDuration = mMP3FrameParser.GetDuration(); + mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(mLastParserDuration)); + } else { + LOG(LogLevel::Debug, "querying duration"); + // Otherwise use the gstreamer duration. +#if GST_VERSION_MAJOR >= 1 + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + GST_FORMAT_TIME, &duration)) { +#else + GstFormat format = GST_FORMAT_TIME; + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + &format, &duration) && format == GST_FORMAT_TIME) { +#endif + LOG(LogLevel::Debug, "have duration %" GST_TIME_FORMAT, GST_TIME_ARGS(duration)); + duration = GST_TIME_AS_USECONDS (duration); + mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(duration)); + } + } + + int n_video = 0, n_audio = 0; + g_object_get(mPlayBin, "n-video", &n_video, "n-audio", &n_audio, nullptr); + + if (!n_video) { + mInfo.mVideo = VideoInfo(); + } + if (!n_audio) { + mInfo.mAudio = AudioInfo(); + } + *aInfo = mInfo; + + *aTags = nullptr; + + // Watch the pipeline for fatal errors +#if GST_VERSION_MAJOR >= 1 + gst_bus_set_sync_handler(mBus, GStreamerReader::ErrorCb, this, nullptr); +#else + gst_bus_set_sync_handler(mBus, GStreamerReader::ErrorCb, this); +#endif + + /* set the pipeline to PLAYING so that it starts decoding and queueing data in + * the appsinks */ + gst_element_set_state(mPlayBin, GST_STATE_PLAYING); + + return NS_OK; +} + +bool +GStreamerReader::IsMediaSeekable() +{ + if (mUseParserDuration) { + return true; + } + + gint64 duration; +#if GST_VERSION_MAJOR >= 1 + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), GST_FORMAT_TIME, + &duration)) { +#else + GstFormat format = GST_FORMAT_TIME; + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), &format, &duration) && + format == GST_FORMAT_TIME) { +#endif + return true; + } + + return false; +} + +nsresult GStreamerReader::CheckSupportedFormats() +{ + bool done = false; + bool unsupported = false; + + GstIterator* it = gst_bin_iterate_recurse(GST_BIN(mPlayBin)); + while (!done) { + GstIteratorResult res; + GstElement* element; + +#if GST_VERSION_MAJOR >= 1 + GValue value = {0,}; + res = gst_iterator_next(it, &value); +#else + res = gst_iterator_next(it, (void **) &element); +#endif + switch(res) { + case GST_ITERATOR_OK: + { +#if GST_VERSION_MAJOR >= 1 + element = GST_ELEMENT (g_value_get_object (&value)); +#endif + GstElementFactory* factory = gst_element_get_factory(element); + if (factory) { + const char* klass = gst_element_factory_get_klass(factory); + GstPad* pad = gst_element_get_static_pad(element, "sink"); + if (pad) { + GstCaps* caps; + +#if GST_VERSION_MAJOR >= 1 + caps = gst_pad_get_current_caps(pad); +#else + caps = gst_pad_get_negotiated_caps(pad); +#endif + + if (caps) { + /* check for demuxers but ignore elements like id3demux */ + if (strstr (klass, "Demuxer") && !strstr(klass, "Metadata")) + unsupported = !GStreamerFormatHelper::Instance()->CanHandleContainerCaps(caps); + else if (strstr (klass, "Decoder") && !strstr(klass, "Generic")) + unsupported = !GStreamerFormatHelper::Instance()->CanHandleCodecCaps(caps); + + gst_caps_unref(caps); + } + gst_object_unref(pad); + } + } + +#if GST_VERSION_MAJOR >= 1 + g_value_unset (&value); +#else + gst_object_unref(element); +#endif + done = unsupported; + break; + } + case GST_ITERATOR_RESYNC: + unsupported = false; + break; + case GST_ITERATOR_ERROR: + done = true; + break; + case GST_ITERATOR_DONE: + done = true; + break; + } + } + + gst_iterator_free(it); + + return unsupported ? NS_ERROR_FAILURE : NS_OK; +} + +nsresult GStreamerReader::ResetDecode() +{ + nsresult res = NS_OK; + + LOG(LogLevel::Debug, "reset decode"); + + if (NS_FAILED(MediaDecoderReader::ResetDecode())) { + res = NS_ERROR_FAILURE; + } + + mVideoQueue.Reset(); + mAudioQueue.Reset(); + + mVideoSinkBufferCount = 0; + mAudioSinkBufferCount = 0; + mReachedAudioEos = false; + mReachedVideoEos = false; +#if GST_VERSION_MAJOR >= 1 + mConfigureAlignment = true; +#endif + + LOG(LogLevel::Debug, "reset decode done"); + + return res; +} + +bool GStreamerReader::DecodeAudioData() +{ + MOZ_ASSERT(OnTaskQueue()); + + GstBuffer *buffer = nullptr; + + { + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + + if (mReachedAudioEos && !mAudioSinkBufferCount) { + return false; + } + + /* Wait something to be decoded before return or continue */ + if (!mAudioSinkBufferCount) { + if(!mVideoSinkBufferCount) { + /* We have nothing decoded so it makes no sense to return to the state machine + * as it will call us back immediately, we'll return again and so on, wasting + * CPU cycles for no job done. So, block here until there is either video or + * audio data available + */ + mon.Wait(); + if (!mAudioSinkBufferCount) { + /* There is still no audio data available, so either there is video data or + * something else has happened (Eos, etc...). Return to the state machine + * to process it. + */ + return true; + } + } + else { + return true; + } + } + +#if GST_VERSION_MAJOR >= 1 + GstSample *sample = gst_app_sink_pull_sample(mAudioAppSink); + buffer = gst_buffer_ref(gst_sample_get_buffer(sample)); + gst_sample_unref(sample); +#else + buffer = gst_app_sink_pull_buffer(mAudioAppSink); +#endif + + mAudioSinkBufferCount--; + } + + int64_t timestamp = GST_BUFFER_TIMESTAMP(buffer); + { + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + timestamp = gst_segment_to_stream_time(&mAudioSegment, + GST_FORMAT_TIME, timestamp); + } + timestamp = GST_TIME_AS_USECONDS(timestamp); + + int64_t offset = GST_BUFFER_OFFSET(buffer); + guint8* data; +#if GST_VERSION_MAJOR >= 1 + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_READ); + unsigned int size = info.size; + data = info.data; +#else + unsigned int size = GST_BUFFER_SIZE(buffer); + data = GST_BUFFER_DATA(buffer); +#endif + int32_t frames = (size / sizeof(AudioDataValue)) / mInfo.mAudio.mChannels; + + typedef AudioCompactor::NativeCopy GstCopy; + mAudioCompactor.Push(offset, + timestamp, + mInfo.mAudio.mRate, + frames, + mInfo.mAudio.mChannels, + GstCopy(data, + size, + mInfo.mAudio.mChannels)); +#if GST_VERSION_MAJOR >= 1 + gst_buffer_unmap(buffer, &info); +#endif + + gst_buffer_unref(buffer); + + return true; +} + +bool GStreamerReader::DecodeVideoFrame(bool &aKeyFrameSkip, + int64_t aTimeThreshold) +{ + MOZ_ASSERT(OnTaskQueue()); + + GstBuffer *buffer = nullptr; + + { + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + + if (mReachedVideoEos && !mVideoSinkBufferCount) { + return false; + } + + /* Wait something to be decoded before return or continue */ + if (!mVideoSinkBufferCount) { + if (!mAudioSinkBufferCount) { + /* We have nothing decoded so it makes no sense to return to the state machine + * as it will call us back immediately, we'll return again and so on, wasting + * CPU cycles for no job done. So, block here until there is either video or + * audio data available + */ + mon.Wait(); + if (!mVideoSinkBufferCount) { + /* There is still no video data available, so either there is audio data or + * something else has happened (Eos, etc...). Return to the state machine + * to process it + */ + return true; + } + } + else { + return true; + } + } + + mDecoder->NotifyDecodedFrames(0, 1, 0); + +#if GST_VERSION_MAJOR >= 1 + GstSample *sample = gst_app_sink_pull_sample(mVideoAppSink); + buffer = gst_buffer_ref(gst_sample_get_buffer(sample)); + gst_sample_unref(sample); +#else + buffer = gst_app_sink_pull_buffer(mVideoAppSink); +#endif + mVideoSinkBufferCount--; + } + + bool isKeyframe = !GST_BUFFER_FLAG_IS_SET(buffer, GST_BUFFER_FLAG_DELTA_UNIT); + if ((aKeyFrameSkip && !isKeyframe)) { + mDecoder->NotifyDecodedFrames(0, 0, 1); + gst_buffer_unref(buffer); + return true; + } + + int64_t timestamp = GST_BUFFER_TIMESTAMP(buffer); + { + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + timestamp = gst_segment_to_stream_time(&mVideoSegment, + GST_FORMAT_TIME, timestamp); + } + NS_ASSERTION(GST_CLOCK_TIME_IS_VALID(timestamp), + "frame has invalid timestamp"); + + timestamp = GST_TIME_AS_USECONDS(timestamp); + int64_t duration = 0; + if (GST_CLOCK_TIME_IS_VALID(GST_BUFFER_DURATION(buffer))) + duration = GST_TIME_AS_USECONDS(GST_BUFFER_DURATION(buffer)); + else if (fpsNum && fpsDen) + /* add 1-frame duration */ + duration = gst_util_uint64_scale(GST_USECOND, fpsDen, fpsNum); + + if (timestamp < aTimeThreshold) { + LOG(LogLevel::Debug, "skipping frame %" GST_TIME_FORMAT + " threshold %" GST_TIME_FORMAT, + GST_TIME_ARGS(timestamp * 1000), + GST_TIME_ARGS(aTimeThreshold * 1000)); + gst_buffer_unref(buffer); + return true; + } + + if (!buffer) + /* no more frames */ + return true; + +#if GST_VERSION_MAJOR >= 1 + if (mConfigureAlignment && buffer->pool) { + GstStructure *config = gst_buffer_pool_get_config(buffer->pool); + GstVideoAlignment align; + if (gst_buffer_pool_config_get_video_alignment(config, &align)) + gst_video_info_align(&mVideoInfo, &align); + gst_structure_free(config); + mConfigureAlignment = false; + } +#endif + + nsRefPtr image = GetImageFromBuffer(buffer); + if (!image) { + /* Ugh, upstream is not calling gst_pad_alloc_buffer(). Fallback to + * allocating a PlanarYCbCrImage backed GstBuffer here and memcpy. + */ + GstBuffer* tmp = nullptr; + CopyIntoImageBuffer(buffer, &tmp, image); + gst_buffer_unref(buffer); + buffer = tmp; + } + + int64_t offset = mResource.Tell(); // Estimate location in media. + nsRefPtr video = VideoData::CreateFromImage(mInfo.mVideo, + mDecoder->GetImageContainer(), + offset, timestamp, duration, + static_cast(image.get()), + isKeyframe, -1, mPicture); + mVideoQueue.Push(video); + + gst_buffer_unref(buffer); + + return true; +} + +nsRefPtr +GStreamerReader::Seek(int64_t aTarget, int64_t aEndTime) +{ + MOZ_ASSERT(OnTaskQueue()); + + gint64 seekPos = aTarget * GST_USECOND; + LOG(LogLevel::Debug, "%p About to seek to %" GST_TIME_FORMAT, + mDecoder, GST_TIME_ARGS(seekPos)); + + int flags = GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT; + if (!gst_element_seek_simple(mPlayBin, + GST_FORMAT_TIME, + static_cast(flags), + seekPos)) { + LOG(LogLevel::Error, "seek failed"); + return SeekPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + LOG(LogLevel::Debug, "seek succeeded"); + GstMessage* message = gst_bus_timed_pop_filtered(mBus, GST_CLOCK_TIME_NONE, + (GstMessageType)(GST_MESSAGE_ASYNC_DONE | GST_MESSAGE_ERROR)); + gst_message_unref(message); + LOG(LogLevel::Debug, "seek completed"); + + return SeekPromise::CreateAndResolve(aTarget, __func__); +} + +media::TimeIntervals GStreamerReader::GetBuffered() +{ + MOZ_ASSERT(OnTaskQueue()); + if (!HaveStartTime()) { + return media::TimeIntervals(); + } + media::TimeIntervals buffered; + if (!mInfo.HasValidMedia()) { + return buffered; + } + +#if GST_VERSION_MAJOR == 0 + GstFormat format = GST_FORMAT_TIME; +#endif + AutoPinned resource(mDecoder->GetResource()); + nsTArray ranges; + resource->GetCachedRanges(ranges); + + if (resource->IsDataCachedToEndOfResource(0)) { + /* fast path for local or completely cached files */ + gint64 duration = + mDuration.Ref().refOr(media::TimeUnit::FromMicroseconds(0)).ToMicroseconds(); + LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]", + (double) duration / GST_MSECOND, GetDataLength()); + buffered += + media::TimeInterval(media::TimeUnit::FromMicroseconds(0), + media::TimeUnit::FromMicroseconds(duration)); + return buffered; + } + + for(uint32_t index = 0; index < ranges.Length(); index++) { + int64_t startOffset = ranges[index].mStart; + int64_t endOffset = ranges[index].mEnd; + gint64 startTime, endTime, duration; + bool haveDuration = false; + +#if GST_VERSION_MAJOR >= 1 + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + startOffset, GST_FORMAT_TIME, &startTime)) + continue; + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + endOffset, GST_FORMAT_TIME, &endTime)) + continue; + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + GST_FORMAT_TIME, &duration)) { + haveDuration = true; + } +#else + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + startOffset, &format, &startTime) || format != GST_FORMAT_TIME) + continue; + if (!gst_element_query_convert(GST_ELEMENT(mPlayBin), GST_FORMAT_BYTES, + endOffset, &format, &endTime) || format != GST_FORMAT_TIME) + continue; + if (gst_element_query_duration(GST_ELEMENT(mPlayBin), + &format, &duration) && format == GST_FORMAT_TIME) { + haveDuration = true; + } +#endif + // Check that the estimated time doesn't go beyond known duration + // as this indicates a buggy gst plugin. + if (haveDuration && endTime > duration) { + LOG(LogLevel::Debug, "Have duration %" GST_TIME_FORMAT "contradicting endTime %" GST_TIME_FORMAT, + GST_TIME_ARGS(duration), GST_TIME_ARGS(endTime)); + endTime = std::min(endTime, duration); + } + + LOG(LogLevel::Debug, "adding range [%f, %f] for [%li %li] size %li", + (double) GST_TIME_AS_USECONDS (startTime) / GST_MSECOND, + (double) GST_TIME_AS_USECONDS (endTime) / GST_MSECOND, + startOffset, endOffset, GetDataLength()); + buffered += + media::TimeInterval(media::TimeUnit::FromMicroseconds(GST_TIME_AS_USECONDS(startTime)), + media::TimeUnit::FromMicroseconds(GST_TIME_AS_USECONDS(endTime))); + } + + return buffered; +} + +void GStreamerReader::ReadAndPushData(guint aLength) +{ + int64_t offset1 = mResource.Tell(); + unused << offset1; + nsresult rv = NS_OK; + + GstBuffer* buffer = gst_buffer_new_and_alloc(aLength); +#if GST_VERSION_MAJOR >= 1 + GstMapInfo info; + gst_buffer_map(buffer, &info, GST_MAP_WRITE); + guint8 *data = info.data; +#else + guint8* data = GST_BUFFER_DATA(buffer); +#endif + uint32_t size = 0, bytesRead = 0; + while(bytesRead < aLength) { + rv = mResource.Read(reinterpret_cast(data + bytesRead), + aLength - bytesRead, &size); + if (NS_FAILED(rv) || size == 0) + break; + + bytesRead += size; + } + + int64_t offset2 = mResource.Tell(); + unused << offset2; + +#if GST_VERSION_MAJOR >= 1 + gst_buffer_unmap(buffer, &info); + gst_buffer_set_size(buffer, bytesRead); +#else + GST_BUFFER_SIZE(buffer) = bytesRead; +#endif + + GstFlowReturn ret = gst_app_src_push_buffer(mSource, gst_buffer_ref(buffer)); + if (ret != GST_FLOW_OK) { + LOG(LogLevel::Error, "ReadAndPushData push ret %s(%d)", gst_flow_get_name(ret), ret); + } + + if (NS_FAILED(rv)) { + /* Terminate the stream if there is an error in reading */ + LOG(LogLevel::Error, "ReadAndPushData read error, rv=%x", rv); + gst_app_src_end_of_stream(mSource); + } else if (bytesRead < aLength) { + /* If we read less than what we wanted, we reached the end */ + LOG(LogLevel::Warning, "ReadAndPushData read underflow, " + "bytesRead=%u, aLength=%u, offset(%lld,%lld)", + bytesRead, aLength, offset1, offset2); + gst_app_src_end_of_stream(mSource); + } + + gst_buffer_unref(buffer); + + /* Ensure offset change is consistent in this function. + * If there are other stream operations on another thread at the same time, + * it will disturb the GStreamer state machine. + */ + MOZ_ASSERT(offset1 + bytesRead == offset2); +} + +void GStreamerReader::NeedDataCb(GstAppSrc* aSrc, + guint aLength, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + reader->NeedData(aSrc, aLength); +} + +void GStreamerReader::NeedData(GstAppSrc* aSrc, guint aLength) +{ + if (aLength == static_cast(-1)) + aLength = DEFAULT_SOURCE_READ_SIZE; + ReadAndPushData(aLength); +} + +void GStreamerReader::EnoughDataCb(GstAppSrc* aSrc, gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + reader->EnoughData(aSrc); +} + +void GStreamerReader::EnoughData(GstAppSrc* aSrc) +{ +} + +gboolean GStreamerReader::SeekDataCb(GstAppSrc* aSrc, + guint64 aOffset, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + return reader->SeekData(aSrc, aOffset); +} + +gboolean GStreamerReader::SeekData(GstAppSrc* aSrc, guint64 aOffset) +{ + aOffset += mDataOffset; + + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + int64_t resourceLength = mResource.GetLength(); + + if (gst_app_src_get_size(mSource) == -1) { + /* It's possible that we didn't know the length when we initialized mSource + * but maybe we do now + */ + gst_app_src_set_size(mSource, GetDataLength()); + } + + nsresult rv = NS_ERROR_FAILURE; + if (aOffset < static_cast(resourceLength)) { + rv = mResource.Seek(SEEK_SET, aOffset); + } + + return NS_SUCCEEDED(rv); +} + +GstFlowReturn GStreamerReader::NewPrerollCb(GstAppSink* aSink, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + + if (aSink == reader->mVideoAppSink) + reader->VideoPreroll(); + else + reader->AudioPreroll(); + return GST_FLOW_OK; +} + +void GStreamerReader::AudioPreroll() +{ + /* The first audio buffer has reached the audio sink. Get rate and channels */ + LOG(LogLevel::Debug, "Audio preroll"); + GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mAudioAppSink), "sink"); +#if GST_VERSION_MAJOR >= 1 + GstCaps *caps = gst_pad_get_current_caps(sinkpad); +#else + GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad); +#endif + GstStructure* s = gst_caps_get_structure(caps, 0); + mInfo.mAudio.mRate = mInfo.mAudio.mChannels = 0; + gst_structure_get_int(s, "rate", (gint*) &mInfo.mAudio.mRate); + gst_structure_get_int(s, "channels", (gint*) &mInfo.mAudio.mChannels); + NS_ASSERTION(mInfo.mAudio.mRate != 0, ("audio rate is zero")); + NS_ASSERTION(mInfo.mAudio.mChannels != 0, ("audio channels is zero")); + NS_ASSERTION(mInfo.mAudio.mChannels > 0 && mInfo.mAudio.mChannels <= MAX_CHANNELS, + "invalid audio channels number"); + gst_caps_unref(caps); + gst_object_unref(sinkpad); +} + +void GStreamerReader::VideoPreroll() +{ + /* The first video buffer has reached the video sink. Get width and height */ + LOG(LogLevel::Debug, "Video preroll"); + GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); + int PARNumerator, PARDenominator; +#if GST_VERSION_MAJOR >= 1 + GstCaps* caps = gst_pad_get_current_caps(sinkpad); + memset (&mVideoInfo, 0, sizeof (mVideoInfo)); + gst_video_info_from_caps(&mVideoInfo, caps); + mFormat = mVideoInfo.finfo->format; + mPicture.width = mVideoInfo.width; + mPicture.height = mVideoInfo.height; + PARNumerator = GST_VIDEO_INFO_PAR_N(&mVideoInfo); + PARDenominator = GST_VIDEO_INFO_PAR_D(&mVideoInfo); +#else + GstCaps* caps = gst_pad_get_negotiated_caps(sinkpad); + gst_video_format_parse_caps(caps, &mFormat, &mPicture.width, &mPicture.height); + if (!gst_video_parse_caps_pixel_aspect_ratio(caps, &PARNumerator, &PARDenominator)) { + PARNumerator = 1; + PARDenominator = 1; + } +#endif + NS_ASSERTION(mPicture.width && mPicture.height, "invalid video resolution"); + + // Calculate display size according to pixel aspect ratio. + nsIntRect pictureRect(0, 0, mPicture.width, mPicture.height); + nsIntSize frameSize = nsIntSize(mPicture.width, mPicture.height); + nsIntSize displaySize = nsIntSize(mPicture.width, mPicture.height); + ScaleDisplayByAspectRatio(displaySize, float(PARNumerator) / float(PARDenominator)); + + // If video frame size is overflow, stop playing. + if (IsValidVideoRegion(frameSize, pictureRect, displaySize)) { + GstStructure* structure = gst_caps_get_structure(caps, 0); + gst_structure_get_fraction(structure, "framerate", &fpsNum, &fpsDen); + mInfo.mVideo.mDisplay = displaySize; + } else { + LOG(LogLevel::Debug, "invalid video region"); + Eos(); + } + gst_caps_unref(caps); + gst_object_unref(sinkpad); +} + +GstFlowReturn GStreamerReader::NewBufferCb(GstAppSink* aSink, + gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + + if (aSink == reader->mVideoAppSink) + reader->NewVideoBuffer(); + else + reader->NewAudioBuffer(); + + return GST_FLOW_OK; +} + +void GStreamerReader::NewVideoBuffer() +{ + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + /* We have a new video buffer queued in the video sink. Increment the counter + * and notify the decode thread potentially blocked in DecodeVideoFrame + */ + + mDecoder->NotifyDecodedFrames(1, 0, 0); + mVideoSinkBufferCount++; + mon.NotifyAll(); +} + +void GStreamerReader::NewAudioBuffer() +{ + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + /* We have a new audio buffer queued in the audio sink. Increment the counter + * and notify the decode thread potentially blocked in DecodeAudioData + */ + mAudioSinkBufferCount++; + mon.NotifyAll(); +} + +void GStreamerReader::EosCb(GstAppSink* aSink, gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(aUserData); + reader->Eos(aSink); +} + +void GStreamerReader::Eos(GstAppSink* aSink) +{ + /* We reached the end of the stream */ + { + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); + /* Potentially unblock DecodeVideoFrame and DecodeAudioData */ + if (aSink == mVideoAppSink) { + mReachedVideoEos = true; + } else if (aSink == mAudioAppSink) { + mReachedAudioEos = true; + } else { + // Assume this is an error causing an EOS. + mReachedAudioEos = true; + mReachedVideoEos = true; + } + mon.NotifyAll(); + } +} + +/** + * This callback is called while the pipeline is automatically built, after a + * new element has been added to the pipeline. We use it to find the + * uridecodebin instance used by playbin and connect to it to apply our + * blacklist. + */ +void +GStreamerReader::PlayElementAddedCb(GstBin *aBin, GstElement *aElement, + gpointer *aUserData) +{ + const static char sUriDecodeBinPrefix[] = "uridecodebin"; + gchar *name = gst_element_get_name(aElement); + + // Attach this callback to uridecodebin, child of playbin. + if (!strncmp(name, sUriDecodeBinPrefix, sizeof(sUriDecodeBinPrefix) - 1)) { + g_signal_connect(G_OBJECT(aElement), "autoplug-sort", + G_CALLBACK(GStreamerReader::AutoplugSortCb), aUserData); + } + + g_free(name); +} + +bool +GStreamerReader::ShouldAutoplugFactory(GstElementFactory* aFactory, GstCaps* aCaps) +{ + bool autoplug; + const gchar *klass = gst_element_factory_get_klass(aFactory); + if (strstr(klass, "Demuxer") && !strstr(klass, "Metadata")) { + autoplug = GStreamerFormatHelper::Instance()->CanHandleContainerCaps(aCaps); + } else if (strstr(klass, "Decoder") && !strstr(klass, "Generic")) { + autoplug = GStreamerFormatHelper::Instance()->CanHandleCodecCaps(aCaps); + } else { + /* we only filter demuxers and decoders, let everything else be autoplugged */ + autoplug = true; + } + + return autoplug; +} + +/** + * This is called by uridecodebin (running inside playbin), after it has found + * candidate factories to continue decoding the stream. We apply the blacklist + * here, disallowing known-crashy plugins. + */ +GValueArray* +GStreamerReader::AutoplugSortCb(GstElement* aElement, GstPad* aPad, + GstCaps* aCaps, GValueArray* aFactories) +{ + if (!aFactories->n_values) { + return nullptr; + } + + /* aFactories[0] is the element factory that is going to be used to + * create the next element needed to demux or decode the stream. + */ + GstElementFactory *factory = (GstElementFactory*) g_value_get_object(g_value_array_get_nth(aFactories, 0)); + if (!ShouldAutoplugFactory(factory, aCaps)) { + /* We don't support this factory. Return an empty array to signal that we + * don't want to continue decoding this (sub)stream. + */ + return g_value_array_new(0); + } + + /* nullptr means that we're ok with the candidates and don't need to apply any + * sorting/filtering. + */ + return nullptr; +} + +/** + * If this is an MP3 stream, pass any new data we get to the MP3 frame parser + * for duration estimation. + */ +void GStreamerReader::NotifyDataArrivedInternal(uint32_t aLength, + int64_t aOffset) +{ + MOZ_ASSERT(OnTaskQueue()); + if (HasVideo()) { + return; + } + if (!mMP3FrameParser.NeedsData()) { + return; + } + + IntervalSet intervals = mFilter.NotifyDataArrived(aLength, aOffset); + for (const auto& interval : intervals) { + nsRefPtr bytes = + mResource.MediaReadAt(interval.mStart, interval.Length()); + NS_ENSURE_TRUE_VOID(bytes); + mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart); + if (!mMP3FrameParser.IsMP3()) { + return; + } + + int64_t duration = mMP3FrameParser.GetDuration(); + if (duration != mLastParserDuration && mUseParserDuration) { + MOZ_ASSERT(mDecoder); + mLastParserDuration = duration; + mDecoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration); + } + } +} + +#if GST_VERSION_MAJOR >= 1 +GstCaps* GStreamerReader::BuildAudioSinkCaps() +{ + GstCaps* caps = gst_caps_from_string("audio/x-raw, channels={1,2}"); + const char* format; +#ifdef MOZ_SAMPLE_TYPE_FLOAT32 +#if MOZ_LITTLE_ENDIAN + format = "F32LE"; +#else + format = "F32BE"; +#endif +#else /* !MOZ_SAMPLE_TYPE_FLOAT32 */ +#if MOZ_LITTLE_ENDIAN + format = "S16LE"; +#else + format = "S16BE"; +#endif +#endif + gst_caps_set_simple(caps, "format", G_TYPE_STRING, format, nullptr); + + return caps; +} + +void GStreamerReader::InstallPadCallbacks() +{ + GstPad* sinkpad = gst_element_get_static_pad(GST_ELEMENT(mVideoAppSink), "sink"); + + gst_pad_add_probe(sinkpad, + (GstPadProbeType) (GST_PAD_PROBE_TYPE_SCHEDULING | + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | + GST_PAD_PROBE_TYPE_EVENT_UPSTREAM | + GST_PAD_PROBE_TYPE_EVENT_FLUSH), + &GStreamerReader::EventProbeCb, this, nullptr); + gst_pad_add_probe(sinkpad, GST_PAD_PROBE_TYPE_QUERY_DOWNSTREAM, + GStreamerReader::QueryProbeCb, nullptr, nullptr); + + gst_pad_set_element_private(sinkpad, this); + gst_object_unref(sinkpad); + + sinkpad = gst_element_get_static_pad(GST_ELEMENT(mAudioAppSink), "sink"); + gst_pad_add_probe(sinkpad, + (GstPadProbeType) (GST_PAD_PROBE_TYPE_SCHEDULING | + GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM | + GST_PAD_PROBE_TYPE_EVENT_UPSTREAM | + GST_PAD_PROBE_TYPE_EVENT_FLUSH), + &GStreamerReader::EventProbeCb, this, nullptr); + gst_object_unref(sinkpad); +} + +GstPadProbeReturn GStreamerReader::EventProbeCb(GstPad *aPad, + GstPadProbeInfo *aInfo, + gpointer aUserData) +{ + GStreamerReader *reader = (GStreamerReader *) aUserData; + GstEvent *aEvent = (GstEvent *)aInfo->data; + return reader->EventProbe(aPad, aEvent); +} + +GstPadProbeReturn GStreamerReader::EventProbe(GstPad *aPad, GstEvent *aEvent) +{ + GstElement* parent = GST_ELEMENT(gst_pad_get_parent(aPad)); + + LOG(LogLevel::Debug, "event probe %s", GST_EVENT_TYPE_NAME (aEvent)); + + switch(GST_EVENT_TYPE(aEvent)) { + case GST_EVENT_SEGMENT: + { + const GstSegment *newSegment; + GstSegment* segment; + + /* Store the segments so we can convert timestamps to stream time, which + * is what the upper layers sync on. + */ + ReentrantMonitorAutoEnter mon(mGstThreadsMonitor); +#if GST_VERSION_MINOR <= 1 && GST_VERSION_MICRO < 1 + ResetDecode(); +#endif + gst_event_parse_segment(aEvent, &newSegment); + if (parent == GST_ELEMENT(mVideoAppSink)) + segment = &mVideoSegment; + else + segment = &mAudioSegment; + gst_segment_copy_into (newSegment, segment); + break; + } + case GST_EVENT_FLUSH_STOP: + /* Reset on seeks */ + ResetDecode(); + break; + default: + break; + } + gst_object_unref(parent); + + return GST_PAD_PROBE_OK; +} + +GstPadProbeReturn GStreamerReader::QueryProbeCb(GstPad* aPad, GstPadProbeInfo* aInfo, gpointer aUserData) +{ + GStreamerReader* reader = reinterpret_cast(gst_pad_get_element_private(aPad)); + return reader->QueryProbe(aPad, aInfo, aUserData); +} + +GstPadProbeReturn GStreamerReader::QueryProbe(GstPad* aPad, GstPadProbeInfo* aInfo, gpointer aUserData) +{ + GstQuery *query = gst_pad_probe_info_get_query(aInfo); + GstPadProbeReturn ret = GST_PAD_PROBE_OK; + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_ALLOCATION: + GstCaps *caps; + GstVideoInfo info; + gboolean need_pool; + + gst_query_parse_allocation(query, &caps, &need_pool); + gst_video_info_init(&info); + gst_video_info_from_caps(&info, caps); + gst_query_add_allocation_param(query, mAllocator, nullptr); + gst_query_add_allocation_pool(query, mBufferPool, info.size, 0, 0); + break; + default: + break; + } + + return ret; +} + +void GStreamerReader::ImageDataFromVideoFrame(GstVideoFrame *aFrame, + PlanarYCbCrImage::Data *aData) +{ + NS_ASSERTION(GST_VIDEO_INFO_IS_YUV(&mVideoInfo), + "Non-YUV video frame formats not supported"); + NS_ASSERTION(GST_VIDEO_FRAME_N_COMPONENTS(aFrame) == 3, + "Unsupported number of components in video frame"); + + aData->mPicX = aData->mPicY = 0; + aData->mPicSize = gfx::IntSize(mPicture.width, mPicture.height); + aData->mStereoMode = StereoMode::MONO; + + aData->mYChannel = GST_VIDEO_FRAME_COMP_DATA(aFrame, 0); + aData->mYStride = GST_VIDEO_FRAME_COMP_STRIDE(aFrame, 0); + aData->mYSize = gfx::IntSize(GST_VIDEO_FRAME_COMP_WIDTH(aFrame, 0), + GST_VIDEO_FRAME_COMP_HEIGHT(aFrame, 0)); + aData->mYSkip = GST_VIDEO_FRAME_COMP_PSTRIDE(aFrame, 0) - 1; + aData->mCbCrStride = GST_VIDEO_FRAME_COMP_STRIDE(aFrame, 1); + aData->mCbCrSize = gfx::IntSize(GST_VIDEO_FRAME_COMP_WIDTH(aFrame, 1), + GST_VIDEO_FRAME_COMP_HEIGHT(aFrame, 1)); + aData->mCbChannel = GST_VIDEO_FRAME_COMP_DATA(aFrame, 1); + aData->mCrChannel = GST_VIDEO_FRAME_COMP_DATA(aFrame, 2); + aData->mCbSkip = GST_VIDEO_FRAME_COMP_PSTRIDE(aFrame, 1) - 1; + aData->mCrSkip = GST_VIDEO_FRAME_COMP_PSTRIDE(aFrame, 2) - 1; +} + +nsRefPtr GStreamerReader::GetImageFromBuffer(GstBuffer* aBuffer) +{ + nsRefPtr image = nullptr; + + if (gst_buffer_n_memory(aBuffer) == 1) { + GstMemory* mem = gst_buffer_peek_memory(aBuffer, 0); + if (GST_IS_MOZ_GFX_MEMORY_ALLOCATOR(mem->allocator)) { + image = moz_gfx_memory_get_image(mem); + + GstVideoFrame frame; + gst_video_frame_map(&frame, &mVideoInfo, aBuffer, GST_MAP_READ); + PlanarYCbCrImage::Data data; + ImageDataFromVideoFrame(&frame, &data); + image->SetDataNoCopy(data); + gst_video_frame_unmap(&frame); + } + } + + return image; +} + +void GStreamerReader::CopyIntoImageBuffer(GstBuffer* aBuffer, + GstBuffer** aOutBuffer, + nsRefPtr &image) +{ + *aOutBuffer = gst_buffer_new_allocate(mAllocator, gst_buffer_get_size(aBuffer), nullptr); + GstMemory *mem = gst_buffer_peek_memory(*aOutBuffer, 0); + GstMapInfo map_info; + gst_memory_map(mem, &map_info, GST_MAP_WRITE); + gst_buffer_extract(aBuffer, 0, map_info.data, gst_buffer_get_size(aBuffer)); + gst_memory_unmap(mem, &map_info); + + /* create a new gst buffer with the newly created memory and copy the + * metadata over from the incoming buffer */ + gst_buffer_copy_into(*aOutBuffer, aBuffer, + (GstBufferCopyFlags)(GST_BUFFER_COPY_METADATA), 0, -1); + image = GetImageFromBuffer(*aOutBuffer); +} +#endif + +} // namespace mozilla + diff --git a/dom/media/gstreamer/GStreamerReader.h b/dom/media/gstreamer/GStreamerReader.h new file mode 100644 index 0000000000..0bc606dfec --- /dev/null +++ b/dom/media/gstreamer/GStreamerReader.h @@ -0,0 +1,273 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#if !defined(GStreamerReader_h_) +#define GStreamerReader_h_ + +#include + +#include +#include +#include +// This include trips -Wreserved-user-defined-literal on clang. Ignoring it +// trips -Wpragmas on GCC (unknown warning), but ignoring that trips +// -Wunknown-pragmas on clang (unknown pragma). +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wpragmas" +#pragma GCC diagnostic ignored "-Wreserved-user-defined-literal" +#include +#pragma GCC diagnostic pop + +#include "MediaDecoderReader.h" +#include "MediaResource.h" +#include "MP3FrameParser.h" +#include "ImageContainer.h" +#include "nsRect.h" + +struct GstURIDecodeBin; + +namespace mozilla { + +class AbstractMediaDecoder; + +class GStreamerReader : public MediaDecoderReader +{ + typedef gfx::IntRect IntRect; + +public: + explicit GStreamerReader(AbstractMediaDecoder* aDecoder); + virtual ~GStreamerReader(); + + virtual nsresult Init(MediaDecoderReader* aCloneDonor) override; + virtual nsRefPtr Shutdown() override; + virtual nsresult ResetDecode() override; + virtual bool DecodeAudioData() override; + virtual bool DecodeVideoFrame(bool &aKeyframeSkip, + int64_t aTimeThreshold) override; + virtual nsresult ReadMetadata(MediaInfo* aInfo, + MetadataTags** aTags) override; + virtual nsRefPtr + Seek(int64_t aTime, int64_t aEndTime) override; + virtual media::TimeIntervals GetBuffered() override; + +protected: + virtual void NotifyDataArrivedInternal(uint32_t aLength, + int64_t aOffset) override; +public: + + virtual bool HasAudio() override { + return mInfo.HasAudio(); + } + + virtual bool HasVideo() override { + return mInfo.HasVideo(); + } + + layers::ImageContainer* GetImageContainer() { return mDecoder->GetImageContainer(); } + + virtual bool IsMediaSeekable() override; + +private: + + void ReadAndPushData(guint aLength); + nsRefPtr GetImageFromBuffer(GstBuffer* aBuffer); + void CopyIntoImageBuffer(GstBuffer *aBuffer, GstBuffer** aOutBuffer, nsRefPtr &image); + GstCaps* BuildAudioSinkCaps(); + void InstallPadCallbacks(); + +#if GST_VERSION_MAJOR >= 1 + void ImageDataFromVideoFrame(GstVideoFrame *aFrame, layers::PlanarYCbCrImage::Data *aData); +#endif + + /* Called once the pipeline is setup to check that the stream only contains + * supported formats + */ + nsresult CheckSupportedFormats(); + + /* Gst callbacks */ + + static GstBusSyncReply ErrorCb(GstBus *aBus, GstMessage *aMessage, gpointer aUserData); + GstBusSyncReply Error(GstBus *aBus, GstMessage *aMessage); + + /* + * We attach this callback to playbin so that when uridecodebin is + * constructed, we can then list for its autoplug-sort signal to blacklist + * the elements it can construct. + */ + static void ElementAddedCb(GstBin *aPlayBin, + GstElement *aElement, + gpointer aUserData); + + /* + * Called on the autoplug-sort signal emitted by uridecodebin for filtering + * the elements it uses. + */ + static GValueArray *ElementFilterCb(GstURIDecodeBin *aBin, + GstPad *aPad, + GstCaps *aCaps, + GValueArray *aFactories, + gpointer aUserData); + + GValueArray *ElementFilter(GstURIDecodeBin *aBin, + GstPad *aPad, + GstCaps *aCaps, + GValueArray *aFactories); + + /* Called on the source-setup signal emitted by playbin. Used to + * configure appsrc . + */ + static void PlayBinSourceSetupCb(GstElement* aPlayBin, + GParamSpec* pspec, + gpointer aUserData); + void PlayBinSourceSetup(GstAppSrc* aSource); + + /* Called from appsrc when we need to read more data from the resource */ + static void NeedDataCb(GstAppSrc* aSrc, guint aLength, gpointer aUserData); + void NeedData(GstAppSrc* aSrc, guint aLength); + + /* Called when appsrc has enough data and we can stop reading */ + static void EnoughDataCb(GstAppSrc* aSrc, gpointer aUserData); + void EnoughData(GstAppSrc* aSrc); + + /* Called when a seek is issued on the pipeline */ + static gboolean SeekDataCb(GstAppSrc* aSrc, + guint64 aOffset, + gpointer aUserData); + gboolean SeekData(GstAppSrc* aSrc, guint64 aOffset); + + /* Called when events reach the sinks. See inline comments */ +#if GST_VERSION_MAJOR == 1 + static GstPadProbeReturn EventProbeCb(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); + GstPadProbeReturn EventProbe(GstPad *aPad, GstEvent *aEvent); +#else + static gboolean EventProbeCb(GstPad* aPad, GstEvent* aEvent, gpointer aUserData); + gboolean EventProbe(GstPad* aPad, GstEvent* aEvent); +#endif + + /* Called when the video part of the pipeline allocates buffers. Used to + * provide PlanarYCbCrImage backed GstBuffers to the pipeline so that a memory + * copy can be avoided when handling YUV buffers from the pipeline to the gfx + * side. + */ +#if GST_VERSION_MAJOR == 1 + static GstPadProbeReturn QueryProbeCb(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); + GstPadProbeReturn QueryProbe(GstPad *aPad, GstPadProbeInfo *aInfo, gpointer aUserData); +#else + static GstFlowReturn AllocateVideoBufferCb(GstPad* aPad, guint64 aOffset, guint aSize, + GstCaps* aCaps, GstBuffer** aBuf); + GstFlowReturn AllocateVideoBufferFull(GstPad* aPad, guint64 aOffset, guint aSize, + GstCaps* aCaps, GstBuffer** aBuf, nsRefPtr& aImage); + GstFlowReturn AllocateVideoBuffer(GstPad* aPad, guint64 aOffset, guint aSize, + GstCaps* aCaps, GstBuffer** aBuf); +#endif + + + /* Called when the pipeline is prerolled, that is when at start or after a + * seek, the first audio and video buffers are queued in the sinks. + */ + static GstFlowReturn NewPrerollCb(GstAppSink* aSink, gpointer aUserData); + void VideoPreroll(); + void AudioPreroll(); + + /* Called when buffers reach the sinks */ + static GstFlowReturn NewBufferCb(GstAppSink* aSink, gpointer aUserData); + void NewVideoBuffer(); + void NewAudioBuffer(); + + /* Called at end of stream, when decoding has finished */ + static void EosCb(GstAppSink* aSink, gpointer aUserData); + /* Notifies that a sink will no longer receive any more data. If nullptr + * is passed to this, we'll assume all streams have reached EOS (for example + * an error has occurred). */ + void Eos(GstAppSink* aSink = nullptr); + + /* Called when an element is added inside playbin. We use it to find the + * decodebin instance. + */ + static void PlayElementAddedCb(GstBin *aBin, GstElement *aElement, + gpointer *aUserData); + + /* Called during decoding, to decide whether a (sub)stream should be decoded or + * ignored */ + static bool ShouldAutoplugFactory(GstElementFactory* aFactory, GstCaps* aCaps); + + /* Called by decodebin during autoplugging. We use it to apply our + * container/codec blacklist. + */ + static GValueArray* AutoplugSortCb(GstElement* aElement, + GstPad* aPad, GstCaps* aCaps, + GValueArray* aFactories); + + // Try to find MP3 headers in this stream using our MP3 frame parser. + nsresult ParseMP3Headers(); + + // Get the length of the stream, excluding any metadata we have ignored at the + // start of the stream: ID3 headers, for example. + int64_t GetDataLength(); + + // Use our own MP3 parser here, largely for consistency with other platforms. + MP3FrameParser mMP3FrameParser; + + // The byte position in the stream where the actual media (ignoring, for + // example, ID3 tags) starts. + uint64_t mDataOffset; + + // We want to be able to decide in |ReadMetadata| whether or not we use the + // duration from the MP3 frame parser, as this backend supports more than just + // MP3. But |NotifyDataArrived| can update the duration and is often called + // _before_ |ReadMetadata|. This flag stops the former from using the parser + // duration until we are sure we want to. + bool mUseParserDuration; + int64_t mLastParserDuration; + +#if GST_VERSION_MAJOR >= 1 + GstAllocator *mAllocator; + GstBufferPool *mBufferPool; + GstVideoInfo mVideoInfo; +#endif + GstElement* mPlayBin; + GstBus* mBus; + GstAppSrc* mSource; + /* video sink bin */ + GstElement* mVideoSink; + /* the actual video app sink */ + GstAppSink* mVideoAppSink; + /* audio sink bin */ + GstElement* mAudioSink; + /* the actual audio app sink */ + GstAppSink* mAudioAppSink; + GstVideoFormat mFormat; + IntRect mPicture; + int mVideoSinkBufferCount; + int mAudioSinkBufferCount; + GstAppSrcCallbacks mSrcCallbacks; + GstAppSinkCallbacks mSinkCallbacks; + /* monitor used to synchronize access to shared state between gstreamer + * threads and other gecko threads */ + ReentrantMonitor mGstThreadsMonitor; + /* video and audio segments we use to convert absolute timestamps to [0, + * stream_duration]. They're set when the pipeline is started or after a seek. + * Concurrent access guarded with mGstThreadsMonitor. + */ + GstSegment mVideoSegment; + GstSegment mAudioSegment; + /* bool used to signal when gst has detected the end of stream and + * DecodeAudioData and DecodeVideoFrame should not expect any more data + */ + bool mReachedAudioEos; + bool mReachedVideoEos; +#if GST_VERSION_MAJOR >= 1 + bool mConfigureAlignment; +#endif + int fpsNum; + int fpsDen; + + MediaResourceIndex mResource; + NotifyDataArrivedFilter mFilter; +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/gstreamer/moz.build b/dom/media/gstreamer/moz.build new file mode 100644 index 0000000000..48d028833a --- /dev/null +++ b/dom/media/gstreamer/moz.build @@ -0,0 +1,38 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS += [ + 'GStreamerDecoder.h', + 'GStreamerFormatHelper.h', + 'GStreamerLoader.h', + 'GStreamerReader.h', +] + +SOURCES += [ + 'GStreamerDecoder.cpp', + 'GStreamerFormatHelper.cpp', + 'GStreamerLoader.cpp', + 'GStreamerReader.cpp', +] + +if CONFIG['GST_API_VERSION'] == '1.0': + SOURCES += [ + 'GStreamerAllocator.cpp', + ] +else: + SOURCES += [ + 'GStreamerMozVideoBuffer.cpp', + 'GStreamerReader-0.10.cpp', + ] + +FINAL_LIBRARY = 'xul' +LOCAL_INCLUDES += [ + '/dom/base', + '/dom/html', +] + +CFLAGS += CONFIG['GSTREAMER_CFLAGS'] +CXXFLAGS += CONFIG['GSTREAMER_CFLAGS'] diff --git a/dom/media/moz.build b/dom/media/moz.build index b3a6acc6e8..8b366de897 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -46,6 +46,9 @@ if CONFIG['MOZ_WAVE']: if CONFIG['MOZ_WEBM']: DIRS += ['webm'] +if CONFIG['MOZ_GSTREAMER']: + DIRS += ['gstreamer'] + if CONFIG['MOZ_DIRECTSHOW']: DIRS += ['directshow'] diff --git a/dom/media/omx/MediaCodecReader.cpp b/dom/media/omx/MediaCodecReader.cpp index 60790d83a5..b99b83bf2c 100644 --- a/dom/media/omx/MediaCodecReader.cpp +++ b/dom/media/omx/MediaCodecReader.cpp @@ -526,27 +526,28 @@ void MediaCodecReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { - nsRefPtr bytes = - mDecoder->GetResource()->MediaReadAt(aOffset, aLength); - NS_ENSURE_TRUE_VOID(bytes); - - MonitorAutoLock monLock(mParserMonitor); - if (mNextParserPosition == mParsedDataLength && - mNextParserPosition >= aOffset && - mNextParserPosition <= aOffset + aLength) { - // No pending parsing runnable currently. And available data are adjacent to - // parsed data. - int64_t shift = mNextParserPosition - aOffset; - const char* buffer = reinterpret_cast(bytes->Elements()) + shift; - uint32_t length = aLength - shift; - int64_t offset = mNextParserPosition; - if (length > 0) { - MonitorAutoUnlock monUnlock(mParserMonitor); - ParseDataSegment(buffer, length, offset); + IntervalSet intervals = mFilter.NotifyDataArrived(aLength, aOffset); + for (const auto& interval : intervals) { + nsRefPtr bytes = + mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length()); + MonitorAutoLock monLock(mParserMonitor); + if (mNextParserPosition == mParsedDataLength && + mNextParserPosition >= interval.mStart && + mNextParserPosition <= interval.mEnd) { + // No pending parsing runnable currently. And available data are adjacent to + // parsed data. + int64_t shift = mNextParserPosition - interval.mStart; + const char* buffer = reinterpret_cast(bytes->Elements()) + shift; + uint32_t length = interval.Length() - shift; + int64_t offset = mNextParserPosition; + if (length > 0) { + MonitorAutoUnlock monUnlock(mParserMonitor); + ParseDataSegment(buffer, length, offset); + } + mParseDataFromCache = false; + mParsedDataLength = offset + length; + mNextParserPosition = mParsedDataLength; } - mParseDataFromCache = false; - mParsedDataLength = offset + length; - mNextParserPosition = mParsedDataLength; } } @@ -847,32 +848,6 @@ MediaCodecReader::WaitFenceAndReleaseOutputBuffer() } } -PLDHashOperator -MediaCodecReader::ReleaseTextureClient(TextureClient* aClient, - size_t& aIndex, - void* aUserArg) -{ - nsRefPtr reader = static_cast(aUserArg); - MOZ_ASSERT(reader, "reader should not be nullptr in ReleaseTextureClient()"); - - return reader->ReleaseTextureClient(aClient, aIndex); -} - -PLDHashOperator -MediaCodecReader::ReleaseTextureClient(TextureClient* aClient, - size_t& aIndex) -{ - MOZ_ASSERT(aClient, "TextureClient should be a valid pointer"); - - aClient->ClearRecycleCallback(); - - if (mVideoTrack.mCodec != nullptr) { - mVideoTrack.mCodec->releaseOutputBuffer(aIndex); - } - - return PL_DHASH_REMOVE; -} - void MediaCodecReader::ReleaseAllTextureClients() { @@ -884,7 +859,17 @@ MediaCodecReader::ReleaseAllTextureClients() } printf_stderr("All TextureClients should be released already"); - mTextureClientIndexes.Enumerate(MediaCodecReader::ReleaseTextureClient, this); + for (auto iter = mTextureClientIndexes.Iter(); !iter.Done(); iter.Next()) { + TextureClient* client = iter.Key(); + size_t& index = iter.Data(); + + client->ClearRecycleCallback(); + + if (mVideoTrack.mCodec != nullptr) { + mVideoTrack.mCodec->releaseOutputBuffer(index); + } + iter.Remove(); + } mTextureClientIndexes.Clear(); } diff --git a/dom/media/omx/MediaCodecReader.h b/dom/media/omx/MediaCodecReader.h index 7ceea2c477..2199b96116 100644 --- a/dom/media/omx/MediaCodecReader.h +++ b/dom/media/omx/MediaCodecReader.h @@ -27,6 +27,8 @@ #include #endif +#include "MP3FrameParser.h" + namespace android { struct ALooper; struct AMessage; @@ -264,10 +266,12 @@ private: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SignalObject) SignalObject(const char* aName); - ~SignalObject(); void Wait(); void Signal(); + protected: + ~SignalObject(); + private: // Forbidden SignalObject() = delete; @@ -395,11 +399,6 @@ private: void WaitFenceAndReleaseOutputBuffer(); void ReleaseRecycledTextureClients(); - static PLDHashOperator ReleaseTextureClient(TextureClient* aClient, - size_t& aIndex, - void* aUserArg); - PLDHashOperator ReleaseTextureClient(TextureClient* aClient, - size_t& aIndex); void ReleaseAllTextureClients(); @@ -439,6 +438,8 @@ private: FenceHandle mReleaseFence; }; nsTArray mPendingReleaseItems; + + NotifyDataArrivedFilter mFilter; }; } // namespace mozilla diff --git a/dom/media/omx/MediaOmxReader.cpp b/dom/media/omx/MediaOmxReader.cpp index 5fb0c261f7..2a4b66f18d 100644 --- a/dom/media/omx/MediaOmxReader.cpp +++ b/dom/media/omx/MediaOmxReader.cpp @@ -474,18 +474,21 @@ void MediaOmxReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset return; } - nsRefPtr bytes = - mDecoder->GetResource()->MediaReadAt(aOffset, aLength); - NS_ENSURE_TRUE_VOID(bytes); - mMP3FrameParser.Parse(bytes->Elements(), aLength, aOffset); - if (!mMP3FrameParser.IsMP3()) { - return; - } + IntervalSet intervals = mFilter.NotifyDataArrived(aLength, aOffset); + for (const auto& interval : intervals) { + nsRefPtr bytes = + mDecoder->GetResource()->MediaReadAt(interval.mStart, interval.Length()); + NS_ENSURE_TRUE_VOID(bytes); + mMP3FrameParser.Parse(bytes->Elements(), interval.Length(), interval.mStart); + if (!mMP3FrameParser.IsMP3()) { + return; + } - int64_t duration = mMP3FrameParser.GetDuration(); - if (duration != mLastParserDuration) { - mLastParserDuration = duration; - decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration); + int64_t duration = mMP3FrameParser.GetDuration(); + if (duration != mLastParserDuration) { + mLastParserDuration = duration; + decoder->DispatchUpdateEstimatedMediaDuration(mLastParserDuration); + } } } diff --git a/dom/media/omx/MediaOmxReader.h b/dom/media/omx/MediaOmxReader.h index 0eac90b2f0..6782b92294 100644 --- a/dom/media/omx/MediaOmxReader.h +++ b/dom/media/omx/MediaOmxReader.h @@ -128,6 +128,8 @@ private: int64_t ProcessCachedData(int64_t aOffset); already_AddRefed SafeGetDecoder(); + + NotifyDataArrivedFilter mFilter; }; } // namespace mozilla diff --git a/dom/media/systemservices/MediaParent.cpp b/dom/media/systemservices/MediaParent.cpp index c818e32348..9ef1439099 100644 --- a/dom/media/systemservices/MediaParent.cpp +++ b/dom/media/systemservices/MediaParent.cpp @@ -83,26 +83,21 @@ class OriginKeyStore : public nsISupports return NS_OK; } - static PLDHashOperator - HashCleaner(const nsACString& aOrigin, nsAutoPtr& aOriginKey, - void *aUserArg) - { - OriginKey* since = static_cast(aUserArg); - - LOG((((aOriginKey->mSecondsStamp >= since->mSecondsStamp)? - "%s: REMOVE %lld >= %lld" : - "%s: KEEP %lld < %lld"), - __FUNCTION__, aOriginKey->mSecondsStamp, since->mSecondsStamp)); - - return (aOriginKey->mSecondsStamp >= since->mSecondsStamp)? - PL_DHASH_REMOVE : PL_DHASH_NEXT; - } - void Clear(int64_t aSinceWhen) { // Avoid int64_t* <-> void* casting offset OriginKey since(nsCString(), aSinceWhen / PR_USEC_PER_SEC); - mKeys.Enumerate(HashCleaner, &since); + for (auto iter = mKeys.Iter(); !iter.Done(); iter.Next()) { + nsAutoPtr& originKey = iter.Data(); + LOG((((originKey->mSecondsStamp >= since.mSecondsStamp)? + "%s: REMOVE %lld >= %lld" : + "%s: KEEP %lld < %lld"), + __FUNCTION__, originKey->mSecondsStamp, since.mSecondsStamp)); + + if (originKey->mSecondsStamp >= since.mSecondsStamp) { + iter.Remove(); + } + } mPersistCount = 0; } diff --git a/dom/media/systemservices/MediaSystemResourceService.cpp b/dom/media/systemservices/MediaSystemResourceService.cpp index d29b561150..29f17da17c 100644 --- a/dom/media/systemservices/MediaSystemResourceService.cpp +++ b/dom/media/systemservices/MediaSystemResourceService.cpp @@ -141,25 +141,6 @@ MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerPar UpdateRequests(aResourceType); } -struct ReleaseResourceData -{ - MediaSystemResourceService* mSelf; - media::MediaSystemResourceManagerParent* mParent; -}; - -/*static*/PLDHashOperator -MediaSystemResourceService::ReleaseResourceForKey(const uint32_t& aKey, - nsAutoPtr& aData, - void* aClosure) -{ - ReleaseResourceData* closure = static_cast(aClosure); - - closure->mSelf->RemoveRequests(closure->mParent, static_cast(aKey)); - closure->mSelf->UpdateRequests(static_cast(aKey)); - - return PLDHashOperator::PL_DHASH_NEXT; -} - void MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerParent* aParent) { @@ -169,8 +150,11 @@ MediaSystemResourceService::ReleaseResource(media::MediaSystemResourceManagerPar return; } - ReleaseResourceData data = { this, aParent }; - mResources.Enumerate(ReleaseResourceForKey, &data); + for (auto iter = mResources.Iter(); !iter.Done(); iter.Next()) { + const uint32_t& key = iter.Key(); + RemoveRequests(aParent, static_cast(key)); + UpdateRequests(static_cast(key)); + } } void diff --git a/dom/media/systemservices/MediaSystemResourceService.h b/dom/media/systemservices/MediaSystemResourceService.h index a0d0967e7a..9329cd9304 100644 --- a/dom/media/systemservices/MediaSystemResourceService.h +++ b/dom/media/systemservices/MediaSystemResourceService.h @@ -77,10 +77,6 @@ private: void UpdateRequests(MediaSystemResourceType aResourceType); - static PLDHashOperator ReleaseResourceForKey(const uint32_t& aKey, - nsAutoPtr& aData, - void* aClosure); - bool mDestroyed; nsClassHashtable mResources; diff --git a/dom/media/webspeech/synth/pico/nsPicoService.cpp b/dom/media/webspeech/synth/pico/nsPicoService.cpp index c2ae1135bf..49794ebe4c 100644 --- a/dom/media/webspeech/synth/pico/nsPicoService.cpp +++ b/dom/media/webspeech/synth/pico/nsPicoService.cpp @@ -495,40 +495,8 @@ nsPicoService::GetServiceType(SpeechServiceType* aServiceType) return NS_OK; } -struct VoiceTraverserData -{ - nsPicoService* mService; - nsSynthVoiceRegistry* mRegistry; -}; - // private methods -static PLDHashOperator -PicoAddVoiceTraverser(const nsAString& aUri, - nsRefPtr& aVoice, - void* aUserArg) -{ - // If we are missing either a language or a voice resource, it is invalid. - if (aVoice->mTaFile.IsEmpty() || aVoice->mSgFile.IsEmpty()) { - return PL_DHASH_REMOVE; - } - - VoiceTraverserData* data = static_cast(aUserArg); - - nsAutoString name; - name.AssignLiteral("Pico "); - name.Append(aVoice->mLanguage); - - // This service is multi-threaded and can handle more than one utterance at a - // time before previous utterances end. So, aQueuesUtterances == false - DebugOnly rv = - data->mRegistry->AddVoice( - data->mService, aUri, name, aVoice->mLanguage, true, false); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice"); - - return PL_DHASH_NEXT; -} - void nsPicoService::Init() { @@ -608,8 +576,28 @@ nsPicoService::Init() void nsPicoService::RegisterVoices() { - VoiceTraverserData data = { this, nsSynthVoiceRegistry::GetInstance() }; - mVoices.Enumerate(PicoAddVoiceTraverser, &data); + nsSynthVoiceRegistry* registry = nsSynthVoiceRegistry::GetInstance(); + + for (auto iter = mVoices.Iter(); !iter.Done(); iter.Next()) { + const nsAString& uri = iter.Key(); + nsRefPtr& voice = iter.Data(); + + // If we are missing either a language or a voice resource, it is invalid. + if (voice->mTaFile.IsEmpty() || voice->mSgFile.IsEmpty()) { + iter.Remove(); + continue; + } + + nsAutoString name; + name.AssignLiteral("Pico "); + name.Append(voice->mLanguage); + + // This service is multi-threaded and can handle more than one utterance at a + // time before previous utterances end. So, aQueuesUtterances == false + DebugOnly rv = + registry->AddVoice(this, uri, name, voice->mLanguage, true, false); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to add voice"); + } mInitialized = true; } diff --git a/dom/svg/SVGFEImageElement.cpp b/dom/svg/SVGFEImageElement.cpp index 1649b67408..dbd0a68b14 100644 --- a/dom/svg/SVGFEImageElement.cpp +++ b/dom/svg/SVGFEImageElement.cpp @@ -235,7 +235,7 @@ SVGFEImageElement::GetPrimitiveDescription(nsSVGFilterInstance* aInstance, Matrix TM = viewBoxTM; TM.PostTranslate(aFilterSubregion.x, aFilterSubregion.y); - Filter filter = ToFilter(nsLayoutUtils::GetGraphicsFilterForFrame(frame)); + Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(frame); FilterPrimitiveDescription descr(PrimitiveType::Image); descr.Attributes().Set(eImageFilter, (uint32_t)filter); diff --git a/gfx/2d/HelpersCairo.h b/gfx/2d/HelpersCairo.h index 84eef0648d..7f753d0155 100644 --- a/gfx/2d/HelpersCairo.h +++ b/gfx/2d/HelpersCairo.h @@ -119,6 +119,8 @@ GfxFilterToCairoFilter(Filter filter) return CAIRO_FILTER_BILINEAR; case Filter::POINT: return CAIRO_FILTER_NEAREST; + default: + MOZ_CRASH("bad filter"); } return CAIRO_FILTER_BILINEAR; diff --git a/gfx/2d/PathHelpers.cpp b/gfx/2d/PathHelpers.cpp index e28f4f053f..a7bf5b46e7 100644 --- a/gfx/2d/PathHelpers.cpp +++ b/gfx/2d/PathHelpers.cpp @@ -184,7 +184,8 @@ AppendEllipseToPath(PathBuilder* aPathBuilder, bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, - const DrawTarget& aDrawTarget) + const DrawTarget& aDrawTarget, + Float aLineWidth) { Matrix mat = aDrawTarget.GetTransform(); if (mat.HasNonTranslation()) { @@ -199,14 +200,21 @@ SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, p2.Round(); p1 -= mat.GetTranslation(); // back into user space p2 -= mat.GetTranslation(); - if (aP1.x == aP2.x) { - // snap vertical line, adding 0.5 to align it to be mid-pixel: - aP1 = p1 + Point(0.5, 0); - aP2 = p2 + Point(0.5, 0); - } else { - // snap horizontal line, adding 0.5 to align it to be mid-pixel: - aP1 = p1 + Point(0, 0.5); - aP2 = p2 + Point(0, 0.5); + + aP1 = p1; + aP2 = p2; + + bool lineWidthIsOdd = (int(aLineWidth) % 2) == 1; + if (lineWidthIsOdd) { + if (aP1.x == aP2.x) { + // snap vertical line, adding 0.5 to align it to be mid-pixel: + aP1 += Point(0.5, 0); + aP2 += Point(0.5, 0); + } else { + // snap horizontal line, adding 0.5 to align it to be mid-pixel: + aP1 += Point(0, 0.5); + aP2 += Point(0, 0.5); + } } return true; } @@ -222,22 +230,26 @@ StrokeSnappedEdgesOfRect(const Rect& aRect, DrawTarget& aDrawTarget, Point p1 = aRect.TopLeft(); Point p2 = aRect.BottomLeft(); - SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.BottomLeft(); p2 = aRect.BottomRight(); - SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.TopLeft(); p2 = aRect.TopRight(); - SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); p1 = aRect.TopRight(); p2 = aRect.BottomRight(); - SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1, p2, aColor, aStrokeOptions); } diff --git a/gfx/2d/PathHelpers.h b/gfx/2d/PathHelpers.h index 6127f2c3d0..e51cdacf3b 100644 --- a/gfx/2d/PathHelpers.h +++ b/gfx/2d/PathHelpers.h @@ -300,7 +300,8 @@ inline already_AddRefed MakePathForEllipse(const DrawTarget& aDrawTarget, * false. */ GFX2D_API bool SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2, - const DrawTarget& aDrawTarget); + const DrawTarget& aDrawTarget, + Float aLineWidth); /** * This function paints each edge of aRect separately, snapping the edges using diff --git a/gfx/2d/Types.h b/gfx/2d/Types.h index ab3429e9e3..d41417a8c8 100644 --- a/gfx/2d/Types.h +++ b/gfx/2d/Types.h @@ -192,7 +192,8 @@ enum class AntialiasMode : int8_t { enum class Filter : int8_t { GOOD, LINEAR, - POINT + POINT, + SENTINEL // one past the last valid value }; enum class PatternType : int8_t { diff --git a/gfx/gl/GLTextureImage.cpp b/gfx/gl/GLTextureImage.cpp index 26ebbe3665..906a2815eb 100644 --- a/gfx/gl/GLTextureImage.cpp +++ b/gfx/gl/GLTextureImage.cpp @@ -270,7 +270,7 @@ TextureImage::TextureImage(const gfx::IntSize& aSize, : mSize(aSize) , mWrapMode(aWrapMode) , mContentType(aContentType) - , mFilter(GraphicsFilter::FILTER_GOOD) + , mFilter(Filter::GOOD) , mFlags(aFlags) {} diff --git a/gfx/gl/GLTextureImage.h b/gfx/gl/GLTextureImage.h index 5c0203e833..ed42249e83 100644 --- a/gfx/gl/GLTextureImage.h +++ b/gfx/gl/GLTextureImage.h @@ -11,7 +11,6 @@ #include "nsTArray.h" #include "gfxTypes.h" #include "GLContextTypes.h" -#include "GraphicsFilter.h" #include "mozilla/gfx/Rect.h" #include "mozilla/RefPtr.h" @@ -199,7 +198,7 @@ public: virtual bool InUpdate() const = 0; GLenum GetWrapMode() const { return mWrapMode; } - void SetFilter(GraphicsFilter aFilter) { mFilter = aFilter; } + void SetFilter(gfx::Filter aFilter) { mFilter = aFilter; } protected: friend class GLContext; @@ -224,7 +223,7 @@ protected: GLenum mWrapMode; ContentType mContentType; gfx::SurfaceFormat mTextureFormat; - GraphicsFilter mFilter; + gfx::Filter mFilter; Flags mFlags; }; diff --git a/gfx/gl/GLXLibrary.h b/gfx/gl/GLXLibrary.h index 8a8bc5c3cc..794b1f8c09 100644 --- a/gfx/gl/GLXLibrary.h +++ b/gfx/gl/GLXLibrary.h @@ -21,6 +21,7 @@ typedef XID GLXContextID; typedef XID GLXWindow; typedef XID GLXPbuffer; // end of stuff from glx.h +#include "prenv.h" struct PRLibrary; class gfxASurface; @@ -108,6 +109,12 @@ public: bool SupportsTextureFromPixmap(gfxASurface* aSurface); bool IsATI() { return mIsATI; } bool GLXVersionCheck(int aMajor, int aMinor); + bool UseSurfaceSharing() { + // Disable surface sharing due to issues with compatible FBConfigs on + // NVIDIA drivers as described in bug 1193015. + static bool useSharing = PR_GetEnv("MOZ_GLX_USE_SURFACE_SHARING"); + return mUseTextureFromPixmap && useSharing; + } private: diff --git a/gfx/ipc/GfxMessageUtils.h b/gfx/ipc/GfxMessageUtils.h index 744907970b..534518beb9 100644 --- a/gfx/ipc/GfxMessageUtils.h +++ b/gfx/ipc/GfxMessageUtils.h @@ -14,12 +14,12 @@ #include #include "mozilla/gfx/Matrix.h" -#include "GraphicsFilter.h" #include "gfxPoint.h" #include "gfxRect.h" #include "nsRect.h" #include "nsRegion.h" #include "gfxTypes.h" +#include "mozilla/layers/AsyncDragMetrics.h" #include "mozilla/layers/LayersTypes.h" #include "mozilla/layers/CompositorTypes.h" #include "FrameMetrics.h" @@ -33,7 +33,6 @@ namespace mozilla { typedef gfxImageFormat PixelFormat; -typedef ::GraphicsFilter GraphicsFilterType; } // namespace mozilla @@ -206,11 +205,11 @@ struct ParamTraits {}; template <> -struct ParamTraits +struct ParamTraits : public ContiguousEnumSerializer< - mozilla::GraphicsFilterType, - GraphicsFilter::FILTER_FAST, - GraphicsFilter::FILTER_SENTINEL> + mozilla::gfx::Filter, + mozilla::gfx::Filter::GOOD, + mozilla::gfx::Filter::SENTINEL> {}; template <> @@ -1098,6 +1097,40 @@ struct ParamTraits mozilla::layers::EventRegionsOverride::ALL_BITS> {}; +template<> +struct ParamTraits + : public ContiguousEnumSerializer< + mozilla::layers::AsyncDragMetrics::DragDirection, + mozilla::layers::AsyncDragMetrics::DragDirection::NONE, + mozilla::layers::AsyncDragMetrics::DragDirection::SENTINEL> +{}; + +template<> +struct ParamTraits +{ + typedef mozilla::layers::AsyncDragMetrics paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.mViewId); + WriteParam(aMsg, aParam.mPresShellId); + WriteParam(aMsg, aParam.mDragStartSequenceNumber); + WriteParam(aMsg, aParam.mScrollbarDragOffset); + WriteParam(aMsg, aParam.mScrollTrack); + WriteParam(aMsg, aParam.mDirection); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return (ReadParam(aMsg, aIter, &aResult->mViewId) && + ReadParam(aMsg, aIter, &aResult->mPresShellId) && + ReadParam(aMsg, aIter, &aResult->mDragStartSequenceNumber) && + ReadParam(aMsg, aIter, &aResult->mScrollbarDragOffset) && + ReadParam(aMsg, aIter, &aResult->mScrollTrack) && + ReadParam(aMsg, aIter, &aResult->mDirection)); + } +}; + } /* namespace IPC */ #endif /* __GFXMESSAGEUTILS_H__ */ diff --git a/gfx/layers/ImageLayers.cpp b/gfx/layers/ImageLayers.cpp index d02ec48466..dc9f36dcd0 100644 --- a/gfx/layers/ImageLayers.cpp +++ b/gfx/layers/ImageLayers.cpp @@ -14,7 +14,7 @@ namespace mozilla { namespace layers { ImageLayer::ImageLayer(LayerManager* aManager, void* aImplData) -: Layer(aManager, aImplData), mFilter(GraphicsFilter::FILTER_GOOD) +: Layer(aManager, aImplData), mFilter(gfx::Filter::GOOD) , mScaleMode(ScaleMode::SCALE_NONE), mDisallowBigImage(false) {} diff --git a/gfx/layers/ImageLayers.h b/gfx/layers/ImageLayers.h index 1fb471ff81..35ca592c96 100644 --- a/gfx/layers/ImageLayers.h +++ b/gfx/layers/ImageLayers.h @@ -7,7 +7,6 @@ #define GFX_IMAGELAYER_H #include "Layers.h" // for Layer, etc -#include "GraphicsFilter.h" // for GraphicsFilter #include "mozilla/gfx/BaseSize.h" // for BaseSize #include "mozilla/gfx/Point.h" // for IntSize #include "mozilla/layers/LayersTypes.h" @@ -39,7 +38,7 @@ public: * CONSTRUCTION PHASE ONLY * Set the filter used to resample this image if necessary. */ - void SetFilter(GraphicsFilter aFilter) + void SetFilter(gfx::Filter aFilter) { if (mFilter != aFilter) { MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this)); @@ -63,7 +62,7 @@ public: ImageContainer* GetContainer() { return mContainer; } - GraphicsFilter GetFilter() { return mFilter; } + gfx::Filter GetFilter() { return mFilter; } const gfx::IntSize& GetScaleToSize() { return mScaleToSize; } ScaleMode GetScaleMode() { return mScaleMode; } @@ -95,7 +94,7 @@ protected: virtual void DumpPacket(layerscope::LayersPacket* aPacket, const void* aParent) override; nsRefPtr mContainer; - GraphicsFilter mFilter; + gfx::Filter mFilter; gfx::IntSize mScaleToSize; ScaleMode mScaleMode; bool mDisallowBigImage; diff --git a/gfx/layers/LayerMetricsWrapper.h b/gfx/layers/LayerMetricsWrapper.h index e00301fd9b..751a2c673a 100644 --- a/gfx/layers/LayerMetricsWrapper.h +++ b/gfx/layers/LayerMetricsWrapper.h @@ -364,6 +364,29 @@ public: return EventRegionsOverride::NoOverride; } + Layer::ScrollDirection GetScrollbarDirection() const + { + MOZ_ASSERT(IsValid()); + + return mLayer->GetScrollbarDirection(); + } + + FrameMetrics::ViewID GetScrollbarTargetContainerId() const + { + MOZ_ASSERT(IsValid()); + + return mLayer->GetScrollbarTargetContainerId(); + } + + int32_t GetScrollbarSize() const + { + if (GetScrollbarDirection() == Layer::VERTICAL) { + return mLayer->GetVisibleRegion().GetBounds().height; + } else { + return mLayer->GetVisibleRegion().GetBounds().width; + } + } + // Expose an opaque pointer to the layer. Mostly used for printf // purposes. This is not intended to be a general-purpose accessor // for the underlying layer. diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 4b5af6b571..ac1422f987 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -10,7 +10,6 @@ #include "ImageLayers.h" // for ImageLayer, etc #include "Layers.h" // for Layer, ContainerLayer, etc #include "Units.h" // for ParentLayerIntRect -#include "GraphicsFilter.h" // for GraphicsFilter #include "gfxRect.h" // for gfxRect #include "gfxUtils.h" // for gfxUtils #include "mozilla/gfx/BaseSize.h" // for BaseSize @@ -403,9 +402,14 @@ struct ImageLayerProperties : public LayerPropertiesBase , mFilter(aImage->GetFilter()) , mScaleToSize(aImage->GetScaleToSize()) , mScaleMode(aImage->GetScaleMode()) + , mLastProducerID(-1) + , mLastFrameID(-1) , mIsMask(aIsMask) { - mFrameID = mImageHost ? mImageHost->GetFrameID() : -1; + if (mImageHost) { + mLastProducerID = mImageHost->GetLastProducerID(); + mLastFrameID = mImageHost->GetLastFrameID(); + } } virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback, @@ -427,7 +431,8 @@ struct ImageLayerProperties : public LayerPropertiesBase mScaleToSize != imageLayer->GetScaleToSize() || mScaleMode != imageLayer->GetScaleMode() || host != mImageHost || - (host && host->GetFrameID() != mFrameID)) { + (host && host->GetProducerID() != mLastProducerID) || + (host && host->GetFrameID() != mLastFrameID)) { aGeometryChanged = true; if (mIsMask) { @@ -451,10 +456,11 @@ struct ImageLayerProperties : public LayerPropertiesBase nsRefPtr mContainer; nsRefPtr mImageHost; - GraphicsFilter mFilter; + Filter mFilter; gfx::IntSize mScaleToSize; - int32_t mFrameID; ScaleMode mScaleMode; + int32_t mLastProducerID; + int32_t mLastFrameID; bool mIsMask; }; @@ -475,11 +481,15 @@ CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask /* = false */) return MakeUnique(static_cast(aRoot)); case Layer::TYPE_IMAGE: return MakeUnique(static_cast(aRoot), aIsMask); - default: + case Layer::TYPE_CANVAS: + case Layer::TYPE_READBACK: + case Layer::TYPE_SHADOW: + case Layer::TYPE_PAINTED: return MakeUnique(aRoot); } - return UniquePtr(nullptr); + MOZ_ASSERT_UNREACHABLE("Unexpected root layer type"); + return MakeUnique(aRoot); } /* static */ UniquePtr diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 0a432ba40c..6b7dd12fa6 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -2111,7 +2111,7 @@ void CanvasLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) { Layer::PrintInfo(aStream, aPrefix); - if (mFilter != GraphicsFilter::FILTER_GOOD) { + if (mFilter != Filter::GOOD) { AppendToString(aStream, mFilter, " [filter=", "]"); } } @@ -2119,27 +2119,18 @@ CanvasLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) // This help function is used to assign the correct enum value // to the packet static void -DumpFilter(layerscope::LayersPacket::Layer* aLayer, const GraphicsFilter& aFilter) +DumpFilter(layerscope::LayersPacket::Layer* aLayer, const Filter& aFilter) { using namespace layerscope; switch (aFilter) { - case GraphicsFilter::FILTER_FAST: - aLayer->set_filter(LayersPacket::Layer::FILTER_FAST); - break; - case GraphicsFilter::FILTER_GOOD: + case Filter::GOOD: aLayer->set_filter(LayersPacket::Layer::FILTER_GOOD); break; - case GraphicsFilter::FILTER_BEST: - aLayer->set_filter(LayersPacket::Layer::FILTER_BEST); + case Filter::LINEAR: + aLayer->set_filter(LayersPacket::Layer::FILTER_LINEAR); break; - case GraphicsFilter::FILTER_NEAREST: - aLayer->set_filter(LayersPacket::Layer::FILTER_NEAREST); - break; - case GraphicsFilter::FILTER_BILINEAR: - aLayer->set_filter(LayersPacket::Layer::FILTER_BILINEAR); - break; - case GraphicsFilter::FILTER_GAUSSIAN: - aLayer->set_filter(LayersPacket::Layer::FILTER_GAUSSIAN); + case Filter::POINT: + aLayer->set_filter(LayersPacket::Layer::FILTER_POINT); break; default: // ignore it @@ -2162,7 +2153,7 @@ void ImageLayer::PrintInfo(std::stringstream& aStream, const char* aPrefix) { Layer::PrintInfo(aStream, aPrefix); - if (mFilter != GraphicsFilter::FILTER_GOOD) { + if (mFilter != Filter::GOOD) { AppendToString(aStream, mFilter, " [filter=", "]"); } } diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 6d6e858aa3..f6bb181fb9 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -13,7 +13,6 @@ #include "Units.h" // for LayerMargin, LayerPoint, ParentLayerIntRect #include "gfxContext.h" #include "gfxTypes.h" -#include "GraphicsFilter.h" // for GraphicsFilter #include "gfxPoint.h" // for gfxPoint #include "gfxRect.h" // for gfxRect #include "gfx2DGlue.h" @@ -2372,7 +2371,7 @@ public: * CONSTRUCTION PHASE ONLY * Set the filter used to resample this image (if necessary). */ - void SetFilter(GraphicsFilter aFilter) + void SetFilter(gfx::Filter aFilter) { if (mFilter != aFilter) { MOZ_LAYERS_LOG_IF_SHADOWABLE(this, ("Layer::Mutated(%p) Filter", this)); @@ -2380,7 +2379,7 @@ public: Mutated(); } } - GraphicsFilter GetFilter() const { return mFilter; } + gfx::Filter GetFilter() const { return mFilter; } MOZ_LAYER_DECL_NAME("CanvasLayer", TYPE_CANVAS) @@ -2404,7 +2403,7 @@ protected: , mPreTransCallbackData(nullptr) , mPostTransCallback(nullptr) , mPostTransCallbackData(nullptr) - , mFilter(GraphicsFilter::FILTER_GOOD) + , mFilter(gfx::Filter::GOOD) , mDirty(false) {} @@ -2427,7 +2426,7 @@ protected: void* mPreTransCallbackData; DidTransactionCallback mPostTransCallback; void* mPostTransCallbackData; - GraphicsFilter mFilter; + gfx::Filter mFilter; private: /** diff --git a/gfx/layers/LayersLogging.cpp b/gfx/layers/LayersLogging.cpp index 4c38142659..df34de79e3 100644 --- a/gfx/layers/LayersLogging.cpp +++ b/gfx/layers/LayersLogging.cpp @@ -28,25 +28,6 @@ AppendToString(std::stringstream& aStream, const void* p, aStream << sfx; } -void -AppendToString(std::stringstream& aStream, const GraphicsFilter& f, - const char* pfx, const char* sfx) -{ - aStream << pfx; - switch (f) { - case GraphicsFilter::FILTER_FAST: aStream << "fast"; break; - case GraphicsFilter::FILTER_GOOD: aStream << "good"; break; - case GraphicsFilter::FILTER_BEST: aStream << "best"; break; - case GraphicsFilter::FILTER_NEAREST: aStream << "nearest"; break; - case GraphicsFilter::FILTER_BILINEAR: aStream << "bilinear"; break; - case GraphicsFilter::FILTER_GAUSSIAN: aStream << "gaussian"; break; - default: - NS_ERROR("unknown filter type"); - aStream << "???"; - } - aStream << sfx; -} - void AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n, const char* pfx, const char* sfx) @@ -276,7 +257,6 @@ AppendToString(std::stringstream& aStream, const Matrix5x4& m, aStream << sfx; } - void AppendToString(std::stringstream& aStream, const Filter filter, const char* pfx, const char* sfx) @@ -287,6 +267,9 @@ AppendToString(std::stringstream& aStream, const Filter filter, case Filter::GOOD: aStream << "Filter::GOOD"; break; case Filter::LINEAR: aStream << "Filter::LINEAR"; break; case Filter::POINT: aStream << "Filter::POINT"; break; + default: + NS_ERROR("unknown filter type"); + aStream << "???"; } aStream << sfx; } diff --git a/gfx/layers/LayersLogging.h b/gfx/layers/LayersLogging.h index 25340405f1..b873d981a5 100644 --- a/gfx/layers/LayersLogging.h +++ b/gfx/layers/LayersLogging.h @@ -7,7 +7,6 @@ #define GFX_LAYERSLOGGING_H #include "FrameMetrics.h" // for FrameMetrics, etc -#include "GraphicsFilter.h" // for GraphicsFilter #include "mozilla/gfx/Point.h" // for IntSize, etc #include "mozilla/gfx/Types.h" // for Filter, SurfaceFormat #include "mozilla/layers/CompositorTypes.h" // for TextureFlags @@ -30,10 +29,6 @@ void AppendToString(std::stringstream& aStream, const void* p, const char* pfx="", const char* sfx=""); -void -AppendToString(std::stringstream& aStream, const GraphicsFilter& f, - const char* pfx="", const char* sfx=""); - void AppendToString(std::stringstream& aStream, FrameMetrics::ViewID n, const char* pfx="", const char* sfx=""); diff --git a/gfx/layers/apz/src/APZCTreeManager.cpp b/gfx/layers/apz/src/APZCTreeManager.cpp index 17fff4af98..9d46c2a993 100644 --- a/gfx/layers/apz/src/APZCTreeManager.cpp +++ b/gfx/layers/apz/src/APZCTreeManager.cpp @@ -14,6 +14,7 @@ #include "mozilla/gfx/Point.h" // for Point #include "mozilla/layers/APZThreadUtils.h" // for AssertOnCompositorThread, etc #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform +#include "mozilla/layers/AsyncDragMetrics.h" // for AsyncDragMetrics #include "mozilla/layers/CompositorParent.h" // for CompositorParent, etc #include "mozilla/layers/LayerMetricsWrapper.h" #include "mozilla/MouseEvents.h" @@ -324,6 +325,21 @@ GetEventRegionsOverride(HitTestingTreeNode* aParent, return result; } +void +APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics) +{ + + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (!apzc) { + return; + } + + // TODO Confirm the input block + //uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber; + //mInputQueue->SetConfirmedMouseBlock(inputBlockId, apzc, aDragMetrics); +} + HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, const FrameMetrics& aMetrics, @@ -352,6 +368,9 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetTransform(), aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(), GetEventRegionsOverride(aParent, aLayer)); + node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(), + aLayer.GetScrollbarDirection(), + aLayer.GetScrollbarSize()); return node; } @@ -522,6 +541,9 @@ APZCTreeManager::PrepareNodeForLayer(const LayerMetricsWrapper& aLayer, GetEventRegionsOverride(aParent, aLayer)); } + node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(), + aLayer.GetScrollbarDirection(), + aLayer.GetScrollbarSize()); return node; } @@ -590,6 +612,18 @@ WillHandleWheelEvent(WidgetWheelEvent* aEvent) !EventStateManager::WheelEventNeedsDeltaMultipliers(aEvent); } +static bool +WillHandleMouseEvent(const WidgetMouseEventBase& aEvent) +{ + if (!gfxPrefs::APZDragEnabled()) { + return false; + } + + return aEvent.mMessage == eMouseMove || + aEvent.mMessage == eMouseDown || + aEvent.mMessage == eMouseUp; +} + template static bool WillHandleInput(const PanGestureOrScrollWheelInput& aPanInput) @@ -635,6 +669,28 @@ APZCTreeManager::ReceiveInputEvent(InputData& aEvent, MultiTouchInput& touchInput = aEvent.AsMultiTouchInput(); result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId); break; + } case MOUSE_INPUT: { + MouseInput& mouseInput = aEvent.AsMouseInput(); + + nsRefPtr apzc = GetTargetAPZC(mouseInput.mOrigin, + &hitResult); + if (apzc) { + result = mInputQueue->ReceiveInputEvent( + apzc, + /* aTargetConfirmed = */ false, + mouseInput, aOutInputBlockId); + + // Update the out-parameters so they are what the caller expects. + apzc->GetGuid(aOutTargetGuid); + + // TODO Dagging on a scrollbar probably behaves differently from + // the other input types in that the gecko coordinates are the same + // as the screen coordinates even though the async transform on the APZC + // is changing. I'm not really sure at this point and it'll take some + // though to figure out properly. + //mouseInput.mOrigin = untransformedOrigin; + } + break; } case SCROLLWHEEL_INPUT: { FlushRepaintsToClearScreenToGeckoTransform(); @@ -951,6 +1007,21 @@ APZCTreeManager::ProcessEvent(WidgetInputEvent& aEvent, return result; } +nsEventStatus +APZCTreeManager::ProcessMouseEvent(WidgetMouseEventBase& aEvent, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId) +{ + MouseInput input(aEvent); + input.mOrigin = ScreenPoint(aEvent.refPoint.x, aEvent.refPoint.y); + + nsEventStatus status = ReceiveInputEvent(input, aOutTargetGuid, aOutInputBlockId); + + aEvent.refPoint.x = input.mOrigin.x; + aEvent.refPoint.y = input.mOrigin.y; + return status; +} + nsEventStatus APZCTreeManager::ProcessWheelEvent(WidgetWheelEvent& aEvent, ScrollableLayerGuid* aOutTargetGuid, @@ -997,6 +1068,13 @@ APZCTreeManager::ReceiveInputEvent(WidgetInputEvent& aEvent, } switch (aEvent.mClass) { + case eMouseEventClass: { + WidgetMouseEventBase& mouseEvent = *aEvent.AsMouseEventBase(); + if (WillHandleMouseEvent(mouseEvent)) { + return ProcessMouseEvent(mouseEvent, aOutTargetGuid, aOutInputBlockId); + } + return ProcessEvent(aEvent, aOutTargetGuid, aOutInputBlockId); + } case eTouchEventClass: { WidgetTouchEvent& touchEvent = *aEvent.AsTouchEvent(); MultiTouchInput touchInput(touchEvent); @@ -1528,6 +1606,34 @@ APZCTreeManager::FindTargetNode(HitTestingTreeNode* aNode, return nullptr; } +nsRefPtr +APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics) +{ + MonitorAutoLock lock(mTreeLock); + + return FindScrollNode(mRootNode, aDragMetrics); +} + +HitTestingTreeNode* +APZCTreeManager::FindScrollNode(HitTestingTreeNode* aNode, + const AsyncDragMetrics& aDragMetrics) +{ + mTreeLock.AssertCurrentThreadOwns(); + + for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) { + if (node->MatchesScrollDragMetrics(aDragMetrics)) { + return node; + } + + HitTestingTreeNode* match = FindScrollNode(node->GetLastChild(), aDragMetrics); + if (match) { + return match; + } + } + + return nullptr; +} + AsyncPanZoomController* APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode, const ParentLayerPoint& aHitTestPoint, diff --git a/gfx/layers/apz/src/APZCTreeManager.h b/gfx/layers/apz/src/APZCTreeManager.h index b58c6ab72b..1397965fbc 100644 --- a/gfx/layers/apz/src/APZCTreeManager.h +++ b/gfx/layers/apz/src/APZCTreeManager.h @@ -18,6 +18,7 @@ #include "mozilla/layers/APZUtils.h" // for HitTestResult #include "mozilla/layers/TouchCounter.h"// for TouchCounter #include "mozilla/Monitor.h" // for Monitor +#include "mozilla/TimeStamp.h" // for mozilla::TimeStamp #include "mozilla/Vector.h" // for mozilla::Vector #include "nsAutoPtr.h" // for nsRefPtr #include "nsCOMPtr.h" // for already_AddRefed @@ -41,6 +42,7 @@ enum AllowedTouchBehavior { }; class Layer; +class AsyncDragMetrics; class AsyncPanZoomController; class CompositorParent; class OverscrollHandoffChain; @@ -96,6 +98,7 @@ class APZCTreeManager { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager) typedef mozilla::layers::AllowedTouchBehavior AllowedTouchBehavior; + typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; // Helper struct to hold some state while we build the hit-testing tree. The // sole purpose of this struct is to shorten the argument list to @@ -294,6 +297,12 @@ public: */ static float GetDPI() { return sDPI; } + /** + * Find the hit testing node for the scrollbar thumb that matches these + * drag metrics. + */ + nsRefPtr FindScrollNode(const AsyncDragMetrics& aDragMetrics); + /** * Sets allowed touch behavior values for current touch-session for specific * input block (determined by aInputBlock). @@ -385,6 +394,9 @@ public: nsRefPtr aOverscrollHandoffChain, bool aHandoff); + void StartScrollbarDrag(const ScrollableLayerGuid& aGuid, + const AsyncDragMetrics& aDragMetrics); + /* * Build the chain of APZCs that will handle overscroll for a pan starting at |aInitialTarget|. */ @@ -427,6 +439,8 @@ private: HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode, const ScrollableLayerGuid& aGuid, GuidComparator aComparator); + HitTestingTreeNode* FindScrollNode(HitTestingTreeNode* aNode, + const AsyncDragMetrics& aDragMetrics); AsyncPanZoomController* GetAPZCAtPoint(HitTestingTreeNode* aNode, const ParentLayerPoint& aHitTestPoint, HitTestResult* aOutHitResult); @@ -445,6 +459,9 @@ private: nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent, ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutInputBlockId); + nsEventStatus ProcessMouseEvent(WidgetMouseEventBase& aInput, + ScrollableLayerGuid* aOutTargetGuid, + uint64_t* aOutInputBlockId); void UpdateWheelTransaction(WidgetInputEvent& aEvent); void UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode, const ZoomConstraints& aConstraints); diff --git a/gfx/layers/apz/src/AsyncDragMetrics.h b/gfx/layers/apz/src/AsyncDragMetrics.h new file mode 100644 index 0000000000..54b60f8230 --- /dev/null +++ b/gfx/layers/apz/src/AsyncDragMetrics.h @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_layers_DragMetrics_h +#define mozilla_layers_DragMetrics_h + +#include "FrameMetrics.h" + +namespace IPC { +template struct ParamTraits; +} // namespace IPC + +namespace mozilla { + +namespace layers { + +class AsyncDragMetrics { + friend struct IPC::ParamTraits; + +public: + enum DragDirection { + NONE, + VERTICAL, + HORIZONTAL, + SENTINEL, + }; + + // IPC constructor + AsyncDragMetrics() + : mViewId(0) + , mPresShellId(0) + , mDragStartSequenceNumber(0) + , mScrollbarDragOffset(0) + , mDirection(NONE) + {} + + AsyncDragMetrics(const FrameMetrics::ViewID& aViewId, + uint32_t aPresShellId, + uint64_t aDragStartSequenceNumber, + CSSIntCoord aScrollbarDragOffset, + const CSSIntRect& aScrollTrack, + DragDirection aDirection) + : mViewId(aViewId) + , mPresShellId(aPresShellId) + , mDragStartSequenceNumber(aDragStartSequenceNumber) + , mScrollbarDragOffset(aScrollbarDragOffset) + , mScrollTrack(aScrollTrack) + , mDirection(aDirection) + {} + + FrameMetrics::ViewID mViewId; + uint32_t mPresShellId; + uint64_t mDragStartSequenceNumber; + CSSIntCoord mScrollbarDragOffset; + CSSIntRect mScrollTrack; + DragDirection mDirection; +}; + +} +} + +#endif diff --git a/gfx/layers/apz/src/AsyncPanZoomController.cpp b/gfx/layers/apz/src/AsyncPanZoomController.cpp index 67ce4a570d..9f004ad252 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.cpp +++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp @@ -13,6 +13,7 @@ #include "Compositor.h" // for Compositor #include "FrameMetrics.h" // for FrameMetrics, etc #include "GestureEventListener.h" // for GestureEventListener +#include "HitTestingTreeNode.h" // for HitTestingTreeNode #include "InputData.h" // for MultiTouchInput, etc #include "InputBlockState.h" // for InputBlockState, TouchBlockState #include "InputQueue.h" // for InputQueue @@ -952,6 +953,95 @@ AsyncPanZoomController::ArePointerEventsConsumable(TouchBlockState* aBlock, uint return true; } +template +static float GetAxisStart(AsyncDragMetrics::DragDirection aDir, T aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x; + } else { + return aValue.y; + } +} + +template +static float GetAxisEnd(AsyncDragMetrics::DragDirection aDir, T aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.x + aValue.width; + } else { + return aValue.y + aValue.height; + } +} + +template +static float GetAxisSize(AsyncDragMetrics::DragDirection aDir, T aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.width; + } else { + return aValue.height; + } +} + +template +static float GetAxisScale(AsyncDragMetrics::DragDirection aDir, T aValue) { + if (aDir == AsyncDragMetrics::HORIZONTAL) { + return aValue.xScale; + } else { + return aValue.yScale; + } +} + +nsEventStatus AsyncPanZoomController::HandleDragEvent(const MouseInput& aEvent, + const AsyncDragMetrics& aDragMetrics) +{ + nsRefPtr node = + GetApzcTreeManager()->FindScrollNode(aDragMetrics); + if (!node) { + return nsEventStatus_eConsumeNoDefault; + } + + CSSPoint scrollFramePoint = aEvent.mLocalOrigin / GetFrameMetrics().GetZoom(); + // The scrollbar can be transformed with the frame but the pres shell + // resolution is only applied to the scroll frame. + CSSPoint scrollbarPoint = scrollFramePoint * GetFrameMetrics().GetPresShellResolution(); + CSSRect cssCompositionBound = GetFrameMetrics().GetCompositionBounds() / GetFrameMetrics().GetZoom(); + + float mousePosition = GetAxisStart(aDragMetrics.mDirection, scrollbarPoint) - + aDragMetrics.mScrollbarDragOffset - + GetAxisStart(aDragMetrics.mDirection, cssCompositionBound) - + GetAxisStart(aDragMetrics.mDirection, aDragMetrics.mScrollTrack); + + float scrollMax = GetAxisEnd(aDragMetrics.mDirection, aDragMetrics.mScrollTrack); + scrollMax -= node->GetScrollSize() / + GetAxisScale(aDragMetrics.mDirection, GetFrameMetrics().GetZoom()) * + GetFrameMetrics().GetPresShellResolution(); + + float scrollPercent = mousePosition / scrollMax; + + float minScrollPosition = + GetAxisStart(aDragMetrics.mDirection, GetFrameMetrics().GetScrollableRect().TopLeft()); + float maxScrollPosition = + GetAxisSize(aDragMetrics.mDirection, GetFrameMetrics().GetScrollableRect()) - + GetAxisSize(aDragMetrics.mDirection, GetFrameMetrics().GetCompositionBounds()); + float scrollPosition = scrollPercent * maxScrollPosition; + + scrollPosition = std::max(scrollPosition, minScrollPosition); + scrollPosition = std::min(scrollPosition, maxScrollPosition); + + CSSPoint scrollOffset; + if (aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) { + scrollOffset = CSSPoint(scrollPosition, 0); + } else { + scrollOffset = CSSPoint(0, scrollPosition); + } + mFrameMetrics.SetScrollOffset(scrollOffset); + ScheduleCompositeAndMaybeRepaint(); + UpdateSharedCompositorFrameMetrics(); + + // Here we consume the events. This means that the content scrollbars + // will only see the initial mouse down and the final mouse up. + // APZ will still update the scroll position. + return nsEventStatus_eConsumeNoDefault; +} + nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent, const Matrix4x4& aTransformToApzc) { APZThreadUtils::AssertOnControllerThread(); @@ -961,7 +1051,7 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent, switch (aEvent.mInputType) { case MULTITOUCH_INPUT: { MultiTouchInput multiTouchInput = aEvent.AsMultiTouchInput(); - if (!multiTouchInput.TransformToLocal(aTransformToApzc)) { + if (!multiTouchInput.TransformToLocal(aTransformToApzc)) { return rv; } @@ -1001,6 +1091,16 @@ nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent, } break; } + case MOUSE_INPUT: { + ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput(); + if (!scrollInput.TransformToLocal(aTransformToApzc)) { + return rv; + } + + // TODO Need to implement blocks to properly handle this. + //rv = HandleDragEvent(scrollInput, dragMetrics); + break; + } case SCROLLWHEEL_INPUT: { ScrollWheelInput scrollInput = aEvent.AsScrollWheelInput(); if (!scrollInput.TransformToLocal(aTransformToApzc)) { @@ -2950,6 +3050,9 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri bool smoothScrollRequested = aLayerMetrics.GetDoSmoothScroll() && (aLayerMetrics.GetScrollGeneration() != mFrameMetrics.GetScrollGeneration()); + // TODO if we're in a drag and scrollOffsetUpdated is set then we want to + // ignore it + if (aIsFirstPaint || isDefault) { // Initialize our internal state to something sane when the content // that was just painted is something we knew nothing about previously diff --git a/gfx/layers/apz/src/AsyncPanZoomController.h b/gfx/layers/apz/src/AsyncPanZoomController.h index 88058a047d..4d4b7d8fbe 100644 --- a/gfx/layers/apz/src/AsyncPanZoomController.h +++ b/gfx/layers/apz/src/AsyncPanZoomController.h @@ -40,6 +40,7 @@ class SharedMemoryBasic; namespace layers { +class AsyncDragMetrics; struct ScrollableLayerGuid; class CompositorParent; class GestureEventListener; @@ -266,6 +267,9 @@ public: */ void SendAsyncScrollEvent(); + nsEventStatus HandleDragEvent(const MouseInput& aEvent, + const AsyncDragMetrics& aDragMetrics); + /** * Handler for events which should not be intercepted by the touch listener. */ diff --git a/gfx/layers/apz/src/HitTestingTreeNode.cpp b/gfx/layers/apz/src/HitTestingTreeNode.cpp index cb3308e963..551c2763fe 100644 --- a/gfx/layers/apz/src/HitTestingTreeNode.cpp +++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp @@ -86,6 +86,30 @@ HitTestingTreeNode::SetLastChild(HitTestingTreeNode* aChild) } } +void +HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize) +{ + mScrollViewId = aScrollViewId; + mScrollDir = aDir; + mScrollSize = aScrollSize;; +} + +bool +HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const +{ + return ((mScrollDir == Layer::HORIZONTAL && + aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) || + (mScrollDir == Layer::VERTICAL && + aDragMetrics.mDirection == AsyncDragMetrics::VERTICAL)) && + mScrollViewId == aDragMetrics.mViewId; +} + +int32_t +HitTestingTreeNode::GetScrollSize() const +{ + return mScrollSize; +} + void HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling) { diff --git a/gfx/layers/apz/src/HitTestingTreeNode.h b/gfx/layers/apz/src/HitTestingTreeNode.h index 4c1a9cf167..b34803a5c6 100644 --- a/gfx/layers/apz/src/HitTestingTreeNode.h +++ b/gfx/layers/apz/src/HitTestingTreeNode.h @@ -9,14 +9,16 @@ #include "APZUtils.h" // for HitTestResult #include "FrameMetrics.h" // for ScrollableLayerGuid +#include "Layers.h" #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/layers/LayersTypes.h" // for EventRegions #include "mozilla/Maybe.h" // for Maybe -#include "mozilla/nsRefPtr.h" // for nsRefPtr +#include "mozilla/nsRefPtr.h" // for nsRefPtr namespace mozilla { namespace layers { +class AsyncDragMetrics; class AsyncPanZoomController; /** @@ -87,6 +89,13 @@ public: const Maybe& aClipRegion, const EventRegionsOverride& aOverride); bool IsOutsideClip(const ParentLayerPoint& aPoint) const; + + /* Scrollbar info */ + + void SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize); + bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const; + int32_t GetScrollSize() const; + /* Convert aPoint into the LayerPixel space for the layer corresponding to * this node. */ Maybe Untransform(const ParentLayerPoint& aPoint) const; @@ -111,6 +120,10 @@ private: uint64_t mLayersId; + FrameMetrics::ViewID mScrollViewId; + Layer::ScrollDirection mScrollDir; + int32_t mScrollSize; + /* Let {L,M} be the {layer, scrollable metrics} pair that this node * corresponds to in the layer tree. mEventRegions contains the event regions * from L, in the case where event-regions are enabled. If event-regions are diff --git a/gfx/layers/basic/BasicCanvasLayer.cpp b/gfx/layers/basic/BasicCanvasLayer.cpp index 080d4b79ef..6d4d977c34 100644 --- a/gfx/layers/basic/BasicCanvasLayer.cpp +++ b/gfx/layers/basic/BasicCanvasLayer.cpp @@ -52,7 +52,7 @@ BasicCanvasLayer::Paint(DrawTarget* aDT, FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, mBounds.width, mBounds.height), - mSurface, ToFilter(mFilter), + mSurface, mFilter, DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)), aMaskLayer); diff --git a/gfx/layers/basic/BasicImageLayer.cpp b/gfx/layers/basic/BasicImageLayer.cpp index d4d3e46d78..ade40a758e 100644 --- a/gfx/layers/basic/BasicImageLayer.cpp +++ b/gfx/layers/basic/BasicImageLayer.cpp @@ -85,8 +85,8 @@ BasicImageLayer::Paint(DrawTarget* aDT, } gfx::IntSize size = mSize = surface->GetSize(); - FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, size.width, size.height), - surface, ToFilter(mFilter), + FillRectWithMask(aDT, aDeviceOffset, Rect(0, 0, size.width, size.height), + surface, mFilter, DrawOptions(GetEffectiveOpacity(), GetEffectiveOperator(this)), aMaskLayer); diff --git a/gfx/layers/client/ClientCanvasLayer.cpp b/gfx/layers/client/ClientCanvasLayer.cpp index e57ee2e234..0e1fcdf6c5 100644 --- a/gfx/layers/client/ClientCanvasLayer.cpp +++ b/gfx/layers/client/ClientCanvasLayer.cpp @@ -92,7 +92,7 @@ ClientCanvasLayer::Initialize(const Data& aData) #elif defined(MOZ_WIDGET_GONK) factory = MakeUnique(mGLContext, caps, forwarder, mFlags); #elif defined(GL_PROVIDER_GLX) - if (sGLXLibrary.UseTextureFromPixmap()) + if (sGLXLibrary.UseSurfaceSharing()) factory = SurfaceFactory_GLXDrawable::Create(mGLContext, caps, forwarder, mFlags); #else if (mGLContext->GetContextType() == GLContextType::EGL) { diff --git a/gfx/layers/client/CompositableClient.h b/gfx/layers/client/CompositableClient.h index 1411f0d2bb..e928fcf42c 100644 --- a/gfx/layers/client/CompositableClient.h +++ b/gfx/layers/client/CompositableClient.h @@ -127,6 +127,10 @@ public: explicit CompositableClient(CompositableForwarder* aForwarder, TextureFlags aFlags = TextureFlags::NO_FLAGS); + virtual void Dump(std::stringstream& aStream, + const char* aPrefix="", + bool aDumpHtml=false) {}; + virtual TextureInfo GetTextureInfo() const = 0; LayersBackend GetCompositorBackendType() const; diff --git a/gfx/layers/client/ContentClient.h b/gfx/layers/client/ContentClient.h index 7a928f98c0..62815b3e81 100644 --- a/gfx/layers/client/ContentClient.h +++ b/gfx/layers/client/ContentClient.h @@ -88,10 +88,6 @@ public: virtual void PrintInfo(std::stringstream& aStream, const char* aPrefix); - virtual void Dump(std::stringstream& aStream, - const char* aPrefix="", - bool aDumpHtml=false) {}; - virtual void Clear() = 0; virtual RotatedContentBuffer::PaintState BeginPaintBuffer(PaintedLayer* aLayer, uint32_t aFlags) = 0; diff --git a/gfx/layers/composite/CanvasLayerComposite.cpp b/gfx/layers/composite/CanvasLayerComposite.cpp index ae9e988347..8734a4aaf6 100644 --- a/gfx/layers/composite/CanvasLayerComposite.cpp +++ b/gfx/layers/composite/CanvasLayerComposite.cpp @@ -6,7 +6,6 @@ #include "CanvasLayerComposite.h" #include "composite/CompositableHost.h" // for CompositableHost #include "gfx2DGlue.h" // for ToFilter -#include "GraphicsFilter.h" // for GraphicsFilter #include "gfxUtils.h" // for gfxUtils, etc #include "mozilla/gfx/Matrix.h" // for Matrix4x4 #include "mozilla/gfx/Point.h" // for Point @@ -129,7 +128,7 @@ CanvasLayerComposite::CleanupResources() gfx::Filter CanvasLayerComposite::GetEffectFilter() { - GraphicsFilter filter = mFilter; + gfx::Filter filter = mFilter; #ifdef ANDROID // Bug 691354 // Using the LINEAR filter we get unexplained artifacts. @@ -137,10 +136,10 @@ CanvasLayerComposite::GetEffectFilter() Matrix matrix; bool is2D = GetEffectiveTransform().Is2D(&matrix); if (is2D && !ThebesMatrix(matrix).HasNonTranslationOrFlip()) { - filter = GraphicsFilter::FILTER_NEAREST; + filter = Filter::POINT; } #endif - return gfx::ToFilter(filter); + return filter; } void diff --git a/gfx/layers/composite/ImageHost.h b/gfx/layers/composite/ImageHost.h index a831637690..e77afa9a00 100644 --- a/gfx/layers/composite/ImageHost.h +++ b/gfx/layers/composite/ImageHost.h @@ -91,6 +91,15 @@ public: return img ? img->mFrameID : -1; } + int32_t GetProducerID() + { + const TimedImage* img = ChooseImage(); + return img ? img->mProducerID : -1; + } + + int32_t GetLastFrameID() const { return mLastFrameID; } + int32_t GetLastProducerID() const { return mLastProducerID; } + enum Bias { // Don't apply bias to frame times BIAS_NONE, diff --git a/gfx/layers/composite/ImageLayerComposite.cpp b/gfx/layers/composite/ImageLayerComposite.cpp index 9d04f879fa..2e3ee0619b 100644 --- a/gfx/layers/composite/ImageLayerComposite.cpp +++ b/gfx/layers/composite/ImageLayerComposite.cpp @@ -166,7 +166,7 @@ ImageLayerComposite::CleanupResources() gfx::Filter ImageLayerComposite::GetEffectFilter() { - return gfx::ToFilter(mFilter); + return mFilter; } void diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 6a13c9df1a..591dd2e109 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -544,6 +544,7 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, , mEGLSurfaceSize(aSurfaceWidth, aSurfaceHeight) , mPauseCompositionMonitor("PauseCompositionMonitor") , mResumeCompositionMonitor("ResumeCompositionMonitor") + , mRootLayerTreeID(AllocateLayerTreeId()) , mOverrideComposeReadiness(false) , mForceCompositionTask(nullptr) , mCompositorThreadHolder(sCompositorThreadHolder) @@ -563,7 +564,6 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(SetThreadPriority)); - mRootLayerTreeID = AllocateLayerTreeId(); { // scope lock MonitorAutoLock lock(*sIndirectLayerTreesLock); diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 4ac9e3d085..9fbef355b6 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -459,7 +459,7 @@ protected: mozilla::Monitor mResumeCompositionMonitor; uint64_t mCompositorID; - uint64_t mRootLayerTreeID; + const uint64_t mRootLayerTreeID; bool mOverrideComposeReadiness; CancelableTask* mForceCompositionTask; diff --git a/gfx/layers/ipc/LayersMessages.ipdlh b/gfx/layers/ipc/LayersMessages.ipdlh index d6ef82b45e..c6eaf0a961 100644 --- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -17,7 +17,7 @@ include "gfxipc/ShadowLayerUtils.h"; include "mozilla/GfxMessageUtils.h"; include "ImageLayers.h"; -using mozilla::GraphicsFilterType from "mozilla/GfxMessageUtils.h"; +using mozilla::gfx::Filter from "mozilla/gfx/2D.h"; using struct mozilla::gfx::Color from "mozilla/gfx/2D.h"; using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h"; using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h"; @@ -252,14 +252,14 @@ struct ContainerLayerAttributes { uint64_t hmdInfo; }; struct ColorLayerAttributes { LayerColor color; IntRect bounds; }; -struct CanvasLayerAttributes { GraphicsFilterType filter; IntRect bounds; }; +struct CanvasLayerAttributes { Filter filter; IntRect bounds; }; struct RefLayerAttributes { int64_t id; // TODO: Once bug 1132895 is fixed we shouldn't need to propagate the override // explicitly here. EventRegionsOverride eventRegionsOverride; }; -struct ImageLayerAttributes { GraphicsFilterType filter; IntSize scaleToSize; ScaleMode scaleMode; }; +struct ImageLayerAttributes { Filter filter; IntSize scaleToSize; ScaleMode scaleMode; }; union SpecificLayerAttributes { null_t; diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index f5daf5d9a8..99937af022 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -94,6 +94,7 @@ EXPORTS.mozilla.layers += [ # proper interface for the code there 'apz/src/APZCTreeManager.h', 'apz/src/APZUtils.h', + 'apz/src/AsyncDragMetrics.h', 'apz/src/AsyncPanZoomAnimation.h', 'apz/src/TouchCounter.h', 'apz/testutil/APZTestData.h', diff --git a/gfx/layers/opengl/CompositorOGL.cpp b/gfx/layers/opengl/CompositorOGL.cpp index 52ed318a5a..c5079d2855 100644 --- a/gfx/layers/opengl/CompositorOGL.cpp +++ b/gfx/layers/opengl/CompositorOGL.cpp @@ -12,9 +12,7 @@ #include "GLUploadHelpers.h" #include "Layers.h" // for WriteSnapshotToDumpFile #include "LayerScope.h" // for LayerScope -#include "gfx2DGlue.h" // for ThebesFilter #include "gfxCrashReporterUtils.h" // for ScopedGfxFeatureReporter -#include "GraphicsFilter.h" // for GraphicsFilter #include "gfxPlatform.h" // for gfxPlatform #include "gfxPrefs.h" // for gfxPrefs #include "gfxRect.h" // for gfxRect diff --git a/gfx/layers/opengl/CompositorOGLVR.cpp b/gfx/layers/opengl/CompositorOGLVR.cpp index e738a79be2..c11a6d31d1 100644 --- a/gfx/layers/opengl/CompositorOGLVR.cpp +++ b/gfx/layers/opengl/CompositorOGLVR.cpp @@ -18,9 +18,8 @@ #include "GLUploadHelpers.h" #include "Layers.h" // for WriteSnapshotToDumpFile #include "LayerScope.h" // for LayerScope -#include "gfx2DGlue.h" // for ThebesFilter +#include "gfxCrashReporterUtils.h" // for ScopedGfxFeatureReporter #include "gfxMatrix.h" // for gfxMatrix -#include "GraphicsFilter.h" // for GraphicsFilter #include "gfxPlatform.h" // for gfxPlatform #include "gfxPrefs.h" // for gfxPrefs #include "gfxRect.h" // for gfxRect diff --git a/gfx/layers/protobuf/LayerScopePacket.pb.cc b/gfx/layers/protobuf/LayerScopePacket.pb.cc index 28e7f42526..6e7c1ab630 100644 --- a/gfx/layers/protobuf/LayerScopePacket.pb.cc +++ b/gfx/layers/protobuf/LayerScopePacket.pb.cc @@ -1186,16 +1186,10 @@ bool LayersPacket_Layer_Filter_IsValid(int value) { } #ifndef _MSC_VER -const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_FAST; const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GOOD; -const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BEST; -const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_NEAREST; -const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_BILINEAR; -const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_GAUSSIAN; +const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_LINEAR; +const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_POINT; const LayersPacket_Layer_Filter LayersPacket_Layer::FILTER_SENTINEL; -const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MIN; -const LayersPacket_Layer_Filter LayersPacket_Layer::Filter_MAX; -const int LayersPacket_Layer::Filter_ARRAYSIZE; #endif // _MSC_VER #ifndef _MSC_VER const int LayersPacket_Layer_Size::kWFieldNumber; diff --git a/gfx/layers/protobuf/LayerScopePacket.pb.h b/gfx/layers/protobuf/LayerScopePacket.pb.h index 6cbb1d1447..5774386b3c 100644 --- a/gfx/layers/protobuf/LayerScopePacket.pb.h +++ b/gfx/layers/protobuf/LayerScopePacket.pb.h @@ -76,18 +76,12 @@ const LayersPacket_Layer_ScrollingDirect LayersPacket_Layer_ScrollingDirect_Scro const int LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE = LayersPacket_Layer_ScrollingDirect_ScrollingDirect_MAX + 1; enum LayersPacket_Layer_Filter { - LayersPacket_Layer_Filter_FILTER_FAST = 0, - LayersPacket_Layer_Filter_FILTER_GOOD = 1, - LayersPacket_Layer_Filter_FILTER_BEST = 2, - LayersPacket_Layer_Filter_FILTER_NEAREST = 3, - LayersPacket_Layer_Filter_FILTER_BILINEAR = 4, - LayersPacket_Layer_Filter_FILTER_GAUSSIAN = 5, - LayersPacket_Layer_Filter_FILTER_SENTINEL = 6 + LayersPacket_Layer_Filter_FILTER_GOOD = 0, + LayersPacket_Layer_Filter_FILTER_LINEAR = 1, + LayersPacket_Layer_Filter_FILTER_POINT = 2, + LayersPacket_Layer_Filter_FILTER_SENTINEL = 3 }; bool LayersPacket_Layer_Filter_IsValid(int value); -const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MIN = LayersPacket_Layer_Filter_FILTER_FAST; -const LayersPacket_Layer_Filter LayersPacket_Layer_Filter_Filter_MAX = LayersPacket_Layer_Filter_FILTER_SENTINEL; -const int LayersPacket_Layer_Filter_Filter_ARRAYSIZE = LayersPacket_Layer_Filter_Filter_MAX + 1; enum Packet_DataType { Packet_DataType_FRAMESTART = 1, @@ -1159,22 +1153,13 @@ class LayersPacket_Layer : public ::google::protobuf::MessageLite { LayersPacket_Layer_ScrollingDirect_ScrollingDirect_ARRAYSIZE; typedef LayersPacket_Layer_Filter Filter; - static const Filter FILTER_FAST = LayersPacket_Layer_Filter_FILTER_FAST; static const Filter FILTER_GOOD = LayersPacket_Layer_Filter_FILTER_GOOD; - static const Filter FILTER_BEST = LayersPacket_Layer_Filter_FILTER_BEST; - static const Filter FILTER_NEAREST = LayersPacket_Layer_Filter_FILTER_NEAREST; - static const Filter FILTER_BILINEAR = LayersPacket_Layer_Filter_FILTER_BILINEAR; - static const Filter FILTER_GAUSSIAN = LayersPacket_Layer_Filter_FILTER_GAUSSIAN; + static const Filter FILTER_LINEAR = LayersPacket_Layer_Filter_FILTER_LINEAR; + static const Filter FILTER_POINT = LayersPacket_Layer_Filter_FILTER_POINT; static const Filter FILTER_SENTINEL = LayersPacket_Layer_Filter_FILTER_SENTINEL; static inline bool Filter_IsValid(int value) { return LayersPacket_Layer_Filter_IsValid(value); } - static const Filter Filter_MIN = - LayersPacket_Layer_Filter_Filter_MIN; - static const Filter Filter_MAX = - LayersPacket_Layer_Filter_Filter_MAX; - static const int Filter_ARRAYSIZE = - LayersPacket_Layer_Filter_Filter_ARRAYSIZE; // accessors ------------------------------------------------------- diff --git a/gfx/thebes/GraphicsFilter.h b/gfx/thebes/GraphicsFilter.h deleted file mode 100644 index 4b9fe3d016..0000000000 --- a/gfx/thebes/GraphicsFilter.h +++ /dev/null @@ -1,20 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef GraphicsFilter_h -#define GraphicsFilter_h - -enum class GraphicsFilter : int { - FILTER_FAST, - FILTER_GOOD, - FILTER_BEST, - FILTER_NEAREST, - FILTER_BILINEAR, - FILTER_GAUSSIAN, - FILTER_SENTINEL -}; - -#endif - diff --git a/gfx/thebes/gfx2DGlue.h b/gfx/thebes/gfx2DGlue.h index bb5788b088..5c79a951a3 100644 --- a/gfx/thebes/gfx2DGlue.h +++ b/gfx/thebes/gfx2DGlue.h @@ -1,3 +1,5 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ @@ -5,7 +7,6 @@ #ifndef GFX_2D_GLUE_H #define GFX_2D_GLUE_H - #include "gfxPlatform.h" #include "gfxRect.h" #include "gfxMatrix.h" @@ -60,52 +61,6 @@ inline Size ToSize(const gfxSize &aSize) return Size(Float(aSize.width), Float(aSize.height)); } -inline Filter ToFilter(GraphicsFilter aFilter) -{ - switch (aFilter) { - case GraphicsFilter::FILTER_NEAREST: - return Filter::POINT; - case GraphicsFilter::FILTER_GOOD: - return Filter::GOOD; - default: - return Filter::LINEAR; - } -} - -inline GraphicsFilter ThebesFilter(Filter aFilter) -{ - switch (aFilter) { - case Filter::POINT: - return GraphicsFilter::FILTER_NEAREST; - default: - return GraphicsFilter::FILTER_BEST; - } -} - -inline ExtendMode ToExtendMode(gfxPattern::GraphicsExtend aExtend) -{ - switch (aExtend) { - case gfxPattern::EXTEND_REPEAT: - return ExtendMode::REPEAT; - case gfxPattern::EXTEND_REFLECT: - return ExtendMode::REFLECT; - default: - return ExtendMode::CLAMP; - } -} - -inline gfxPattern::GraphicsExtend ThebesExtend(ExtendMode aExtend) -{ - switch (aExtend) { - case ExtendMode::REPEAT: - return gfxPattern::EXTEND_REPEAT; - case ExtendMode::REFLECT: - return gfxPattern::EXTEND_REFLECT; - default: - return gfxPattern::EXTEND_PAD; - } -} - inline gfxPoint ThebesPoint(const Point &aPoint) { return gfxPoint(aPoint.x, aPoint.y); diff --git a/gfx/thebes/gfxDrawable.cpp b/gfx/thebes/gfxDrawable.cpp index c7ee4af239..ec78b59c68 100644 --- a/gfx/thebes/gfxDrawable.cpp +++ b/gfx/thebes/gfxDrawable.cpp @@ -34,7 +34,7 @@ gfxSurfaceDrawable::DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, bool aRepeat, - const GraphicsFilter& aFilter, + const Filter& aFilter, gfxFloat aOpacity) { if (!mSourceSurface) { @@ -60,7 +60,7 @@ bool gfxSurfaceDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, bool aRepeat, - const GraphicsFilter& aFilter, + const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { @@ -77,7 +77,7 @@ gfxSurfaceDrawable::DrawInternal(gfxContext* aContext, const gfxRect& aFillRect, const IntRect& aSamplingRect, bool aRepeat, - const GraphicsFilter& aFilter, + const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { @@ -91,7 +91,7 @@ gfxSurfaceDrawable::DrawInternal(gfxContext* aContext, patternTransform.Invert(); SurfacePattern pattern(mSourceSurface, extend, - patternTransform, ToFilter(aFilter), aSamplingRect); + patternTransform, aFilter, aSamplingRect); Rect fillRect = ToRect(aFillRect); DrawTarget* dt = aContext->GetDrawTarget(); @@ -116,7 +116,7 @@ gfxCallbackDrawable::gfxCallbackDrawable(gfxDrawingCallback* aCallback, } already_AddRefed -gfxCallbackDrawable::MakeSurfaceDrawable(const GraphicsFilter aFilter) +gfxCallbackDrawable::MakeSurfaceDrawable(const Filter aFilter) { SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(gfxContentType::COLOR_ALPHA); @@ -141,7 +141,7 @@ bool gfxCallbackDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, bool aRepeat, - const GraphicsFilter& aFilter, + const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { @@ -181,7 +181,7 @@ public: virtual bool operator()(gfxContext* aContext, const gfxRect& aFillRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const gfxMatrix& aTransform = gfxMatrix()) { return mDrawable->Draw(aContext, aFillRect, false, aFilter, 1.0, @@ -205,7 +205,7 @@ bool gfxPatternDrawable::Draw(gfxContext* aContext, const gfxRect& aFillRect, bool aRepeat, - const GraphicsFilter& aFilter, + const Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform) { diff --git a/gfx/thebes/gfxDrawable.h b/gfx/thebes/gfxDrawable.h index 502376cd3e..e1a470146d 100644 --- a/gfx/thebes/gfxDrawable.h +++ b/gfx/thebes/gfxDrawable.h @@ -9,7 +9,6 @@ #include "nsAutoPtr.h" #include "gfxRect.h" #include "gfxMatrix.h" -#include "GraphicsFilter.h" #include "mozilla/gfx/2D.h" class gfxContext; @@ -36,14 +35,14 @@ public: virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, bool aRepeat, - const GraphicsFilter& aFilter, + const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()) = 0; virtual bool DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, bool aRepeat, - const GraphicsFilter& aFilter, + const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0) { return false; @@ -71,14 +70,14 @@ public: virtual bool Draw(gfxContext* aContext, const gfxRect& aFillRect, bool aRepeat, - const GraphicsFilter& aFilter, + const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0, const gfxMatrix& aTransform = gfxMatrix()); virtual bool DrawWithSamplingRect(gfxContext* aContext, const gfxRect& aFillRect, const gfxRect& aSamplingRect, bool aRepeat, - const GraphicsFilter& aFilter, + const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity = 1.0); protected: @@ -86,7 +85,7 @@ protected: const gfxRect& aFillRect, const mozilla::gfx::IntRect& aSamplingRect, bool aRepeat, - const GraphicsFilter& aFilter, + const mozilla::gfx::Filter& aFilter, gfxFloat aOpacity, const gfxMatrix& aTransform = gfxMatrix()); @@ -113,9 +112,9 @@ public: * @return whether drawing was successful */ virtual bool operator()(gfxContext* aContext, - const gfxRect& aFillRect, - const GraphicsFilter& aFilter, - const gfxMatrix& aTransform = gfxMatrix()) = 0; + const gfxRect& aFillRect, + const mozilla::gfx::Filter& aFilter, + const gfxMatrix& aTransform = gfxMatrix()) = 0; }; @@ -129,14 +128,14 @@ public: virtual ~gfxCallbackDrawable() {} virtual bool Draw(gfxContext* aContext, - const gfxRect& aFillRect, - bool aRepeat, - const GraphicsFilter& aFilter, - gfxFloat aOpacity = 1.0, - const gfxMatrix& aTransform = gfxMatrix()); + const gfxRect& aFillRect, + bool aRepeat, + const mozilla::gfx::Filter& aFilter, + gfxFloat aOpacity = 1.0, + const gfxMatrix& aTransform = gfxMatrix()); protected: - already_AddRefed MakeSurfaceDrawable(const GraphicsFilter aFilter = GraphicsFilter::FILTER_FAST); + already_AddRefed MakeSurfaceDrawable(mozilla::gfx::Filter aFilter = mozilla::gfx::Filter::LINEAR); nsRefPtr mCallback; nsRefPtr mSurfaceDrawable; @@ -153,11 +152,11 @@ public: virtual ~gfxPatternDrawable(); virtual bool Draw(gfxContext* aContext, - const gfxRect& aFillRect, - bool aRepeat, - const GraphicsFilter& aFilter, - gfxFloat aOpacity = 1.0, - const gfxMatrix& aTransform = gfxMatrix()); + const gfxRect& aFillRect, + bool aRepeat, + const mozilla::gfx::Filter& aFilter, + gfxFloat aOpacity = 1.0, + const gfxMatrix& aTransform = gfxMatrix()); protected: already_AddRefed MakeCallbackDrawable(); diff --git a/gfx/thebes/gfxPattern.cpp b/gfx/thebes/gfxPattern.cpp index f83aff7d35..dd1ea16fa5 100644 --- a/gfx/thebes/gfxPattern.cpp +++ b/gfx/thebes/gfxPattern.cpp @@ -20,14 +20,14 @@ using namespace mozilla::gfx; gfxPattern::gfxPattern(const Color& aColor) - : mExtend(EXTEND_NONE) + : mExtend(ExtendMode::CLAMP) { mGfxPattern.InitColorPattern(ToDeviceColor(aColor)); } // linear gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1) - : mExtend(EXTEND_NONE) + : mExtend(ExtendMode::CLAMP) { mGfxPattern.InitLinearGradientPattern(Point(x0, y0), Point(x1, y1), nullptr); } @@ -35,7 +35,7 @@ gfxPattern::gfxPattern(gfxFloat x0, gfxFloat y0, gfxFloat x1, gfxFloat y1) // radial gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, gfxFloat cx1, gfxFloat cy1, gfxFloat radius1) - : mExtend(EXTEND_NONE) + : mExtend(ExtendMode::CLAMP) { mGfxPattern.InitRadialGradientPattern(Point(cx0, cy0), Point(cx1, cy1), radius0, radius1, nullptr); @@ -44,9 +44,9 @@ gfxPattern::gfxPattern(gfxFloat cx0, gfxFloat cy0, gfxFloat radius0, // Azure gfxPattern::gfxPattern(SourceSurface *aSurface, const Matrix &aPatternToUserSpace) : mPatternToUserSpace(aPatternToUserSpace) - , mExtend(EXTEND_NONE) + , mExtend(ExtendMode::CLAMP) { - mGfxPattern.InitSurfacePattern(aSurface, ToExtendMode(mExtend), Matrix(), // matrix is overridden in GetPattern() + mGfxPattern.InitSurfacePattern(aSurface, mExtend, Matrix(), // matrix is overridden in GetPattern() mozilla::gfx::Filter::GOOD); } @@ -75,8 +75,7 @@ gfxPattern::SetColorStops(GradientStops* aStops) void gfxPattern::CacheColorStops(const DrawTarget *aDT) { - mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, mStopsList, - ToExtendMode(mExtend)); + mStops = gfxGradientCache::GetOrCreateGradientStops(aDT, mStopsList, mExtend); } void @@ -132,15 +131,14 @@ gfxPattern::GetPattern(const DrawTarget *aTarget, if (!mStops && !mStopsList.IsEmpty()) { mStops = aTarget->CreateGradientStops(mStopsList.Elements(), - mStopsList.Length(), - ToExtendMode(mExtend)); + mStopsList.Length(), mExtend); } switch (mGfxPattern.GetPattern()->GetType()) { case PatternType::SURFACE: { SurfacePattern* surfacePattern = static_cast(mGfxPattern.GetPattern()); surfacePattern->mMatrix = patternToUser; - surfacePattern->mExtendMode = ToExtendMode(mExtend); + surfacePattern->mExtendMode = mExtend; break; } case PatternType::LINEAR_GRADIENT: { @@ -164,9 +162,9 @@ gfxPattern::GetPattern(const DrawTarget *aTarget, } void -gfxPattern::SetExtend(GraphicsExtend extend) +gfxPattern::SetExtend(ExtendMode aExtend) { - mExtend = extend; + mExtend = aExtend; mStops = nullptr; } @@ -183,29 +181,23 @@ gfxPattern::IsOpaque() return false; } -gfxPattern::GraphicsExtend -gfxPattern::Extend() const -{ - return mExtend; -} - void -gfxPattern::SetFilter(GraphicsFilter filter) +gfxPattern::SetFilter(gfx::Filter filter) { if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) { return; } - static_cast(mGfxPattern.GetPattern())->mFilter = ToFilter(filter); + static_cast(mGfxPattern.GetPattern())->mFilter = filter; } -GraphicsFilter +Filter gfxPattern::Filter() const { if (mGfxPattern.GetPattern()->GetType() != PatternType::SURFACE) { - return GraphicsFilter::FILTER_GOOD; + return gfx::Filter::GOOD; } - return ThebesFilter(static_cast(mGfxPattern.GetPattern())->mFilter); + return static_cast(mGfxPattern.GetPattern())->mFilter; } bool diff --git a/gfx/thebes/gfxPattern.h b/gfx/thebes/gfxPattern.h index 04ed39f614..845d8dcc17 100644 --- a/gfx/thebes/gfxPattern.h +++ b/gfx/thebes/gfxPattern.h @@ -12,7 +12,6 @@ #include "mozilla/Alignment.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/PatternHelpers.h" -#include "GraphicsFilter.h" #include "nsISupportsImpl.h" #include "nsAutoPtr.h" #include "nsTArray.h" @@ -53,31 +52,13 @@ public: mozilla::gfx::Matrix *aOriginalUserToDevice = nullptr); bool IsOpaque(); - enum GraphicsExtend { - EXTEND_NONE, - EXTEND_REPEAT, - EXTEND_REFLECT, - EXTEND_PAD, - - // Our own private flag for setting either NONE or PAD, - // depending on what the platform does for NONE. This is only - // relevant for surface patterns; for all other patterns, it - // behaves identical to PAD. On MacOS X, this becomes "NONE", - // because Quartz does the thing that we want at image edges; - // similarily on the win32 printing surface, since - // everything's done with GDI there. On other platforms, it - // usually becomes PAD. - EXTEND_PAD_EDGE = 1000 - }; - - // none, repeat, reflect - void SetExtend(GraphicsExtend extend); - GraphicsExtend Extend() const; + // clamp, repeat, reflect + void SetExtend(mozilla::gfx::ExtendMode aExtend); int CairoStatus(); - void SetFilter(GraphicsFilter filter); - GraphicsFilter Filter() const; + void SetFilter(mozilla::gfx::Filter filter); + mozilla::gfx::Filter Filter() const; /* returns TRUE if it succeeded */ bool GetSolidColor(mozilla::gfx::Color& aColorOut); @@ -91,7 +72,7 @@ private: mozilla::gfx::Matrix mPatternToUserSpace; mozilla::RefPtr mStops; nsTArray mStopsList; - GraphicsExtend mExtend; + mozilla::gfx::ExtendMode mExtend; }; #endif /* GFX_PATTERN_H */ diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 2965891581..1e37c4d1a3 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -2090,26 +2090,19 @@ gfxPlatform::GetLog(eGfxLog aWhichLog) switch (aWhichLog) { case eGfxLog_fontlist: return sFontlistLog; - break; case eGfxLog_fontinit: return sFontInitLog; - break; case eGfxLog_textrun: return sTextrunLog; - break; case eGfxLog_textrunui: return sTextrunuiLog; - break; case eGfxLog_cmapdata: return sCmapDataLog; - break; case eGfxLog_textperf: return sTextPerfLog; - break; - default: - break; } + MOZ_ASSERT_UNREACHABLE("Unexpected log type"); return nullptr; } @@ -2450,6 +2443,10 @@ gfxPlatform::GetApzSupportInfo(mozilla::widget::InfoObject& aObj) if (SupportsApzTouchInput()) { aObj.DefineProperty("ApzTouchInput", 1); } + + if (SupportsApzDragInput()) { + aObj.DefineProperty("ApzDragInput", 1); + } } /*static*/ bool @@ -2567,3 +2564,9 @@ gfxPlatform::UpdateDeviceInitData() SetDeviceInitData(data); } + +bool +gfxPlatform::SupportsApzDragInput() const +{ + return gfxPrefs::APZDragEnabled(); +} diff --git a/gfx/thebes/gfxPlatform.h b/gfx/thebes/gfxPlatform.h index 22f91cbd4f..b419c9e15d 100644 --- a/gfx/thebes/gfxPlatform.h +++ b/gfx/thebes/gfxPlatform.h @@ -632,6 +632,7 @@ public: virtual bool SupportsApzTouchInput() const { return false; } + bool SupportsApzDragInput() const; virtual void FlushContentDrawing() {} diff --git a/gfx/thebes/gfxPrefs.h b/gfx/thebes/gfxPrefs.h index e2aa862c4c..09d70cd111 100644 --- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -150,6 +150,7 @@ private: DECL_GFX_PREF(Live, "apz.cross_slide.enabled", APZCrossSlideEnabled, bool, false); DECL_GFX_PREF(Live, "apz.danger_zone_x", APZDangerZoneX, int32_t, 50); DECL_GFX_PREF(Live, "apz.danger_zone_y", APZDangerZoneY, int32_t, 100); + DECL_GFX_PREF(Live, "apz.drag.enabled", APZDragEnabled, bool, false); DECL_GFX_PREF(Live, "apz.enlarge_displayport_when_clipped", APZEnlargeDisplayPortWhenClipped, bool, false); DECL_GFX_PREF(Live, "apz.fling_accel_base_mult", APZFlingAccelBaseMultiplier, float, 1.0f); DECL_GFX_PREF(Live, "apz.fling_accel_interval_ms", APZFlingAccelInterval, int32_t, 500); diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 0875040432..c29d0adbe5 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -447,8 +447,8 @@ CreateSamplingRestrictedDrawable(gfxDrawable* aDrawable, nsRefPtr tmpCtx = new gfxContext(target); tmpCtx->SetOp(OptimalFillOp()); - aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true, - GraphicsFilter::FILTER_FAST, 1.0, gfxMatrix::Translation(needed.TopLeft())); + aDrawable->Draw(tmpCtx, needed - needed.TopLeft(), true, Filter::LINEAR, + 1.0, gfxMatrix::Translation(needed.TopLeft())); RefPtr surface = target->Snapshot(); nsRefPtr drawable = new gfxSurfaceDrawable(surface, size, gfxMatrix::Translation(-needed.TopLeft())); @@ -536,9 +536,9 @@ DeviceToImageTransform(gfxContext* aContext) /* These heuristics are based on Source/WebCore/platform/graphics/skia/ImageSkia.cpp:computeResamplingMode() */ #ifdef MOZ_GFX_OPTIMIZE_MOBILE -static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, - int aImgWidth, int aImgHeight, - float aSourceWidth, float aSourceHeight) +static Filter ReduceResamplingFilter(Filter aFilter, + int aImgWidth, int aImgHeight, + float aSourceWidth, float aSourceHeight) { // Images smaller than this in either direction are considered "small" and // are not resampled ever (see below). @@ -553,7 +553,7 @@ static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, || aImgHeight <= kSmallImageSizeThreshold) { // Never resample small images. These are often used for borders and // rules (think 1x1 images used to make lines). - return GraphicsFilter::FILTER_NEAREST; + return Filter::POINT; } if (aImgHeight * kLargeStretch <= aSourceHeight || aImgWidth * kLargeStretch <= aSourceWidth) { @@ -564,7 +564,7 @@ static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, // (which might be large) and then is stretching it to fill some part // of the page. if (fabs(aSourceWidth - aImgWidth)/aImgWidth < 0.5 || fabs(aSourceHeight - aImgHeight)/aImgHeight < 0.5) - return GraphicsFilter::FILTER_NEAREST; + return Filter::POINT; // The image is growing a lot and in more than one direction. Resampling // is slow and doesn't give us very much when growing a lot. @@ -592,9 +592,9 @@ static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, return aFilter; } #else -static GraphicsFilter ReduceResamplingFilter(GraphicsFilter aFilter, - int aImgWidth, int aImgHeight, - int aSourceWidth, int aSourceHeight) +static Filter ReduceResamplingFilter(Filter aFilter, + int aImgWidth, int aImgHeight, + int aSourceWidth, int aSourceHeight) { // Just pass the filter through unchanged return aFilter; @@ -621,7 +621,7 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, gfxContext* aContext, const ImageRegion& aRegion, Rect aImageRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const SurfaceFormat aFormat, gfxFloat aOpacity) { @@ -689,7 +689,7 @@ PrescaleAndTileDrawable(gfxDrawable* aDrawable, aContext->CurrentAntialiasMode()); SurfacePattern scaledImagePattern(scaledImage, ExtendMode::REPEAT, - Matrix(), ToFilter(aFilter)); + Matrix(), aFilter); destDrawTarget->FillRect(scaledNeededRect, scaledImagePattern, drawOptions); } return true; @@ -702,7 +702,7 @@ gfxUtils::DrawPixelSnapped(gfxContext* aContext, const gfxSize& aImageSize, const ImageRegion& aRegion, const SurfaceFormat aFormat, - GraphicsFilter aFilter, + Filter aFilter, uint32_t aImageFlags, gfxFloat aOpacity) { diff --git a/gfx/thebes/gfxUtils.h b/gfx/thebes/gfxUtils.h index 77a704d65b..090d31edf4 100644 --- a/gfx/thebes/gfxUtils.h +++ b/gfx/thebes/gfxUtils.h @@ -7,7 +7,6 @@ #define GFX_UTILS_H #include "gfxTypes.h" -#include "GraphicsFilter.h" #include "imgIContainer.h" #include "mozilla/gfx/2D.h" #include "mozilla/RefPtr.h" @@ -79,7 +78,7 @@ public: const gfxSize& aImageSize, const ImageRegion& aRegion, const mozilla::gfx::SurfaceFormat aFormat, - GraphicsFilter aFilter, + mozilla::gfx::Filter aFilter, uint32_t aImageFlags = imgIContainer::FLAG_NONE, gfxFloat aOpacity = 1.0); diff --git a/gfx/thebes/gfxWindowsNativeDrawing.cpp b/gfx/thebes/gfxWindowsNativeDrawing.cpp index f3407806b3..e6f7282f3b 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.cpp +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp @@ -288,9 +288,9 @@ gfxWindowsNativeDrawing::PaintToContext() pat->SetMatrix(m); if (mNativeDrawFlags & DO_NEAREST_NEIGHBOR_FILTERING) - pat->SetFilter(GraphicsFilter::FILTER_FAST); + pat->SetFilter(Filter::LINEAR); - pat->SetExtend(gfxPattern::EXTEND_PAD); + pat->SetExtend(ExtendMode::CLAMP); mContext->SetPattern(pat); mContext->Fill(); mContext->Restore(); diff --git a/gfx/thebes/moz.build b/gfx/thebes/moz.build index 7e8f16d92b..e1cec9c13c 100644 --- a/gfx/thebes/moz.build +++ b/gfx/thebes/moz.build @@ -49,7 +49,6 @@ EXPORTS += [ 'gfxTypes.h', 'gfxUserFontSet.h', 'gfxUtils.h', - 'GraphicsFilter.h', 'RoundedRect.h', 'SoftwareVsyncSource.h', 'VsyncSource.h', diff --git a/image/ClippedImage.cpp b/image/ClippedImage.cpp index de24f49d34..a6aa9c68cf 100644 --- a/image/ClippedImage.cpp +++ b/image/ClippedImage.cpp @@ -92,7 +92,7 @@ public: virtual bool operator()(gfxContext* aContext, const gfxRect& aFillRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const gfxMatrix& aTransform) { MOZ_ASSERT(aTransform.IsIdentity(), @@ -266,7 +266,7 @@ ClippedImage::GetFrameInternal(const nsIntSize& aSize, gfxUtils::DrawPixelSnapped(ctx, drawable, aSize, ImageRegion::Create(aSize), SurfaceFormat::B8G8R8A8, - GraphicsFilter::FILTER_FAST, + Filter::LINEAR, imgIContainer::FLAG_CLAMP); // Cache the resulting surface. @@ -323,7 +323,7 @@ ClippedImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -381,7 +381,7 @@ ClippedImage::DrawSingleTile(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -440,7 +440,7 @@ ClippedImage::GetOrientation() nsIntSize ClippedImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, uint32_t aFlags) + Filter aFilter, uint32_t aFlags) { if (!ShouldClip()) { return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter, diff --git a/image/ClippedImage.h b/image/ClippedImage.h index c4dba021f7..621d572d21 100644 --- a/image/ClippedImage.h +++ b/image/ClippedImage.h @@ -50,7 +50,7 @@ public: const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) override; NS_IMETHOD RequestDiscard() override; @@ -59,7 +59,7 @@ public: override; nsIntSize OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, uint32_t aFlags) override; protected: @@ -78,7 +78,7 @@ private: const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags); diff --git a/image/DynamicImage.cpp b/image/DynamicImage.cpp index 6aa1bfdffd..dd7c89015a 100644 --- a/image/DynamicImage.cpp +++ b/image/DynamicImage.cpp @@ -188,8 +188,7 @@ DynamicImage::GetFrameAtSize(const IntSize& aSize, nsRefPtr context = new gfxContext(dt); auto result = Draw(context, aSize, ImageRegion::Create(aSize), - aWhichFrame, GraphicsFilter::FILTER_NEAREST, - Nothing(), aFlags); + aWhichFrame, Filter::POINT, Nothing(), aFlags); return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr; } @@ -219,7 +218,7 @@ DynamicImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -325,7 +324,7 @@ DynamicImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime) nsIntSize DynamicImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, uint32_t aFlags) + Filter aFilter, uint32_t aFlags) { IntSize size(mDrawable->Size()); return nsIntSize(size.width, size.height); diff --git a/image/FrozenImage.cpp b/image/FrozenImage.cpp index cf0dbf72b5..0dd2ec3f3e 100644 --- a/image/FrozenImage.cpp +++ b/image/FrozenImage.cpp @@ -74,7 +74,7 @@ FrozenImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t /* aWhichFrame - ignored */, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { diff --git a/image/FrozenImage.h b/image/FrozenImage.h index ff54ce666d..3f87f30436 100644 --- a/image/FrozenImage.h +++ b/image/FrozenImage.h @@ -50,7 +50,7 @@ public: const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) override; NS_IMETHOD_(void) RequestRefresh(const TimeStamp& aTime) override; diff --git a/image/ImageWrapper.cpp b/image/ImageWrapper.cpp index 02139b7306..3289678669 100644 --- a/image/ImageWrapper.cpp +++ b/image/ImageWrapper.cpp @@ -13,6 +13,7 @@ namespace mozilla { using gfx::DataSourceSurface; +using gfx::Filter; using gfx::SourceSurface; using layers::LayerManager; using layers::ImageContainer; @@ -205,7 +206,7 @@ ImageWrapper::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -304,7 +305,7 @@ ImageWrapper::PropagateUseCounters(nsIDocument* aParentDocument) nsIntSize ImageWrapper::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, uint32_t aFlags) + Filter aFilter, uint32_t aFlags) { return mInnerImage->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter, aFlags); diff --git a/image/OrientedImage.cpp b/image/OrientedImage.cpp index 0ec6dfe1d7..08e5e5887c 100644 --- a/image/OrientedImage.cpp +++ b/image/OrientedImage.cpp @@ -115,9 +115,8 @@ OrientedImage::GetFrame(uint32_t aWhichFrame, // Draw. nsRefPtr ctx = new gfxContext(target); ctx->Multiply(OrientationMatrix(size)); - gfxUtils::DrawPixelSnapped(ctx, drawable, size, - ImageRegion::Create(size), - surfaceFormat, GraphicsFilter::FILTER_FAST); + gfxUtils::DrawPixelSnapped(ctx, drawable, size, ImageRegion::Create(size), + surfaceFormat, Filter::LINEAR); return target->Snapshot(); } @@ -273,7 +272,7 @@ OrientedImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -310,7 +309,7 @@ OrientedImage::Draw(gfxContext* aContext, nsIntSize OrientedImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, uint32_t aFlags) + Filter aFilter, uint32_t aFlags) { if (!mOrientation.SwapsWidthAndHeight()) { return InnerImage()->OptimalImageSizeForDest(aDest, aWhichFrame, aFilter, diff --git a/image/OrientedImage.h b/image/OrientedImage.h index cbfe308d20..5db5d70f41 100644 --- a/image/OrientedImage.h +++ b/image/OrientedImage.h @@ -47,14 +47,14 @@ public: const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) override; NS_IMETHOD_(nsIntRect) GetImageSpaceInvalidationRect( const nsIntRect& aRect) override; nsIntSize OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, + gfx::Filter aFilter, uint32_t aFlags) override; protected: diff --git a/image/RasterImage.cpp b/image/RasterImage.cpp index f8fa08bc9a..58c0f3dc76 100644 --- a/image/RasterImage.cpp +++ b/image/RasterImage.cpp @@ -1443,7 +1443,7 @@ RasterImage::DrawInternal(DrawableFrameRef&& aFrameRef, gfxContext* aContext, const IntSize& aSize, const ImageRegion& aRegion, - GraphicsFilter aFilter, + Filter aFilter, uint32_t aFlags) { gfxContextMatrixAutoSaveRestore saveMatrix(aContext); @@ -1478,7 +1478,7 @@ RasterImage::DrawInternal(DrawableFrameRef&& aFrameRef, //****************************************************************************** /* [noscript] void draw(in gfxContext aContext, - * in gfxGraphicsFilter aFilter, + * in Filter aFilter, * [const] in gfxMatrix aUserSpaceToImageSpace, * [const] in gfxRect aFill, * [const] in IntRect aSubimage, @@ -1491,7 +1491,7 @@ RasterImage::Draw(gfxContext* aContext, const IntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& /*aSVGContext - ignored*/, uint32_t aFlags) { @@ -1518,9 +1518,9 @@ RasterImage::Draw(gfxContext* aContext, mProgressTracker->OnUnlockedDraw(); } - // If we're not using GraphicsFilter::FILTER_GOOD, we shouldn't high-quality - // scale or downscale during decode. - uint32_t flags = aFilter == GraphicsFilter::FILTER_GOOD + // If we're not using Filter::GOOD, we shouldn't high-quality scale or + // downscale during decode. + uint32_t flags = aFilter == Filter::GOOD ? aFlags : aFlags & ~FLAG_HIGH_QUALITY_SCALING; @@ -1812,7 +1812,7 @@ RasterImage::PropagateUseCounters(nsIDocument*) IntSize RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, uint32_t aFlags) + Filter aFilter, uint32_t aFlags) { MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX || aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX, @@ -1824,8 +1824,7 @@ RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, IntSize destSize(ceil(aDest.width), ceil(aDest.height)); - if (aFilter == GraphicsFilter::FILTER_GOOD && - CanDownscaleDuringDecode(destSize, aFlags)) { + if (aFilter == Filter::GOOD && CanDownscaleDuringDecode(destSize, aFlags)) { return destSize; } diff --git a/image/RasterImage.h b/image/RasterImage.h index c962cf69c8..7c75b8b689 100644 --- a/image/RasterImage.h +++ b/image/RasterImage.h @@ -258,7 +258,7 @@ private: gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, - GraphicsFilter aFilter, + gfx::Filter aFilter, uint32_t aFlags); already_AddRefed CopyFrame(uint32_t aWhichFrame, diff --git a/image/VectorImage.cpp b/image/VectorImage.cpp index 5ce1060bc6..ca1699e6ae 100644 --- a/image/VectorImage.cpp +++ b/image/VectorImage.cpp @@ -263,7 +263,7 @@ public: { } virtual bool operator()(gfxContext* aContext, const gfxRect& aFillRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const gfxMatrix& aTransform); private: nsRefPtr mSVGDocumentWrapper; @@ -276,7 +276,7 @@ private: bool SVGDrawingCallback::operator()(gfxContext* aContext, const gfxRect& aFillRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const gfxMatrix& aTransform) { MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper"); @@ -717,10 +717,8 @@ VectorImage::GetFrameAtSize(const IntSize& aSize, nsRefPtr context = new gfxContext(dt); - auto result = Draw(context, aSize, - ImageRegion::Create(aSize), - aWhichFrame, GraphicsFilter::FILTER_NEAREST, - Nothing(), aFlags); + auto result = Draw(context, aSize, ImageRegion::Create(aSize), + aWhichFrame, Filter::POINT, Nothing(), aFlags); return result == DrawResult::SUCCESS ? dt->Snapshot() : nullptr; } @@ -743,7 +741,7 @@ struct SVGDrawingParameters SVGDrawingParameters(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, float aAnimationTime, uint32_t aFlags) @@ -768,7 +766,7 @@ struct SVGDrawingParameters IntSize size; IntRect imageRect; ImageRegion region; - GraphicsFilter filter; + Filter filter; const Maybe& svgContext; nsIntSize viewportSize; float animationTime; @@ -781,7 +779,7 @@ struct SVGDrawingParameters * [const] in nsIntSize aSize, * [const] in ImageRegion aRegion, * in uint32_t aWhichFrame, - * in gfxGraphicsFilter aFilter, + * in Filter aFilter, * [const] in MaybeSVGImageContext aSVGContext, * in uint32_t aFlags); */ NS_IMETHODIMP_(DrawResult) @@ -789,7 +787,7 @@ VectorImage::Draw(gfxContext* aContext, const nsIntSize& aSize, const ImageRegion& aRegion, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, const Maybe& aSVGContext, uint32_t aFlags) { @@ -900,7 +898,7 @@ VectorImage::CreateSurfaceAndShow(const SVGDrawingParameters& aParams) nsresult rv = frame->InitWithDrawable(svgDrawable, aParams.size, SurfaceFormat::B8G8R8A8, - GraphicsFilter::FILTER_NEAREST, aParams.flags); + Filter::POINT, aParams.flags); // If we couldn't create the frame, it was probably because it would end // up way too big. Generally it also wouldn't fit in the cache, but the prefs @@ -1280,7 +1278,7 @@ VectorImage::ReportUseCounters() nsIntSize VectorImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame, - GraphicsFilter aFilter, + Filter aFilter, uint32_t aFlags) { MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX || diff --git a/image/imgFrame.cpp b/image/imgFrame.cpp index 16aa92c9aa..f6c90ecfa9 100644 --- a/image/imgFrame.cpp +++ b/image/imgFrame.cpp @@ -237,7 +237,7 @@ nsresult imgFrame::InitWithDrawable(gfxDrawable* aDrawable, const nsIntSize& aSize, const SurfaceFormat aFormat, - GraphicsFilter aFilter, + Filter aFilter, uint32_t aImageFlags) { // Assert for properties that should be verified by decoders, @@ -545,7 +545,7 @@ imgFrame::SurfaceForDrawing(bool aDoPadding, } bool imgFrame::Draw(gfxContext* aContext, const ImageRegion& aRegion, - GraphicsFilter aFilter, uint32_t aImageFlags) + Filter aFilter, uint32_t aImageFlags) { PROFILER_LABEL("imgFrame", "Draw", js::ProfileEntry::Category::GRAPHICS); diff --git a/image/imgFrame.h b/image/imgFrame.h index e5f62d01aa..228a25e654 100644 --- a/image/imgFrame.h +++ b/image/imgFrame.h @@ -108,6 +108,7 @@ class imgFrame typedef gfx::Color Color; typedef gfx::DataSourceSurface DataSourceSurface; typedef gfx::DrawTarget DrawTarget; + typedef gfx::Filter Filter; typedef gfx::IntSize IntSize; typedef gfx::SourceSurface SourceSurface; typedef gfx::SurfaceFormat SurfaceFormat; @@ -154,7 +155,7 @@ public: nsresult InitWithDrawable(gfxDrawable* aDrawable, const nsIntSize& aSize, const SurfaceFormat aFormat, - GraphicsFilter aFilter, + Filter aFilter, uint32_t aImageFlags); DrawableFrameRef DrawableRef(); @@ -173,7 +174,7 @@ public: void SetRawAccessOnly(); bool Draw(gfxContext* aContext, const ImageRegion& aRegion, - GraphicsFilter aFilter, uint32_t aImageFlags); + Filter aFilter, uint32_t aImageFlags); nsresult ImageUpdated(const nsIntRect& aUpdateRect); diff --git a/image/imgIContainer.idl b/image/imgIContainer.idl index 53d2a1829a..730a0cf1e6 100644 --- a/image/imgIContainer.idl +++ b/image/imgIContainer.idl @@ -10,7 +10,6 @@ #include "gfxContext.h" #include "gfxMatrix.h" #include "gfxRect.h" -#include "GraphicsFilter.h" #include "mozilla/gfx/2D.h" #include "mozilla/Maybe.h" #include "mozilla/RefPtr.h" @@ -94,7 +93,7 @@ native DrawResult(mozilla::image::DrawResult); [ref] native gfxMatrix(gfxMatrix); [ref] native gfxRect(gfxRect); [ref] native gfxSize(gfxSize); -native gfxGraphicsFilter(GraphicsFilter); +native Filter(mozilla::gfx::Filter); [ref] native nsIntRect(nsIntRect); native nsIntRectByVal(nsIntRect); [ref] native nsIntSize(nsIntSize); @@ -119,7 +118,7 @@ native nsIntSizeByVal(nsIntSize); * * Internally, imgIContainer also manages animation of images. */ -[scriptable, builtinclass, uuid(4e5a0547-6c54-4051-8b52-1f2fdd667696)] +[scriptable, builtinclass, uuid(7c795421-a79c-43ac-9e20-6d4e8a9dfb76)] interface imgIContainer : nsISupports { /** @@ -165,7 +164,7 @@ interface imgIContainer : nsISupports */ [notxpcom, nostdcall] nsIntSizeByVal optimalImageSizeForDest([const] in gfxSize aDest, in uint32_t aWhichFrame, - in gfxGraphicsFilter aFilter, in uint32_t aFlags); + in Filter aFilter, in uint32_t aFlags); /** * Enumerated values for the 'type' attribute (below). @@ -421,7 +420,7 @@ interface imgIContainer : nsISupports [const] in nsIntSize aSize, [const] in ImageRegion aRegion, in uint32_t aWhichFrame, - in gfxGraphicsFilter aFilter, + in Filter aFilter, [const] in MaybeSVGImageContext aSVGContext, in uint32_t aFlags); diff --git a/js/public/TraceableHashTable.h b/js/public/TraceableHashTable.h index 83f29dffc5..2993d22fa9 100644 --- a/js/public/TraceableHashTable.h +++ b/js/public/TraceableHashTable.h @@ -153,6 +153,164 @@ class HandleBase> : public TraceableHashMapOperations>, A,B,C,D,E,F> {}; +// A TraceableHashSet is a HashSet with an additional trace method that knows +// how to visit all set element. HashSets that contain GC pointers that must +// be traced to be kept alive will generally want to use this TraceableHashSet +// specializeation in lieu of HashSet. +// +// Most types of GC pointers can be traced with no extra infrastructure. For +// structs and non-gc-pointer members, ensure that there is a specialization of +// DefaultTracer with an appropriate trace method available to handle the +// custom type. +// +// Note that although this HashSet's trace will deal correctly with moved +// elements, it does not itself know when to barrier or trace elements. To +// function properly it must either be used with Rooted or barriered and traced +// manually. +template , + typename AllocPolicy = TempAllocPolicy, + typename ElemTraceFunc = DefaultTracer> +class TraceableHashSet : public HashSet, + public JS::Traceable +{ + using Base = HashSet; + + public: + explicit TraceableHashSet(AllocPolicy a = AllocPolicy()) : Base(a) {} + + static void trace(TraceableHashSet* set, JSTracer* trc) { set->trace(trc); } + void trace(JSTracer* trc) { + if (!this->initialized()) + return; + for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { + T elem = e.front(); + ElemTraceFunc::trace(trc, &elem, "hashset element"); + if (elem != e.front()) + e.rekeyFront(elem); + } + } + + // TraceableHashSet is movable + TraceableHashSet(TraceableHashSet&& rhs) : Base(mozilla::Forward(rhs)) {} + void operator=(TraceableHashSet&& rhs) { + MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited"); + Base::operator=(mozilla::Forward(rhs)); + } + + private: + // TraceableHashSet is not copyable or assignable + TraceableHashSet(const TraceableHashSet& hs) = delete; + TraceableHashSet& operator=(const TraceableHashSet& hs) = delete; +}; + +template +class TraceableHashSetOperations +{ + using Set = TraceableHashSet; + using Lookup = typename Set::Lookup; + using Ptr = typename Set::Ptr; + using AddPtr = typename Set::AddPtr; + using Range = typename Set::Range; + using Enum = typename Set::Enum; + + const Set& set() const { return static_cast(this)->extract(); } + + public: + bool initialized() const { return set().initialized(); } + Ptr lookup(const Lookup& l) const { return set().lookup(l); } + AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); } + Range all() const { return set().all(); } + bool empty() const { return set().empty(); } + uint32_t count() const { return set().count(); } + size_t capacity() const { return set().capacity(); } + uint32_t generation() const { return set().generation(); } + bool has(const Lookup& l) const { return set().lookup(l).found(); } +}; + +template +class MutableTraceableHashSetOperations + : public TraceableHashSetOperations +{ + using Set = TraceableHashSet; + using Lookup = typename Set::Lookup; + using Ptr = typename Set::Ptr; + using AddPtr = typename Set::AddPtr; + using Range = typename Set::Range; + using Enum = typename Set::Enum; + + Set& set() { return static_cast(this)->extract(); } + + public: + bool init(uint32_t len = 16) { return set().init(len); } + void clear() { set().clear(); } + void finish() { set().finish(); } + void remove(const Lookup& l) { set().remove(l); } + + template + bool add(AddPtr& p, TInput&& t) { + return set().add(p, mozilla::Forward(t)); + } + + template + bool relookupOrAdd(AddPtr& p, const Lookup& l, TInput&& t) { + return set().relookupOrAdd(p, l, mozilla::Forward(t)); + } + + template + bool put(TInput&& t) { + return set().put(mozilla::Forward(t)); + } + + template + bool putNew(TInput&& t) { + return set().putNew(mozilla::Forward(t)); + } + + template + bool putNew(const Lookup& l, TInput&& t) { + return set().putNew(l, mozilla::Forward(t)); + } +}; + +template +class RootedBase> + : public MutableTraceableHashSetOperations>, T, HP, AP, TF> +{ + using Set = TraceableHashSet; + + friend class TraceableHashSetOperations, T, HP, AP, TF>; + const Set& extract() const { return *static_cast*>(this)->address(); } + + friend class MutableTraceableHashSetOperations, T, HP, AP, TF>; + Set& extract() { return *static_cast*>(this)->address(); } +}; + +template +class MutableHandleBase> + : public MutableTraceableHashSetOperations>, + T, HP, AP, TF> +{ + using Set = TraceableHashSet; + + friend class TraceableHashSetOperations, T, HP, AP, TF>; + const Set& extract() const { + return *static_cast*>(this)->address(); + } + + friend class MutableTraceableHashSetOperations, T, HP, AP, TF>; + Set& extract() { return *static_cast*>(this)->address(); } +}; + +template +class HandleBase> + : public TraceableHashSetOperations>, T, HP, AP, TF> +{ + using Set = TraceableHashSet; + friend class TraceableHashSetOperations, T, HP, AP, TF>; + const Set& extract() const { return *static_cast*>(this)->address(); } +}; + } /* namespace js */ #endif /* gc_HashTable_h */ diff --git a/js/src/aclocal.m4 b/js/src/aclocal.m4 index a5fed46c07..b60acb3d77 100644 --- a/js/src/aclocal.m4 +++ b/js/src/aclocal.m4 @@ -32,6 +32,7 @@ builtin(include, ../../build/autoconf/ffi.m4)dnl builtin(include, ../../build/autoconf/clang-plugin.m4)dnl builtin(include, ../../build/autoconf/alloc.m4)dnl builtin(include, ../../build/autoconf/jemalloc.m4)dnl +builtin(include, ../../build/autoconf/rust.m4)dnl define([__MOZ_AC_INIT_PREPARE], defn([AC_INIT_PREPARE])) define([AC_INIT_PREPARE], diff --git a/js/src/configure.in b/js/src/configure.in index f89e9764af..659333ffab 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -125,6 +125,8 @@ MOZ_ARG_WITH_STRING(dist-dir, TOP_DIST=dist) AC_SUBST(TOP_DIST) +MOZ_BUILD_BACKEND + MOZ_DEFAULT_COMPILER COMPILE_ENVIRONMENT=1 @@ -297,6 +299,10 @@ MOZ_TOOL_VARIABLES MOZ_CHECK_COMPILER_WRAPPER +AC_PROG_CPP +AC_PROG_CXXCPP +MOZ_RUST_SUPPORT + dnl Special win32 checks dnl ======================================================== @@ -426,8 +432,6 @@ case "$target" in CFLAGS="$CFLAGS -D_HAS_EXCEPTIONS=0" CXXFLAGS="$CXXFLAGS -D_HAS_EXCEPTIONS=0" - - MOZ_FIND_WINSDK_VERSION else # Check w32api version _W32API_MAJOR_VERSION=`echo $W32API_VERSION | $AWK -F\. '{ print $1 }'` @@ -463,10 +467,9 @@ case "$target" in then AC_MSG_ERROR([windres version $WINDRES_VERSION or higher is required to build.]) fi - - MOZ_WINSDK_MAXVER=0x06030000 fi # !GNU_CC + MOZ_FIND_WINSDK_VERSION AC_DEFINE_UNQUOTED(WINVER,0x$WINVER) AC_DEFINE_UNQUOTED(_WIN32_WINNT,0x$WINVER) # Require OS features provided by IE 6.0 SP2 (XP SP2) @@ -488,9 +491,6 @@ case "$target" in ;; esac -AC_PROG_CPP -AC_PROG_CXXCPP - if test -n "$_WIN32_MSVC"; then SKIP_PATH_CHECKS=1 SKIP_COMPILER_CHECKS=1 @@ -699,6 +699,9 @@ dnl ============================================================== MOZILLA_VERSION=`$PYTHON $srcdir/python/mozbuild/mozbuild/milestone.py --topsrcdir $srcdir` MOZILLA_UAVERSION=`$PYTHON $srcdir/python/mozbuild/mozbuild/milestone.py --topsrcdir $srcdir --uaversion` MOZILLA_SYMBOLVERSION=`$PYTHON $srcdir/python/mozbuild/mozbuild/milestone.py --topsrcdir $srcdir --symbolversion` +if test -z "$MOZILLA_VERSION"; then + AC_MSG_ERROR([failed to read version info from milestone file]) +fi AC_DEFINE_UNQUOTED(MOZILLA_VERSION,"$MOZILLA_VERSION") AC_DEFINE_UNQUOTED(MOZILLA_VERSION_U,$MOZILLA_VERSION) @@ -1405,46 +1408,6 @@ else _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_JS_CONFDEFS_H_ $(ACDEFINES)' fi -dnl gcc can come with its own linker so it is better to use the pass-thru calls -dnl MKSHLIB_FORCE_ALL is used to force the linker to include all object -dnl files present in an archive. MKSHLIB_UNFORCE_ALL reverts the linker to -dnl normal behavior. -dnl ======================================================== -MKSHLIB_FORCE_ALL= -MKSHLIB_UNFORCE_ALL= - -if test "$COMPILE_ENVIRONMENT"; then -if test "$GNU_CC"; then - AC_MSG_CHECKING(whether ld has archive extraction flags) - AC_CACHE_VAL(ac_cv_mkshlib_force_and_unforce, - [_SAVE_LDFLAGS=$LDFLAGS; _SAVE_LIBS=$LIBS - ac_cv_mkshlib_force_and_unforce="no" - exec 3<&0 < GCCompartmentGroupIter; - } /* namespace gc */ } /* namespace js */ diff --git a/js/src/jsnum.h b/js/src/jsnum.h index aefe15f2ab..f94bf085b3 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -14,16 +14,17 @@ #include "js/Conversions.h" -// This macro should be "1" if the compiler being used supports built-in -// functions like __builtin_sadd_overflow. + +// This macro is should be `one' if current compiler supports builtin functions +// like __builtin_sadd_overflow. #if __GNUC__ >= 5 // GCC 5 and above supports these functions. - #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1 + #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 1 #else - // For CLANG, we can use its own function to check for this. + // For CLANG, we use its own function to check for this. #ifdef __has_builtin - #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x) - #endif + #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) __has_builtin(x) + #endif #endif #ifndef BUILTIN_CHECKED_ARITHMETIC_SUPPORTED #define BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(x) 0 @@ -278,8 +279,8 @@ inline bool SafeAdd(int32_t one, int32_t two, int32_t* res) { #if BUILTIN_CHECKED_ARITHMETIC_SUPPORTED(__builtin_sadd_overflow) - // Use the compiler's builtin function. - return !__builtin_sadd_overflow(one, two, res); + // Using compiler's builtin function. + return !__builtin_sadd_overflow(one, two, res); #else // Use unsigned for the 32-bit operation since signed overflow gets // undefined behavior. diff --git a/js/src/json.cpp b/js/src/json.cpp index 205a9fc120..c47fb4daf4 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -136,13 +136,19 @@ class StringifyContext : sb(sb), gap(gap), replacer(cx, replacer), + stack(cx, TraceableHashSet(cx)), propertyList(propertyList), depth(0) {} + bool init() { + return stack.init(8); + } + StringBuffer& sb; const StringBuffer& gap; RootedObject replacer; + Rooted> stack; const AutoIdVector& propertyList; uint32_t depth; }; @@ -296,6 +302,32 @@ IsFilteredValue(const Value& v) return v.isUndefined() || v.isSymbol() || IsCallable(v); } +class CycleDetector +{ + public: + CycleDetector(StringifyContext* scx, HandleObject obj) + : stack(&scx->stack), obj_(obj) { + } + + bool foundCycle(JSContext* cx) { + auto addPtr = stack.lookupForAdd(obj_); + if (addPtr) { + JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_JSON_CYCLIC_VALUE, + js_object_str); + return false; + } + return stack.add(addPtr, obj_); + } + + ~CycleDetector() { + stack.remove(obj_); + } + + private: + MutableHandle> stack; + HandleObject obj_; +}; + /* ES5 15.12.3 JO. */ static bool JO(JSContext* cx, HandleObject obj, StringifyContext* scx) @@ -311,14 +343,9 @@ JO(JSContext* cx, HandleObject obj, StringifyContext* scx) */ /* Steps 1-2, 11. */ - AutoCycleDetector detect(cx, obj); - if (!detect.init()) + CycleDetector detect(scx, obj); + if (!detect.foundCycle(cx)) return false; - if (detect.foundCycle()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_JSON_CYCLIC_VALUE, - js_object_str); - return false; - } if (!scx->sb.append('{')) return false; @@ -406,14 +433,9 @@ JA(JSContext* cx, HandleObject obj, StringifyContext* scx) */ /* Steps 1-2, 11. */ - AutoCycleDetector detect(cx, obj); - if (!detect.init()) + CycleDetector detect(scx, obj); + if (!detect.foundCycle(cx)) return false; - if (detect.foundCycle()) { - JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_JSON_CYCLIC_VALUE, - js_object_str); - return false; - } if (!scx->sb.append('[')) return false; @@ -694,6 +716,8 @@ js::Stringify(JSContext* cx, MutableHandleValue vp, JSObject* replacer_, Value s /* Step 11. */ StringifyContext scx(cx, sb, gap, replacer, propertyList); + if (!scx.init()) + return false; if (!PreprocessValue(cx, wrapper, HandleId(emptyId), vp, &scx)) return false; if (IsFilteredValue(vp)) diff --git a/js/src/jspropertytree.cpp b/js/src/jspropertytree.cpp index 11345370bf..7e45aa0c85 100644 --- a/js/src/jspropertytree.cpp +++ b/js/src/jspropertytree.cpp @@ -229,7 +229,7 @@ Shape::fixupDictionaryShapeAfterMovingGC() // Get a fake cell pointer to use for the calls below. This might not point // to the beginning of a cell, but will point into the right arena and will // have the right alignment. - Cell *cell = reinterpret_cast(uintptr_t(listp) & ~CellMask); + Cell* cell = reinterpret_cast(uintptr_t(listp) & ~CellMask); // It's possible that this shape is unreachable and that listp points to the // location of a dead object in the nursery, in which case we should never @@ -245,12 +245,12 @@ Shape::fixupDictionaryShapeAfterMovingGC() IsObjectAllocKind(kind)); if (kind == AllocKind::SHAPE || kind == AllocKind::ACCESSOR_SHAPE) { // listp points to the parent field of the next shape. - Shape *next = reinterpret_cast(uintptr_t(listp) - + Shape* next = reinterpret_cast(uintptr_t(listp) - offsetof(Shape, parent)); listp = &gc::MaybeForwarded(next)->parent; } else { // listp points to the shape_ field of an object. - JSObject *last = reinterpret_cast(uintptr_t(listp) - + JSObject* last = reinterpret_cast(uintptr_t(listp) - JSObject::offsetOfShape()); listp = &gc::MaybeForwarded(last)->as().shape_; } diff --git a/js/src/tests/ecma_5/JSON/cyclic-stringify-unrelated.js b/js/src/tests/ecma_5/JSON/cyclic-stringify-unrelated.js new file mode 100644 index 0000000000..127c2ca600 --- /dev/null +++ b/js/src/tests/ecma_5/JSON/cyclic-stringify-unrelated.js @@ -0,0 +1,39 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 1197097; +var summary = "JSON.stringify shouldn't use context-wide cycle detection"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var arr; + +// Nested yet separate JSON.stringify is okay. +arr = [{}]; +assertEq(JSON.stringify(arr, function(k, v) { + assertEq(JSON.stringify(arr), "[{}]"); + return v; +}), "[{}]"); + +// SpiderMonkey censors cycles in array-joining. This mechanism must not +// interfere with the cycle detection in JSON.stringify. +arr = [{ + toString: function() { + var s = JSON.stringify(arr); + assertEq(s, "[{}]"); + return s; + } +}]; +assertEq(arr.join(), "[{}]"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 6d5c40bac7..ddff166da5 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -1042,10 +1042,10 @@ public: MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame()); mContainerAnimatedGeometryRoot = isAtRoot ? mContainerReferenceFrame - : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder, aManager); + : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder); MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(), mContainerAnimatedGeometryRoot)); - NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(aManager), + NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(mBuilder), "Container items never return true for ShouldFixToViewport"); mContainerFixedPosFrame = FindFixedPosFrameForLayerData(mContainerAnimatedGeometryRoot, false); @@ -2097,18 +2097,30 @@ ContainerState::GetLayerCreationHint(const nsIFrame* aAnimatedGeometryRoot) if (mParameters.mInLowPrecisionDisplayPort) { return LayerManager::SCROLLABLE; } - nsIFrame* animatedGeometryRootParent = aAnimatedGeometryRoot->GetParent(); - nsIScrollableFrame* scrollable = do_QueryFrame(animatedGeometryRootParent); - if (scrollable -#ifdef MOZ_B2G - && scrollable->WantAsyncScroll() -#endif - ) { - // WantAsyncScroll() returns false when the frame has overflow:hidden, - // so we won't create tiled layers for overflow:hidden frames even if - // they have a display port. The main purpose of the WantAsyncScroll check - // is to allow the B2G camera app to use hardware composer for compositing. - return LayerManager::SCROLLABLE; + + // Check whether there's any active scroll frame on the animated geometry + // root chain. + nsIFrame* fParent; + for (const nsIFrame* f = aAnimatedGeometryRoot; + f != mContainerAnimatedGeometryRoot; + f = nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, + fParent, mContainerAnimatedGeometryRoot)) { + fParent = nsLayoutUtils::GetCrossDocParentFrame(f); + if (!fParent) { + break; + } + nsIScrollableFrame* scrollable = do_QueryFrame(fParent); + if (scrollable + #ifdef MOZ_B2G + && scrollable->WantAsyncScroll() + #endif + ) { + // WantAsyncScroll() returns false when the frame has overflow:hidden, + // so we won't create tiled layers for overflow:hidden frames even if + // they have a display port. The main purpose of the WantAsyncScroll check + // is to allow the B2G camera app to use hardware composer for compositing. + return LayerManager::SCROLLABLE; + } } return LayerManager::NONE; } @@ -3254,7 +3266,23 @@ void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueB // use a mask layer for rounded rect clipping. // data->mCommonClipCount may be -1 if we haven't put any actual // drawable items in this layer (i.e. it's only catching events). - int32_t commonClipCount = std::max(0, data->mCommonClipCount); + int32_t commonClipCount; + // If the layer contains a single item fixed to the viewport, we removed + // its clip in ProcessDisplayItems() and saved it to set on the layer instead. + // Set the clip on the layer now. + if (data->mSingleItemFixedToViewport && data->mItemClip.HasClip()) { + nsIntRect layerClipRect = ScaleToNearestPixels(data->mItemClip.GetClipRect()); + layerClipRect.MoveBy(mParameters.mOffset); + data->mLayer->SetClipRect(Some(ViewAs(layerClipRect))); + // There is only one item, so all of the clips are in common to all items. + // data->mCommonClipCount will be zero because we removed the clip from + // the display item. (It could also be -1 if we're inside an inactive + // layer tree in which we don't call UpdateCommonClipCount() at all.) + MOZ_ASSERT(data->mCommonClipCount == -1 || data->mCommonClipCount == 0); + commonClipCount = data->mItemClip.GetRoundedRectCount(); + } else { + commonClipCount = std::max(0, data->mCommonClipCount); + } SetupMaskLayer(layer, data->mItemClip, data->mVisibleRegion, commonClipCount); // copy commonClipCount to the entry FrameLayerBuilder::PaintedLayerItemsEntry* entry = mLayerBuilder-> @@ -3674,7 +3702,7 @@ ContainerState::ChooseAnimatedGeometryRoot(const nsDisplayList& aList, // Try using the actual active scrolled root of the backmost item, as that // should result in the least invalidation when scrolling. *aAnimatedGeometryRoot = - nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder, mManager); + nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); return true; } return false; @@ -3851,7 +3879,7 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) const nsIFrame* animatedGeometryRoot; const nsIFrame* animatedGeometryRootForScrollMetadata = nullptr; const nsIFrame* realAnimatedGeometryRootOfItem = - nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder, mManager); + nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder); if (mFlattenToSingleLayer) { forceInactive = true; animatedGeometryRoot = lastAnimatedGeometryRoot; @@ -3892,6 +3920,19 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) item->SetClip(mBuilder, clip); } + bool shouldFixToViewport = !animatedGeometryRoot->GetParent() && + item->ShouldFixToViewport(mBuilder); + + // For items that are fixed to the viewport, remove their clip at the + // display item level because additional areas could be brought into + // view by async scrolling. Save the clip so we can set it on the layer + // instead later. + DisplayItemClip fixedToViewportClip = DisplayItemClip::NoClip(); + if (shouldFixToViewport) { + fixedToViewportClip = item->GetClip(); + item->SetClip(mBuilder, DisplayItemClip::NoClip()); + } + bool snap; nsRect itemContent = item->GetBounds(mBuilder, &snap); if (itemType == nsDisplayItem::TYPE_LAYER_EVENT_REGIONS) { @@ -3921,16 +3962,21 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) bounds.IntersectRect(bounds, itemClip.GetClipRect()); } } + bounds = fixedToViewportClip.ApplyNonRoundedIntersection(bounds); ((nsRect&)mAccumulatedChildBounds).UnionRect(mAccumulatedChildBounds, bounds); #endif - // We haven't computed visibility at this point, so item->GetVisibleRect() - // is just the dirty rect that item was initialized with. We intersect it - // with the clipped item bounds to get a tighter visible rect. - nsIntRect itemVisibleRect = itemDrawRect.Intersect( - ScaleToOutsidePixels(item->GetVisibleRect(), false)); - bool shouldFixToViewport = !animatedGeometryRoot->GetParent() && - item->ShouldFixToViewport(mManager); + nsIntRect itemVisibleRect = itemDrawRect; + if (!shouldFixToViewport) { + // We haven't computed visibility at this point, so item->GetVisibleRect() + // is just the dirty rect that item was initialized with. We intersect it + // with the clipped item bounds to get a tighter visible rect. + // However, we don't do this for fixed background images, because their + // clips can move asynchronously so we want the layer to contain the + // whole bounds of the display item. + itemVisibleRect = itemVisibleRect.Intersect( + ScaleToOutsidePixels(item->GetVisibleRect(), false)); + } if (maxLayers != -1 && layerCount >= maxLayers) { forceInactive = true; @@ -4182,6 +4228,13 @@ ContainerState::ProcessDisplayItems(nsDisplayList* aList) paintedLayerData->Accumulate(this, item, opaquePixels, itemVisibleRect, itemClip, layerState); + // If we removed the clip from the display item above because it's + // fixed to the viewport, save it on the PaintedLayerData so we can + // set it on the layer later. + if (fixedToViewportClip.HasClip()) { + paintedLayerData->mItemClip = fixedToViewportClip; + } + if (!paintedLayerData->mLayer) { // Try to recycle the old layer of this display item. nsRefPtr layer = @@ -4435,6 +4488,15 @@ FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData* aLayerData, layerBuilder->WillEndTransaction(); tempManager->AbortTransaction(); +#ifdef MOZ_DUMP_PAINTING + if (gfxUtils::DumpDisplayList() || gfxUtils::sDumpPainting) { + fprintf_stderr(gfxUtils::sDumpPaintFile, "Basic layer tree for painting contents of display item %s(%p):\n", aItem->Name(), aItem->Frame()); + std::stringstream stream; + tempManager->Dump(stream, "", gfxUtils::sDumpPaintingToFile); + fprint_stderr(gfxUtils::sDumpPaintFile, stream); // not a typo, fprint_stderr declared in LayersLogging.h + } +#endif + nsIntPoint offset = GetLastPaintOffset(layer) - GetTranslationForPaintedLayer(layer); props->MoveBy(-offset); nsIntRegion invalid = props->ComputeDifferences(tmpLayer, nullptr); @@ -4818,6 +4880,11 @@ ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer if (clipRect) { clippedOpaque.AndWith(ParentLayerIntRect::ToUntyped(*clipRect)); } + if (e->mLayer->GetIsFixedPosition() && !e->mLayer->IsClipFixed()) { + // The clip can move asynchronously, so we can't rely on opaque parts + // staying in the same place. + clippedOpaque.SetEmpty(); + } data->mOpaqueRegion.Or(data->mOpaqueRegion, clippedOpaque); if (e->mHideAllLayersBelow) { hideAll = true; diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index a506338749..462c0293a7 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -55,7 +55,6 @@ #include "mozilla/Telemetry.h" #include "gfxUtils.h" #include "gfxGradientCache.h" -#include "GraphicsFilter.h" #include "nsInlineFrame.h" #include @@ -5091,7 +5090,7 @@ nsImageRenderer::Draw(nsPresContext* aPresContext, return DrawResult::SUCCESS; } - GraphicsFilter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); + Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); switch (mType) { case eStyleImageType_Image: @@ -5322,14 +5321,13 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, subImage = ImageOps::Clip(image, srcRect); } - GraphicsFilter graphicsFilter = - nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); + Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame); if (!RequiresScaling(aFill, aHFill, aVFill, aUnitSize)) { nsLayoutUtils::DrawSingleImage(*aRenderingContext.ThebesContext(), aPresContext, subImage, - graphicsFilter, + filter, aFill, aDirtyRect, nullptr, imgIContainer::FLAG_NONE); @@ -5340,7 +5338,7 @@ nsImageRenderer::DrawBorderImageComponent(nsPresContext* aPresContext, nsLayoutUtils::DrawImage(*aRenderingContext.ThebesContext(), aPresContext, subImage, - graphicsFilter, + filter, tile, aFill, tile.TopLeft(), aDirtyRect, imgIContainer::FLAG_NONE); return; diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 00c1554395..2fac88435e 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -1466,6 +1466,23 @@ TriggerPendingAnimations(nsIDocument* aDocument, const_cast(&aReadyTime)); } +LayerManager* +nsDisplayListBuilder::GetWidgetLayerManager(nsView** aView, bool* aAllowRetaining) +{ + nsView* view = RootReferenceFrame()->GetView(); + if (aView) { + *aView = view; + } + if (RootReferenceFrame() != nsLayoutUtils::GetDisplayRootFrame(RootReferenceFrame())) { + return nullptr; + } + nsIWidget* window = RootReferenceFrame()->GetNearestWidget(); + if (window) { + return window->GetLayerManager(aAllowRetaining); + } + return nullptr; +} + /** * We paint by executing a layer manager transaction, constructing a * single layer representing the display list, and then making it the @@ -1483,17 +1500,10 @@ already_AddRefed nsDisplayList::PaintRoot(nsDisplayListBuilder* aB bool doBeginTransaction = true; nsView *view = nullptr; if (aFlags & PAINT_USE_WIDGET_LAYERS) { - nsIFrame* rootReferenceFrame = aBuilder->RootReferenceFrame(); - view = rootReferenceFrame->GetView(); - NS_ASSERTION(rootReferenceFrame == nsLayoutUtils::GetDisplayRootFrame(rootReferenceFrame), - "Reference frame must be a display root for us to use the layer manager"); - nsIWidget* window = rootReferenceFrame->GetNearestWidget(); - if (window) { - layerManager = window->GetLayerManager(&allowRetaining); - if (layerManager) { - doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION); - widgetTransaction = true; - } + layerManager = aBuilder->GetWidgetLayerManager(&view, &allowRetaining); + if (layerManager) { + doBeginTransaction = !(aFlags & PAINT_EXISTING_TRANSACTION); + widgetTransaction = true; } } if (!layerManager) { @@ -2360,11 +2370,12 @@ nsDisplayBackgroundImage::IsNonEmptyFixedImage() const } bool -nsDisplayBackgroundImage::ShouldFixToViewport(LayerManager* aManager) +nsDisplayBackgroundImage::ShouldFixToViewport(nsDisplayListBuilder* aBuilder) { // APZ needs background-attachment:fixed images layerized for correctness. + nsRefPtr layerManager = aBuilder->GetWidgetLayerManager(); if (!nsLayoutUtils::UsesAsyncScrolling(mFrame) && - aManager && aManager->ShouldAvoidComponentAlphaLayers()) { + layerManager && layerManager->ShouldAvoidComponentAlphaLayers()) { return false; } @@ -3701,7 +3712,7 @@ RequiredLayerStateForChildren(nsDisplayListBuilder* aBuilder, LayerState result = LAYER_INACTIVE; for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) { if (result == LAYER_INACTIVE && - nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder, aManager) != + nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) != aExpectedAnimatedGeometryRootForChildren) { result = LAYER_ACTIVE; } @@ -3960,7 +3971,7 @@ nsDisplayOpacity::GetLayerState(nsDisplayListBuilder* aBuilder, return LAYER_ACTIVE; return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, - nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder, aManager)); + nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder)); } bool diff --git a/layout/base/nsDisplayList.h b/layout/base/nsDisplayList.h index 48f61cb7e0..37d5a281eb 100644 --- a/layout/base/nsDisplayList.h +++ b/layout/base/nsDisplayList.h @@ -191,6 +191,8 @@ public: mMode = PLUGIN_GEOMETRY; } + mozilla::layers::LayerManager* GetWidgetLayerManager(nsView** aView = nullptr, bool* aAllowRetaining = nullptr); + /** * @return true if the display is being built in order to determine which * frame is under the mouse position. @@ -1442,7 +1444,7 @@ public: * @return true if the contents of this item are rendered fixed relative * to the nearest viewport. */ - virtual bool ShouldFixToViewport(LayerManager* aManager) + virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) { return false; } virtual bool ClearsBackground() @@ -2560,7 +2562,7 @@ public: static nsRegion GetInsideClipRegion(nsDisplayItem* aItem, nsPresContext* aPresContext, uint8_t aClip, const nsRect& aRect, bool* aSnap); - virtual bool ShouldFixToViewport(LayerManager* aManager) override; + virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) override; protected: typedef class mozilla::layers::ImageContainer ImageContainer; diff --git a/layout/base/nsLayoutDebugger.cpp b/layout/base/nsLayoutDebugger.cpp index 95348dd977..c7aa8bac6b 100644 --- a/layout/base/nsLayoutDebugger.cpp +++ b/layout/base/nsLayoutDebugger.cpp @@ -141,15 +141,29 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, aStream << " "; } } + nsAutoString contentData; nsIFrame* f = aItem->Frame(); - nsAutoString fName; #ifdef DEBUG_FRAME_DUMP - f->GetFrameName(fName); + f->GetFrameName(contentData); #endif + nsIContent* content = f->GetContent(); + if (content) { + nsString tmp; + if (content->GetID()) { + content->GetID()->ToString(tmp); + contentData.AppendLiteral(" id:"); + contentData.Append(tmp); + } + if (content->GetClasses()) { + content->GetClasses()->ToString(tmp); + contentData.AppendLiteral(" class:"); + contentData.Append(tmp); + } + } bool snap; nsRect rect = aItem->GetBounds(aBuilder, &snap); nsRect layerRect = rect - - nsLayoutUtils::GetAnimatedGeometryRootFor(aItem, aBuilder, nullptr)-> + nsLayoutUtils::GetAnimatedGeometryRootFor(aItem, aBuilder)-> GetOffsetToCrossDoc(aItem->ReferenceFrame()); nscolor color; nsRect vis = aItem->GetVisibleRect(); @@ -166,7 +180,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, } #endif aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s", - aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(fName).get(), + aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(), (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""), rect.x, rect.y, rect.width, rect.height, layerRect.x, layerRect.y, layerRect.width, layerRect.height, @@ -180,7 +194,7 @@ PrintDisplayItemTo(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem, aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r->x, r->y, r->width, r->height); } - if (aItem->ShouldFixToViewport(nullptr)) { + if (aItem->ShouldFixToViewport(aBuilder)) { aStream << " fixed"; } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 192b4d829d..941f9bbe80 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -8,7 +8,9 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/BasicEvents.h" +#include "mozilla/ClearOnShutdown.h" #include "mozilla/EventDispatcher.h" +#include "mozilla/FloatingPoint.h" #include "mozilla/gfx/PathHelpers.h" #include "mozilla/Likely.h" #include "mozilla/Maybe.h" @@ -110,6 +112,14 @@ #include "nsTransitionManager.h" #include "RestyleManager.h" +// Make sure getpid() works. +#ifdef XP_WIN +#include +#define getpid _getpid +#else +#include +#endif + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::image; @@ -453,6 +463,15 @@ GetSuitableScale(float aMaxScale, float aMinScale, // transform animation, unless that would make us rasterize something // larger than the screen. But we never want to go smaller than the // minimum scale over the animation. + if (FuzzyEqualsMultiplicative(displayVisibleRatio, aMaxScale, .01f)) { + // Using aMaxScale may make us rasterize something a fraction larger than + // the screen. However, if aMaxScale happens to be the final scale of a + // transform animation it is better to use aMaxScale so that for the + // fraction of a second before we delayerize the composited texture it has + // a better chance of being pixel aligned and composited without resampling + // (avoiding visually clunky delayerization). + return aMaxScale; + } return std::max(std::min(aMaxScale, displayVisibleRatio), aMinScale); } @@ -826,7 +845,10 @@ GetMaxDisplayPortSize(nsIContent* aContent) } nsPresContext* presContext = frame->PresContext(); - uint32_t maxSizeInDevPixels = lm->GetMaxTextureSize(); + int32_t maxSizeInDevPixels = lm->GetMaxTextureSize(); + if (maxSizeInDevPixels < 0 || maxSizeInDevPixels == INT_MAX) { + return nscoord_MAX; + } return presContext->DevPixelsToAppUnits(maxSizeInDevPixels); } @@ -1043,11 +1065,9 @@ GetDisplayPortImpl(nsIContent* aContent, nsRect *aResult, float aMultiplier) if (!gfxPrefs::LayersTilesEnabled()) { // Either we should have gotten a valid rect directly from the displayport // base, or we should have computed a valid rect from the margins. - NS_ASSERTION(GetMaxDisplayPortSize(aContent) <= 0 || - result.width < GetMaxDisplayPortSize(aContent), + NS_ASSERTION(result.width <= GetMaxDisplayPortSize(aContent), "Displayport must be a valid texture size"); - NS_ASSERTION(GetMaxDisplayPortSize(aContent) <= 0 || - result.height < GetMaxDisplayPortSize(aContent), + NS_ASSERTION(result.height <= GetMaxDisplayPortSize(aContent), "Displayport must be a valid texture size"); } @@ -1064,6 +1084,13 @@ nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult) return GetDisplayPortImpl(aContent, aResult, 1.0f); } +/* static */ bool +nsLayoutUtils::GetDisplayPortForVisibilityTesting(nsIContent* aContent, + nsRect* aResult) +{ + return GetDisplayPortImpl(aContent, aResult, 1.0f); +} + bool nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, nsIPresShell* aPresShell, @@ -1072,7 +1099,7 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, RepaintMode aRepaintMode) { MOZ_ASSERT(aContent); - MOZ_ASSERT(aContent->GetCurrentDoc() == aPresShell->GetDocument()); + MOZ_ASSERT(aContent->GetComposedDoc() == aPresShell->GetDocument()); DisplayPortMarginsPropertyData* currentData = static_cast(aContent->GetProperty(nsGkAtoms::DisplayPortMargins)); @@ -1109,6 +1136,10 @@ nsLayoutUtils::SetDisplayPortMargins(nsIContent* aContent, } } + // Display port margins changing means that the set of visible images may + // have drastically changed. Schedule an update. + aPresShell->ScheduleImageVisibilityUpdate(); + return true; } @@ -1816,11 +1847,10 @@ nsLayoutUtils::GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder, nsIFrame* nsLayoutUtils::GetAnimatedGeometryRootFor(nsDisplayItem* aItem, - nsDisplayListBuilder* aBuilder, - LayerManager* aManager) + nsDisplayListBuilder* aBuilder) { nsIFrame* f = aItem->Frame(); - if (aItem->ShouldFixToViewport(aManager)) { + if (aItem->ShouldFixToViewport(aBuilder)) { // Make its active scrolled root be the active scrolled root of // the enclosing viewport, since it shouldn't be scrolled by scrolled // frames in its document. InvalidateFixedBackgroundFramesFromList in @@ -2849,7 +2879,20 @@ nsLayoutUtils::CombineBreakType(uint8_t aOrigBreakType, #include static bool gDumpEventList = false; -int gPaintCount = 0; + +// nsLayoutUtils::PaintFrame() can call itself recursively, so rather than +// maintaining a single paint count, we need a stack. +StaticAutoPtr> gPaintCountStack; + +struct AutoNestedPaintCount { + AutoNestedPaintCount() { + gPaintCountStack->AppendElement(0); + } + ~AutoNestedPaintCount() { + gPaintCountStack->RemoveElementAt(gPaintCountStack->Length() - 1); + } +}; + #endif nsIFrame* @@ -3044,6 +3087,17 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram PROFILER_LABEL("nsLayoutUtils", "PaintFrame", js::ProfileEntry::Category::GRAPHICS); +#ifdef MOZ_DUMP_PAINTING + if (!gPaintCountStack) { + gPaintCountStack = new nsTArray(); + ClearOnShutdown(&gPaintCountStack); + + gPaintCountStack->AppendElement(0); + } + ++gPaintCountStack->LastElement(); + AutoNestedPaintCount nestedPaintCount; +#endif + if (aFlags & PAINT_WIDGET_LAYERS) { nsView* view = aFrame->GetView(); if (!(view && view->GetWidget() && GetDisplayRootFrame(aFrame) == aFrame)) { @@ -3186,25 +3240,9 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram aFrame->BuildDisplayListForStackingContext(&builder, dirtyRect, &list); } - const bool paintAllContinuations = aFlags & PAINT_ALL_CONTINUATIONS; - NS_ASSERTION(!paintAllContinuations || !aFrame->GetPrevContinuation(), - "If painting all continuations, the frame must be " - "first-continuation"); nsIAtom* frameType = aFrame->GetType(); - if (paintAllContinuations) { - nsIFrame* currentFrame = aFrame; - while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) { - PROFILER_LABEL("nsLayoutUtils", "PaintFrame::ContinuationsBuildDisplayList", - js::ProfileEntry::Category::GRAPHICS); - - nsRect frameDirty = dirtyRect - builder.ToReferenceFrame(currentFrame); - currentFrame->BuildDisplayListForStackingContext(&builder, - frameDirty, &list); - } - } - // For the viewport frame in print preview/page layout we want to paint // the grey background behind the page, not the canvas color. if (frameType == nsGkAtoms::viewportFrame && @@ -3247,7 +3285,13 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram #ifdef MOZ_DUMP_PAINTING if (gfxUtils::sDumpPaintingToFile) { nsCString string("dump-"); - string.AppendInt(gPaintCount); + // Include the process ID in the dump file name, to make sure that in an + // e10s setup different processes don't clobber each other's dump files. + string.AppendInt(getpid()); + for (int paintCount : *gPaintCountStack) { + string.AppendLiteral("-"); + string.AppendInt(paintCount); + } string.AppendLiteral(".html"); gfxUtils::sDumpPaintFile = fopen(string.BeginReading(), "w"); } else { @@ -3333,7 +3377,6 @@ nsLayoutUtils::PaintFrame(nsRenderingContext* aRenderingContext, nsIFrame* aFram fclose(gfxUtils::sDumpPaintFile); } gfxUtils::sDumpPaintFile = savedDumpFile; - gPaintCount++; #endif std::stringstream lsStream; @@ -5873,10 +5916,10 @@ nsLayoutUtils::GetClosestLayer(nsIFrame* aFrame) return aFrame->PresContext()->PresShell()->FrameManager()->GetRootFrame(); } -GraphicsFilter +Filter nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame) { - GraphicsFilter defaultFilter = GraphicsFilter::FILTER_GOOD; + Filter defaultFilter = Filter::GOOD; nsStyleContext *sc; if (nsCSSRendering::IsCanvasFrame(aForFrame)) { nsCSSRendering::FindBackground(aForFrame, &sc); @@ -5886,11 +5929,11 @@ nsLayoutUtils::GetGraphicsFilterForFrame(nsIFrame* aForFrame) switch (sc->StyleSVG()->mImageRendering) { case NS_STYLE_IMAGE_RENDERING_OPTIMIZESPEED: - return GraphicsFilter::FILTER_FAST; + return Filter::POINT; case NS_STYLE_IMAGE_RENDERING_OPTIMIZEQUALITY: - return GraphicsFilter::FILTER_BEST; + return Filter::LINEAR; case NS_STYLE_IMAGE_RENDERING_CRISPEDGES: - return GraphicsFilter::FILTER_NEAREST; + return Filter::POINT; default: return defaultFilter; } @@ -6026,7 +6069,7 @@ ComputeSnappedImageDrawingParameters(gfxContext* aCtx, const nsPoint aAnchor, const nsRect aDirty, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, uint32_t aImageFlags) { if (aDest.IsEmpty() || aFill.IsEmpty()) @@ -6194,7 +6237,7 @@ static DrawResult DrawImageInternal(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, @@ -6240,7 +6283,7 @@ DrawImageInternal(gfxContext& aContext, nsLayoutUtils::DrawSingleUnscaledImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsPoint& aDest, const nsRect* aDirty, uint32_t aImageFlags, @@ -6281,7 +6324,7 @@ nsLayoutUtils::DrawSingleUnscaledImage(gfxContext& aContext, nsLayoutUtils::DrawSingleImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aDirty, const SVGImageContext* aSVGContext, @@ -6400,7 +6443,7 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, const CSSIntSize& aImageSize, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsSize& aRepeatSize, @@ -6412,7 +6455,7 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext, js::ProfileEntry::Category::GRAPHICS); if (UseBackgroundNearestFiltering()) { - aGraphicsFilter = GraphicsFilter::FILTER_NEAREST; + aGraphicsFilter = Filter::POINT; } SVGImageContext svgContext(aImageSize, Nothing()); @@ -6446,7 +6489,7 @@ nsLayoutUtils::DrawBackgroundImage(gfxContext& aContext, nsLayoutUtils::DrawImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, @@ -8169,7 +8212,8 @@ void StrokeLineWithSnapping(const nsPoint& aP1, const nsPoint& aP2, { Point p1 = NSPointToPoint(aP1, aAppUnitsPerDevPixel); Point p2 = NSPointToPoint(aP2, aAppUnitsPerDevPixel); - SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1, p2, aPattern, aStrokeOptions, aDrawOptions); } @@ -8515,11 +8559,13 @@ nsLayoutUtils::ComputeFrameMetrics(nsIFrame* aForFrame, nsLayoutUtils::CalculateRootCompositionSize(aScrollFrame ? aScrollFrame : aForFrame, isRootContentDocRootScrollFrame, metrics)); - if (gfxPrefs::APZPrintTree()) { + if (gfxPrefs::APZPrintTree() || gfxPrefs::APZTestLoggingEnabled()) { if (nsIContent* content = frameForCompositionBoundsCalculation->GetContent()) { nsAutoString contentDescription; content->Describe(contentDescription); metrics.SetContentDescription(NS_LossyConvertUTF16toASCII(contentDescription)); + nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(), scrollId, "contentDescription", + metrics.GetContentDescription().get()); } } @@ -8634,3 +8680,26 @@ nsLayoutUtils::ShouldUseNoFramesSheet(nsIDocument* aDocument) } return !allowSubframes; } + +/* static */ void +nsLayoutUtils::GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult) +{ + aResult.Truncate(); + AppendFrameTextContent(aFrame, aResult); +} + +/* static */ void +nsLayoutUtils::AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult) +{ + if (aFrame->GetType() == nsGkAtoms::textFrame) { + auto textFrame = static_cast(aFrame); + auto offset = textFrame->GetContentOffset(); + auto length = textFrame->GetContentLength(); + textFrame->GetContent()-> + GetText()->AppendTo(aResult, offset, length); + } else { + for (nsIFrame* child : aFrame->PrincipalChildList()) { + AppendFrameTextContent(child, aResult); + } + } +} diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index b2a2939113..d2d889ecf5 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -16,7 +16,6 @@ #include "mozilla/layout/FrameChildList.h" #include "nsThreadUtils.h" #include "nsIPrincipal.h" -#include "GraphicsFilter.h" #include "nsCSSPseudoElements.h" #include "FrameMetrics.h" #include "nsIWidget.h" @@ -112,7 +111,6 @@ struct DisplayPortMarginsPropertyData { */ class nsLayoutUtils { - typedef ::GraphicsFilter GraphicsFilter; typedef mozilla::dom::DOMRectList DOMRectList; typedef mozilla::layers::Layer Layer; typedef mozilla::ContainerLayerParameters ContainerLayerParameters; @@ -120,6 +118,7 @@ class nsLayoutUtils typedef mozilla::gfx::SourceSurface SourceSurface; typedef mozilla::gfx::Color Color; typedef mozilla::gfx::DrawTarget DrawTarget; + typedef mozilla::gfx::Filter Filter; typedef mozilla::gfx::Float Float; typedef mozilla::gfx::Point Point; typedef mozilla::gfx::Rect Rect; @@ -165,6 +164,16 @@ public: */ static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult = nullptr); + /** + * @return the display port for the given element which should be used for + * visibility testing purposes. + * + * If low-precision buffers are enabled, this is the critical display port; + * otherwise, it's the same display port returned by GetDisplayPort(). + */ + static bool GetDisplayPortForVisibilityTesting(nsIContent* aContent, + nsRect* aResult = nullptr); + enum class RepaintMode : uint8_t { Repaint, DoNotRepaint @@ -540,8 +549,7 @@ public: * geometry root. */ static nsIFrame* GetAnimatedGeometryRootFor(nsDisplayItem* aItem, - nsDisplayListBuilder* aBuilder, - mozilla::layers::LayerManager* aManager); + nsDisplayListBuilder* aBuilder); /** * Finds the nearest ancestor frame to aFrame that is considered to have (or @@ -994,11 +1002,10 @@ public: PAINT_IGNORE_SUPPRESSION = 0x08, PAINT_DOCUMENT_RELATIVE = 0x10, PAINT_HIDE_CARET = 0x20, - PAINT_ALL_CONTINUATIONS = 0x40, - PAINT_TO_WINDOW = 0x80, - PAINT_EXISTING_TRANSACTION = 0x100, - PAINT_NO_COMPOSITE = 0x200, - PAINT_COMPRESSED = 0x400 + PAINT_TO_WINDOW = 0x40, + PAINT_EXISTING_TRANSACTION = 0x80, + PAINT_NO_COMPOSITE = 0x100, + PAINT_COMPRESSED = 0x200 }; /** @@ -1697,7 +1704,7 @@ public: /** * Gets the graphics filter for the frame */ - static GraphicsFilter GetGraphicsFilterForFrame(nsIFrame* aFrame); + static Filter GetGraphicsFilterForFrame(nsIFrame* aFrame); /* N.B. The only difference between variants of the Draw*Image * functions below is the type of the aImage argument. @@ -1733,7 +1740,7 @@ public: nsPresContext* aPresContext, imgIContainer* aImage, const CSSIntSize& aImageSize, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsSize& aRepeatSize, @@ -1759,7 +1766,7 @@ public: static DrawResult DrawImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aFill, const nsPoint& aAnchor, @@ -1813,7 +1820,7 @@ public: static DrawResult DrawSingleUnscaledImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsPoint& aDest, const nsRect* aDirty, uint32_t aImageFlags, @@ -1844,7 +1851,7 @@ public: static DrawResult DrawSingleImage(gfxContext& aContext, nsPresContext* aPresContext, imgIContainer* aImage, - GraphicsFilter aGraphicsFilter, + Filter aGraphicsFilter, const nsRect& aDest, const nsRect& aDirty, const mozilla::SVGImageContext* aSVGContext, @@ -2740,6 +2747,26 @@ public: static bool ShouldUseNoScriptSheet(nsIDocument* aDocument); static bool ShouldUseNoFramesSheet(nsIDocument* aDocument); + /** + * Get the text content inside the frame. This methods traverse the + * frame tree and collect the content from text frames. Note that this + * method is similiar to nsContentUtils::GetNodeTextContent, but it at + * least differs from that method in the following things: + * 1. it skips text content inside nodes like style, script, textarea + * which don't generate an in-tree text frame for the text; + * 2. it skips elements with display property set to none; + * 3. it skips out-of-flow elements; + * 4. it includes content inside pseudo elements; + * 5. it may include part of text content of a node if a text frame + * inside is split to different continuations. + */ + static void GetFrameTextContent(nsIFrame* aFrame, nsAString& aResult); + + /** + * Same as GetFrameTextContent but appends the result rather than sets it. + */ + static void AppendFrameTextContent(nsIFrame* aFrame, nsAString& aResult); + private: static uint32_t sFontSizeInflationEmPerLine; static uint32_t sFontSizeInflationMinTwips; diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 78a363ce93..21140a4a0c 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -5726,7 +5726,9 @@ PresShell::MarkImagesInSubtreeVisible(nsIFrame* aFrame, const nsRect& aRect) nsIScrollableFrame* scrollFrame = do_QueryFrame(aFrame); if (scrollFrame) { nsRect displayPort; - bool usingDisplayport = nsLayoutUtils::GetDisplayPort(aFrame->GetContent(), &displayPort); + bool usingDisplayport = + nsLayoutUtils::GetDisplayPortForVisibilityTesting(aFrame->GetContent(), + &displayPort); if (usingDisplayport) { rect = displayPort; } else { diff --git a/layout/build/moz.build b/layout/build/moz.build index 217cbe4cb6..25e779a592 100644 --- a/layout/build/moz.build +++ b/layout/build/moz.build @@ -121,6 +121,10 @@ if CONFIG['MOZ_WEBSPEECH_POCKETSPHINX']: '/media/sphinxbase', ] + +if CONFIG['MOZ_GSTREAMER']: + CXXFLAGS += CONFIG['GSTREAMER_CFLAGS'] + if CONFIG['MOZ_SECUREELEMENT']: LOCAL_INCLUDES += [ '/dom/secureelement', diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp index de3bbbcb93..958e545fd6 100644 --- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -96,6 +96,10 @@ #include "AndroidMediaPluginHost.h" #endif +#ifdef MOZ_GSTREAMER +#include "GStreamerFormatHelper.h" +#endif + #ifdef MOZ_FFMPEG #include "FFmpegRuntimeLinker.h" #endif @@ -391,6 +395,10 @@ nsLayoutStatics::Shutdown() AndroidMediaPluginHost::Shutdown(); #endif +#ifdef MOZ_GSTREAMER + GStreamerFormatHelper::Shutdown(); +#endif + #ifdef MOZ_FFMPEG FFmpegRuntimeLinker::Unlink(); #endif diff --git a/layout/generic/nsCanvasFrame.h b/layout/generic/nsCanvasFrame.h index aacbf77daf..4e56b80af0 100644 --- a/layout/generic/nsCanvasFrame.h +++ b/layout/generic/nsCanvasFrame.h @@ -249,7 +249,7 @@ public: mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT()); } - virtual bool ShouldFixToViewport(LayerManager* aManager) override + virtual bool ShouldFixToViewport(nsDisplayListBuilder* aBuilder) override { // Put background-attachment:fixed canvas background images in their own // compositing layer. Since we know their background painting area can't diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 852a0f3a99..55c74f4c95 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -752,7 +752,7 @@ nsImageFrame::MaybeDecodeForPredictedSize() // Determine the optimal image size to use. uint32_t flags = imgIContainer::FLAG_HIGH_QUALITY_SCALING | imgIContainer::FLAG_ASYNC_NOTIFY; - GraphicsFilter filter = nsLayoutUtils::GetGraphicsFilterForFrame(this); + Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(this); gfxSize gfxPredictedScreenSize = gfxSize(predictedScreenIntSize.width, predictedScreenIntSize.height); nsIntSize predictedImageSize = diff --git a/layout/generic/nsImageMap.cpp b/layout/generic/nsImageMap.cpp index a816307c93..b912973712 100644 --- a/layout/generic/nsImageMap.cpp +++ b/layout/generic/nsImageMap.cpp @@ -536,7 +536,8 @@ void PolyArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget, p2.y = pc->CSSPixelsToDevPixels(mCoords[i+1]); p1snapped = p1; p2snapped = p2; - SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1snapped, p2snapped, aColor, aStrokeOptions); p1 = p2; } @@ -544,7 +545,8 @@ void PolyArea::Draw(nsIFrame* aFrame, DrawTarget& aDrawTarget, p2.y = pc->CSSPixelsToDevPixels(mCoords[1]); p1snapped = p1; p2snapped = p2; - SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget); + SnapLineToDevicePixelsForStroking(p1snapped, p2snapped, aDrawTarget, + aStrokeOptions.mLineWidth); aDrawTarget.StrokeLine(p1snapped, p2snapped, aColor, aStrokeOptions); } } diff --git a/layout/generic/nsPluginFrame.cpp b/layout/generic/nsPluginFrame.cpp index 1efb624eea..826312e661 100644 --- a/layout/generic/nsPluginFrame.cpp +++ b/layout/generic/nsPluginFrame.cpp @@ -1426,12 +1426,11 @@ nsPluginFrame::BuildLayer(nsDisplayListBuilder* aBuilder, imglayer->SetScaleToSize(size, ScaleMode::STRETCH); imglayer->SetContainer(container); - GraphicsFilter filter = - nsLayoutUtils::GetGraphicsFilterForFrame(this); + Filter filter = nsLayoutUtils::GetGraphicsFilterForFrame(this); #ifdef MOZ_GFX_OPTIMIZE_MOBILE if (!aManager->IsCompositingCheap()) { // Pixman just horrible with bilinear filter scaling - filter = GraphicsFilter::FILTER_NEAREST; + filter = Filter::POINT; } #endif imglayer->SetFilter(filter); diff --git a/layout/generic/nsRubyBaseContainerFrame.cpp b/layout/generic/nsRubyBaseContainerFrame.cpp index dd96e3e3b7..be33805ab7 100644 --- a/layout/generic/nsRubyBaseContainerFrame.cpp +++ b/layout/generic/nsRubyBaseContainerFrame.cpp @@ -8,13 +8,12 @@ #include "nsRubyBaseContainerFrame.h" #include "nsRubyTextContainerFrame.h" - +#include "nsRubyBaseFrame.h" +#include "nsRubyTextFrame.h" #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/WritingModes.h" -#include "nsRubyBaseFrame.h" -#include "nsRubyTextFrame.h" -#include "nsContentUtils.h" +#include "nsLayoutUtils.h" #include "nsLineLayout.h" #include "nsPresContext.h" #include "nsStyleContext.h" @@ -129,9 +128,18 @@ GetIsLineBreakAllowed(nsIFrame* aFrame, bool aIsLineBreakable, *aAllowLineBreak = allowLineBreak; } +/** + * @param aBaseISizeData is an in/out param. This method updates the + * `skipWhitespace` and `trailingWhitespace` fields of the struct with + * the base level frame. Note that we don't need to do the same thing + * for ruby text frames, because they are text run container themselves + * (see nsTextFrame.cpp:BuildTextRuns), and thus no whitespace collapse + * happens across the boundary of those frames. + */ static nscoord CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, - const RubyColumnEnumerator& aEnumerator) + const RubyColumnEnumerator& aEnumerator, + nsIFrame::InlineIntrinsicISizeData* aBaseISizeData) { nscoord max = 0; uint32_t levelCount = aEnumerator.GetLevelCount(); @@ -139,9 +147,22 @@ CalculateColumnPrefISize(nsRenderingContext* aRenderingContext, nsIFrame* frame = aEnumerator.GetFrameAtLevel(i); if (frame) { nsIFrame::InlinePrefISizeData data; + if (i == 0) { + data.lineContainer = aBaseISizeData->lineContainer; + data.skipWhitespace = aBaseISizeData->skipWhitespace; + data.trailingWhitespace = aBaseISizeData->trailingWhitespace; + } else { + // The line container of ruby text frames is their parent, + // ruby text container frame. + data.lineContainer = frame->GetParent(); + } frame->AddInlinePrefISize(aRenderingContext, &data); MOZ_ASSERT(data.prevLines == 0, "Shouldn't have prev lines"); max = std::max(max, data.currentLine); + if (i == 0) { + aBaseISizeData->skipWhitespace = data.skipWhitespace; + aBaseISizeData->trailingWhitespace = data.trailingWhitespace; + } } } return max; @@ -161,11 +182,16 @@ nsRubyBaseContainerFrame::AddInlineMinISize( // Since spans are not breakable internally, use our pref isize // directly if there is any span. nsIFrame::InlinePrefISizeData data; + data.lineContainer = aData->lineContainer; + data.skipWhitespace = aData->skipWhitespace; + data.trailingWhitespace = aData->trailingWhitespace; AddInlinePrefISize(aRenderingContext, &data); aData->currentLine += data.currentLine; if (data.currentLine > 0) { aData->atStartOfLine = false; } + aData->skipWhitespace = data.skipWhitespace; + aData->trailingWhitespace = data.trailingWhitespace; return; } } @@ -189,7 +215,8 @@ nsRubyBaseContainerFrame::AddInlineMinISize( } } firstFrame = false; - nscoord isize = CalculateColumnPrefISize(aRenderingContext, enumerator); + nscoord isize = CalculateColumnPrefISize(aRenderingContext, + enumerator, aData); aData->currentLine += isize; if (isize > 0) { aData->atStartOfLine = false; @@ -209,7 +236,7 @@ nsRubyBaseContainerFrame::AddInlinePrefISize( RubyColumnEnumerator enumerator( static_cast(frame), textContainers); for (; !enumerator.AtEnd(); enumerator.Next()) { - sum += CalculateColumnPrefISize(aRenderingContext, enumerator); + sum += CalculateColumnPrefISize(aRenderingContext, enumerator, aData); } } for (uint32_t i = 0, iend = textContainers.Length(); i < iend; i++) { @@ -383,10 +410,6 @@ nsRubyBaseContainerFrame::Reflow(nsPresContext* aPresContext, }; nscoord spanISize = ReflowSpans(reflowState); isize = std::max(isize, spanISize); - if (isize > aReflowState.AvailableISize() && - aReflowState.mLineLayout->HasOptionalBreakPosition()) { - aStatus = NS_INLINE_LINE_BREAK_BEFORE(); - } } for (uint32_t i = 0; i < rtcCount; i++) { @@ -586,8 +609,7 @@ nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, nsAutoString baseText; if (aColumn.mBaseFrame) { - nsContentUtils::GetNodeTextContent(aColumn.mBaseFrame->GetContent(), - true, baseText); + nsLayoutUtils::GetFrameTextContent(aColumn.mBaseFrame, baseText); } // Reflow text frames @@ -595,8 +617,7 @@ nsRubyBaseContainerFrame::ReflowOneColumn(const ReflowState& aReflowState, nsRubyTextFrame* textFrame = aColumn.mTextFrames[i]; if (textFrame) { nsAutoString annotationText; - nsContentUtils::GetNodeTextContent(textFrame->GetContent(), - true, annotationText); + nsLayoutUtils::GetFrameTextContent(textFrame, annotationText); // Per CSS Ruby spec, the content comparison for auto-hiding // takes place prior to white spaces collapsing (white-space) diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index ea21840ce0..4a62ba4d11 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -589,6 +589,21 @@ RenderFrameParent::HitTest(const nsRect& aRect) return mTouchRegion.Contains(aRect); } +void +RenderFrameParent::StartScrollbarDrag(const AsyncDragMetrics& aDragMetrics) +{ + if (GetApzcTreeManager()) { + uint64_t layersId = GetLayersId(); + ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, + aDragMetrics.mViewId); + + APZThreadUtils::RunOnControllerThread( + NewRunnableMethod(GetApzcTreeManager(), + &APZCTreeManager::StartScrollbarDrag, + guid, aDragMetrics)); + } +} + void RenderFrameParent::GetTextureFactoryIdentifier(TextureFactoryIdentifier* aTextureFactoryIdentifier) { diff --git a/layout/ipc/RenderFrameParent.h b/layout/ipc/RenderFrameParent.h index 062bdded7b..21f3b53347 100644 --- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -25,6 +25,7 @@ class InputEvent; namespace layers { class APZCTreeManager; +class AsyncDragMetrics; class TargetConfig; struct TextureFactoryIdentifier; struct ScrollableLayerGuid; @@ -36,6 +37,7 @@ class RemoteContentController; class RenderFrameParent : public PRenderFrameParent { + typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::ContainerLayer ContainerLayer; typedef mozilla::layers::Layer Layer; @@ -93,6 +95,8 @@ public: bool HitTest(const nsRect& aRect); + void StartScrollbarDrag(const AsyncDragMetrics& aDragMetrics); + void GetTextureFactoryIdentifier(TextureFactoryIdentifier* aTextureFactoryIdentifier); inline uint64_t GetLayersId() { return mLayersId; } diff --git a/layout/reftests/async-scrolling/bg-fixed-child-clip-1.html b/layout/reftests/async-scrolling/bg-fixed-child-clip-1.html new file mode 100644 index 0000000000..a39c96e8a3 --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-clip-1.html @@ -0,0 +1,16 @@ + + + +
+
+ + + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-clip-2.html b/layout/reftests/async-scrolling/bg-fixed-child-clip-2.html new file mode 100644 index 0000000000..ec63dc8fd6 --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-clip-2.html @@ -0,0 +1,20 @@ + + + +
+
+
+
+ + + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-clip-ref.html b/layout/reftests/async-scrolling/bg-fixed-child-clip-ref.html new file mode 100644 index 0000000000..7ae3847742 --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-clip-ref.html @@ -0,0 +1,11 @@ + + + +
+
+ + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-mask-ref.html b/layout/reftests/async-scrolling/bg-fixed-child-mask-ref.html new file mode 100644 index 0000000000..c71b15b434 --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-mask-ref.html @@ -0,0 +1,11 @@ + + + +
+
+ + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-mask.html b/layout/reftests/async-scrolling/bg-fixed-child-mask.html new file mode 100644 index 0000000000..a280259f4d --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-mask.html @@ -0,0 +1,17 @@ + + + +
+
+ + + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-no-culling-ref.html b/layout/reftests/async-scrolling/bg-fixed-child-no-culling-ref.html new file mode 100644 index 0000000000..8f647b7c9e --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-no-culling-ref.html @@ -0,0 +1,11 @@ + + + +
+
+ + diff --git a/layout/reftests/async-scrolling/bg-fixed-child-no-culling.html b/layout/reftests/async-scrolling/bg-fixed-child-no-culling.html new file mode 100644 index 0000000000..9c385c20c8 --- /dev/null +++ b/layout/reftests/async-scrolling/bg-fixed-child-no-culling.html @@ -0,0 +1,14 @@ + + + +
+
+
+
+ + diff --git a/layout/reftests/async-scrolling/reftest.list b/layout/reftests/async-scrolling/reftest.list index 9e30470aab..49347a9e1a 100644 --- a/layout/reftests/async-scrolling/reftest.list +++ b/layout/reftests/async-scrolling/reftest.list @@ -2,6 +2,12 @@ skip-if(!asyncPan) == bg-fixed-1.html bg-fixed-1-ref.html skip-if(!asyncPan) == bg-fixed-cover-1.html bg-fixed-cover-1-ref.html skip-if(!asyncPan) == bg-fixed-cover-2.html bg-fixed-cover-2-ref.html skip-if(!asyncPan) == bg-fixed-cover-3.html bg-fixed-cover-3-ref.html +skip-if(!asyncPan) == bg-fixed-child.html bg-fixed-child-ref.html +skip-if(!asyncPan) == bg-fixed-child-clip-1.html bg-fixed-child-clip-ref.html +skip-if(!asyncPan) == bg-fixed-child-clip-2.html bg-fixed-child-clip-ref.html +fuzzy(1,246) skip-if(!asyncPan) == bg-fixed-child-mask.html bg-fixed-child-mask-ref.html +skip-if(!asyncPan) == bg-fixed-child-no-culling.html bg-fixed-child-no-culling-ref.html +fuzzy-if(B2G,2,5366) skip-if(!asyncPan) == bg-fixed-transformed-image.html bg-fixed-transformed-image-ref.html skip-if(!asyncPan) == element-1.html element-1-ref.html pref(layers.force-active,true) skip-if(!asyncPan) == iframe-1.html iframe-1-ref.html skip-if(!asyncPan) == nested-1.html nested-1-ref.html diff --git a/layout/reftests/css-ruby/intrinsic-isize-2-ref.html b/layout/reftests/css-ruby/intrinsic-isize-2-ref.html new file mode 100644 index 0000000000..cb706c13e6 --- /dev/null +++ b/layout/reftests/css-ruby/intrinsic-isize-2-ref.html @@ -0,0 +1,31 @@ + + + + + Bug 1180443 - Intrinsic ISize calculation of ruby with whitespace + + + +

No line break should happen in any block, and the content should just fit in the block.

+ +
+ ABC DEF +
+
+ XYZ ABCDEF XYZ +
+ +
+ あい うえ +
+
+ お あいうえ お +
+
+ + diff --git a/layout/reftests/css-ruby/intrinsic-isize-2.html b/layout/reftests/css-ruby/intrinsic-isize-2.html new file mode 100644 index 0000000000..05991bb1c4 --- /dev/null +++ b/layout/reftests/css-ruby/intrinsic-isize-2.html @@ -0,0 +1,31 @@ + + + + + Bug 1180443 - Intrinsic ISize calculation of ruby with whitespace + + + +

No line break should happen in any block, and the content should just fit in the block.

+ +
+ ABC DEF +
+
+ XYZ ABCDEF XYZ +
+ +
+ あい うえ +
+
+ お あいうえ お +
+
+ + diff --git a/layout/reftests/css-ruby/lang-specific-style-1-ref.html b/layout/reftests/css-ruby/lang-specific-style-1-ref.html new file mode 100644 index 0000000000..379c82bd3b --- /dev/null +++ b/layout/reftests/css-ruby/lang-specific-style-1-ref.html @@ -0,0 +1,14 @@ + + + + + Bug 1133624 - Test for lang-specific default stylesheet for ruby + + + +

base x xtext x x

+

base x xtext x x

+

base x xtext x x

+

base x xtext x x

+ + diff --git a/layout/reftests/css-ruby/lang-specific-style-1.html b/layout/reftests/css-ruby/lang-specific-style-1.html new file mode 100644 index 0000000000..1df3e71e5a --- /dev/null +++ b/layout/reftests/css-ruby/lang-specific-style-1.html @@ -0,0 +1,14 @@ + + + + + Bug 1133624 - Test for lang-specific default stylesheet for ruby + + + +

base x xtext x x

+

base x xtext x x

+

base x xtext x x

+

base x xtext x x

+ + diff --git a/layout/reftests/css-ruby/reftest.list b/layout/reftests/css-ruby/reftest.list index 9ab3f19c98..609f6f9775 100644 --- a/layout/reftests/css-ruby/reftest.list +++ b/layout/reftests/css-ruby/reftest.list @@ -22,8 +22,10 @@ test-pref(dom.meta-viewport.enabled,true) test-pref(font.size.inflation.emPerLin == intra-level-whitespace-2.html intra-level-whitespace-2-ref.html == intra-level-whitespace-3.html intra-level-whitespace-3-ref.html == intrinsic-isize-1.html intrinsic-isize-1-ref.html +== intrinsic-isize-2.html intrinsic-isize-2-ref.html == justification-1.html justification-1-ref.html == justification-2.html justification-2-ref.html +fuzzy-if(winWidget,255,792) == lang-specific-style-1.html lang-specific-style-1-ref.html # bug 1134947 == line-breaking-1.html line-breaking-1-ref.html == line-breaking-2.html line-breaking-2-ref.html == line-break-suppression-1.html line-break-suppression-1-ref.html diff --git a/layout/reftests/w3c-css/submitted/ruby/reftest.list b/layout/reftests/w3c-css/submitted/ruby/reftest.list index c12e992e36..1883fb9886 100644 --- a/layout/reftests/w3c-css/submitted/ruby/reftest.list +++ b/layout/reftests/w3c-css/submitted/ruby/reftest.list @@ -9,4 +9,5 @@ == ruby-autohide-001.html ruby-autohide-001-ref.html == ruby-autohide-002.html ruby-autohide-002-ref.html == ruby-autohide-003.html ruby-autohide-003-ref.html +== ruby-autohide-004.html ruby-autohide-001-ref.html diff --git a/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html new file mode 100644 index 0000000000..4499cf3e16 --- /dev/null +++ b/layout/reftests/w3c-css/submitted/ruby/ruby-autohide-004.html @@ -0,0 +1,15 @@ + + + + + CSS Test: Autohide ruby annotations which are identical to their bases + + + + + + + 振 + + + diff --git a/layout/reftests/xul/inactive-fixed-bg-bug1205630-ref.html b/layout/reftests/xul/inactive-fixed-bg-bug1205630-ref.html new file mode 100644 index 0000000000..2322e4004d --- /dev/null +++ b/layout/reftests/xul/inactive-fixed-bg-bug1205630-ref.html @@ -0,0 +1,44 @@ + + + +Testcase + + + + +
+
+
+ + + + + + + + + + diff --git a/layout/reftests/xul/inactive-fixed-bg-bug1205630.xul b/layout/reftests/xul/inactive-fixed-bg-bug1205630.xul new file mode 100644 index 0000000000..306338c3fd --- /dev/null +++ b/layout/reftests/xul/inactive-fixed-bg-bug1205630.xul @@ -0,0 +1,36 @@ + + + + +
+
+
+ + + + + + + + +
diff --git a/layout/reftests/xul/reftest.list b/layout/reftests/xul/reftest.list index 93e463eb4c..0069d62eb8 100644 --- a/layout/reftests/xul/reftest.list +++ b/layout/reftests/xul/reftest.list @@ -8,3 +8,4 @@ skip-if(cocoaWidget) skip-if((B2G&&browserIsRemote)||Mulet) == accesskey.xul acc fails-if(cocoaWidget) skip-if((B2G&&browserIsRemote)||Mulet) == tree-row-outline-1.xul tree-row-outline-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop skip-if((B2G&&browserIsRemote)||Mulet) != tree-row-outline-1.xul tree-row-outline-1-notref.xul # Initial mulet triage: parity with B2G/B2G Desktop skip-if((B2G&&browserIsRemote)||Mulet) == text-small-caps-1.xul text-small-caps-1-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop +skip-if((B2G&&browserIsRemote)||Mulet) == inactive-fixed-bg-bug1205630.xul inactive-fixed-bg-bug1205630-ref.html diff --git a/layout/style/html.css b/layout/style/html.css index 9548459131..9c0819f0b1 100644 --- a/layout/style/html.css +++ b/layout/style/html.css @@ -844,6 +844,12 @@ marquee[direction="up"], marquee[direction="down"] { rtc:lang(zh-TW), rt:lang(zh-TW) { font-size: 30%; /* bopomofo */ } + rtc:lang(zh), rt:lang(zh) { + ruby-align: center; + } + rtc:lang(zh-TW), rt:lang(zh-TW) { + font-size: 30%; /* bopomofo */ + } rtc > rt { font-size: inherit; } diff --git a/layout/svg/nsSVGGradientFrame.cpp b/layout/svg/nsSVGGradientFrame.cpp index 9af45a8270..e26b95270f 100644 --- a/layout/svg/nsSVGGradientFrame.cpp +++ b/layout/svg/nsSVGGradientFrame.cpp @@ -278,11 +278,11 @@ nsSVGGradientFrame::GetPaintServerPattern(nsIFrame* aSource, uint16_t aSpread = GetSpreadMethod(); if (aSpread == SVG_SPREADMETHOD_PAD) - gradient->SetExtend(gfxPattern::EXTEND_PAD); + gradient->SetExtend(ExtendMode::CLAMP); else if (aSpread == SVG_SPREADMETHOD_REFLECT) - gradient->SetExtend(gfxPattern::EXTEND_REFLECT); + gradient->SetExtend(ExtendMode::REFLECT); else if (aSpread == SVG_SPREADMETHOD_REPEAT) - gradient->SetExtend(gfxPattern::EXTEND_REPEAT); + gradient->SetExtend(ExtendMode::REPEAT); gradient->SetMatrix(patternMatrix); diff --git a/layout/svg/nsSVGGradientFrame.h b/layout/svg/nsSVGGradientFrame.h index 314b0a6053..591b83089b 100644 --- a/layout/svg/nsSVGGradientFrame.h +++ b/layout/svg/nsSVGGradientFrame.h @@ -39,6 +39,8 @@ typedef nsSVGPaintServerFrame nsSVGGradientFrameBase; */ class nsSVGGradientFrame : public nsSVGGradientFrameBase { + typedef mozilla::gfx::ExtendMode ExtendMode; + protected: explicit nsSVGGradientFrame(nsStyleContext* aContext); diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 73964ce9d2..981e94b7bb 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -618,9 +618,9 @@ public: , mFlags (aFlags) {} virtual bool operator()(gfxContext* aContext, - const gfxRect& aFillRect, - const GraphicsFilter& aFilter, - const gfxMatrix& aTransform) override; + const gfxRect& aFillRect, + const Filter& aFilter, + const gfxMatrix& aTransform) override; private: nsIFrame* mFrame; nsSize mPaintServerSize; @@ -631,7 +631,7 @@ private: bool PaintFrameCallback::operator()(gfxContext* aContext, const gfxRect& aFillRect, - const GraphicsFilter& aFilter, + const Filter& aFilter, const gfxMatrix& aTransform) { if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER) @@ -674,8 +674,7 @@ PaintFrameCallback::operator()(gfxContext* aContext, nsRect dirty(-offset.x, -offset.y, mPaintServerSize.width, mPaintServerSize.height); - uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM | - nsLayoutUtils::PAINT_ALL_CONTINUATIONS; + uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM; if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) { flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES; } @@ -684,6 +683,23 @@ PaintFrameCallback::operator()(gfxContext* aContext, dirty, NS_RGBA(0, 0, 0, 0), flags); + nsIFrame* currentFrame = mFrame; + while ((currentFrame = currentFrame->GetNextContinuation()) != nullptr) { + offset = currentFrame->GetOffsetToCrossDoc(mFrame); + devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel; + + aContext->Save(); + aContext->Multiply(gfxMatrix::Scaling(1/scaleX, 1/scaleY)); + aContext->Multiply(gfxMatrix::Translation(devPxOffset)); + aContext->Multiply(gfxMatrix::Scaling(scaleX, scaleY)); + + nsLayoutUtils::PaintFrame(&context, currentFrame, + dirty - offset, NS_RGBA(0, 0, 0, 0), + flags); + + aContext->Restore(); + } + aContext->Restore(); mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER); diff --git a/layout/svg/nsSVGIntegrationUtils.h b/layout/svg/nsSVGIntegrationUtils.h index e14a9fa01a..f8781aa83e 100644 --- a/layout/svg/nsSVGIntegrationUtils.h +++ b/layout/svg/nsSVGIntegrationUtils.h @@ -7,7 +7,6 @@ #define NSSVGINTEGRATIONUTILS_H_ #include "gfxMatrix.h" -#include "GraphicsFilter.h" #include "gfxRect.h" #include "nsAutoPtr.h" diff --git a/layout/svg/nsSVGPatternFrame.cpp b/layout/svg/nsSVGPatternFrame.cpp index 0b2a6d257e..90ff295150 100644 --- a/layout/svg/nsSVGPatternFrame.cpp +++ b/layout/svg/nsSVGPatternFrame.cpp @@ -723,7 +723,7 @@ nsSVGPatternFrame::GetPaintServerPattern(nsIFrame *aSource, if (!pattern || pattern->CairoStatus()) return nullptr; - pattern->SetExtend(gfxPattern::EXTEND_REPEAT); + pattern->SetExtend(ExtendMode::REPEAT); return pattern.forget(); } diff --git a/layout/xul/nsSliderFrame.cpp b/layout/xul/nsSliderFrame.cpp index 04993e0814..93845a2ca6 100644 --- a/layout/xul/nsSliderFrame.cpp +++ b/layout/xul/nsSliderFrame.cpp @@ -23,6 +23,7 @@ #include "nsIDOMMouseEvent.h" #include "nsScrollbarButtonFrame.h" #include "nsISliderListener.h" +#include "nsIScrollableFrame.h" #include "nsIScrollbarMediator.h" #include "nsScrollbarFrame.h" #include "nsRepeatService.h" @@ -35,9 +36,13 @@ #include "mozilla/Preferences.h" #include "mozilla/LookAndFeel.h" #include "mozilla/MouseEvents.h" +#include "mozilla/layers/AsyncDragMetrics.h" +#include "mozilla/layers/InputAPZContext.h" #include using namespace mozilla; +using mozilla::layers::AsyncDragMetrics; +using mozilla::layers::InputAPZContext; bool nsSliderFrame::gMiddlePref = false; int32_t nsSliderFrame::gSnapMultiplier; @@ -858,6 +863,53 @@ nsSliderMediator::HandleEvent(nsIDOMEvent* aEvent) return NS_OK; } +bool +nsSliderFrame::StartAPZDrag(WidgetGUIEvent* aEvent) +{ + if (!gfxPlatform::GetPlatform()->SupportsApzDragInput()) { + return false; + } + + nsContainerFrame* cf = GetScrollbar()->GetParent(); + if (!cf) { + return false; + } + + nsIContent* scrollableContent = cf->GetContent(); + if (!scrollableContent) { + return false; + } + + mozilla::layers::FrameMetrics::ViewID scrollTargetId; + bool hasID = nsLayoutUtils::FindIDFor(scrollableContent, &scrollTargetId); + bool hasAPZView = hasID && (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID); + + if (!hasAPZView) { + return false; + } + + nsIFrame* scrollbarBox = GetScrollbar(); + nsCOMPtr scrollbar = GetContentOfBox(scrollbarBox); + + // This rect is the range in which the scroll thumb can slide in. + nsRect sliderTrack = GetRect() - scrollbarBox->GetPosition(); + CSSIntRect sliderTrackCSS = CSSIntRect::FromAppUnitsRounded(sliderTrack); + + uint64_t inputblockId = InputAPZContext::GetInputBlockId(); + uint32_t presShellId = PresContext()->PresShell()->GetPresShellId(); + AsyncDragMetrics dragMetrics(scrollTargetId, presShellId, inputblockId, + NSAppUnitsToIntPixels(mDragStart, + float(AppUnitsPerCSSPixel())), + sliderTrackCSS, + IsHorizontal() ? AsyncDragMetrics::HORIZONTAL : + AsyncDragMetrics::VERTICAL); + + // When we start an APZ drag, we wont get mouse events for the drag. + // APZ will consume them all and only notify us of the new scroll position. + this->GetNearestWidget()->StartAsyncScrollbarDrag(dragMetrics); + return true; +} + nsresult nsSliderFrame::StartDrag(nsIDOMEvent* aEvent) { @@ -925,6 +977,8 @@ nsSliderFrame::StartDrag(nsIDOMEvent* aEvent) mDragStart = pos - mThumbStart; + StartAPZDrag(event); + #ifdef DEBUG_SLIDER printf("Pressed mDragStart=%d\n",mDragStart); #endif diff --git a/layout/xul/nsSliderFrame.h b/layout/xul/nsSliderFrame.h index 2b2a74818f..1cbdda275f 100644 --- a/layout/xul/nsSliderFrame.h +++ b/layout/xul/nsSliderFrame.h @@ -100,6 +100,8 @@ public: nsresult StartDrag(nsIDOMEvent* aEvent); nsresult StopDrag(); + bool StartAPZDrag(mozilla::WidgetGUIEvent* aEvent); + static int32_t GetCurrentPosition(nsIContent* content); static int32_t GetMinPosition(nsIContent* content); static int32_t GetMaxPosition(nsIContent* content); diff --git a/layout/xul/tree/nsTreeBodyFrame.cpp b/layout/xul/tree/nsTreeBodyFrame.cpp index 7dfce0faaf..13f0fb4f68 100644 --- a/layout/xul/tree/nsTreeBodyFrame.cpp +++ b/layout/xul/tree/nsTreeBodyFrame.cpp @@ -3251,7 +3251,8 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex, pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2)); Point p2(pc->AppUnitsToGfxUnits(destX), pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2)); - SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget, + strokeOptions.mLineWidth); drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions); } @@ -3272,7 +3273,8 @@ nsTreeBodyFrame::PaintCell(int32_t aRowIndex, else if (i == level) p2.y = pc->AppUnitsToGfxUnits(lineY + mRowHeight / 2); - SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget); + SnapLineToDevicePixelsForStroking(p1, p2, *drawTarget, + strokeOptions.mLineWidth); drawTarget->StrokeLine(p1, p2, colorPatt, strokeOptions); } } @@ -3418,11 +3420,11 @@ nsTreeBodyFrame::PaintTwisty(int32_t aRowIndex, if (imageSize.height < twistyRect.height) { pt.y += (twistyRect.height - imageSize.height)/2; } - + // Paint the image. - nsLayoutUtils::DrawSingleUnscaledImage(*aRenderingContext.ThebesContext(), - aPresContext, image, - GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect, + nsLayoutUtils::DrawSingleUnscaledImage( + *aRenderingContext.ThebesContext(), aPresContext, image, + Filter::POINT, pt, &aDirtyRect, imgIContainer::FLAG_NONE, &imageSize); } } @@ -3761,7 +3763,7 @@ nsTreeBodyFrame::PaintCheckbox(int32_t aRowIndex, // Paint the image. nsLayoutUtils::DrawSingleUnscaledImage(*aRenderingContext.ThebesContext(), aPresContext, - image, GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect, + image, Filter::POINT, pt, &aDirtyRect, imgIContainer::FLAG_NONE, &imageSize); } } diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index c82adcff6b..9694bab252 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -368,6 +368,10 @@ pref("media.webm.enabled", true); pref("media.webm.intel_decoder.enabled", false); #endif #endif +#ifdef MOZ_GSTREAMER +pref("media.gstreamer.enabled", true); +pref("media.gstreamer.enable-blacklist", true); +#endif #ifdef MOZ_APPLEMEDIA pref("media.apple.mp3.enabled", true); pref("media.apple.mp4.enabled", true); @@ -390,8 +394,8 @@ pref("media.webrtc.debug.log_file", ""); pref("media.webrtc.debug.aec_dump_max_size", 4194304); // 4MB #ifdef MOZ_WIDGET_GONK -pref("media.navigator.video.default_width",320); -pref("media.navigator.video.default_height",240); +pref("media.navigator.video.default_width", 320); +pref("media.navigator.video.default_height", 240); pref("media.peerconnection.enabled", true); pref("media.peerconnection.video.enabled", true); pref("media.navigator.video.max_fs", 1200); // 640x480 == 1200mb @@ -557,6 +561,7 @@ pref("apz.axis_lock.breakout_angle", "0.3926991"); // PI / 8 (22.5 degrees) pref("apz.axis_lock.direct_pan_angle", "1.047197"); // PI / 3 (60 degrees) pref("apz.content_response_timeout", 300); pref("apz.cross_slide.enabled", false); +pref("apz.drag.enabled", false); pref("apz.danger_zone_x", 50); pref("apz.danger_zone_y", 100); pref("apz.enlarge_displayport_when_clipped", false); @@ -3890,6 +3895,9 @@ pref("print.print_extra_margin", 0); // twips pref("layout.css.scroll-behavior.enabled", false); pref("layout.css.scroll-behavior.property-enabled", false); +// CSS Scroll Snapping requires the C++ APZC +pref("layout.css.scroll-snap.enabled", false); + /* PostScript print module prefs */ // pref("print.postscript.enabled", true); pref("print.postscript.paper_size", "letter"); @@ -3958,6 +3966,9 @@ pref("print.print_extra_margin", 0); // twips // font names +pref("font.name.serif.ar", "serif"); +pref("font.name.sans-serif.ar", "sans-serif"); +pref("font.name.monospace.ar", "monospace"); pref("font.size.fixed.ar", 12); pref("font.name.serif.el", "serif"); @@ -4008,7 +4019,9 @@ pref("font.name.serif.zh-HK", "serif"); pref("font.name.sans-serif.zh-HK", "sans-serif"); pref("font.name.monospace.zh-HK", "monospace"); -// zh-TW +pref("font.name.serif.zh-TW", "serif"); +pref("font.name.sans-serif.zh-TW", "sans-serif"); +pref("font.name.monospace.zh-TW", "monospace"); /* PostScript print module prefs */ // pref("print.postscript.enabled", true); @@ -4389,6 +4402,8 @@ pref("gl.msaa-level", 0); #else pref("gl.msaa-level", 2); #endif +pref("gl.require-hardware", false); + pref("webgl.force-enabled", false); pref("webgl.disabled", false); pref("webgl.disable-angle", false); @@ -4407,7 +4422,16 @@ pref("webgl.enable-privileged-extensions", false); pref("webgl.bypass-shader-validation", false); pref("webgl.enable-prototype-webgl2", false); pref("webgl.disable-fail-if-major-performance-caveat", false); -pref("gl.require-hardware", false); + +#ifdef RELEASE_BUILD +// Keep this disabled on Release and Beta for now. (see bug 1171228) +pref("webgl.enable-debug-renderer-info", false); +#else +pref("webgl.enable-debug-renderer-info", true); +#endif + +pref("webgl.renderer-string-override", ""); +pref("webgl.vendor-string-override", ""); #ifdef XP_WIN pref("webgl.angle.try-d3d11", true); @@ -4613,6 +4637,9 @@ pref("notification.feature.enabled", false); // Web Notification pref("dom.webnotifications.enabled", true); +#if !defined(RELEASE_BUILD) +pref("dom.webnotifications.serviceworker.enabled", true); +#endif // Alert animation effect, name is disableSlidingEffect for backwards-compat. pref("alerts.disableSlidingEffect", false); @@ -5111,6 +5138,7 @@ pref("dom.beforeAfterKeyboardEvent.enabled", false); pref("dom.presentation.enabled", false); pref("dom.presentation.tcp_server.debug", false); pref("dom.presentation.discovery.enabled", true); +pref("dom.presentation.discovery.timeout_ms", 10000); pref("dom.presentation.discoverable", false); #ifdef XP_MACOSX diff --git a/python/mozbuild/mozbuild/backend/android_eclipse.py b/python/mozbuild/mozbuild/backend/android_eclipse.py index 97508d1378..a1bd995c1d 100644 --- a/python/mozbuild/mozbuild/backend/android_eclipse.py +++ b/python/mozbuild/mozbuild/backend/android_eclipse.py @@ -24,6 +24,7 @@ from ..frontend.data import ( ) from ..makeutil import Makefile from ..util import ensureParentDir +from mozbuild.base import ExecutionSummary def pretty_print(element): @@ -38,22 +39,17 @@ class AndroidEclipseBackend(CommonBackend): """Backend that generates Android Eclipse project files. """ - def _init(self): - CommonBackend._init(self) - - def detailed(summary): - s = 'Wrote {:d} Android Eclipse projects to {:s}; ' \ - '{:d} created; {:d} updated'.format( - summary.created_count + summary.updated_count, - mozpath.join(self.environment.topobjdir, 'android_eclipse'), - summary.created_count, - summary.updated_count) - - return s - - # This is a little kludgy and could be improved with a better API. - self.summary.backend_detailed_summary = types.MethodType(detailed, - self.summary) + def summary(self): + return ExecutionSummary( + 'AndroidEclipse backend executed in {execution_time:.2f}s\n' + 'Wrote {projects:d} Android Eclipse projects to {path:s}; ' + '{created:d} created; {updated:d} updated', + execution_time=self._execution_time, + projects=self._created_count + self._updated_count, + path=mozpath.join(self.environment.topobjdir, 'android_eclipse'), + created=self._created_count, + updated=self._updated_count, + ) def consume_object(self, obj): """Write out Android Eclipse project files.""" @@ -257,7 +253,7 @@ class AndroidEclipseBackend(CommonBackend): # When we re-create the build backend, we kill everything that was there. if os.path.isdir(project_directory): - self.summary.updated_count += 1 + self._updated_count += 1 else: - self.summary.created_count += 1 + self._created_count += 1 copier.copy(project_directory, skip_if_older=False, remove_unaccounted=True) diff --git a/python/mozbuild/mozbuild/backend/base.py b/python/mozbuild/mozbuild/backend/base.py index 5c67572d66..0096409392 100644 --- a/python/mozbuild/mozbuild/backend/base.py +++ b/python/mozbuild/mozbuild/backend/base.py @@ -21,102 +21,9 @@ import mozpack.path as mozpath from ..preprocessor import Preprocessor from ..pythonutil import iter_modules_in_path from ..util import FileAvoidWrite -from ..frontend.data import ( - ContextDerived, - ReaderSummary, -) +from ..frontend.data import ContextDerived from .configenvironment import ConfigEnvironment - - -class BackendConsumeSummary(object): - """Holds state about what a backend did. - - This is used primarily to print a summary of what the backend did - so people know what's going on. - """ - def __init__(self): - # How many moz.build files were read. This includes included files. - self.mozbuild_count = 0 - - # The number of derived objects from the read moz.build files. - self.object_count = 0 - - # The number of backend files created. - self.created_count = 0 - - # The number of backend files updated. - self.updated_count = 0 - - # The number of unchanged backend files. - self.unchanged_count = 0 - - # The number of deleted backend files. - self.deleted_count = 0 - - # The total wall time this backend spent consuming objects. If - # the iterable passed into consume() is a generator, this includes the - # time spent to read moz.build files. - self.wall_time = 0.0 - - # CPU time spent by during the interval captured by wall_time. - self.cpu_time = 0.0 - - # The total wall time spent executing moz.build files. This is just - # the read and execute time. It does not cover consume time. - self.mozbuild_execution_time = 0.0 - - # The total wall time spent emitting objects from sandboxes. - self.emitter_execution_time = 0.0 - - # The total wall time spent in the backend. This counts the time the - # backend writes out files, etc. - self.backend_execution_time = 0.0 - - # How much wall time the system spent doing other things. This is - # wall_time - mozbuild_execution_time - emitter_execution_time - - # backend_execution_time. - self.other_time = 0.0 - - # Mapping of changed file paths to diffs of the changes. - self.file_diffs = {} - - @property - def reader_summary(self): - return 'Finished reading {:d} moz.build files in {:.2f}s'.format( - self.mozbuild_count, - self.mozbuild_execution_time) - - @property - def emitter_summary(self): - return 'Processed into {:d} build config descriptors in {:.2f}s'.format( - self.object_count, self.emitter_execution_time) - - @property - def backend_summary(self): - return 'Backend executed in {:.2f}s'.format(self.backend_execution_time) - - def backend_detailed_summary(self): - """Backend summary to be supplied by BuildBackend implementations.""" - return None - - @property - def total_summary(self): - efficiency_value = self.cpu_time / self.wall_time if self.wall_time else 100 - return 'Total wall time: {:.2f}s; CPU time: {:.2f}s; Efficiency: ' \ - '{:.0%}; Untracked: {:.2f}s'.format( - self.wall_time, self.cpu_time, efficiency_value, - self.other_time) - - def summaries(self): - yield self.reader_summary - yield self.emitter_summary - yield self.backend_summary - - detailed = self.backend_detailed_summary() - if detailed: - yield detailed - - yield self.total_summary +from mozbuild.base import ExecutionSummary class BuildBackend(LoggingMixin): @@ -135,7 +42,6 @@ class BuildBackend(LoggingMixin): self.populate_logger() self.environment = environment - self.summary = BackendConsumeSummary() # Files whose modification should cause a new read and backend # generation. @@ -155,8 +61,44 @@ class BuildBackend(LoggingMixin): self._environments = {} self._environments[environment.topobjdir] = environment + # The number of backend files created. + self._created_count = 0 + + # The number of backend files updated. + self._updated_count = 0 + + # The number of unchanged backend files. + self._unchanged_count = 0 + + # The number of deleted backend files. + self._deleted_count = 0 + + # The total wall time spent in the backend. This counts the time the + # backend writes out files, etc. + self._execution_time = 0.0 + + # Mapping of changed file paths to diffs of the changes. + self.file_diffs = {} + self._init() + def summary(self): + return ExecutionSummary( + self.__class__.__name__.replace('Backend', '') + + ' backend executed in {execution_time:.2f}s\n ' + '{total:d} total backend files; ' + '{created:d} created; ' + '{updated:d} updated; ' + '{unchanged:d} unchanged; ' + '{deleted:d} deleted', + execution_time=self._execution_time, + total=self._created_count + self._updated_count + + self._unchanged_count, + created=self._created_count, + updated=self._updated_count, + unchanged=self._unchanged_count, + deleted=self._deleted_count) + def _init(): """Hook point for child classes to perform actions during __init__. @@ -173,24 +115,14 @@ class BuildBackend(LoggingMixin): base class consumes objects and calls methods (possibly) implemented by child classes. """ - cpu_start = time.clock() - time_start = time.time() - backend_time = 0.0 - for obj in objs: - self.summary.object_count += 1 obj_start = time.time() self.consume_object(obj) - backend_time += time.time() - obj_start + self._execution_time += time.time() - obj_start if isinstance(obj, ContextDerived): self.backend_input_files |= obj.context_all_paths - if isinstance(obj, ReaderSummary): - self.summary.mozbuild_count = obj.total_file_count - self.summary.mozbuild_execution_time = obj.total_sandbox_execution_time - self.summary.emitter_execution_time = obj.total_emitter_execution_time - # Pull in all loaded Python as dependencies so any Python changes that # could influence our output result in a rescan. self.backend_input_files |= set(iter_modules_in_path( @@ -198,14 +130,14 @@ class BuildBackend(LoggingMixin): finished_start = time.time() self.consume_finished() - backend_time += time.time() - finished_start + self._execution_time += time.time() - finished_start # Purge backend files created in previous run, but not created anymore delete_files = self._backend_output_list - self._backend_output_files for path in delete_files: try: os.unlink(mozpath.join(self.environment.topobjdir, path)) - self.summary.deleted_count += 1 + self._deleted_count += 1 except OSError: pass # Remove now empty directories @@ -216,24 +148,14 @@ class BuildBackend(LoggingMixin): pass # Write out the list of backend files generated, if it changed. - if self.summary.deleted_count or self.summary.created_count or \ + if self._deleted_count or self._created_count or \ not os.path.exists(self._backend_output_list_file): with open(self._backend_output_list_file, 'w') as fh: fh.write('\n'.join(sorted(self._backend_output_files))) - elif self.summary.updated_count: + elif self._updated_count: with open(self._backend_output_list_file, 'a'): os.utime(self._backend_output_list_file, None) - self.summary.cpu_time = time.clock() - cpu_start - self.summary.wall_time = time.time() - time_start - self.summary.backend_execution_time = backend_time - self.summary.other_time = self.summary.wall_time - \ - self.summary.mozbuild_execution_time - \ - self.summary.emitter_execution_time - \ - self.summary.backend_execution_time - - return self.summary - @abstractmethod def consume_object(self, obj): """Consumes an individual TreeMetadata instance. @@ -250,7 +172,7 @@ class BuildBackend(LoggingMixin): """Context manager to write a file. This is a glorified wrapper around FileAvoidWrite with integration to - update the BackendConsumeSummary on this instance. + update the summary data on this instance. Example usage: @@ -276,13 +198,13 @@ class BuildBackend(LoggingMixin): self._backend_output_files.add(mozpath.relpath(fh.name, self.environment.topobjdir)) existed, updated = fh.close() if not existed: - self.summary.created_count += 1 + self._created_count += 1 elif updated: - self.summary.updated_count += 1 + self._updated_count += 1 if fh.diff: - self.summary.file_diffs[fh.name] = fh.diff + self.file_diffs[fh.name] = fh.diff else: - self.summary.unchanged_count += 1 + self._unchanged_count += 1 @contextmanager def _get_preprocessor(self, obj): diff --git a/python/mozbuild/mozbuild/backend/cpp_eclipse.py b/python/mozbuild/mozbuild/backend/cpp_eclipse.py index a7b4a5548d..50897a0a39 100644 --- a/python/mozbuild/mozbuild/backend/cpp_eclipse.py +++ b/python/mozbuild/mozbuild/backend/cpp_eclipse.py @@ -15,6 +15,7 @@ from .common import CommonBackend from ..frontend.data import ( Defines, ) +from mozbuild.base import ExecutionSummary # TODO Have ./mach eclipse generate the workspace and index it: # /Users/bgirard/mozilla/eclipse/eclipse/eclipse/eclipse -application org.eclipse.cdt.managedbuilder.core.headlessbuild -data $PWD/workspace -importAll $PWD/eclipse @@ -41,15 +42,15 @@ class CppEclipseBackend(CommonBackend): # Note: We need the C Pre Processor (CPP) flags, not the CXX flags self._cppflags = self.environment.substs.get('CPPFLAGS', '') - def detailed(summary): - return ('Generated Cpp Eclipse workspace in "%s".\n' + \ - 'If missing, import the project using File > Import > General > Existing Project into workspace\n' + \ - '\n' + \ - 'Run with: eclipse -data %s\n') \ - % (self._workspace_dir, self._workspace_dir) - - self.summary.backend_detailed_summary = types.MethodType(detailed, - self.summary) + def summary(self): + return ExecutionSummary( + 'CppEclipse backend executed in {execution_time:.2f}s\n' + 'Generated Cpp Eclipse workspace in "{workspace:s}".\n' + 'If missing, import the project using File > Import > General > Existing Project into workspace\n' + '\n' + 'Run with: eclipse -data {workspace:s}\n', + execution_time=self._execution_time, + workspace=self._workspace_dir) def _get_workspace_path(self): return CppEclipseBackend.get_workspace_path(self.environment.topsrcdir, self.environment.topobjdir) diff --git a/python/mozbuild/mozbuild/backend/recursivemake.py b/python/mozbuild/mozbuild/backend/recursivemake.py index f4a1dc6b59..37a0913129 100644 --- a/python/mozbuild/mozbuild/backend/recursivemake.py +++ b/python/mozbuild/mozbuild/backend/recursivemake.py @@ -367,26 +367,8 @@ class RecursiveMakeBackend(CommonBackend): self._backend_files = {} self._idl_dirs = set() - def detailed(summary): - s = '{:d} total backend files; ' \ - '{:d} created; {:d} updated; {:d} unchanged; ' \ - '{:d} deleted; {:d} -> {:d} Makefile'.format( - summary.created_count + summary.updated_count + - summary.unchanged_count, - summary.created_count, - summary.updated_count, - summary.unchanged_count, - summary.deleted_count, - summary.makefile_in_count, - summary.makefile_out_count) - - return s - - # This is a little kludgy and could be improved with a better API. - self.summary.backend_detailed_summary = types.MethodType(detailed, - self.summary) - self.summary.makefile_in_count = 0 - self.summary.makefile_out_count = 0 + self._makefile_in_count = 0 + self._makefile_out_count = 0 self._test_manifests = {} @@ -419,6 +401,13 @@ class RecursiveMakeBackend(CommonBackend): 'tools': set(), } + def summary(self): + summary = super(RecursiveMakeBackend, self).summary() + summary.extend('; {makefile_in:d} -> {makefile_out:d} Makefile', + makefile_in=self._makefile_in_count, + makefile_out=self._makefile_out_count) + return summary + def _get_backend_file_for(self, obj): if obj.objdir not in self._backend_files: self._backend_files[obj.objdir] = \ @@ -779,7 +768,7 @@ class RecursiveMakeBackend(CommonBackend): if not stub: self.log(logging.DEBUG, 'substitute_makefile', {'path': makefile}, 'Substituting makefile: {path}') - self.summary.makefile_in_count += 1 + self._makefile_in_count += 1 for tier, skiplist in self._may_skip.items(): if bf.relobjdir in skiplist: @@ -1424,7 +1413,7 @@ INSTALL_TARGETS += %(prefix)s # the autogenerated one automatically. self.backend_input_files.add(obj.input_path) - self.summary.makefile_out_count += 1 + self._makefile_out_count += 1 def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources, unified_ipdl_cppsrcs_mapping): diff --git a/python/mozbuild/mozbuild/backend/visualstudio.py b/python/mozbuild/mozbuild/backend/visualstudio.py index c2bf60bf4e..a5412e65fa 100644 --- a/python/mozbuild/mozbuild/backend/visualstudio.py +++ b/python/mozbuild/mozbuild/backend/visualstudio.py @@ -27,6 +27,7 @@ from ..frontend.data import ( Sources, UnifiedSources, ) +from mozbuild.base import ExecutionSummary MSBUILD_NAMESPACE = 'http://schemas.microsoft.com/developer/msbuild/2003' @@ -84,12 +85,12 @@ class VisualStudioBackend(CommonBackend): self._paths_to_configs = {} self._libs_to_paths = {} - def detailed(summary): - return 'Generated Visual Studio solution at %s' % ( - os.path.join(self._out_dir, 'mozilla.sln')) - - self.summary.backend_detailed_summary = types.MethodType(detailed, - self.summary) + def summary(self): + return ExecutionSummary( + 'VisualStudio backend executed in {execution_time:.2f}s\n' + 'Generated Visual Studio solution at {path:s}', + execution_time=self._execution_time, + path=os.path.join(self._out_dir, 'mozilla.sln')) def consume_object(self, obj): # Just acknowledge everything. diff --git a/python/mozbuild/mozbuild/base.py b/python/mozbuild/mozbuild/base.py index 862502fb7f..f161cefa3e 100644 --- a/python/mozbuild/mozbuild/base.py +++ b/python/mozbuild/mozbuild/base.py @@ -802,3 +802,22 @@ class PathArgument(object): def objdir_path(self): return mozpath.join(self.topobjdir, self.relpath()) + + +class ExecutionSummary(dict): + """Helper for execution summaries.""" + + def __init__(self, summary_format, **data): + self._summary_format = '' + assert 'execution_time' in data + self.extend(summary_format, **data) + + def extend(self, summary_format, **data): + self._summary_format += summary_format + self.update(data) + + def __str__(self): + return self._summary_format.format(**self) + + def __getattr__(self, key): + return self[key] diff --git a/python/mozbuild/mozbuild/config_status.py b/python/mozbuild/mozbuild/config_status.py index 5c66416039..6053b50cfa 100644 --- a/python/mozbuild/mozbuild/config_status.py +++ b/python/mozbuild/mozbuild/config_status.py @@ -11,8 +11,9 @@ from __future__ import absolute_import, print_function import logging import os import sys +import time -from optparse import OptionParser +from argparse import ArgumentParser from mach.logging import LoggingManager from mozbuild.backend.configenvironment import ConfigEnvironment @@ -21,6 +22,7 @@ from mozbuild.base import MachCommandConditions from mozbuild.frontend.emitter import TreeMetadataEmitter from mozbuild.frontend.reader import BuildReader from mozbuild.mozinfo import write_mozinfo +from itertools import chain log_manager = LoggingManager() @@ -72,20 +74,31 @@ def config_status(topobjdir='.', topsrcdir='.', raise Exception('topsrcdir must be defined as an absolute directory: ' '%s' % topsrcdir) - parser = OptionParser() - parser.add_option('--recheck', dest='recheck', action='store_true', - help='update config.status by reconfiguring in the same conditions') - parser.add_option('-v', '--verbose', dest='verbose', action='store_true', - help='display verbose output') - parser.add_option('-n', dest='not_topobjdir', action='store_true', - help='do not consider current directory as top object directory') - parser.add_option('-d', '--diff', action='store_true', - help='print diffs of changed files.') - parser.add_option('-b', '--backend', - choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', 'VisualStudio'], - default='RecursiveMake', - help='what backend to build (default: RecursiveMake).') - options, args = parser.parse_args() + default_backends = ['RecursiveMake'] + # We have a chicken/egg problem, where we only have a dict for substs after + # creating the ConfigEnvironment, which requires argument parsing to have + # occurred. + for name, value in substs: + if name == 'BUILD_BACKENDS': + default_backends = value + break + + parser = ArgumentParser() + parser.add_argument('--recheck', dest='recheck', action='store_true', + help='update config.status by reconfiguring in the same conditions') + parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', + help='display verbose output') + parser.add_argument('-n', dest='not_topobjdir', action='store_true', + help='do not consider current directory as top object directory') + parser.add_argument('-d', '--diff', action='store_true', + help='print diffs of changed files.') + parser.add_argument('-b', '--backend', nargs='+', + choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', + 'VisualStudio', 'FasterMake'], + default=default_backends, + help='what backend to build (default: %s).' % + ' '.join(default_backends)) + options = parser.parse_args() # Without -n, the current directory is meant to be the top object directory if not options.not_topobjdir: @@ -100,22 +113,31 @@ def config_status(topobjdir='.', topsrcdir='.', write_mozinfo(os.path.join(topobjdir, 'mozinfo.json'), env, os.environ) # Make an appropriate backend instance, defaulting to RecursiveMakeBackend. - backend_cls = RecursiveMakeBackend - if options.backend == 'AndroidEclipse': - from mozbuild.backend.android_eclipse import AndroidEclipseBackend - if not MachCommandConditions.is_android(env): - raise Exception('The Android Eclipse backend is not available with this configuration.') - backend_cls = AndroidEclipseBackend - elif options.backend == 'CppEclipse': - from mozbuild.backend.cpp_eclipse import CppEclipseBackend - backend_cls = CppEclipseBackend - if os.name == 'nt': - raise Exception('Eclipse is not supported on Windows. Consider using Visual Studio instead.') - elif options.backend == 'VisualStudio': - from mozbuild.backend.visualstudio import VisualStudioBackend - backend_cls = VisualStudioBackend + backends_cls = [] + for backend in options.backend: + if backend == 'AndroidEclipse': + from mozbuild.backend.android_eclipse import AndroidEclipseBackend + if not MachCommandConditions.is_android(env): + raise Exception('The Android Eclipse backend is not available with this configuration.') + backends_cls.append(AndroidEclipseBackend) + elif backend == 'CppEclipse': + from mozbuild.backend.cpp_eclipse import CppEclipseBackend + backends_cls.append(CppEclipseBackend) + if os.name == 'nt': + raise Exception('Eclipse is not supported on Windows. Consider using Visual Studio instead.') + elif backend == 'VisualStudio': + from mozbuild.backend.visualstudio import VisualStudioBackend + backends_cls.append(VisualStudioBackend) + elif backend == 'FasterMake': + from mozbuild.backend.fastermake import FasterMakeBackend + backends_cls.append(FasterMakeBackend) + else: + backends_cls.append(RecursiveMakeBackend) - the_backend = backend_cls(env) + cpu_start = time.clock() + time_start = time.time() + + backends = [cls(env) for cls in backends_cls] reader = BuildReader(env) emitter = TreeMetadataEmitter(env) @@ -131,17 +153,41 @@ def config_status(topobjdir='.', topsrcdir='.', log_manager.add_terminal_logging(level=log_level) log_manager.enable_unstructured() - print('Walking the dog...', file=sys.stderr) - summary = the_backend.consume(definitions) + print('Reticulating splines...', file=sys.stderr) + if len(backends) > 1: + definitions = list(definitions) - for line in summary.summaries(): - print(line, file=sys.stderr) + for the_backend in backends: + the_backend.consume(definitions) + + execution_time = 0.0 + for obj in chain((reader, emitter), backends): + summary = obj.summary() + print(summary, file=sys.stderr) + execution_time += summary.execution_time + + cpu_time = time.clock() - cpu_start + wall_time = time.time() - time_start + efficiency = cpu_time / wall_time if wall_time else 100 + untracked = wall_time - execution_time + + print( + 'Total wall time: {:.2f}s; CPU time: {:.2f}s; Efficiency: ' + '{:.0%}; Untracked: {:.2f}s'.format( + wall_time, cpu_time, efficiency, untracked), + file=sys.stderr + ) if options.diff: - for path, diff in sorted(summary.file_diffs.items()): - print('\n'.join(diff)) + for the_backend in backends: + for path, diff in sorted(the_backend.file_diffs.items()): + print('\n'.join(diff)) + + # Advertise Visual Studio if appropriate. + #if os.name == 'nt' and 'VisualStudio' not in options.backend: + # print(VISUAL_STUDIO_ADVERTISEMENT) # Advertise Eclipse if it is appropriate. if MachCommandConditions.is_android(env): - if options.backend == 'RecursiveMake': + if 'AndroidEclipse' not in options.backend: print(ANDROID_IDE_ADVERTISEMENT) diff --git a/python/mozbuild/mozbuild/frontend/context.py b/python/mozbuild/mozbuild/frontend/context.py index 76a3fefdb2..c8d94665b6 100644 --- a/python/mozbuild/mozbuild/frontend/context.py +++ b/python/mozbuild/mozbuild/frontend/context.py @@ -83,7 +83,6 @@ class Context(KeyedDefaultDict): # a list to be a problem. self._all_paths = [] self.config = config - self.execution_time = 0 self._sandbox = None KeyedDefaultDict.__init__(self, self._factory) diff --git a/python/mozbuild/mozbuild/frontend/data.py b/python/mozbuild/mozbuild/frontend/data.py index 7012a66219..1cbffe03ed 100644 --- a/python/mozbuild/mozbuild/frontend/data.py +++ b/python/mozbuild/mozbuild/frontend/data.py @@ -39,17 +39,6 @@ class TreeMetadata(object): self._ack = True -class ReaderSummary(TreeMetadata): - """A summary of what the reader did.""" - - def __init__(self, total_file_count, total_sandbox_execution_time, - total_emitter_execution_time): - TreeMetadata.__init__(self) - self.total_file_count = total_file_count - self.total_sandbox_execution_time = total_sandbox_execution_time - self.total_emitter_execution_time = total_emitter_execution_time - - class ContextDerived(TreeMetadata): """Build object derived from a single Context instance. diff --git a/python/mozbuild/mozbuild/frontend/emitter.py b/python/mozbuild/mozbuild/frontend/emitter.py index 83f06dc285..62c56dbbd0 100644 --- a/python/mozbuild/mozbuild/frontend/emitter.py +++ b/python/mozbuild/mozbuild/frontend/emitter.py @@ -59,7 +59,6 @@ from .data import ( PreprocessedTestWebIDLFile, PreprocessedWebIDLFile, Program, - ReaderSummary, Resources, SharedLibrary, SimpleProgram, @@ -86,6 +85,8 @@ from .context import ( TemplateContext, ) +from mozbuild.base import ExecutionSummary + class TreeMetadataEmitter(LoggingMixin): """Converts the executed mozbuild files into data structures. @@ -127,19 +128,27 @@ class TreeMetadataEmitter(LoggingMixin): # Add security/nss manually, since it doesn't have a subconfigure. self._external_paths.add('security/nss') + self._emitter_time = 0.0 + self._object_count = 0 + + def summary(self): + return ExecutionSummary( + 'Processed into {object_count:d} build config descriptors in ' + '{execution_time:.2f}s', + execution_time=self._emitter_time, + object_count=self._object_count) + def emit(self, output): """Convert the BuildReader output into data structures. The return value from BuildReader.read_topsrcdir() (a generator) is typically fed into this function. """ - file_count = 0 - sandbox_execution_time = 0.0 - emitter_time = 0.0 contexts = {} def emit_objs(objs): for o in objs: + self._object_count += 1 yield o if not o._ack: raise Exception('Unhandled object of type %s' % type(o)) @@ -157,14 +166,10 @@ class TreeMetadataEmitter(LoggingMixin): start = time.time() # We need to expand the generator for the timings to work. objs = list(self.emit_from_context(out)) - emitter_time += time.time() - start + self._emitter_time += time.time() - start for o in emit_objs(objs): yield o - # Update the stats. - file_count += len(out.all_paths) - sandbox_execution_time += out.execution_time - else: raise Exception('Unhandled output type: %s' % type(out)) @@ -173,12 +178,10 @@ class TreeMetadataEmitter(LoggingMixin): if self.config.substs.get('COMPILE_ENVIRONMENT', True): start = time.time() objs = list(self._emit_libs_derived(contexts)) - emitter_time += time.time() - start + self._emitter_time += time.time() - start for o in emit_objs(objs): yield o - yield ReaderSummary(file_count, sandbox_execution_time, emitter_time) - def _emit_libs_derived(self, contexts): # First do FINAL_LIBRARY linkage. for lib in (l for libs in self._libs.values() for l in libs): diff --git a/python/mozbuild/mozbuild/frontend/gyp_reader.py b/python/mozbuild/mozbuild/frontend/gyp_reader.py index 1346a66e81..4b29ba6a62 100644 --- a/python/mozbuild/mozbuild/frontend/gyp_reader.py +++ b/python/mozbuild/mozbuild/frontend/gyp_reader.py @@ -6,7 +6,6 @@ from __future__ import absolute_import, unicode_literals import gyp import sys -import time import os import mozpack.path as mozpath from mozpack.files import FileFinder @@ -83,8 +82,6 @@ def read_from_gyp(config, path, output, vars, non_unified_sources = set()): processor. """ - time_start = time.time() - # gyp expects plain str instead of unicode. The frontend code gives us # unicode strings, so convert them. path = encode(path) @@ -234,6 +231,4 @@ def read_from_gyp(config, path, output, vars, non_unified_sources = set()): context['DEFINES']['_UNICODE'] = True context['DISABLE_STL_WRAPPING'] = True - context.execution_time = time.time() - time_start yield context - time_start = time.time() diff --git a/python/mozbuild/mozbuild/frontend/reader.py b/python/mozbuild/mozbuild/frontend/reader.py index 1a7f5340c4..69596181a0 100644 --- a/python/mozbuild/mozbuild/frontend/reader.py +++ b/python/mozbuild/mozbuild/frontend/reader.py @@ -73,6 +73,9 @@ from .context import ( TemplateContext, ) +from mozbuild.base import ExecutionSummary + + if sys.version_info.major == 2: text_type = unicode type_type = types.TypeType @@ -864,6 +867,16 @@ class BuildReader(object): self._execution_stack = [] self._finder = finder + self._execution_time = 0.0 + self._file_count = 0 + + def summary(self): + return ExecutionSummary( + 'Finished reading {file_count:d} moz.build files in ' + '{execution_time:.2f}s', + file_count=self._file_count, + execution_time=self._execution_time) + def read_topsrcdir(self): """Read the tree of linked moz.build files. @@ -1098,7 +1111,8 @@ class BuildReader(object): sandbox = MozbuildSandbox(context, metadata=metadata, finder=self._finder) sandbox.exec_file(path) - context.execution_time = time.time() - time_start + self._execution_time += time.time() - time_start + self._file_count += len(context.all_paths) # Yield main context before doing any processing. This gives immediate # consumers an opportunity to change state before our remaining @@ -1136,6 +1150,7 @@ class BuildReader(object): raise SandboxValidationError('Cannot find %s.' % source, context) non_unified_sources.add(source) + time_start = time.time() for gyp_context in read_from_gyp(context.config, mozpath.join(curdir, gyp_dir.input), mozpath.join(context.objdir, @@ -1144,6 +1159,8 @@ class BuildReader(object): non_unified_sources = non_unified_sources): gyp_context.update(gyp_dir.sandbox_vars) gyp_contexts.append(gyp_context) + self._file_count += len(gyp_context.all_paths) + self._execution_time += time.time() - time_start for gyp_context in gyp_contexts: context['DIRS'].append(mozpath.relpath(gyp_context.objdir, context.objdir)) diff --git a/python/mozbuild/mozbuild/jar.py b/python/mozbuild/mozbuild/jar.py index 391c884114..4844350bd1 100644 --- a/python/mozbuild/mozbuild/jar.py +++ b/python/mozbuild/mozbuild/jar.py @@ -20,6 +20,7 @@ from MozZipFile import ZipFile from cStringIO import StringIO from mozbuild.util import ( + ensureParentDir, lock_file, PushbackIter, ) @@ -200,6 +201,7 @@ class JarMaker(object): with the given chrome base path, and updates the given manifest file. ''' + ensureParentDir(manifestPath) lock = lock_file(manifestPath + '.lck') try: myregister = dict.fromkeys(map(lambda s: s.replace('%', @@ -388,7 +390,7 @@ class JarMaker(object): m.group('optPreprocess') or '', m.group('optOverwrite') or '', out, - m.group('locale') or '', + m.group('locale').replace('%', '%%') or '', ) for _srcdir in src_base: finder = FileFinder(_srcdir, find_executables=False) diff --git a/python/mozbuild/mozbuild/mach_commands.py b/python/mozbuild/mozbuild/mach_commands.py index 28b423e662..a24bb6a147 100644 --- a/python/mozbuild/mozbuild/mach_commands.py +++ b/python/mozbuild/mozbuild/mach_commands.py @@ -526,11 +526,11 @@ class Build(MachCommandBase): help='Show a diff of changes.') # It would be nice to filter the choices below based on # conditions, but that is for another day. - @CommandArgument('-b', '--backend', - choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', 'VisualStudio'], - default='RecursiveMake', - help='Which backend to build (default: RecursiveMake).') - def build_backend(self, backend='RecursiveMake', diff=False): + @CommandArgument('-b', '--backend', nargs='+', + choices=['RecursiveMake', 'AndroidEclipse', 'CppEclipse', + 'VisualStudio', 'FasterMake'], + help='Which backend to build.') + def build_backend(self, backend, diff=False): python = self.virtualenv_manager.python_path config_status = os.path.join(self.topobjdir, 'config.status') @@ -540,7 +540,10 @@ class Build(MachCommandBase): % backend) return 1 - args = [python, config_status, '--backend=%s' % backend] + args = [python, config_status] + if backend: + args.append('--backend') + args.extend(backend) if diff: args.append('--diff') diff --git a/python/mozbuild/mozbuild/mozconfig.py b/python/mozbuild/mozbuild/mozconfig.py index b0981f1d12..2e829422d8 100644 --- a/python/mozbuild/mozbuild/mozconfig.py +++ b/python/mozbuild/mozbuild/mozconfig.py @@ -8,6 +8,7 @@ import filecmp import os import re import subprocess +import traceback from collections import defaultdict from mach.mixin.process import ProcessExecutionMixin @@ -31,6 +32,13 @@ by a command inside your mozconfig failing. Please change your mozconfig to not error and/or to catch errors in executed commands. '''.strip() +MOZCONFIG_BAD_OUTPUT = ''' +Evaluation of your mozconfig produced unexpected output. This could be +triggered by a command inside your mozconfig failing or producing some warnings +or error messages. Please change your mozconfig to not error and/or to catch +errors in executed commands. +'''.strip() + class MozconfigFindException(Exception): """Raised when a mozconfig location is not defined properly.""" @@ -234,7 +242,17 @@ class MozconfigLoader(ProcessExecutionMixin): raise MozconfigLoadException(path, MOZCONFIG_BAD_EXIT_CODE, lines) - parsed = self._parse_loader_output(output) + try: + parsed = self._parse_loader_output(output) + except AssertionError: + # _parse_loader_output uses assertions to verify the + # well-formedness of the shell output; when these fail, it + # generally means there was a problem with the output, but we + # include the assertion traceback just to be sure. + print('Assertion failed in _parse_loader_output:') + traceback.print_exc() + raise MozconfigLoadException(path, MOZCONFIG_BAD_OUTPUT, + output.splitlines()) def diff_vars(vars_before, vars_after): set1 = set(vars_before.keys()) - self.IGNORE_SHELL_VARIABLES diff --git a/python/mozbuild/mozbuild/mozconfig_loader b/python/mozbuild/mozbuild/mozconfig_loader index 569c690307..84e9836dfe 100755 --- a/python/mozbuild/mozbuild/mozconfig_loader +++ b/python/mozbuild/mozbuild/mozconfig_loader @@ -36,10 +36,23 @@ ac_add_app_options() { } mk_add_options() { - local opt + local opt name op value for opt; do echo "------BEGIN_MK_OPTION" echo $opt + # Remove any leading "export" + opt=${opt#export} + case "$opt" in + *\?=*) op="?=" ;; + *:=*) op=":=" ;; + *+=*) op="+=" ;; + *=*) op="=" ;; + esac + # Remove the operator and the value that follows + name=${opt%%${op}*} + # Note: $(echo ${name}) strips the variable from any leading and trailing + # whitespaces. + eval "$(echo ${name})_IS_SET=1" echo "------END_MK_OPTION" done } diff --git a/python/mozbuild/mozbuild/test/frontend/test_emitter.py b/python/mozbuild/mozbuild/test/frontend/test_emitter.py index 4d57b8ee3c..fea7962c49 100644 --- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py +++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py @@ -25,7 +25,6 @@ from mozbuild.frontend.data import ( JsPreferenceFile, LocalInclude, Program, - ReaderSummary, Resources, SimpleProgram, Sources, @@ -77,17 +76,12 @@ class TestEmitterBasic(unittest.TestCase): objs = list(ack(o) for o in emitter.emit(reader.read_topsrcdir())) self.assertGreater(len(objs), 0) - self.assertIsInstance(objs[-1], ReaderSummary) filtered = [] for obj in objs: if filter_common and isinstance(obj, DirectoryTraversal): continue - # Always filter ReaderSummary because it's asserted above. - if isinstance(obj, ReaderSummary): - continue - filtered.append(obj) return filtered diff --git a/python/mozbuild/mozbuild/test/test_mozconfig.py b/python/mozbuild/mozbuild/test/test_mozconfig.py index 87ed8da58e..87c93fd524 100644 --- a/python/mozbuild/mozbuild/test/test_mozconfig.py +++ b/python/mozbuild/mozbuild/test/test_mozconfig.py @@ -319,6 +319,10 @@ class TestMozconfigLoader(unittest.TestCase): self.assertEqual(result['make_flags'], ['-j8', '-s']) self.assertEqual(result['make_extra'], ['FOO=BAR BAZ', 'BIZ=1']) + vars = result['vars']['added'] + for var in ('MOZ_OBJDIR', 'MOZ_MAKE_FLAGS', 'FOO', 'BIZ'): + self.assertEqual(vars.get('%s_IS_SET' % var), '1') + def test_read_empty_mozconfig_objdir_environ(self): os.environ[b'MOZ_OBJDIR'] = b'obj-firefox' with NamedTemporaryFile(mode='w') as mozconfig: diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index 9053c332b6..de9fa505b3 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -138,7 +138,7 @@ let snapshotFormatters = { let apzInfo = []; let formatApzInfo = function (info) { let out = []; - for (let type of ['Wheel', 'Touch']) { + for (let type of ['Wheel', 'Touch', 'Drag']) { let key = 'Apz' + type + 'Input'; let warningKey = key + 'Warning'; @@ -211,7 +211,7 @@ let snapshotFormatters = { let out = Object.create(data); if (apzInfo.length == 0) - out.asyncPanZoom = "none"; + out.asyncPanZoom = localizedMsg(["apzNone"]); else out.asyncPanZoom = apzInfo.join("; "); diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build index edf3c0137a..3f734988f8 100644 --- a/toolkit/library/moz.build +++ b/toolkit/library/moz.build @@ -363,6 +363,9 @@ if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['MOZ_ENABLE_QT']: OS_LIBS += CONFIG['XEXT_LIBS'] +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' and CONFIG['MOZ_GSTREAMER']: + OS_LIBS += CONFIG['GSTREAMER_LIBS'] + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows': OS_LIBS += [ 'usp10', diff --git a/toolkit/locales/en-US/chrome/global/aboutSupport.properties b/toolkit/locales/en-US/chrome/global/aboutSupport.properties index f38b6eb4f0..54ab5118dc 100644 --- a/toolkit/locales/en-US/chrome/global/aboutSupport.properties +++ b/toolkit/locales/en-US/chrome/global/aboutSupport.properties @@ -98,8 +98,10 @@ canSandboxMedia = Media Plugin Sandboxing multiProcessStatus = %1$S/%2$S (default: %3$S) asyncPanZoom = Asynchronous Pan/Zoom +apzNone = none wheelEnabled = wheel input enabled touchEnabled = touch input enabled +dragEnabled = scrollbar drag enabled # LOCALIZATION NOTE %1 will be replaced with the key of a preference. wheelWarning = async wheel input disabled due to unsupported pref: %S diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index 0ab4dd7f67..60ad1f534a 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -457,7 +457,7 @@ static nsDefaultMimeTypeEntry defaultMimeEntries [] = { VIDEO_WEBM, "webm" }, { AUDIO_WEBM, "webm" }, #endif -#if defined(MOZ_WMF) +#if defined(MOZ_GSTREAMER) || defined(MOZ_WMF) { VIDEO_MP4, "mp4" }, { AUDIO_MP4, "m4a" }, { AUDIO_MP3, "mp3" }, diff --git a/widget/InputData.cpp b/widget/InputData.cpp index 2c2e76fde0..58e036b133 100644 --- a/widget/InputData.cpp +++ b/widget/InputData.cpp @@ -71,6 +71,18 @@ MouseInput::MouseInput(const WidgetMouseEventBase& aMouseEvent) } } +bool +MouseInput::TransformToLocal(const gfx::Matrix4x4& aTransform) +{ + Maybe point = UntransformTo(aTransform, mOrigin); + if (!point) { + return false; + } + mLocalOrigin = *point; + + return true; +} + MultiTouchInput::MultiTouchInput(const WidgetTouchEvent& aTouchEvent) : InputData(MULTITOUCH_INPUT, aTouchEvent.time, aTouchEvent.timeStamp, aTouchEvent.modifiers) diff --git a/widget/InputData.h b/widget/InputData.h index acdd192ded..937012ed86 100644 --- a/widget/InputData.h +++ b/widget/InputData.h @@ -283,6 +283,8 @@ public: bool IsLeftButton() const { return mButtonType == LEFT_BUTTON; } + bool TransformToLocal(const gfx::Matrix4x4& aTransform); + MouseType mType; ButtonType mButtonType; ScreenPoint mOrigin; diff --git a/widget/PuppetWidget.cpp b/widget/PuppetWidget.cpp index cb5f9588a9..776c25b941 100644 --- a/widget/PuppetWidget.cpp +++ b/widget/PuppetWidget.cpp @@ -1137,6 +1137,12 @@ uint32_t PuppetWidget::GetMaxTouchPoints() const return sTouchPoints; } +void +PuppetWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) +{ + mTabChild->SendStartScrollbarDrag(aDragMetrics); +} + PuppetScreen::PuppetScreen(void *nativeScreen) { } diff --git a/widget/PuppetWidget.h b/widget/PuppetWidget.h index 6991f48698..b8b4b3f9fe 100644 --- a/widget/PuppetWidget.h +++ b/widget/PuppetWidget.h @@ -251,6 +251,7 @@ public: virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override; virtual uint32_t GetMaxTouchPoints() const override; + virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override; protected: bool mEnabled; bool mVisible; diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm index 8f7dca5de3..592ea2d567 100644 --- a/widget/cocoa/nsCocoaUtils.mm +++ b/widget/cocoa/nsCocoaUtils.mm @@ -35,6 +35,7 @@ using mozilla::gfx::BackendType; using mozilla::gfx::DataSourceSurface; using mozilla::gfx::DrawTarget; using mozilla::gfx::Factory; +using mozilla::gfx::Filter; using mozilla::gfx::IntPoint; using mozilla::gfx::IntRect; using mozilla::gfx::IntSize; @@ -501,7 +502,7 @@ nsresult nsCocoaUtils::CreateNSImageFromImageContainer(imgIContainer *aImage, ui } aImage->Draw(context, scaledSize, ImageRegion::Create(scaledSize), - aWhichFrame, GraphicsFilter::FILTER_NEAREST, Nothing(), + aWhichFrame, Filter::POINT, Nothing(), imgIContainer::FLAG_SYNC_DECODE); surface = drawTarget->Snapshot(); diff --git a/widget/nsBaseDragService.cpp b/widget/nsBaseDragService.cpp index 952e1ce45e..a1a79ea068 100644 --- a/widget/nsBaseDragService.cpp +++ b/widget/nsBaseDragService.cpp @@ -671,7 +671,7 @@ nsBaseDragService::DrawDragForImage(nsPresContext* aPresContext, imgContainer->Draw(ctx, destSize, ImageRegion::Create(destSize), imgIContainer::FRAME_CURRENT, - GraphicsFilter::FILTER_GOOD, Nothing(), + Filter::GOOD, Nothing(), imgIContainer::FLAG_SYNC_DECODE); *aSurface = dt->Snapshot(); } else { diff --git a/widget/nsBaseWidget.cpp b/widget/nsBaseWidget.cpp index 3f7f36c294..9bf1cc7680 100644 --- a/widget/nsBaseWidget.cpp +++ b/widget/nsBaseWidget.cpp @@ -1672,6 +1672,22 @@ nsBaseWidget::GetRootAccessible() #endif // ACCESSIBILITY +void +nsBaseWidget::StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) +{ + if (!AsyncPanZoomEnabled()) { + return; + } + + MOZ_ASSERT(XRE_IsParentProcess() && mCompositorParent); + + int layersId = mCompositorParent->RootLayerTreeId();; + ScrollableLayerGuid guid(layersId, aDragMetrics.mPresShellId, aDragMetrics.mViewId); + + APZThreadUtils::RunOnControllerThread( + NewRunnableMethod(mAPZC.get(), &APZCTreeManager::StartScrollbarDrag, guid, aDragMetrics)); +} + nsresult nsIWidget::SynthesizeNativeTouchTap(nsIntPoint aPointerScreenPoint, bool aLongTap, nsIObserver* aObserver) diff --git a/widget/nsBaseWidget.h b/widget/nsBaseWidget.h index 095347eb65..395e3d8e7e 100644 --- a/widget/nsBaseWidget.h +++ b/widget/nsBaseWidget.h @@ -300,6 +300,8 @@ public: return false; } + virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) override; + /** * Use this when GetLayerManager() returns a BasicLayerManager * (nsBaseWidget::GetLayerManager() does). This sets up the widget's diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h index aa8c72c6ff..3d0c1a008c 100644 --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h @@ -48,6 +48,7 @@ namespace plugins { class PluginWidgetChild; } // namespace plugins namespace layers { +class AsyncDragMetrics; class Composer2D; class Compositor; class CompositorChild; @@ -315,6 +316,7 @@ class nsIWidget : public nsISupports { public: typedef mozilla::layers::Composer2D Composer2D; typedef mozilla::layers::CompositorChild CompositorChild; + typedef mozilla::layers::AsyncDragMetrics AsyncDragMetrics; typedef mozilla::layers::FrameMetrics FrameMetrics; typedef mozilla::layers::LayerManager LayerManager; typedef mozilla::layers::LayerManagerComposite LayerManagerComposite; @@ -1641,6 +1643,8 @@ class nsIWidget : public nsISupports { */ virtual bool CaptureWidgetOnScreen(mozilla::RefPtr aDT) = 0; + virtual void StartAsyncScrollbarDrag(const AsyncDragMetrics& aDragMetrics) = 0; + private: class LongTapInfo {