WebKit Bugzilla
Attachment 345754 Details for
Bug 187850
: [MediaCapabilities] Platform integration
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-187850-20180725142625.patch (text/plain), 38.63 KB, created by
Philippe Normand
on 2018-07-25 05:26:26 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Philippe Normand
Created:
2018-07-25 05:26:26 PDT
Size:
38.63 KB
patch
obsolete
>Subversion Revision: 234192 >diff --git a/Source/WebCore/Modules/mediacapabilities/MediaCapabilities.cpp b/Source/WebCore/Modules/mediacapabilities/MediaCapabilities.cpp >index fdfb6df76b99b2035e05272777928403ab438ca4..3c2b6e26c9a798143156a6ac3a3cf93e5bc81124 100644 >--- a/Source/WebCore/Modules/mediacapabilities/MediaCapabilities.cpp >+++ b/Source/WebCore/Modules/mediacapabilities/MediaCapabilities.cpp >@@ -27,8 +27,10 @@ > #include "MediaCapabilities.h" > > #include "ContentType.h" >+#include "JSMediaCapabilitiesInfo.h" > #include "MediaDecodingConfiguration.h" > #include "MediaEncodingConfiguration.h" >+#include "MediaEngineConfigurationFactory.h" > #include <wtf/HashSet.h> > > namespace WebCore { >@@ -195,8 +197,33 @@ void MediaCapabilities::decodingInfo(MediaDecodingConfiguration&& configuration, > // 5. In parallel, run the create a MediaCapabilitiesInfo algorithm with configuration and resolve p with its result. > // 6. Return p. > m_taskQueue.enqueueTask([configuration = WTFMove(configuration), promise = WTFMove(promise)] () mutable { >- UNUSED_PARAM(configuration); >- UNUSED_PARAM(promise); >+ // 2.2.3 If configuration is of type MediaDecodingConfiguration, run the following substeps: >+ >+ auto info = MediaCapabilitiesInfo::create(); >+ auto engineConfiguration = MediaEngineConfigurationFactory::createDecodingConfiguration(configuration); >+ >+ if (engineConfiguration) { >+ // 1. If the user agent is able to decode the media represented by >+ // configuration, set supported to true. Otherwise set it to false. >+ info->setSupported(engineConfiguration->canDecodeMedia()); >+ >+ if (info->supported()) { >+ // 2. If the user agent is able to decode the media represented by >+ // configuration at a pace that allows a smooth playback, set smooth to >+ // true. Otherwise set it to false. >+ info->setSmooth(engineConfiguration->canSmoothlyDecodeMedia()); >+ >+ // 3. If the user agent is able to decode the media represented by >+ // configuration in a power efficient manner, set powerEfficient to >+ // true. Otherwise set it to false. The user agent SHOULD NOT take into >+ // consideration the current power source in order to determine the >+ // decoding power efficiency unless the deviceâs power source has side >+ // effects such as enabling different decoding modules. >+ info->setPowerEfficient(engineConfiguration->canPowerEfficientlyDecodeMedia()); >+ } >+ } >+ >+ promise->resolveWithNewlyCreated<IDLInterface<MediaCapabilitiesInfo>>(WTFMove(info)); > }); > } > >diff --git a/Source/WebCore/Modules/mediacapabilities/MediaCapabilitiesInfo.h b/Source/WebCore/Modules/mediacapabilities/MediaCapabilitiesInfo.h >index 119a127a5590465fa2fd7ace124f5e3fc741fa4e..af7adb8fc878ea2b7f8d672081be7ce06019a78b 100644 >--- a/Source/WebCore/Modules/mediacapabilities/MediaCapabilitiesInfo.h >+++ b/Source/WebCore/Modules/mediacapabilities/MediaCapabilitiesInfo.h >@@ -31,6 +31,14 @@ namespace WebCore { > > class MediaCapabilitiesInfo : public RefCounted<MediaCapabilitiesInfo> { > public: >+ >+ static Ref<MediaCapabilitiesInfo> create() >+ { >+ return adoptRef(*new MediaCapabilitiesInfo()); >+ } >+ >+ ~MediaCapabilitiesInfo() = default; >+ > bool supported() const { return m_supported; } > void setSupported(bool supported) { m_supported = supported; } > >@@ -41,6 +49,8 @@ public: > void setPowerEfficient(bool powerEfficient) { m_powerEfficient = powerEfficient; } > > private: >+ MediaCapabilitiesInfo() = default; >+ > bool m_supported { false }; > bool m_smooth { false }; > bool m_powerEfficient { false }; >diff --git a/Source/WebCore/PlatformGTK.cmake b/Source/WebCore/PlatformGTK.cmake >index ee5f1bb73939d95d2e0a17fe018cb217a63bce9c..3a57a4251e76c35054a448a1b6fdd9b8b368adfc 100644 >--- a/Source/WebCore/PlatformGTK.cmake >+++ b/Source/WebCore/PlatformGTK.cmake >@@ -30,6 +30,8 @@ list(APPEND WebCore_INCLUDE_DIRECTORIES > "${WEBCORE_DIR}/platform/graphics/opentype" > "${WEBCORE_DIR}/platform/graphics/wayland" > "${WEBCORE_DIR}/platform/graphics/x11" >+ "${WEBCORE_DIR}/platform/mediacapabilities" >+ "${WEBCORE_DIR}/platform/mediacapabilities/gstreamer" > "${WEBCORE_DIR}/platform/mediastream/gtk" > "${WEBCORE_DIR}/platform/mediastream/gstreamer" > "${WEBCORE_DIR}/platform/mock/mediasource" >diff --git a/Source/WebCore/Sources.txt b/Source/WebCore/Sources.txt >index 230a00c5e079b3b0589f50940698c32bfdcb0480..d7c1a8ddfecfda0090f1274e4ea8666534c75f6f 100644 >--- a/Source/WebCore/Sources.txt >+++ b/Source/WebCore/Sources.txt >@@ -1721,6 +1721,9 @@ platform/graphics/transforms/TransformState.cpp > platform/graphics/transforms/TransformationMatrix.cpp > platform/graphics/transforms/TranslateTransformOperation.cpp > >+platform/mediacapabilities/MediaEngineConfiguration.cpp >+platform/mediacapabilities/MediaEngineConfigurationFactory.cpp >+ > platform/mediastream/CaptureDeviceManager.cpp > platform/mediastream/MediaConstraints.cpp > platform/mediastream/MediaEndpointConfiguration.cpp >@@ -1741,6 +1744,7 @@ platform/mediastream/libwebrtc/LibWebRTCProvider.cpp > > platform/mock/DeviceOrientationClientMock.cpp > platform/mock/GeolocationClientMock.cpp >+platform/mock/MediaEngineDecodingConfigurationMock.cpp > platform/mock/MockRealtimeAudioSource.cpp > platform/mock/MockRealtimeMediaSource.cpp > platform/mock/MockRealtimeMediaSourceCenter.cpp >diff --git a/Source/WebCore/platform/GStreamer.cmake b/Source/WebCore/platform/GStreamer.cmake >index 8ede1758ccb391f0c803fa9089d99ff8171344f9..a2679b98a78143572e3bbf9a03c10cb94ecbc399 100644 >--- a/Source/WebCore/platform/GStreamer.cmake >+++ b/Source/WebCore/platform/GStreamer.cmake >@@ -33,6 +33,8 @@ if (ENABLE_VIDEO OR ENABLE_WEB_AUDIO) > platform/graphics/gstreamer/mse/SourceBufferPrivateGStreamer.cpp > platform/graphics/gstreamer/mse/WebKitMediaSourceGStreamer.cpp > >+ platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.cpp >+ > platform/mediastream/libwebrtc/GStreamerVideoDecoderFactory.cpp > platform/mediastream/libwebrtc/GStreamerVideoEncoderFactory.cpp > platform/mediastream/libwebrtc/LibWebRTCAudioModule.cpp >diff --git a/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.cpp b/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..0e5065550b1faf98bae017386fba34f5b83a7f9f >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.cpp >@@ -0,0 +1,69 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "MediaEngineConfiguration.h" >+ >+namespace WebCore { >+ >+MediaEngineVideoConfiguration::MediaEngineVideoConfiguration(VideoConfiguration&& config) >+ : m_type(config.contentType) >+ , m_width(config.width) >+ , m_height(config.height) >+ , m_bitrate(config.bitrate) >+{ >+ bool ok = false; >+ m_framerate = config.framerate.toDouble(&ok); >+ if (ok) >+ return; >+ >+ m_framerate = 0; >+ auto frameratePieces = config.framerate.split('/'); >+ if (frameratePieces.size() != 2) >+ return; >+ >+ double numerator = frameratePieces[0].toDouble(&ok); >+ if (!ok) >+ return; >+ >+ double denominator = frameratePieces[1].toDouble(&ok); >+ if (!ok) >+ return; >+ >+ if (!std::isfinite(numerator) || !std::isfinite(denominator)) >+ return; >+ >+ m_framerate = numerator / denominator; >+} >+ >+MediaEngineConfiguration::MediaEngineConfiguration(MediaConfiguration&& config) >+{ >+ if (config.video) >+ m_videoConfiguration = MediaEngineVideoConfiguration::create(config.video.value()); >+} >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.h b/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.h >new file mode 100644 >index 0000000000000000000000000000000000000000..e55e0ad9d46cdea2198987e754024ca7943826c7 >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/MediaEngineConfiguration.h >@@ -0,0 +1,86 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "ContentType.h" >+#include "IntSize.h" >+#include "MediaConfiguration.h" >+#include "VideoConfiguration.h" >+ >+#include <wtf/Forward.h> >+ >+namespace WebCore { >+class MediaEngineConfiguration; >+ >+class MediaEngineVideoConfiguration : public RefCounted<MediaEngineVideoConfiguration> { >+public: >+ static Ref<MediaEngineVideoConfiguration> create(VideoConfiguration& config) >+ { >+ return adoptRef(*new MediaEngineVideoConfiguration(WTFMove(config))); >+ }; >+ >+ ContentType contentType() const { return m_type; }; >+ IntSize size() const { return IntSize(m_width, m_height); }; >+ uint64_t bitrate() const { return m_bitrate; }; >+ double framerate() const { return m_framerate; }; >+ >+private: >+ MediaEngineVideoConfiguration(VideoConfiguration&&); >+ >+ ContentType m_type; >+ uint32_t m_width; >+ uint32_t m_height; >+ uint64_t m_bitrate; >+ double m_framerate; >+}; >+ >+class MediaEngineConfiguration : public RefCounted<MediaEngineConfiguration> { >+public: >+ MediaEngineConfiguration(MediaConfiguration&&); >+ virtual ~MediaEngineConfiguration() = default; >+ >+ enum class ImplementationType { >+ Empty, >+ GStreamer, >+ Mock, >+ }; >+ >+ virtual ImplementationType implementationType() const { return ImplementationType::Empty; }; >+ >+ RefPtr<MediaEngineVideoConfiguration> videoConfiguration() const { return m_videoConfiguration; }; >+ >+private: >+ RefPtr<MediaEngineVideoConfiguration> m_videoConfiguration; >+}; >+ >+} // namespace WebCore >+ >+#define SPECIALIZE_TYPE_TRAITS_MEDIA_ENGINE_CONFIGURATION(ToValueTypeName, ImplementationTypeName) \ >+ SPECIALIZE_TYPE_TRAITS_BEGIN(ToValueTypeName) \ >+ static bool isType(const WebCore::MediaEngineConfiguration& config) { return config.implementationType() == ImplementationTypeName; } \ >+ SPECIALIZE_TYPE_TRAITS_END() >diff --git a/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.cpp b/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..342c17db1355faf5cbd91cc75ab3d6fdff8c4771 >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.cpp >@@ -0,0 +1,69 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "MediaEngineConfigurationFactory.h" >+ >+#include "DeprecatedGlobalSettings.h" >+#include "MediaEngineDecodingConfiguration.h" >+#include "MediaEngineDecodingConfigurationMock.h" >+#if USE(GSTREAMER) >+#include "MediaEngineDecodingConfigurationGStreamer.h" >+#endif >+ >+namespace WebCore { >+ >+static bool& mockEnabled() >+{ >+ static bool enabled; >+ return enabled; >+} >+ >+RefPtr<MediaEngineDecodingConfiguration> MediaEngineConfigurationFactory::createDecodingConfiguration(MediaDecodingConfiguration& config) >+{ >+ if (mockEnabled()) >+ return MediaEngineDecodingConfigurationMock::create(config); >+ >+#if USE(GSTREAMER) >+ if (DeprecatedGlobalSettings::isGStreamerEnabled()) >+ return MediaEngineDecodingConfigurationGStreamer::create(config); >+#endif >+ >+ return nullptr; >+} >+ >+void MediaEngineConfigurationFactory::enableMock() >+{ >+ mockEnabled() = true; >+} >+ >+void MediaEngineConfigurationFactory::disableMock() >+{ >+ mockEnabled() = false; >+} >+ >+} >diff --git a/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.h b/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.h >new file mode 100644 >index 0000000000000000000000000000000000000000..4f3251601f818efb8b2954c261dd605a651925e4 >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/MediaEngineConfigurationFactory.h >@@ -0,0 +1,44 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "MediaDecodingConfiguration.h" >+#include "MediaEngineDecodingConfiguration.h" >+#include <wtf/Forward.h> >+ >+namespace WebCore { >+ >+class MediaEngineConfigurationFactory { >+public: >+ static RefPtr<MediaEngineDecodingConfiguration> createDecodingConfiguration(MediaDecodingConfiguration&); >+ >+ WEBCORE_EXPORT static void enableMock(); >+ WEBCORE_EXPORT static void disableMock(); >+}; >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/mediacapabilities/MediaEngineDecodingConfiguration.h b/Source/WebCore/platform/mediacapabilities/MediaEngineDecodingConfiguration.h >new file mode 100644 >index 0000000000000000000000000000000000000000..cdac105401ae2fc6aeac357ae49cdb59e08e9e44 >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/MediaEngineDecodingConfiguration.h >@@ -0,0 +1,57 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "MediaDecodingConfiguration.h" >+#include "MediaEngineConfiguration.h" >+ >+#include <wtf/Forward.h> >+ >+namespace WebCore { >+ >+class MediaEngineDecodingConfiguration : public MediaEngineConfiguration { >+public: >+ MediaEngineDecodingConfiguration(MediaDecodingConfiguration&& config) >+ : MediaEngineConfiguration(reinterpret_cast<MediaConfiguration&&>(config)) >+ , m_decodingType(config.type) >+ { >+ } >+ >+ virtual ~MediaEngineDecodingConfiguration() = default; >+ >+ virtual bool canDecodeMedia() { return false; }; >+ virtual bool canSmoothlyDecodeMedia() { return false; }; >+ virtual bool canPowerEfficientlyDecodeMedia() { return false; }; >+ >+ MediaDecodingType decodingType() const { return m_decodingType; }; >+ >+private: >+ MediaDecodingType m_decodingType; >+}; >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.cpp b/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..f5d90b28da070456cfe20b7648d1b59b18fc698b >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.cpp >@@ -0,0 +1,55 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "MediaEngineDecodingConfigurationGStreamer.h" >+ >+#include "ContentType.h" >+#include "IntSize.h" >+#include "NotImplemented.h" >+ >+namespace WebCore { >+ >+bool MediaEngineDecodingConfigurationGStreamer::canDecodeMedia() >+{ >+ notImplemented(); >+ return true; >+} >+ >+bool MediaEngineDecodingConfigurationGStreamer::canSmoothlyDecodeMedia() >+{ >+ notImplemented(); >+ return true; >+} >+ >+bool MediaEngineDecodingConfigurationGStreamer::canPowerEfficientlyDecodeMedia() >+{ >+ notImplemented(); >+ return true; >+} >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.h b/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.h >new file mode 100644 >index 0000000000000000000000000000000000000000..a78ac7027c6a9e858b2f47dc7725b788aad2b2e4 >--- /dev/null >+++ b/Source/WebCore/platform/mediacapabilities/gstreamer/MediaEngineDecodingConfigurationGStreamer.h >@@ -0,0 +1,57 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "MediaDecodingConfiguration.h" >+#include "MediaEngineDecodingConfiguration.h" >+ >+#include <wtf/Forward.h> >+ >+namespace WebCore { >+ >+class MediaEngineDecodingConfigurationGStreamer final : public MediaEngineDecodingConfiguration { >+public: >+ static RefPtr<MediaEngineDecodingConfigurationGStreamer> create(MediaDecodingConfiguration& config) >+ { >+ return adoptRef(new MediaEngineDecodingConfigurationGStreamer(WTFMove(config))); >+ }; >+ >+ ImplementationType implementationType() const final { return ImplementationType::GStreamer; } >+ >+ bool canDecodeMedia() final; >+ bool canSmoothlyDecodeMedia() final; >+ bool canPowerEfficientlyDecodeMedia() final; >+ >+private: >+ MediaEngineDecodingConfigurationGStreamer(MediaDecodingConfiguration&& config) >+ : MediaEngineDecodingConfiguration(WTFMove(config)) { }; >+}; >+ >+} >+ >+SPECIALIZE_TYPE_TRAITS_MEDIA_ENGINE_CONFIGURATION(WebCore::MediaEngineDecodingConfigurationGStreamer, WebCore::MediaEngineDecodingConfiguration::ImplementationType::GStreamer); >diff --git a/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.cpp b/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.cpp >new file mode 100644 >index 0000000000000000000000000000000000000000..452d7b53e4447a89b849eb8f5abe2cee2b48d384 >--- /dev/null >+++ b/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.cpp >@@ -0,0 +1,73 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#include "config.h" >+#include "MediaEngineDecodingConfigurationMock.h" >+ >+#include "ContentType.h" >+#include "IntSize.h" >+ >+namespace WebCore { >+ >+bool MediaEngineDecodingConfigurationMock::canDecodeMedia() >+{ >+ // The mock implementation supports only local file playback. >+ if (decodingType() == MediaDecodingType::MediaSource) >+ return false; >+ >+ // Maxing out video decoding support at 720P. >+ RefPtr<MediaEngineVideoConfiguration> videoConfig = videoConfiguration(); >+ if (videoConfig) { >+ IntSize size = videoConfig->size(); >+ if (size.width() > 1280 && size.height() > 720) >+ return false; >+ } >+ >+ return true; >+} >+ >+bool MediaEngineDecodingConfigurationMock::canSmoothlyDecodeMedia() >+{ >+ RefPtr<MediaEngineVideoConfiguration> videoConfig = videoConfiguration(); >+ if (videoConfig) { >+ if (videoConfig->framerate() > 30) >+ return false; >+ } >+ return true; >+} >+ >+bool MediaEngineDecodingConfigurationMock::canPowerEfficientlyDecodeMedia() >+{ >+ RefPtr<MediaEngineVideoConfiguration> videoConfig = videoConfiguration(); >+ if (videoConfig) { >+ if (videoConfig->contentType().containerType() != "video/mp4") >+ return false; >+ } >+ return true; >+} >+ >+} // namespace WebCore >diff --git a/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.h b/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.h >new file mode 100644 >index 0000000000000000000000000000000000000000..76dae041f43f95acd4784f39123e60c15e7ae5a1 >--- /dev/null >+++ b/Source/WebCore/platform/mock/MediaEngineDecodingConfigurationMock.h >@@ -0,0 +1,56 @@ >+/* >+ * Copyright (C) 2018 Igalia S.L. >+ * >+ * Redistribution and use in source and binary forms, with or without >+ * modification, are permitted provided that the following conditions >+ * are met: >+ * >+ * 1. Redistributions of source code must retain the above copyright >+ * notice, this list of conditions and the following disclaimer. >+ * 2. Redistributions in binary form must reproduce the above >+ * copyright notice, this list of conditions and the following >+ * disclaimer in the documentation and/or other materials provided >+ * with the distribution. >+ * >+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS >+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT >+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR >+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT >+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, >+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT >+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, >+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY >+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT >+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE >+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. >+ */ >+ >+#pragma once >+ >+#include "MediaDecodingConfiguration.h" >+#include "MediaEngineDecodingConfiguration.h" >+ >+#include <wtf/Forward.h> >+ >+namespace WebCore { >+ >+class MediaEngineDecodingConfigurationMock final : public MediaEngineDecodingConfiguration { >+public: >+ static RefPtr<MediaEngineDecodingConfigurationMock> create(MediaDecodingConfiguration& config) >+ { >+ return adoptRef(new MediaEngineDecodingConfigurationMock(WTFMove(config))); >+ }; >+ >+ ImplementationType implementationType() const final { return ImplementationType::Mock; } >+ bool canDecodeMedia() final; >+ bool canSmoothlyDecodeMedia() final; >+ bool canPowerEfficientlyDecodeMedia() final; >+ >+private: >+ explicit MediaEngineDecodingConfigurationMock(MediaDecodingConfiguration&& config) >+ : MediaEngineDecodingConfiguration(WTFMove(config)) { }; >+}; >+ >+} >+ >+SPECIALIZE_TYPE_TRAITS_MEDIA_ENGINE_CONFIGURATION(WebCore::MediaEngineDecodingConfigurationMock, WebCore::MediaEngineDecodingConfiguration::ImplementationType::Mock); >diff --git a/Source/WebCore/testing/Internals.cpp b/Source/WebCore/testing/Internals.cpp >index 94d155009effb7224765caa9fb32972ee2867225..038db1a86b4fae291c04b730f65ac10b7b689d26 100644 >--- a/Source/WebCore/testing/Internals.cpp >+++ b/Source/WebCore/testing/Internals.cpp >@@ -100,6 +100,7 @@ > #include "LibWebRTCProvider.h" > #include "LoaderStrategy.h" > #include "MallocStatistics.h" >+#include "MediaEngineConfigurationFactory.h" > #include "MediaPlayer.h" > #include "MediaProducer.h" > #include "MediaResourceLoader.h" >@@ -507,6 +508,8 @@ void Internals::resetToConsistentState(Page& page) > page.setFullscreenAutoHideDuration(0_s); > page.setFullscreenInsets({ }); > page.setFullscreenControlsHidden(false); >+ >+ MediaEngineConfigurationFactory::disableMock(); > } > > Internals::Internals(Document& document) >@@ -3434,6 +3437,17 @@ void Internals::setShouldGenerateTimestamps(SourceBuffer& buffer, bool flag) > > #endif > >+void Internals::enableMockMediaCapabilities() >+{ >+#if USE(AVFOUNDATION) >+ WebCore::DeprecatedGlobalSettings::setAVFoundationEnabled(false); >+#endif >+#if USE(GSTREAMER) >+ WebCore::DeprecatedGlobalSettings::setGStreamerEnabled(false); >+#endif >+ MediaEngineConfigurationFactory::enableMock(); >+} >+ > #if ENABLE(VIDEO) > > ExceptionOr<void> Internals::beginMediaSessionInterruption(const String& interruptionString) >diff --git a/Source/WebCore/testing/Internals.h b/Source/WebCore/testing/Internals.h >index feb6f71b5293f5846b7dabe2c50ad529b0769059..9afcfc12a7981dde40f86b95ab04c9f30ba50ec9 100644 >--- a/Source/WebCore/testing/Internals.h >+++ b/Source/WebCore/testing/Internals.h >@@ -470,6 +470,8 @@ public: > Ref<MockCDMFactory> registerMockCDM(); > #endif > >+ void enableMockMediaCapabilities(); >+ > #if ENABLE(SPEECH_SYNTHESIS) > void enableMockSpeechSynthesizer(); > #endif >diff --git a/Source/WebCore/testing/Internals.idl b/Source/WebCore/testing/Internals.idl >index 41e542352a35c6dfb962d59f5c533ed7b5ab0754..9b7c81659cebb82806a234efdc5bbbccfcac1dce 100644 >--- a/Source/WebCore/testing/Internals.idl >+++ b/Source/WebCore/testing/Internals.idl >@@ -471,6 +471,7 @@ enum EventThrottlingBehavior { > > [Conditional=LEGACY_ENCRYPTED_MEDIA] void initializeMockCDM(); > [Conditional=ENCRYPTED_MEDIA] MockCDMFactory registerMockCDM(); >+ void enableMockMediaCapabilities(); > > [Conditional=SPEECH_SYNTHESIS] void enableMockSpeechSynthesizer(); > >diff --git a/LayoutTests/media/mediacapabilities/mock-decodingInfo-expected.txt b/LayoutTests/media/mediacapabilities/mock-decodingInfo-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..75917e21720633f8bf39e16377aa4bc60b803402 >--- /dev/null >+++ b/LayoutTests/media/mediacapabilities/mock-decodingInfo-expected.txt >@@ -0,0 +1,31 @@ >+RUN(internals.enableMockMediaCapabilities()) >+ >+Smooth and power efficient 720P MP4 decoding >+RUN(promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs="avc1"', height: 720, bitrate: 1000, width: 1280, framerate: 24 } });) >+Promise resolved OK >+info.supported == true OK >+info.smooth == true OK >+info.powerEfficient == true OK >+ >+Not smooth (too high framerate) but power efficient 720P MP4 decoding >+RUN(promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs="avc1"', height: 720, bitrate: 1000, width: 1280, framerate: 60 } });) >+Promise resolved OK >+info.supported == true OK >+info.smooth == false OK >+info.powerEfficient == true OK >+ >+Unsupported protocol: MSE >+RUN(promise = navigator.mediaCapabilities.decodingInfo({ type: 'media-source', video: { contentType: 'video/mp4; codecs="avc1"', height: 720, bitrate: 1000, width: 1280, framerate: 24 } });) >+Promise resolved OK >+info.supported == false OK >+info.smooth == false OK >+info.powerEfficient == false OK >+ >+Unsupported video resolution: 1080P >+RUN(promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs="avc1"', height: 1080, bitrate: 1000, width: 1920, framerate: 24 } });) >+Promise resolved OK >+info.supported == false OK >+info.smooth == false OK >+info.powerEfficient == false OK >+END OF TEST >+ >diff --git a/LayoutTests/media/mediacapabilities/mock-decodingInfo.html b/LayoutTests/media/mediacapabilities/mock-decodingInfo.html >new file mode 100644 >index 0000000000000000000000000000000000000000..3ca9ddca0927cff7844e60555308077ae0ae6eaa >--- /dev/null >+++ b/LayoutTests/media/mediacapabilities/mock-decodingInfo.html >@@ -0,0 +1,69 @@ >+<!DOCTYPE html> >+<html> >+<head> >+ <script src=../video-test.js></script> >+ <script type="text/javascript"> >+ var promise; >+ >+ function doTest() >+ { >+ if (!window.internals) { >+ failTest("Internals is required for this test.") >+ return; >+ } >+ >+ run('internals.enableMockMediaCapabilities()'); >+ next(); >+ } >+ >+ function next() { >+ if (!tests.length) { >+ endTest(); >+ return; >+ } >+ >+ var nextTest = tests.shift(); >+ consoleWrite(''); >+ nextTest(); >+ } >+ >+ tests = [ >+ function() { >+ consoleWrite('Smooth and power efficient 720P MP4 decoding'); >+ run("promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs=\"avc1\"', height: 720, bitrate: 1000, width: 1280, framerate: 24 } });"); >+ shouldResolve(promise).then((info) => { >+ checkMediaCapabilitiesInfo(info, true, true, true); >+ next(); >+ }, next); >+ }, >+ function() { >+ consoleWrite('Not smooth (too high framerate) but power efficient 720P MP4 decoding'); >+ run("promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs=\"avc1\"', height: 720, bitrate: 1000, width: 1280, framerate: 60 } });"); >+ shouldResolve(promise).then((info) => { >+ checkMediaCapabilitiesInfo(info, true, false, true); >+ next(); >+ }, next); >+ }, >+ function() { >+ consoleWrite('Unsupported protocol: MSE'); >+ run("promise = navigator.mediaCapabilities.decodingInfo({ type: 'media-source', video: { contentType: 'video/mp4; codecs=\"avc1\"', height: 720, bitrate: 1000, width: 1280, framerate: 24 } });"); >+ shouldResolve(promise).then((info) => { >+ checkMediaCapabilitiesInfo(info, false, false, false); >+ next(); >+ }, next); >+ }, >+ function() { >+ consoleWrite('Unsupported video resolution: 1080P'); >+ run("promise = navigator.mediaCapabilities.decodingInfo({ type: 'file', video: { contentType: 'video/mp4; codecs=\"avc1\"', height: 1080, bitrate: 1000, width: 1920, framerate: 24 } });"); >+ shouldResolve(promise).then((info) => { >+ checkMediaCapabilitiesInfo(info, false, false, false); >+ next(); >+ }, next); >+ }, >+ ]; >+ </script> >+</head> >+<body onload="doTest()"> >+ <div id="log"></div> >+</body> >+</html> >diff --git a/LayoutTests/media/video-test.js b/LayoutTests/media/video-test.js >index db8e4198d60270d03fafc2a793fef5d8c45827d5..2c3acc5fc654f356105b2e836a46fafb11c424b5 100644 >--- a/LayoutTests/media/video-test.js >+++ b/LayoutTests/media/video-test.js >@@ -487,3 +487,9 @@ function handlePromise(promise) { > function handle() { } > return promise.then(handle, handle); > } >+ >+function checkMediaCapabilitiesInfo(info, expectedSupported, expectedSmooth, expectedPowerEfficient) { >+ logResult(info.supported == expectedSupported, "info.supported == " + expectedSupported); >+ logResult(info.smooth == expectedSmooth, "info.smooth == " + expectedSmooth); >+ logResult(info.powerEfficient == expectedPowerEfficient, "info.powerEfficient == " + expectedPowerEfficient); >+}
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 187850
:
345754
|
346480
|
346481
|
346484
|
346492
|
346498