зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
253591c98f
|
@ -51,3 +51,5 @@ if CONFIG['MOZ_ENABLE_DBUS']:
|
|||
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -29,3 +29,5 @@ if CONFIG['ACCESSIBILITY']:
|
|||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -22,3 +22,5 @@ LOCAL_INCLUDES += [
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -50,3 +50,5 @@ FINAL_LIBRARY = 'xul'
|
|||
# macros which conflicts with std::min/max. Suppress the macros:
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -59,3 +59,5 @@ LOCAL_INCLUDES += [
|
|||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -24,3 +24,5 @@ LOCAL_INCLUDES += [
|
|||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -23,3 +23,5 @@ FINAL_LIBRARY = 'xul'
|
|||
# macros which conflicts with std::min/max. Suppress the macros:
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -47,3 +47,6 @@ else:
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -1073,3 +1073,8 @@ pref("dom.mozSettings.SettingsDB.verbose.enabled", false);
|
|||
pref("dom.mozSettings.SettingsManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsRequestManager.verbose.enabled", false);
|
||||
pref("dom.mozSettings.SettingsService.verbose.enabled", false);
|
||||
|
||||
// Controlling whether we want to allow forcing some Settings
|
||||
// IndexedDB transactions to be opened as readonly or keep everything as
|
||||
// readwrite.
|
||||
pref("dom.mozSettings.allowForceReadOnly", false);
|
||||
|
|
|
@ -74,3 +74,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
OS_LIBS += [
|
||||
'version',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f0e6d8bde961683b7862b4eb0bb04c49d9699f3c"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f0e6d8bde961683b7862b4eb0bb04c49d9699f3c"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "fbcff6610f8b99726ef14b0c6d894b4a7053eecf",
|
||||
"revision": "d937413f736efd995436c211649f930191429b66",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="f0e6d8bde961683b7862b4eb0bb04c49d9699f3c"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="7a865bd6163e9a0372861d27450b3434875ef5c1"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="58734e8a48157f99d5b733412b600c2e04c954fe"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -18,3 +18,5 @@ else:
|
|||
]
|
||||
DEFINES['B2G_NAME'] = '"%s-bin%s"' % (PROGRAM, CONFIG['BIN_SUFFIX'])
|
||||
DEFINES['GAIA_PATH'] = '"gaia/profile"'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -72,3 +72,5 @@ if CONFIG['HAVE_CLOCK_MONOTONIC']:
|
|||
OS_LIBS += CONFIG['REALTIME_LIBS']
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -17,3 +17,5 @@ FINAL_LIBRARY = 'browsercomps'
|
|||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -34,3 +34,5 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
|||
# GTK2: Need to link with glib for GNOME shell service
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'gtk2', 'gtk3'):
|
||||
OS_LIBS += CONFIG['TK_LIBS']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -21,3 +21,5 @@ FINAL_LIBRARY = 'browsercomps'
|
|||
LOCAL_INCLUDES += [
|
||||
'../build'
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -39,3 +39,5 @@ for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'):
|
|||
LOCAL_INCLUDES += [
|
||||
'../build',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -52,3 +52,5 @@ EXTRA_PP_JS_MODULES += [
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = 'browsercomps'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -49,3 +49,5 @@ for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION'):
|
|||
DEFINES[var] = '"%s"' % CONFIG[var]
|
||||
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -3856,7 +3856,6 @@ if test -n "$MOZ_FMP4"; then
|
|||
else
|
||||
MOZ_FMP4=
|
||||
fi
|
||||
MOZ_EME=1
|
||||
MOZ_FFMPEG=
|
||||
MOZ_WEBRTC=1
|
||||
MOZ_PEERCONNECTION=
|
||||
|
@ -5327,6 +5326,7 @@ MOZ_ARG_DISABLE_BOOL(fmp4,
|
|||
|
||||
if test -n "$MOZ_FMP4"; then
|
||||
AC_DEFINE(MOZ_FMP4)
|
||||
MOZ_EME=1
|
||||
fi;
|
||||
|
||||
dnl ========================================================
|
||||
|
@ -5339,6 +5339,9 @@ MOZ_ARG_DISABLE_BOOL(eme,
|
|||
MOZ_EME=1)
|
||||
|
||||
if test -n "$MOZ_EME"; then
|
||||
if test -z "$MOZ_FMP4"; then
|
||||
AC_MSG_ERROR([Encrypted Media Extension support requires Fragmented MP4 support])
|
||||
fi
|
||||
AC_DEFINE(MOZ_EME)
|
||||
fi;
|
||||
|
||||
|
|
|
@ -24,3 +24,5 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
|||
LOCAL_INCLUDES += ['/uriloader/exthandler/mac']
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -11,14 +11,19 @@
|
|||
#include "nsIDocument.h" // For nsIDocument
|
||||
#include "nsIPresShell.h" // For nsIPresShell
|
||||
#include "nsLayoutUtils.h" // For PostRestyleEvent (remove after bug 1073336)
|
||||
#include "PendingPlayerTracker.h" // For PendingPlayerTracker
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline, mSource)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationPlayer, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationPlayer, Release)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationPlayer, mTimeline,
|
||||
mSource, mReady)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AnimationPlayer)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AnimationPlayer)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AnimationPlayer)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
JSObject*
|
||||
AnimationPlayer::WrapObject(JSContext* aCx)
|
||||
|
@ -47,6 +52,10 @@ AnimationPlayer::GetCurrentTime() const
|
|||
AnimationPlayState
|
||||
AnimationPlayer::PlayState() const
|
||||
{
|
||||
if (mIsPending) {
|
||||
return AnimationPlayState::Pending;
|
||||
}
|
||||
|
||||
Nullable<TimeDuration> currentTime = GetCurrentTime();
|
||||
if (currentTime.IsNull()) {
|
||||
return AnimationPlayState::Idle;
|
||||
|
@ -63,6 +72,26 @@ AnimationPlayer::PlayState() const
|
|||
return AnimationPlayState::Running;
|
||||
}
|
||||
|
||||
Promise*
|
||||
AnimationPlayer::GetReady(ErrorResult& aRv)
|
||||
{
|
||||
// Lazily create the ready promise if it doesn't exist
|
||||
if (!mReady) {
|
||||
nsIGlobalObject* global = mTimeline->GetParentObject();
|
||||
if (global) {
|
||||
mReady = Promise::Create(global, aRv);
|
||||
if (mReady && PlayState() != AnimationPlayState::Pending) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!mReady) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
return mReady;
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Play()
|
||||
{
|
||||
|
@ -110,7 +139,7 @@ AnimationPlayer::Tick()
|
|||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::ResolveStartTime()
|
||||
AnimationPlayer::StartNow()
|
||||
{
|
||||
// Currently we only expect this method to be called when we are in the
|
||||
// middle of initiating/resuming playback so we should have an unresolved
|
||||
|
@ -125,6 +154,24 @@ AnimationPlayer::ResolveStartTime()
|
|||
MOZ_ASSERT(!readyTime.IsNull(), "Missing or inactive timeline");
|
||||
mStartTime.SetValue(readyTime.Value() - mHoldTime.Value());
|
||||
mHoldTime.SetNull();
|
||||
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::Cancel()
|
||||
{
|
||||
if (mIsPending) {
|
||||
CancelPendingPlay();
|
||||
if (mReady) {
|
||||
mReady->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
}
|
||||
|
||||
mHoldTime.SetNull();
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -173,7 +220,8 @@ AnimationPlayer::ComposeStyle(nsRefPtr<css::AnimValuesStyleRule>& aStyleRule,
|
|||
}
|
||||
|
||||
AnimationPlayState playState = PlayState();
|
||||
if (playState == AnimationPlayState::Running) {
|
||||
if (playState == AnimationPlayState::Running ||
|
||||
playState == AnimationPlayState::Pending) {
|
||||
aNeedsRefreshes = true;
|
||||
}
|
||||
|
||||
|
@ -197,21 +245,32 @@ AnimationPlayer::DoPlay()
|
|||
return;
|
||||
}
|
||||
|
||||
ResolveStartTime();
|
||||
// Clear ready promise. We'll create a new one lazily.
|
||||
mReady = nullptr;
|
||||
|
||||
StartNow();
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::DoPause()
|
||||
{
|
||||
if (IsPaused()) {
|
||||
return;
|
||||
if (mIsPending) {
|
||||
CancelPendingPlay();
|
||||
// Resolve the ready promise since we currently only use it for
|
||||
// players that are waiting to play. Later (in bug 1109390), we will
|
||||
// use this for players waiting to pause as well and then we won't
|
||||
// want to resolve it just yet.
|
||||
if (mReady) {
|
||||
mReady->MaybeResolve(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark this as no longer running on the compositor so that next time
|
||||
// we update animations we won't throttle them and will have a chance
|
||||
// to remove the animation from any layer it might be on.
|
||||
mIsRunningOnCompositor = false;
|
||||
|
||||
// Bug 927349 - check for null result here and go to pending state
|
||||
// Bug 1109390 - check for null result here and go to pending state
|
||||
mHoldTime = GetCurrentTime();
|
||||
mStartTime.SetNull();
|
||||
}
|
||||
|
@ -234,6 +293,24 @@ AnimationPlayer::PostUpdate()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
AnimationPlayer::CancelPendingPlay()
|
||||
{
|
||||
if (!mIsPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIDocument* doc = GetRenderedDocument();
|
||||
if (doc) {
|
||||
PendingPlayerTracker* tracker = doc->GetPendingPlayerTracker();
|
||||
if (tracker) {
|
||||
tracker->RemovePlayPending(*this);
|
||||
}
|
||||
}
|
||||
|
||||
mIsPending = false;
|
||||
}
|
||||
|
||||
StickyTimeDuration
|
||||
AnimationPlayer::SourceContentEnd() const
|
||||
{
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/dom/Animation.h" // for Animation
|
||||
#include "mozilla/dom/AnimationPlayerBinding.h" // for AnimationPlayState
|
||||
#include "mozilla/dom/AnimationTimeline.h" // for AnimationTimeline
|
||||
#include "mozilla/dom/Promise.h" // for Promise
|
||||
#include "nsCSSProperty.h" // for nsCSSProperty
|
||||
|
||||
// X11 has a #define for CurrentTime.
|
||||
|
@ -37,7 +38,8 @@ class CSSTransitionPlayer;
|
|||
|
||||
namespace dom {
|
||||
|
||||
class AnimationPlayer : public nsWrapperCache
|
||||
class AnimationPlayer : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
protected:
|
||||
virtual ~AnimationPlayer() { }
|
||||
|
@ -45,13 +47,14 @@ protected:
|
|||
public:
|
||||
explicit AnimationPlayer(AnimationTimeline* aTimeline)
|
||||
: mTimeline(aTimeline)
|
||||
, mIsPending(false)
|
||||
, mIsRunningOnCompositor(false)
|
||||
, mIsPreviousStateFinished(false)
|
||||
{
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationPlayer)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationPlayer)
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AnimationPlayer)
|
||||
|
||||
AnimationTimeline* GetParentObject() const { return mTimeline; }
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
@ -65,6 +68,7 @@ public:
|
|||
Nullable<TimeDuration> GetStartTime() const { return mStartTime; }
|
||||
Nullable<TimeDuration> GetCurrentTime() const;
|
||||
AnimationPlayState PlayState() const;
|
||||
virtual Promise* GetReady(ErrorResult& aRv);
|
||||
virtual void Play();
|
||||
virtual void Pause();
|
||||
bool IsRunningOnCompositor() const { return mIsRunningOnCompositor; }
|
||||
|
@ -84,10 +88,11 @@ public:
|
|||
|
||||
void SetSource(Animation* aSource);
|
||||
void Tick();
|
||||
// Sets the start time of the player to the current time of its timeline.
|
||||
// This should only be called on a player that is currently waiting to play
|
||||
// (and therefore has a null start time but a fixed hold time).
|
||||
void ResolveStartTime();
|
||||
|
||||
// Sets the start time of a player that is waiting to play to the current
|
||||
// time of its timeline.
|
||||
void StartNow();
|
||||
void Cancel();
|
||||
|
||||
const nsString& Name() const {
|
||||
return mSource ? mSource->Name() : EmptyString();
|
||||
|
@ -128,6 +133,10 @@ protected:
|
|||
|
||||
void FlushStyle() const;
|
||||
void PostUpdate();
|
||||
// Remove this player from the pending player tracker and resets mIsPending
|
||||
// as necessary. The caller is responsible for resolving or aborting the
|
||||
// mReady promise as necessary.
|
||||
void CancelPendingPlay();
|
||||
StickyTimeDuration SourceContentEnd() const;
|
||||
|
||||
nsIDocument* GetRenderedDocument() const;
|
||||
|
@ -140,6 +149,18 @@ protected:
|
|||
// The beginning of the delay period.
|
||||
Nullable<TimeDuration> mStartTime; // Timeline timescale
|
||||
Nullable<TimeDuration> mHoldTime; // Player timescale
|
||||
|
||||
// A Promise that is replaced on each call to Play() (and in future Pause())
|
||||
// and fulfilled when Play() is successfully completed.
|
||||
// This object is lazily created by GetReady.
|
||||
nsRefPtr<Promise> mReady;
|
||||
|
||||
// Indicates if the player is in the pending state. We use this rather
|
||||
// than checking if this player is tracked by a PendingPlayerTracker.
|
||||
// This is because the PendingPlayerTracker is associated with the source
|
||||
// content's document but we need to know if we're pending even if the
|
||||
// source content loses association with its document.
|
||||
bool mIsPending;
|
||||
bool mIsRunningOnCompositor;
|
||||
// Indicates whether we were in the finished state during our
|
||||
// most recent unthrottled sample (our last ComposeStyle call).
|
||||
|
|
|
@ -55,17 +55,12 @@ AnimationTimeline::GetCurrentTimeStamp() const
|
|||
result = timing->GetNavigationStartTimeStamp();
|
||||
}
|
||||
|
||||
nsIPresShell* presShell = mDocument->GetShell();
|
||||
if (MOZ_UNLIKELY(!presShell)) {
|
||||
nsRefreshDriver* refreshDriver = GetRefreshDriver();
|
||||
if (!refreshDriver) {
|
||||
return result;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (MOZ_UNLIKELY(!presContext)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = presContext->RefreshDriver()->MostRecentRefresh();
|
||||
result = refreshDriver->MostRecentRefresh();
|
||||
// FIXME: We would like to assert that:
|
||||
// mLastCurrentTime.IsNull() || result >= mLastCurrentTime
|
||||
// but due to bug 1043078 this will not be the case when the refresh driver
|
||||
|
@ -104,5 +99,21 @@ AnimationTimeline::ToTimeStamp(const TimeDuration& aTimeDuration) const
|
|||
return result;
|
||||
}
|
||||
|
||||
nsRefreshDriver*
|
||||
AnimationTimeline::GetRefreshDriver() const
|
||||
{
|
||||
nsIPresShell* presShell = mDocument->GetShell();
|
||||
if (MOZ_UNLIKELY(!presShell)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
if (MOZ_UNLIKELY(!presContext)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return presContext->RefreshDriver();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "nsIDocument.h"
|
||||
|
||||
struct JSContext;
|
||||
class nsRefreshDriver;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -26,10 +27,17 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ~AnimationTimeline() { }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationTimeline)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationTimeline)
|
||||
|
||||
nsISupports* GetParentObject() const { return mDocument; }
|
||||
nsIGlobalObject* GetParentObject() const
|
||||
{
|
||||
return mDocument->GetParentObject();
|
||||
}
|
||||
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
|
||||
|
||||
// AnimationTimeline methods
|
||||
|
@ -44,8 +52,7 @@ public:
|
|||
|
||||
protected:
|
||||
TimeStamp GetCurrentTimeStamp() const;
|
||||
|
||||
virtual ~AnimationTimeline() { }
|
||||
nsRefreshDriver* GetRefreshDriver() const;
|
||||
|
||||
nsCOMPtr<nsIDocument> mDocument;
|
||||
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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 "PendingPlayerTracker.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION(PendingPlayerTracker, mPlayPendingSet)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(PendingPlayerTracker, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(PendingPlayerTracker, Release)
|
||||
|
||||
void
|
||||
PendingPlayerTracker::AddPlayPending(dom::AnimationPlayer& aPlayer)
|
||||
{
|
||||
mPlayPendingSet.PutEntry(&aPlayer);
|
||||
}
|
||||
|
||||
void
|
||||
PendingPlayerTracker::RemovePlayPending(dom::AnimationPlayer& aPlayer)
|
||||
{
|
||||
mPlayPendingSet.RemoveEntry(&aPlayer);
|
||||
}
|
||||
|
||||
bool
|
||||
PendingPlayerTracker::IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const
|
||||
{
|
||||
return mPlayPendingSet.Contains(const_cast<dom::AnimationPlayer*>(&aPlayer));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,36 @@
|
|||
/* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
|
||||
/* 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_dom_PendingPlayerTracker_h
|
||||
#define mozilla_dom_PendingPlayerTracker_h
|
||||
|
||||
#include "mozilla/dom/AnimationPlayer.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PendingPlayerTracker MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PendingPlayerTracker)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PendingPlayerTracker)
|
||||
|
||||
void AddPlayPending(dom::AnimationPlayer& aPlayer);
|
||||
void RemovePlayPending(dom::AnimationPlayer& aPlayer);
|
||||
bool IsWaitingToPlay(dom::AnimationPlayer const& aPlayer) const;
|
||||
|
||||
private:
|
||||
~PendingPlayerTracker() { }
|
||||
|
||||
typedef nsTHashtable<nsRefPtrHashKey<dom::AnimationPlayer>>
|
||||
AnimationPlayerSet;
|
||||
|
||||
AnimationPlayerSet mPlayPendingSet;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_PendingPlayerTracker_h
|
|
@ -14,11 +14,16 @@ EXPORTS.mozilla.dom += [
|
|||
'AnimationTimeline.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
'PendingPlayerTracker.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Animation.cpp',
|
||||
'AnimationEffect.cpp',
|
||||
'AnimationPlayer.cpp',
|
||||
'AnimationTimeline.cpp',
|
||||
'PendingPlayerTracker.cpp',
|
||||
]
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes xyz {
|
||||
|
@ -11,35 +12,26 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'xyz 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Animation effect name matches keyframes rule name');
|
||||
div.remove();
|
||||
}, 'Effect name makes keyframe rule');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\yz 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Escaped animation effect name matches keyframes rule name');
|
||||
div.remove();
|
||||
}, 'Escaped animation name');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'x\\79 z 100s';
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, 'xyz',
|
||||
'Hex-escaped animation effect name matches keyframes rule'
|
||||
+ ' name');
|
||||
div.remove();
|
||||
}, 'Animation name with hex-escape');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim {
|
||||
|
@ -12,24 +13,12 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
|
@ -39,27 +28,22 @@ async_test(function(t) {
|
|||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
player.pause();
|
||||
});
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
});
|
||||
div.remove();
|
||||
player.pause();
|
||||
return player.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() a running animation');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
|
@ -69,22 +53,18 @@ async_test(function(t) {
|
|||
|
||||
player.pause();
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
assert_equals(getMarginLeft(cs), 0,
|
||||
'Paused value of margin-left is zero');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
|
@ -95,18 +75,15 @@ async_test(function(t) {
|
|||
|
||||
player.play();
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > 0,
|
||||
'Playing value of margin-left is greater than zero');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() overrides animation-play-state');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
|
@ -115,35 +92,32 @@ async_test(function(t) {
|
|||
'Initial value of margin-left is zero');
|
||||
|
||||
player.play();
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
|
||||
var previousAnimVal;
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
div.style.animationPlayState = 'paused';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
});
|
||||
player.ready.then(function() {
|
||||
div.style.animationPlayState = 'running';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
});
|
||||
div.remove();
|
||||
}).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is running');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
div.style.animationPlayState = 'paused';
|
||||
cs.animationPlayState; // Trigger style resolution
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Animated value of margin-left does not change when'
|
||||
+ ' paused by style');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() is overridden by later setting "animation-play-state: paused"');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
|
@ -158,20 +132,17 @@ async_test(function(t) {
|
|||
player.play();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
});
|
||||
div.remove();
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'paused',
|
||||
'animation-play-state is paused');
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'Playing value of margin-left is increasing');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'play() flushes pending changes to animation-play-state first');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
|
@ -193,16 +164,13 @@ async_test(function(t) {
|
|||
player.pause();
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
});
|
||||
div.remove();
|
||||
waitForFrame().then(t.step_func(function() {
|
||||
assert_equals(cs.animationPlayState, 'running',
|
||||
'animation-play-state is paused');
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'Paused value of margin-left does not change');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() applies pending changes to animation-play-state first');
|
||||
// (Note that we can't actually test for this; see comment above, in test-body.)
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
|
@ -9,23 +10,23 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player.playState, 'running');
|
||||
// Bug 927349: Check for pending state here
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_equals(player.playState, 'running');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Player returns correct playState when running');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
|
@ -33,8 +34,8 @@ test(function() {
|
|||
assert_equals(player.playState, 'paused');
|
||||
}, 'Player returns correct playState when paused');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s';
|
||||
|
||||
|
@ -43,14 +44,15 @@ test(function() {
|
|||
assert_equals(player.playState, 'paused');
|
||||
}, 'Player.playState updates when paused by script');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
div.style.animation = 'anim 1000s paused';
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
div.style.animationPlayState = 'running';
|
||||
// This test also checks that calling playState flushes style
|
||||
// Bug 927349: Make this check for 'pending'
|
||||
assert_equals(player.playState, 'running');
|
||||
}, 'Player.playState updates when resumed by setting style');
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes abc {
|
||||
to { transform: translate(10px) }
|
||||
}
|
||||
</style>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
var originalReadyPromise = player.ready;
|
||||
player.ready.then(function() {
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
player.pause();
|
||||
// TODO: When we implement deferred pausing (bug 1109390), change this to
|
||||
// assert_not_equals and wait on the new promise before continuing.
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise does not change when pausing (for now)');
|
||||
player.play();
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs after calling'
|
||||
+ ' play()');
|
||||
t.done();
|
||||
});
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s paused';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
var originalReadyPromise = player.ready;
|
||||
div.style.animationPlayState = 'running';
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'After updating animation-play-state a new ready promise'
|
||||
+ ' object is created');
|
||||
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(function() {
|
||||
var promiseBeforeCallingPlay = player.ready;
|
||||
player.play();
|
||||
assert_equals(player.ready, promiseBeforeCallingPlay,
|
||||
'Ready promise has same object identity after redundant call'
|
||||
+ ' to play()');
|
||||
t.done();
|
||||
});
|
||||
}, 'Redundant calls to play() do not generate new ready promise objects');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'abc 100s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
|
||||
player.ready.then(function(resolvedPlayer) {
|
||||
assert_equals(resolvedPlayer, player,
|
||||
'Object identity of player passed to Promise callback'
|
||||
+ ' matches the player object owning the Promise');
|
||||
t.done();
|
||||
});
|
||||
}, 'The ready promise is fulfilled with its AnimationPlayer');
|
||||
|
||||
</script>
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim { }
|
||||
|
@ -9,19 +10,12 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players[0].source.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
div.remove();
|
||||
}, 'Returned CSS animations have the correct Animation.target');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
|
@ -12,23 +13,24 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
var originalStartTime = originalPlayer.startTime;
|
||||
var originalCurrentTime = originalPlayer.currentTime;
|
||||
var originalStartTime;
|
||||
var originalCurrentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||
// simply reflect the current time).
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
originalPlayer.ready.then(function() {
|
||||
originalStartTime = originalPlayer.startTime;
|
||||
originalCurrentTime = originalPlayer.currentTime;
|
||||
|
||||
// Wait a moment so we can confirm the startTime doesn't change (and
|
||||
// doesn't simply reflect the current time).
|
||||
return waitForFrame();
|
||||
}).then(t.step_func(function() {
|
||||
div.style.animationDuration = '200s';
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
assert_equals(player, originalPlayer,
|
||||
|
@ -41,13 +43,12 @@ async_test(function(t) {
|
|||
assert_not_equals(player.currentTime, originalCurrentTime,
|
||||
'AnimationPlayer.currentTime has updated in next'
|
||||
+ ' requestAnimationFrame callback');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'AnimationPlayers preserve their startTime when changed');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
|
@ -65,7 +66,7 @@ test(function() {
|
|||
}, 'Updated AnimationPlayers maintain their order in the list');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 200s, anim1 100s';
|
||||
|
||||
// Store original state
|
||||
|
@ -73,8 +74,10 @@ async_test(function(t) {
|
|||
var player1 = players[0];
|
||||
var player2 = players[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait before continuing so we can compare start times (otherwise the
|
||||
// new player objects and existing player objects will all have the same
|
||||
// start time).
|
||||
waitForAllPlayers(players).then(waitForFrame).then(t.step_func(function() {
|
||||
// Swap duration of first and second in list and prepend animation at the
|
||||
// same time
|
||||
div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
|
||||
|
@ -87,63 +90,69 @@ async_test(function(t) {
|
|||
'Second player is in third position after update');
|
||||
assert_equals(players[1].startTime, players[2].startTime,
|
||||
'Old players have the same start time');
|
||||
// TODO: Check that players[0].startTime === null
|
||||
return players[0].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime > players[1].startTime,
|
||||
'New player has later start time');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'Only the startTimes of existing animations are preserved');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondPlayer = div.getAnimationPlayers()[1];
|
||||
|
||||
// Wait before continuing so we can compare start times
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
secondPlayer.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
// Trim list of animations
|
||||
div.style.animationName = 'anim1';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1, 'List of players was trimmed');
|
||||
assert_equals(players[0], secondPlayer,
|
||||
'Remaining player is the second one in the list');
|
||||
assert_equals(typeof(players[0].startTime), 'number',
|
||||
'Remaining player has resolved startTime');
|
||||
assert_true(players[0].startTime < players[0].timeline.currentTime,
|
||||
'Remaining player preserves startTime');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'Animations are removed from the start of the list while preserving'
|
||||
+ ' the state of existing players');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var firstAddedPlayer = div.getAnimationPlayers()[0];
|
||||
var firstAddedPlayer = div.getAnimationPlayers()[0],
|
||||
secondAddedPlayer,
|
||||
players;
|
||||
|
||||
// Wait and add second player
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
firstAddedPlayer.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
var secondAddedPlayer = div.getAnimationPlayers()[0];
|
||||
secondAddedPlayer = div.getAnimationPlayers()[0];
|
||||
|
||||
// Wait again and add another player
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_not_equals(firstAddedPlayer, secondAddedPlayer,
|
||||
'New players are added to start of the list');
|
||||
assert_equals(players[0], secondAddedPlayer,
|
||||
'Second player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(players[2], firstAddedPlayer,
|
||||
'First player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_true(players[1].startTime > players[0].startTime,
|
||||
'Interleaved player starts later than existing players');
|
||||
assert_true(players[0].startTime > players[2].startTime,
|
||||
'Original players retain their start time');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
return secondAddedPlayer.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_not_equals(firstAddedPlayer, secondAddedPlayer,
|
||||
'New players are added to start of the list');
|
||||
assert_equals(players[0], secondAddedPlayer,
|
||||
'Second player remains in same position after'
|
||||
+ ' interleaving');
|
||||
assert_equals(players[2], firstAddedPlayer,
|
||||
'First player remains in same position after'
|
||||
+ ' interleaving');
|
||||
return players[1].ready;
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[1].startTime > players[0].startTime,
|
||||
'Interleaved player starts later than existing players');
|
||||
assert_true(players[0].startTime > players[2].startTime,
|
||||
'Original players retain their start time');
|
||||
t.done();
|
||||
}));
|
||||
}, 'Player state is preserved when interleaving animations in list');
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<style>
|
||||
@keyframes anim1 {
|
||||
|
@ -18,95 +19,94 @@
|
|||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for an element'
|
||||
+ ' with no animations');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for non-animated content');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.animation = 'anim1 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns a player running CSS Animations');
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
players[0].ready.then(t.step_func(function() {
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS animation has a sensible start time');
|
||||
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait a moment then add a second animation.
|
||||
//
|
||||
// We wait for the next frame so that we can test that the start times of
|
||||
// the animations differ.
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.animation = 'anim1 100s, anim2 100s';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers returns one player for each value of'
|
||||
+ ' animation-name');
|
||||
// Wait until both players are ready
|
||||
// (We don't make any assumptions about the order of the players since
|
||||
// that is the purpose of the following test.)
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime < players[1].startTime,
|
||||
'Additional players for CSS animations start after the original'
|
||||
+ ' animation and appear later in the list');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Animations');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation that targets multiple properties
|
||||
div.style.animation = 'multiPropAnim 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns only one player for a CSS Animation'
|
||||
+ ' that targets multiple properties');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for multi-property animations');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add an animation
|
||||
div.style.backgroundColor = 'red';
|
||||
div.style.animation = 'anim1 100s';
|
||||
window.getComputedStyle(div).backgroundColor;
|
||||
|
||||
// Wait a moment then add a transition
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
// Wait until a frame after the animation starts, then add a transition
|
||||
var players = div.getAnimationPlayers();
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
div.style.transition = 'all 100s';
|
||||
div.style.backgroundColor = 'green';
|
||||
|
||||
var players = div.getAnimationPlayers();
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers returns players for both animations and '
|
||||
'getAnimationPlayers returns players for both animations and'
|
||||
+ ' transitions that run simultaneously');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime > players[1].startTime,
|
||||
'players for transitions appear before animations even if they '
|
||||
'players for transitions appear before animations even if they'
|
||||
+ ' start later');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for both CSS Animations and Transitions at once');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers does not return players for finished '
|
||||
+ ' (and non-forwards-filling) CSS Animations');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
|
@ -115,14 +115,13 @@ async_test(function(t) {
|
|||
}, 'getAnimationPlayers for CSS Animations that have finished');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('animationend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns players for CSS Animations that have'
|
||||
+ ' finished but are filling forwards');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
|
@ -131,8 +130,8 @@ async_test(function(t) {
|
|||
}, 'getAnimationPlayers for CSS Animations that have finished but are'
|
||||
+ ' forwards filling');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'none 100s';
|
||||
|
||||
var players = div.getAnimationPlayers();
|
||||
|
@ -145,12 +144,10 @@ test(function() {
|
|||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns players only for those CSS Animations whose'
|
||||
+ ' animation-name is not none');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with animation-name: none');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'missing 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 0,
|
||||
|
@ -162,67 +159,66 @@ test(function() {
|
|||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns players only for those CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with animation-name: missing');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, notyet 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers initally only returns players for CSS Animations whose'
|
||||
+ ' animation-name is found');
|
||||
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
var keyframes = '@keyframes notyet { to { left: 100px; } }';
|
||||
document.styleSheets[0].insertRule(keyframes, 0);
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers includes player when @keyframes rule is added'
|
||||
+ ' later');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime < players[1].startTime,
|
||||
'Newly added player has a later start time');
|
||||
document.styleSheets[0].deleteRule(0);
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Animations where the @keyframes rule is added'
|
||||
+ ' later');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s, anim1 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 2,
|
||||
'getAnimationPlayers returns one player for each CSS animation-name'
|
||||
+ ' even if the names are duplicated');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with duplicated animation-name');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'empty 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns players for CSS animations with an'
|
||||
+ ' empty keyframes rule');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for CSS Animations with empty keyframes rule');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s 100s';
|
||||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 1,
|
||||
'getAnimationPlayers returns animations for CSS animations whose'
|
||||
+ ' delay makes them start later');
|
||||
assert_true(players[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the player is'
|
||||
+ ' not in the future');
|
||||
div.remove();
|
||||
players[0].ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(players[0].startTime <= document.timeline.currentTime,
|
||||
'For CSS Animations in delay phase, the start time of the player is'
|
||||
+ ' not in the future');
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS animations in delay phase');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 0s 100s';
|
||||
assert_equals(div.getAnimationPlayers().length, 1,
|
||||
'getAnimationPlayers returns animations for CSS animations whose'
|
||||
|
@ -230,8 +226,8 @@ test(function() {
|
|||
div.remove();
|
||||
}, 'getAnimationPlayers for zero-duration CSS Animations');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.animation = 'anim1 100s';
|
||||
var originalPlayer = div.getAnimationPlayers()[0];
|
||||
|
||||
|
@ -252,8 +248,6 @@ test(function() {
|
|||
assert_equals(originalPlayer, extendedPlayer,
|
||||
'getAnimationPlayers returns the same objects even when their'
|
||||
+ ' duration changes');
|
||||
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers returns objects with the same identity');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a transition
|
||||
div.style.left = '0px';
|
||||
|
@ -18,7 +18,6 @@ test(function() {
|
|||
|
||||
assert_equals(div.getAnimationPlayers()[0].source.effect.name, '',
|
||||
'Animation effects for transitions have an empty name');
|
||||
div.remove();
|
||||
}, 'Effect name for transitions');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,28 +2,17 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function getMarginLeft(cs) {
|
||||
return parseFloat(cs.marginLeft);
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
var cs = window.getComputedStyle(div);
|
||||
|
||||
div.style.marginLeft = '0px';
|
||||
|
@ -37,30 +26,23 @@ async_test(function(t) {
|
|||
'Initial value of margin-left is zero');
|
||||
var previousAnimVal = getMarginLeft(cs);
|
||||
|
||||
waitForFrame().then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.pause();
|
||||
});
|
||||
player.ready.then(waitForFrame).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left is initially increasing');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.pause();
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.play();
|
||||
});
|
||||
return waitForFrame();
|
||||
}).then(function() {
|
||||
t.step(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
});
|
||||
div.remove();
|
||||
})).then(t.step_func(function() {
|
||||
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||
'margin-left does not increase after calling pause()');
|
||||
previousAnimVal = getMarginLeft(cs);
|
||||
player.play();
|
||||
return player.ready.then(waitForFrame);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(getMarginLeft(cs) > previousAnimVal,
|
||||
'margin-left increases after calling play()');
|
||||
t.done();
|
||||
});
|
||||
}));
|
||||
}, 'pause() and play() a transition');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv(t);
|
||||
div.style.transform = 'translate(0px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
div.style.transition = 'transform 100s';
|
||||
div.style.transform = 'translate(10px)';
|
||||
window.getComputedStyle(div).transform;
|
||||
|
||||
var player = div.getAnimationPlayers()[0];
|
||||
var originalReadyPromise = player.ready;
|
||||
|
||||
player.ready.then(t.step_func(function() {
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise is the same object when playing completes');
|
||||
player.pause();
|
||||
// TODO: When we implement deferred pausing, change this to
|
||||
// assert_not_equals and wait on the new promise before continuing.
|
||||
assert_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise does not change when pausing (for now)');
|
||||
player.play();
|
||||
assert_not_equals(player.ready, originalReadyPromise,
|
||||
'Ready promise object identity differs after calling'
|
||||
+ ' play()');
|
||||
t.done();
|
||||
}));
|
||||
}, 'A new ready promise is created each time play() is called'
|
||||
+ ' the animation property');
|
||||
|
||||
</script>
|
|
@ -2,13 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(function() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.left = '0px';
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
@ -18,7 +18,6 @@ test(function() {
|
|||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players[0].source.target, div,
|
||||
'Animation.target is the animatable div');
|
||||
div.remove();
|
||||
}, 'Returned CSS transitions have the correct Animation.target');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -2,18 +2,13 @@
|
|||
<meta charset=utf-8>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../testcommon.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function addDiv() {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
return div;
|
||||
}
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Add a couple of transitions
|
||||
div.style.left = '0px';
|
||||
|
@ -27,34 +22,35 @@ async_test(function(t) {
|
|||
var players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 2,
|
||||
'getAnimationPlayers() returns one player per transitioning property');
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(players[0].startTime, players[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
|
||||
// Wait a moment then add a third transition
|
||||
window.requestAnimationFrame(t.step_func(function() {
|
||||
waitForAllPlayers(players).then(t.step_func(function() {
|
||||
var startTime = players[0].startTime;
|
||||
assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
|
||||
'CSS transitions have sensible start times');
|
||||
assert_equals(players[0].startTime, players[1].startTime,
|
||||
'CSS transitions started together have the same start time');
|
||||
// Wait a moment then add a third transition
|
||||
return waitForFrame();
|
||||
})).then(t.step_func(function() {
|
||||
div.style.backgroundColor = 'green';
|
||||
players = div.getAnimationPlayers();
|
||||
assert_equals(players.length, 3,
|
||||
'getAnimationPlayers returns players for all running CSS Transitions');
|
||||
return waitForAllPlayers(players);
|
||||
})).then(t.step_func(function() {
|
||||
assert_true(players[1].startTime < players[2].startTime,
|
||||
'Player for additional CSS transition starts after the original'
|
||||
+ ' transitions and appears later in the list');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
}, 'getAnimationPlayers for CSS Transitions');
|
||||
|
||||
async_test(function(t) {
|
||||
var div = addDiv();
|
||||
var div = addDiv(t);
|
||||
|
||||
// Set up event listener
|
||||
div.addEventListener('transitionend', t.step_func(function() {
|
||||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers does not return finished CSS Transitions');
|
||||
div.remove();
|
||||
t.done();
|
||||
}));
|
||||
|
||||
|
@ -67,8 +63,8 @@ async_test(function(t) {
|
|||
window.getComputedStyle(div).left;
|
||||
}, 'getAnimationPlayers for CSS Transitions that have finished');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
// Try to transition non-animatable property animation-duration
|
||||
div.style.animationDuration = '10s';
|
||||
|
@ -80,11 +76,10 @@ test(function() {
|
|||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for a transition'
|
||||
+ ' of a non-animatable property');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for transition on non-animatable property');
|
||||
|
||||
test(function() {
|
||||
var div = addDiv();
|
||||
test(function(t) {
|
||||
var div = addDiv(t);
|
||||
|
||||
div.style.setProperty('-vendor-unsupported', '0px', '');
|
||||
window.getComputedStyle(div).transitionProperty;
|
||||
|
@ -94,7 +89,6 @@ test(function() {
|
|||
assert_equals(div.getAnimationPlayers().length, 0,
|
||||
'getAnimationPlayers returns an empty sequence for a transition'
|
||||
+ ' of an unsupported property');
|
||||
div.remove();
|
||||
}, 'getAnimationPlayers for transition on unsupported property');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
testcommon.js
|
||||
|
||||
[animation-timeline/test_animation-timeline.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-animations/test_animations-dynamic-changes.html]
|
||||
[css-animations/test_animation-effect-name.html]
|
||||
[css-animations/test_animation-pausing.html]
|
||||
[css-animations/test_animation-player-playstate.html]
|
||||
[css-animations/test_animation-player-ready.html]
|
||||
[css-animations/test_animation-target.html]
|
||||
[css-animations/test_element-get-animation-players.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[css-transitions/test_animation-effect-name.html]
|
||||
[css-transitions/test_animation-pausing.html]
|
||||
[css-transitions/test_animation-player-ready.html]
|
||||
[css-transitions/test_animation-target.html]
|
||||
[css-transitions/test_element-get-animation-players.html]
|
||||
skip-if = buildapp == 'mulet'
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Appends a div to the document body.
|
||||
*
|
||||
* @param t The testharness.js Test object. If provided, this will be used
|
||||
* to register a cleanup callback to remove the div when the test
|
||||
* finishes.
|
||||
*/
|
||||
function addDiv(t) {
|
||||
var div = document.createElement('div');
|
||||
document.body.appendChild(div);
|
||||
if (t && typeof t.add_cleanup === 'function') {
|
||||
t.add_cleanup(function() { div.remove(); });
|
||||
}
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise wrapper for requestAnimationFrame.
|
||||
*/
|
||||
function waitForFrame() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
window.requestAnimationFrame(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper that takes a sequence of N players and returns:
|
||||
*
|
||||
* Promise.all([players[0].ready, players[1].ready, ... players[N-1].ready]);
|
||||
*/
|
||||
function waitForAllPlayers(players) {
|
||||
return Promise.all(players.map(function(player) { return player.ready; }));
|
||||
}
|
|
@ -505,7 +505,16 @@ this.DOMApplicationRegistry = {
|
|||
// Installs a 3rd party app.
|
||||
installPreinstalledApp: function installPreinstalledApp(aId) {
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
let app = this.webapps[aId];
|
||||
// In some cases, the app might be already installed under a different ID but
|
||||
// with the same manifestURL. In that case, the only content of the webapp will
|
||||
// be the id of the old version, which is the one we'll keep.
|
||||
let destId = this.webapps[aId].oldId || aId;
|
||||
// We don't need the oldId anymore
|
||||
if (destId !== aId) {
|
||||
delete this.webapps[aId];
|
||||
}
|
||||
|
||||
let app = this.webapps[destId];
|
||||
let baseDir, isPreinstalled = false;
|
||||
try {
|
||||
baseDir = FileUtils.getDir("coreAppsDir", ["webapps", aId], false);
|
||||
|
@ -545,10 +554,10 @@ this.DOMApplicationRegistry = {
|
|||
}
|
||||
|
||||
debug("Installing 3rd party app : " + aId +
|
||||
" from " + baseDir.path);
|
||||
" from " + baseDir.path + " to " + destId);
|
||||
|
||||
// We copy this app to DIRECTORY_NAME/$aId, and set the base path as needed.
|
||||
let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true);
|
||||
// We copy this app to DIRECTORY_NAME/$destId, and set the base path as needed.
|
||||
let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", destId], true, true);
|
||||
|
||||
filesToMove.forEach(function(aFile) {
|
||||
let file = baseDir.clone();
|
||||
|
@ -568,7 +577,7 @@ this.DOMApplicationRegistry = {
|
|||
return isPreinstalled;
|
||||
}
|
||||
|
||||
app.origin = "app://" + aId;
|
||||
app.origin = "app://" + destId;
|
||||
|
||||
// Do this for all preinstalled apps... we can't know at this
|
||||
// point if the updates will be signed or not and it doesn't
|
||||
|
@ -593,7 +602,7 @@ this.DOMApplicationRegistry = {
|
|||
// If we are unable to extract the manifest, cleanup and remove this app.
|
||||
debug("Cleaning up: " + e);
|
||||
destDir.remove(true);
|
||||
delete this.webapps[aId];
|
||||
delete this.webapps[destId];
|
||||
} finally {
|
||||
zipReader.close();
|
||||
}
|
||||
|
@ -667,7 +676,13 @@ this.DOMApplicationRegistry = {
|
|||
for (let id in data) {
|
||||
// Core apps have ids matching their domain name (eg: dialer.gaiamobile.org)
|
||||
// Use that property to check if they are new or not.
|
||||
if (!(id in this.webapps)) {
|
||||
// Note that in some cases, the id might change, but the
|
||||
// manifest URL wont. So consider that the app is old if
|
||||
// the id does not exist already and if there's no other id
|
||||
// for the manifestURL.
|
||||
var oldId = (id in this.webapps) ? id :
|
||||
this._appIdForManifestURL(data[id].manifestURL);
|
||||
if (!oldId) {
|
||||
this.webapps[id] = data[id];
|
||||
this.webapps[id].basePath = appDir.path;
|
||||
|
||||
|
@ -688,11 +703,17 @@ this.DOMApplicationRegistry = {
|
|||
// we fall into this case if the app is present in /system/b2g/webapps/webapps.json
|
||||
// and in /data/local/webapps/webapps.json: this happens when updating gaia apps
|
||||
// Confere bug 989876
|
||||
// We also should fall in this case when the app is a preinstalled third party app.
|
||||
for (let field in data[id]) {
|
||||
if (fieldsBlacklist.indexOf(field) === -1) {
|
||||
this.webapps[id][field] = data[id][field];
|
||||
this.webapps[oldId][field] = data[id][field];
|
||||
}
|
||||
}
|
||||
// If the id for the app has changed on the update, keep a pointer to the old one
|
||||
// since we'll need this to update the app files.
|
||||
if (id !== oldId) {
|
||||
this.webapps[id] = {oldId: oldId};
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
|
|
|
@ -569,8 +569,6 @@ AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
|
|||
AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
|
||||
: ScriptSettingsStackEntry()
|
||||
{
|
||||
MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(),
|
||||
!JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread()));
|
||||
if (aIsMainThread) {
|
||||
mCxPusher.emplace(static_cast<JSContext*>(nullptr),
|
||||
/* aAllowNull = */ true);
|
||||
|
|
|
@ -375,7 +375,7 @@ nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx,
|
|||
new DisplayPortPropertyData(displayport, aPriority),
|
||||
nsINode::DeleteProperty<DisplayPortPropertyData>);
|
||||
|
||||
if (nsLayoutUtils::UsesAsyncScrolling()) {
|
||||
if (nsLayoutUtils::UsesAsyncScrolling() && gfxPrefs::LayoutUseContainersForRootFrames()) {
|
||||
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
|
||||
if (rootScrollFrame && content == rootScrollFrame->GetContent()) {
|
||||
// We are setting a root displayport for a document.
|
||||
|
|
|
@ -1995,6 +1995,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStateObjectCached)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUndoManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAnimationTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPlayerTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegistry)
|
||||
|
@ -2078,6 +2079,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
|
|||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCachedEncoder)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mUndoManager)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnimationTimeline)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPlayerTracker)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateContentsOwner)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildrenCollection)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mRegistry)
|
||||
|
@ -5266,7 +5268,7 @@ nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
|
|||
ErrorResult& aRv)
|
||||
{
|
||||
nsIPresShell* shell = GetShell();
|
||||
if (!shell) {
|
||||
if (!shell || !shell->GetCanvasFrame()) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
|
@ -7391,6 +7393,16 @@ nsDocument::GetAnimationController()
|
|||
return mAnimationController;
|
||||
}
|
||||
|
||||
PendingPlayerTracker*
|
||||
nsDocument::GetOrCreatePendingPlayerTracker()
|
||||
{
|
||||
if (!mPendingPlayerTracker) {
|
||||
mPendingPlayerTracker = new PendingPlayerTracker();
|
||||
}
|
||||
|
||||
return mPendingPlayerTracker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the "direction" property of the document.
|
||||
*
|
||||
|
|
|
@ -59,6 +59,7 @@
|
|||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/EventStates.h"
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include "mozilla/PendingPlayerTracker.h"
|
||||
#include "mozilla/dom/DOMImplementation.h"
|
||||
#include "mozilla/dom/StyleSheetList.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
@ -1047,6 +1048,15 @@ public:
|
|||
// If HasAnimationController is true, this is guaranteed to return non-null.
|
||||
nsSMILAnimationController* GetAnimationController() MOZ_OVERRIDE;
|
||||
|
||||
virtual mozilla::PendingPlayerTracker*
|
||||
GetPendingPlayerTracker() MOZ_FINAL
|
||||
{
|
||||
return mPendingPlayerTracker;
|
||||
}
|
||||
|
||||
virtual mozilla::PendingPlayerTracker*
|
||||
GetOrCreatePendingPlayerTracker() MOZ_OVERRIDE;
|
||||
|
||||
void SetImagesNeedAnimating(bool aAnimating) MOZ_OVERRIDE;
|
||||
|
||||
virtual void SuppressEventHandling(SuppressionType aWhat,
|
||||
|
@ -1511,6 +1521,10 @@ protected:
|
|||
// Array of observers
|
||||
nsTObserverArray<nsIDocumentObserver*> mObservers;
|
||||
|
||||
// Tracker for animation players that are waiting to start.
|
||||
// nullptr until GetOrCreatePendingPlayerTracker is called.
|
||||
nsRefPtr<mozilla::PendingPlayerTracker> mPendingPlayerTracker;
|
||||
|
||||
// Weak reference to the scope object (aka the script global object)
|
||||
// that, unlike mScriptGlobalObject, is never unset once set. This
|
||||
// is a weak reference to avoid leaks due to circular references.
|
||||
|
|
|
@ -86,6 +86,7 @@ namespace mozilla {
|
|||
class CSSStyleSheet;
|
||||
class ErrorResult;
|
||||
class EventStates;
|
||||
class PendingPlayerTracker;
|
||||
class SVGAttrAnimationRuleProcessor;
|
||||
|
||||
namespace css {
|
||||
|
@ -1822,6 +1823,17 @@ public:
|
|||
// mAnimationController isn't yet initialized.
|
||||
virtual nsSMILAnimationController* GetAnimationController() = 0;
|
||||
|
||||
// Gets the tracker for animation players that are waiting to start.
|
||||
// Returns nullptr if there is no pending player tracker for this document
|
||||
// which will be the case if there have never been any CSS animations or
|
||||
// transitions on elements in the document.
|
||||
virtual mozilla::PendingPlayerTracker* GetPendingPlayerTracker() = 0;
|
||||
|
||||
// Gets the tracker for animation players that are waiting to start and
|
||||
// creates it if it doesn't already exist. As a result, the return value
|
||||
// will never be nullptr.
|
||||
virtual mozilla::PendingPlayerTracker* GetOrCreatePendingPlayerTracker() = 0;
|
||||
|
||||
// Makes the images on this document capable of having their animation
|
||||
// active or suspended. An Image will animate as long as at least one of its
|
||||
// owning Documents needs it to animate; otherwise it can suspend.
|
||||
|
|
|
@ -20,10 +20,11 @@ using namespace mozilla;
|
|||
CameraControlImpl::CameraControlImpl()
|
||||
: mListenerLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"))
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mHardwareState(CameraControlListener::kHardwareClosed)
|
||||
, mHardwareState(CameraControlListener::kHardwareUninitialized)
|
||||
, mHardwareStateChangeReason(NS_OK)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
mCurrentConfiguration.mMode = ICameraControl::kUnspecifiedMode;
|
||||
|
||||
// reuse the same camera thread to conserve resources
|
||||
nsCOMPtr<nsIThread> ct = do_QueryInterface(sCameraThread);
|
||||
|
@ -79,7 +80,7 @@ CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aN
|
|||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
const char* state[] = { "closed", "open", "failed" };
|
||||
const char* state[] = { "uninitialized", "closed", "open", "failed" };
|
||||
MOZ_ASSERT(aNewState >= 0);
|
||||
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
|
||||
DOM_CAMERA_LOGI("New hardware state is '%s' (reason=0x%x)\n",
|
||||
|
|
|
@ -34,6 +34,7 @@ public:
|
|||
|
||||
enum HardwareState
|
||||
{
|
||||
kHardwareUninitialized,
|
||||
kHardwareClosed,
|
||||
kHardwareOpen,
|
||||
kHardwareOpenFailed
|
||||
|
|
|
@ -202,6 +202,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
|||
, mGetCameraPromise(aPromise)
|
||||
, mWindow(aWindow)
|
||||
, mPreviewState(CameraControlListener::kPreviewStopped)
|
||||
, mSetInitialConfig(false)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
|
||||
mInput = new CameraPreviewMediaStream(this);
|
||||
|
@ -236,8 +237,14 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
|||
}
|
||||
|
||||
if (haveInitialConfig) {
|
||||
config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth;
|
||||
config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
|
||||
rv = SelectPreviewSize(aInitialConfig.mPreviewSize, config.mPreviewSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
mListener->OnUserError(DOMCameraControlListener::kInStartCamera, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
config.mPictureSize.width = aInitialConfig.mPictureSize.mWidth;
|
||||
config.mPictureSize.height = aInitialConfig.mPictureSize.mHeight;
|
||||
config.mRecorderProfile = aInitialConfig.mRecorderProfile;
|
||||
}
|
||||
|
||||
|
@ -274,6 +281,9 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
|||
// Start the camera...
|
||||
if (haveInitialConfig) {
|
||||
rv = mCameraControl->Start(&config);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSetInitialConfig = true;
|
||||
}
|
||||
} else {
|
||||
rv = mCameraControl->Start();
|
||||
}
|
||||
|
@ -281,6 +291,9 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
|
|||
} else {
|
||||
if (haveInitialConfig) {
|
||||
rv = mCameraControl->SetConfiguration(config);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
mSetInitialConfig = true;
|
||||
}
|
||||
} else {
|
||||
rv = NS_OK;
|
||||
}
|
||||
|
@ -308,6 +321,46 @@ nsDOMCameraControl::IsWindowStillActive()
|
|||
return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID());
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDOMCameraControl::SelectPreviewSize(const CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize)
|
||||
{
|
||||
if (aRequestedPreviewSize.mWidth && aRequestedPreviewSize.mHeight) {
|
||||
aSelectedPreviewSize.width = aRequestedPreviewSize.mWidth;
|
||||
aSelectedPreviewSize.height = aRequestedPreviewSize.mHeight;
|
||||
} else {
|
||||
/* Use the window width and height if no preview size is provided.
|
||||
Note that the width and height are actually reversed from the
|
||||
camera perspective. */
|
||||
int32_t width = 0;
|
||||
int32_t height = 0;
|
||||
float ratio = 0.0;
|
||||
nsresult rv;
|
||||
|
||||
rv = mWindow->GetDevicePixelRatio(&ratio);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mWindow->GetInnerWidth(&height);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = mWindow->GetInnerHeight(&width);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(width > 0);
|
||||
MOZ_ASSERT(height > 0);
|
||||
MOZ_ASSERT(ratio > 0.0);
|
||||
aSelectedPreviewSize.width = std::ceil(width * ratio);
|
||||
aSelectedPreviewSize.height = std::ceil(height * ratio);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Setter for weighted regions: { top, bottom, left, right, weight }
|
||||
nsresult
|
||||
nsDOMCameraControl::Set(uint32_t aKey, const Optional<Sequence<CameraRegion> >& aValue, uint32_t aLimit)
|
||||
|
@ -801,9 +854,14 @@ nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration,
|
|||
}
|
||||
|
||||
ICameraControl::Configuration config;
|
||||
aRv = SelectPreviewSize(aConfiguration.mPreviewSize, config.mPreviewSize);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
config.mRecorderProfile = aConfiguration.mRecorderProfile;
|
||||
config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth;
|
||||
config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight;
|
||||
config.mPictureSize.width = aConfiguration.mPictureSize.mWidth;
|
||||
config.mPictureSize.height = aConfiguration.mPictureSize.mHeight;
|
||||
config.mMode = ICameraControl::kPictureMode;
|
||||
if (aConfiguration.mMode == CameraMode::Video) {
|
||||
config.mMode = ICameraControl::kVideoMode;
|
||||
|
@ -1041,6 +1099,20 @@ nsDOMCameraControl::DispatchStateEvent(const nsString& aType, const nsString& aS
|
|||
DispatchTrustedEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
nsDOMCameraControl::OnGetCameraComplete()
|
||||
{
|
||||
// The hardware is open, so we can return a camera to JS, even if
|
||||
// the preview hasn't started yet.
|
||||
nsRefPtr<Promise> promise = mGetCameraPromise.forget();
|
||||
if (promise) {
|
||||
CameraGetPromiseData data;
|
||||
data.mCamera = this;
|
||||
data.mConfiguration = *mCurrentConfiguration;
|
||||
promise->MaybeResolve(data);
|
||||
}
|
||||
}
|
||||
|
||||
// Camera Control event handlers--must only be called from the Main Thread!
|
||||
void
|
||||
nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState,
|
||||
|
@ -1055,22 +1127,16 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
|||
case CameraControlListener::kHardwareOpen:
|
||||
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n");
|
||||
MOZ_ASSERT(aReason == NS_OK);
|
||||
{
|
||||
if (!mSetInitialConfig) {
|
||||
// The hardware is open, so we can return a camera to JS, even if
|
||||
// the preview hasn't started yet.
|
||||
nsRefPtr<Promise> promise = mGetCameraPromise.forget();
|
||||
if (promise) {
|
||||
CameraGetPromiseData data;
|
||||
data.mCamera = this;
|
||||
data.mConfiguration = *mCurrentConfiguration;
|
||||
promise->MaybeResolve(data);
|
||||
}
|
||||
OnGetCameraComplete();
|
||||
}
|
||||
break;
|
||||
|
||||
case CameraControlListener::kHardwareClosed:
|
||||
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
|
||||
{
|
||||
if (!mSetInitialConfig) {
|
||||
nsRefPtr<Promise> promise = mReleasePromise.forget();
|
||||
if (promise) {
|
||||
promise->MaybeResolve(JS::UndefinedHandleValue);
|
||||
|
@ -1102,6 +1168,9 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
|||
NS_LITERAL_STRING("close"),
|
||||
eventInit);
|
||||
DispatchTrustedEvent(event);
|
||||
} else {
|
||||
// The configuration failed and we forced the camera to shutdown.
|
||||
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -1111,6 +1180,9 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
|
|||
OnUserError(DOMCameraControlListener::kInStartCamera, NS_ERROR_NOT_AVAILABLE);
|
||||
break;
|
||||
|
||||
case CameraControlListener::kHardwareUninitialized:
|
||||
break;
|
||||
|
||||
default:
|
||||
DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState);
|
||||
MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state");
|
||||
|
@ -1227,9 +1299,17 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
|
|||
mCurrentConfiguration->mMaxMeteringAreas);
|
||||
DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n",
|
||||
mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight);
|
||||
DOM_CAMERA_LOGI(" picture size (w x h) : %d x %d\n",
|
||||
mCurrentConfiguration->mPictureSize.mWidth, mCurrentConfiguration->mPictureSize.mHeight);
|
||||
DOM_CAMERA_LOGI(" recorder profile : %s\n",
|
||||
NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get());
|
||||
|
||||
if (mSetInitialConfig) {
|
||||
OnGetCameraComplete();
|
||||
mSetInitialConfig = false;
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<Promise> promise = mSetConfigurationPromise.forget();
|
||||
if (promise) {
|
||||
promise->MaybeResolve(*aConfiguration);
|
||||
|
@ -1241,6 +1321,9 @@ nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration
|
|||
eventInit.mPreviewSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
|
||||
mCurrentConfiguration->mPreviewSize.mWidth,
|
||||
mCurrentConfiguration->mPreviewSize.mHeight);
|
||||
eventInit.mPictureSize = new DOMRect(static_cast<DOMMediaStream*>(this), 0, 0,
|
||||
mCurrentConfiguration->mPictureSize.mWidth,
|
||||
mCurrentConfiguration->mPictureSize.mHeight);
|
||||
|
||||
nsRefPtr<CameraConfigurationEvent> event =
|
||||
CameraConfigurationEvent::Constructor(this,
|
||||
|
@ -1359,6 +1442,16 @@ nsDOMCameraControl::OnUserError(CameraControlListener::UserContext aContext, nsr
|
|||
break;
|
||||
|
||||
case CameraControlListener::kInSetConfiguration:
|
||||
if (mSetInitialConfig) {
|
||||
// If the SetConfiguration() call in the constructor fails, there
|
||||
// is nothing we can do except release the camera hardware. This
|
||||
// will trigger a hardware state change, and when the flag that
|
||||
// got us here is set in that handler, we replace the normal reason
|
||||
// code with one that indicates the hardware isn't available.
|
||||
DOM_CAMERA_LOGI("Failed to configure cached camera, stopping\n");
|
||||
mCameraControl->Stop();
|
||||
return;
|
||||
}
|
||||
promise = mSetConfigurationPromise.forget();
|
||||
break;
|
||||
|
||||
|
|
|
@ -171,6 +171,7 @@ protected:
|
|||
void OnTakePictureComplete(nsIDOMBlob* aPicture);
|
||||
void OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces);
|
||||
|
||||
void OnGetCameraComplete();
|
||||
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState, nsresult aReason);
|
||||
void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState);
|
||||
void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum);
|
||||
|
@ -179,6 +180,7 @@ protected:
|
|||
void OnUserError(CameraControlListener::UserContext aContext, nsresult aError);
|
||||
|
||||
bool IsWindowStillActive();
|
||||
nsresult SelectPreviewSize(const dom::CameraSize& aRequestedPreviewSize, ICameraControl::Size& aSelectedPreviewSize);
|
||||
|
||||
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
|
||||
|
||||
|
@ -222,6 +224,8 @@ protected:
|
|||
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
|
||||
DOMCameraControlListener::PreviewState mPreviewState;
|
||||
|
||||
bool mSetInitialConfig;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
// cached camera control, to improve start-up time
|
||||
static StaticRefPtr<ICameraControl> sCachedCameraControl;
|
||||
|
|
|
@ -215,6 +215,8 @@ DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguratio
|
|||
config->mRecorderProfile = mConfiguration.mRecorderProfile;
|
||||
config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
|
||||
config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
|
||||
config->mPictureSize.mWidth = mConfiguration.mPictureSize.width;
|
||||
config->mPictureSize.mHeight = mConfiguration.mPictureSize.height;
|
||||
config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
|
||||
config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ using namespace android;
|
|||
// Construct nsGonkCameraControl on the main thread.
|
||||
nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
|
||||
: mCameraId(aCameraId)
|
||||
, mLastPictureSize({0, 0})
|
||||
, mLastThumbnailSize({0, 0})
|
||||
, mPreviewFps(30)
|
||||
, mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102
|
||||
|
@ -136,10 +135,16 @@ nsGonkCameraControl::StartInternal(const Configuration* aInitialConfig)
|
|||
}
|
||||
|
||||
OnHardwareStateChange(CameraControlListener::kHardwareOpen, NS_OK);
|
||||
if (aInitialConfig) {
|
||||
return StartPreviewImpl();
|
||||
}
|
||||
|
||||
if (aInitialConfig) {
|
||||
rv = StartPreviewInternal();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
OnConfigurationChange();
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -180,7 +185,7 @@ nsGonkCameraControl::Initialize()
|
|||
mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
|
||||
mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0;
|
||||
|
||||
mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize);
|
||||
mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mCurrentConfiguration.mPictureSize);
|
||||
mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize);
|
||||
|
||||
nsString luminance; // check for support
|
||||
|
@ -197,7 +202,7 @@ nsGonkCameraControl::Initialize()
|
|||
DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas);
|
||||
DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas);
|
||||
DOM_CAMERA_LOGI(" - default picture size: %u x %u\n",
|
||||
mLastPictureSize.width, mLastPictureSize.height);
|
||||
mCurrentConfiguration.mPictureSize.width, mCurrentConfiguration.mPictureSize.height);
|
||||
DOM_CAMERA_LOGI(" - default picture file format: %s\n",
|
||||
NS_ConvertUTF16toUTF8(mFileFormat).get());
|
||||
DOM_CAMERA_LOGI(" - default picture quality: %f\n", quality);
|
||||
|
@ -222,6 +227,11 @@ nsGonkCameraControl::Initialize()
|
|||
mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize);
|
||||
DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n",
|
||||
mLastRecorderSize.width, mLastRecorderSize.height);
|
||||
|
||||
Size preferred;
|
||||
mParams.Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
DOM_CAMERA_LOGI(" - preferred video preview size: %u x %u\n",
|
||||
preferred.width, preferred.height);
|
||||
} else {
|
||||
mLastRecorderSize = mCurrentConfiguration.mPreviewSize;
|
||||
}
|
||||
|
@ -261,23 +271,66 @@ nsGonkCameraControl::~nsGonkCameraControl()
|
|||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::ValidateConfiguration(const Configuration& aConfig, Configuration& aValidatedConfig)
|
||||
{
|
||||
nsAutoTArray<Size, 16> supportedSizes;
|
||||
Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes);
|
||||
|
||||
nsresult rv = GetSupportedSize(aConfig.mPictureSize, supportedSizes,
|
||||
aValidatedConfig.mPictureSize);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n",
|
||||
aConfig.mPictureSize.width, aConfig.mPictureSize.height);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
rv = LoadRecorderProfiles();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsString profileName = aConfig.mRecorderProfile;
|
||||
if (profileName.IsEmpty()) {
|
||||
profileName.AssignASCII("default");
|
||||
}
|
||||
|
||||
RecorderProfile* profile;
|
||||
if (!mRecorderProfiles.Get(profileName, &profile)) {
|
||||
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
|
||||
NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get());
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
aValidatedConfig.mMode = aConfig.mMode;
|
||||
aValidatedConfig.mPreviewSize = aConfig.mPreviewSize;
|
||||
aValidatedConfig.mRecorderProfile = profile->GetName();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
|
||||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
nsresult rv;
|
||||
// Ensure sanity of all provided parameters and determine defaults if
|
||||
// none are provided when given a new configuration
|
||||
Configuration config;
|
||||
nsresult rv = ValidateConfiguration(aConfig, config);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
{
|
||||
ICameraControlParameterSetAutoEnter set(this);
|
||||
|
||||
switch (aConfig.mMode) {
|
||||
switch (config.mMode) {
|
||||
case kPictureMode:
|
||||
rv = SetPictureConfiguration(aConfig);
|
||||
rv = SetPictureConfiguration(config);
|
||||
break;
|
||||
|
||||
case kVideoMode:
|
||||
rv = SetVideoConfiguration(aConfig);
|
||||
rv = SetVideoConfiguration(config);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -291,16 +344,15 @@ nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig)
|
|||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_RECORDINGHINT, aConfig.mMode == kVideoMode);
|
||||
rv = Set(CAMERA_PARAM_RECORDINGHINT, config.mMode == kVideoMode);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set recording hint (0x%x)\n", rv);
|
||||
}
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mMode = aConfig.mMode;
|
||||
mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
|
||||
|
||||
OnConfigurationChange();
|
||||
mCurrentConfiguration.mMode = config.mMode;
|
||||
mCurrentConfiguration.mRecorderProfile = config.mRecorderProfile;
|
||||
mCurrentConfiguration.mPictureSize = config.mPictureSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -335,7 +387,18 @@ nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
|
|||
|
||||
// Restart the preview
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
return StartPreviewImpl();
|
||||
rv = StartPreviewInternal();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
StopPreviewImpl();
|
||||
return rv;
|
||||
}
|
||||
|
||||
// OnConfigurationChange() indicates the success case of this operation.
|
||||
// It must not be fired until all intermediate steps, including starting
|
||||
// the preview, have completed successfully.
|
||||
OnConfigurationChange();
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -412,39 +475,29 @@ nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
|
|||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
|
||||
nsTArray<Size> sizes;
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
Size max({0, 0});
|
||||
nsresult rv = SelectCaptureAndPreviewSize(aConfig.mPreviewSize,
|
||||
aConfig.mPictureSize, max,
|
||||
CAMERA_PARAM_PICTURE_SIZE);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preview;
|
||||
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE(
|
||||
"Failed to find a supported preview size, requested size %ux%u (0x%x)",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set supported preview size %ux%u (0x%x)",
|
||||
preview.width, preview.height, rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = preview;
|
||||
|
||||
if (mSeparateVideoAndPreviewSizesSupported) {
|
||||
MaybeAdjustVideoSize();
|
||||
}
|
||||
|
||||
rv = UpdateThumbnailSize();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps);
|
||||
|
||||
DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height,
|
||||
preview.width, preview.height,
|
||||
mCurrentConfiguration.mPreviewSize.width,
|
||||
mCurrentConfiguration.mPreviewSize.height,
|
||||
mPreviewFps);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -683,7 +736,7 @@ nsGonkCameraControl::SetLocation(const Position& aLocation)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StartPreviewImpl()
|
||||
nsGonkCameraControl::StartPreviewInternal()
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
RETURN_IF_NO_CAMERA_HW();
|
||||
|
@ -702,10 +755,19 @@ nsGonkCameraControl::StartPreviewImpl()
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StartPreviewImpl()
|
||||
{
|
||||
nsresult rv = StartPreviewInternal();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
OnPreviewStateChange(CameraControlListener::kPreviewStarted);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::StopPreviewImpl()
|
||||
{
|
||||
|
@ -814,8 +876,8 @@ nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize)
|
|||
|
||||
if (area != 0 &&
|
||||
delta < smallestDelta &&
|
||||
supportedSizes[i].width * mLastPictureSize.height ==
|
||||
mLastPictureSize.width * supportedSizes[i].height) {
|
||||
supportedSizes[i].width * mCurrentConfiguration.mPictureSize.height ==
|
||||
mCurrentConfiguration.mPictureSize.width * supportedSizes[i].height) {
|
||||
smallestDelta = delta;
|
||||
smallestDeltaIndex = i;
|
||||
}
|
||||
|
@ -898,7 +960,8 @@ nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
|
|||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) {
|
||||
if (aSize.width == mCurrentConfiguration.mPictureSize.width &&
|
||||
aSize.height == mCurrentConfiguration.mPictureSize.height) {
|
||||
DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height);
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -926,7 +989,7 @@ nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize)
|
|||
return rv;
|
||||
}
|
||||
|
||||
mLastPictureSize = best;
|
||||
mCurrentConfiguration.mPictureSize = best;
|
||||
|
||||
// Finally, update the thumbnail size in case the picture aspect ratio changed.
|
||||
// Some drivers will fail to take a picture if the thumbnail size is not the
|
||||
|
@ -1377,6 +1440,11 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
|
|||
uint32_t minSizeDelta = UINT32_MAX;
|
||||
uint32_t delta;
|
||||
|
||||
if (aSupportedSizes.IsEmpty()) {
|
||||
// no valid sizes
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!aSize.width && !aSize.height) {
|
||||
// no size specified, take the first supported size
|
||||
best = aSupportedSizes[0];
|
||||
|
@ -1432,91 +1500,41 @@ nsGonkCameraControl::GetSupportedSize(const Size& aSize,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize)
|
||||
nsGonkCameraControl::SelectCaptureAndPreviewSize(const Size& aPreviewSize,
|
||||
const Size& aCaptureSize,
|
||||
const Size& aMaxSize,
|
||||
uint32_t aCaptureSizeKey)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
|
||||
|
||||
DOM_CAMERA_LOGI("Setting video size to %ux%u, preview size to %ux%u\n",
|
||||
aVideoSize.width, aVideoSize.height,
|
||||
aPreviewSize.width, aPreviewSize.height);
|
||||
// At this point, we know the capture size has been validated and replaced
|
||||
// if necessary with the best matching supported value.
|
||||
DOM_CAMERA_LOGI("Select capture size %ux%u, preview size %ux%u, maximum size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height,
|
||||
aPreviewSize.width, aPreviewSize.height,
|
||||
aMaxSize.width, aMaxSize.height);
|
||||
|
||||
Size oldSize;
|
||||
nsresult rv = Get(CAMERA_PARAM_PREVIEWSIZE, oldSize);
|
||||
nsAutoTArray<Size, 16> sizes;
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, aPreviewSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = Set(CAMERA_PARAM_VIDEOSIZE, aVideoSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Set(CAMERA_PARAM_VIDEOSIZE, oldSize); // error, try to restore the original preview size
|
||||
return rv;
|
||||
// May optionally apply a ceiling to the preview size. Any supported preview
|
||||
// size with an area larger than the maximum will be ignored regardless of
|
||||
// aspect ratio or delta to requested preview size.
|
||||
uint32_t maxArea = aMaxSize.width * aMaxSize.height;
|
||||
if (maxArea == 0) {
|
||||
maxArea = UINT32_MAX;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = aPreviewSize;
|
||||
mLastRecorderSize = aVideoSize;
|
||||
const uint32_t previewArea = aPreviewSize.width * aPreviewSize.height;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize)
|
||||
{
|
||||
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
|
||||
MOZ_ASSERT(mSeparateVideoAndPreviewSizesSupported);
|
||||
|
||||
nsTArray<Size> sizes;
|
||||
|
||||
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size video;
|
||||
rv = GetSupportedSize(aVideoSize, sizes, video);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %ux%u",
|
||||
aVideoSize.width, aVideoSize.height);
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, sizes);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preview;
|
||||
rv = GetSupportedSize(aConfig.mPreviewSize, sizes, preview);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %ux%u",
|
||||
aConfig.mPreviewSize.width, aConfig.mPreviewSize.height);
|
||||
return rv;
|
||||
}
|
||||
|
||||
Size preferred;
|
||||
rv = Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// If the requested preview size has the same aspect ratio as the
|
||||
// requested video size, *and* is the same size or smaller than
|
||||
// the preferred video size, then we're done.
|
||||
const uint32_t preferredArea = preferred.width * preferred.height;
|
||||
if (video.width * aConfig.mPreviewSize.height == aConfig.mPreviewSize.width * video.height &&
|
||||
preview.width * preview.height <= preferredArea) {
|
||||
// We're done: set the video and preview sizes and return...
|
||||
return SetVideoAndPreviewSize(preview, video);
|
||||
}
|
||||
|
||||
// Otherwise, if the requested preview size is larger than the preferred
|
||||
// size, or there is an aspect ratio mismatch, then we need to set the
|
||||
// preview size to the closest size smaller than the preferred size,
|
||||
// preferably with the same aspect ratio as the requested video size.
|
||||
// We should select a preview size with the same aspect ratio as the capture
|
||||
// size and minimize the delta with the requested preview size. If we are
|
||||
// unable to find any supported preview sizes which match the aspect ratio
|
||||
// of the capture size, we fallback to only minimizing the delta with the
|
||||
// requested preview size.
|
||||
|
||||
SizeIndex bestSizeMatch = 0; // initializers to keep warnings away
|
||||
SizeIndex bestSizeMatchWithAspectRatio = 0;
|
||||
|
@ -1528,13 +1546,19 @@ nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, con
|
|||
|
||||
for (SizeIndex i = 0; i < sizes.Length(); ++i) {
|
||||
const Size& s = sizes[i];
|
||||
const uint32_t area = s.width * s.height;
|
||||
if (area > preferredArea) {
|
||||
|
||||
// preview size must be smaller or equal to the capture size
|
||||
if (aCaptureSize.width < s.width || aCaptureSize.height < s.height) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t delta = preferredArea - area;
|
||||
if (s.width * video.height == video.width * s.height) {
|
||||
const uint32_t area = s.width * s.height;
|
||||
if (area > maxArea) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t delta = abs(static_cast<long int>(previewArea - area));
|
||||
if (s.width * aCaptureSize.height == aCaptureSize.width * s.height) {
|
||||
if (delta == 0) {
|
||||
// exact match, including aspect ratio--we can stop now
|
||||
bestSizeMatchWithAspectRatio = i;
|
||||
|
@ -1553,19 +1577,41 @@ nsGonkCameraControl::SelectVideoAndPreviewSize(const Configuration& aConfig, con
|
|||
}
|
||||
}
|
||||
|
||||
Size previewSize;
|
||||
if (foundSizeMatchWithAspectRatio) {
|
||||
preview = sizes[bestSizeMatchWithAspectRatio];
|
||||
previewSize = sizes[bestSizeMatchWithAspectRatio];
|
||||
} else if (foundSizeMatch) {
|
||||
DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of video size %ux%u\n",
|
||||
video.width, video.height);
|
||||
preview = sizes[bestSizeMatch];
|
||||
DOM_CAMERA_LOGW("Unable to match a preview size with aspect ratio of capture size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height);
|
||||
previewSize = sizes[bestSizeMatch];
|
||||
} else {
|
||||
DOM_CAMERA_LOGE("Unable to find a preview size for video size %ux%u\n",
|
||||
video.width, video.height);
|
||||
DOM_CAMERA_LOGE("Unable to find a preview size for capture size %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height);
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return SetVideoAndPreviewSize(preview, video);
|
||||
DOM_CAMERA_LOGI("Setting capture size to %ux%u, preview size to %ux%u\n",
|
||||
aCaptureSize.width, aCaptureSize.height,
|
||||
previewSize.width, previewSize.height);
|
||||
|
||||
Size oldSize;
|
||||
rv = Get(CAMERA_PARAM_PREVIEWSIZE, oldSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWSIZE, previewSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
rv = Set(aCaptureSizeKey, aCaptureSize);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Set(CAMERA_PARAM_PREVIEWSIZE, oldSize); // error, try to restore the original preview size
|
||||
return rv;
|
||||
}
|
||||
|
||||
mCurrentConfiguration.mPreviewSize = previewSize;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -1573,13 +1619,6 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
|||
{
|
||||
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
|
||||
|
||||
// The application may cache an old configuration and already have
|
||||
// a desired recorder profile without checking the capabilities first
|
||||
nsresult rv = LoadRecorderProfiles();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
RecorderProfile* profile;
|
||||
if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) {
|
||||
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
|
||||
|
@ -1605,7 +1644,14 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
|||
if (mSeparateVideoAndPreviewSizesSupported) {
|
||||
// The camera supports two video streams: a low(er) resolution preview
|
||||
// stream and and a potentially high(er) resolution stream for encoding.
|
||||
rv = SelectVideoAndPreviewSize(aConfig, size);
|
||||
Size preferred;
|
||||
rv = Get(CAMERA_PARAM_PREFERRED_PREVIEWSIZE_FOR_VIDEO, preferred);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = SelectCaptureAndPreviewSize(aConfig.mPreviewSize, size, preferred,
|
||||
CAMERA_PARAM_VIDEOSIZE);
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set video and preview sizes (0x%x)\n", rv);
|
||||
return rv;
|
||||
|
@ -1623,6 +1669,8 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
|
|||
mCurrentConfiguration.mPreviewSize = size;
|
||||
}
|
||||
|
||||
mLastRecorderSize = size;
|
||||
|
||||
rv = Set(CAMERA_PARAM_PREVIEWFRAMERATE, static_cast<int>(fps));
|
||||
if (NS_FAILED(rv)) {
|
||||
DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv);
|
||||
|
@ -1903,9 +1951,12 @@ nsGonkCameraControl::LoadRecorderProfiles()
|
|||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsTArray<RecorderProfile>::size_type bestIndexMatch = 0;
|
||||
int bestAreaMatch = 0;
|
||||
|
||||
// Limit profiles to those video sizes supported by the camera hardware...
|
||||
for (nsTArray<RecorderProfile>::size_type i = 0; i < profiles.Length(); ++i) {
|
||||
int width = profiles[i]->GetVideo().GetSize().width;
|
||||
int width = profiles[i]->GetVideo().GetSize().width;
|
||||
int height = profiles[i]->GetVideo().GetSize().height;
|
||||
if (width < 0 || height < 0) {
|
||||
DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n",
|
||||
|
@ -1916,10 +1967,22 @@ nsGonkCameraControl::LoadRecorderProfiles()
|
|||
if (static_cast<uint32_t>(width) == sizes[n].width &&
|
||||
static_cast<uint32_t>(height) == sizes[n].height) {
|
||||
mRecorderProfiles.Put(profiles[i]->GetName(), profiles[i]);
|
||||
int area = width * height;
|
||||
if (area > bestAreaMatch) {
|
||||
bestIndexMatch = i;
|
||||
bestAreaMatch = area;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default profile is the one with the largest area.
|
||||
if (bestAreaMatch > 0) {
|
||||
nsAutoString name;
|
||||
name.AssignASCII("default");
|
||||
mRecorderProfiles.Put(name, profiles[bestIndexMatch]);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
@ -105,10 +105,12 @@ protected:
|
|||
|
||||
nsresult Initialize();
|
||||
|
||||
nsresult ValidateConfiguration(const Configuration& aConfig, Configuration& aValidatedConfig);
|
||||
nsresult SetConfigurationInternal(const Configuration& aConfig);
|
||||
nsresult SetPictureConfiguration(const Configuration& aConfig);
|
||||
nsresult SetVideoConfiguration(const Configuration& aConfig);
|
||||
nsresult StartInternal(const Configuration* aInitialConfig);
|
||||
nsresult StartPreviewInternal();
|
||||
nsresult StopInternal();
|
||||
|
||||
template<class T> nsresult SetAndPush(uint32_t aKey, const T& aValue);
|
||||
|
@ -133,8 +135,8 @@ protected:
|
|||
nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
|
||||
uint64_t aMaxVideoLengthMs);
|
||||
nsresult SetupRecordingFlash(bool aAutoEnableLowLightTorch);
|
||||
nsresult SelectVideoAndPreviewSize(const Configuration& aConfig, const Size& aVideoSize);
|
||||
nsresult SetVideoAndPreviewSize(const Size& aPreviewSize, const Size& aVideoSize);
|
||||
nsresult SelectCaptureAndPreviewSize(const Size& aPreviewSize, const Size& aCaptureSize,
|
||||
const Size& aMaxSize, uint32_t aCaptureSizeKey);
|
||||
nsresult MaybeAdjustVideoSize();
|
||||
nsresult PausePreview();
|
||||
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
|
||||
|
@ -158,7 +160,6 @@ protected:
|
|||
|
||||
android::sp<android::GonkCameraHardware> mCameraHw;
|
||||
|
||||
Size mLastPictureSize;
|
||||
Size mLastThumbnailSize;
|
||||
Size mLastRecorderSize;
|
||||
uint32_t mPreviewFps;
|
||||
|
|
|
@ -145,6 +145,7 @@ public:
|
|||
struct Configuration {
|
||||
Mode mMode;
|
||||
Size mPreviewSize;
|
||||
Size mPictureSize;
|
||||
nsString mRecorderProfile;
|
||||
};
|
||||
|
||||
|
|
|
@ -13,3 +13,5 @@ support-files = camera_common.js
|
|||
[test_bug1022766.html]
|
||||
[test_bug1037322.html]
|
||||
[test_bug1099390.html]
|
||||
[test_bug1104913.html]
|
||||
[test_camera_bad_initial_config.html]
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -57,14 +57,17 @@ var Camera = {
|
|||
ok(cfg.mode === "unspecified", "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === 0 && cfg.previewSize.height === 0,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.recorderProfile === "",
|
||||
ok(cfg.recorderProfile === "default",
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
|
||||
// Apply our specific configuration
|
||||
camera.setConfiguration(config).then(setConfig_onSuccess, onError);
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
|
||||
var cfg = {
|
||||
mode: 'unspecified',
|
||||
};
|
||||
navigator.mozCameras.getCamera(whichCamera, cfg).then(getCamera_onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bug 1104913</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'qvga',
|
||||
pictureSize: {
|
||||
width: 640,
|
||||
height: 480
|
||||
},
|
||||
previewSize: {
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
function onError(e) {
|
||||
ok(false, "Error: " + JSON.stringify(e));
|
||||
}
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
|
||||
start: function test_start() {
|
||||
function getCamera_onSuccess(d) {
|
||||
var camera = d.camera;
|
||||
var cfg = d.configuration;
|
||||
Camera.cameraObj = camera;
|
||||
Camera.viewfinder.mozSrcObject = camera;
|
||||
Camera.viewfinder.play();
|
||||
|
||||
// Check the default configuration
|
||||
ok(cfg.mode === config.mode, "Initial mode = " + cfg.mode);
|
||||
ok(cfg.previewSize.width === config.previewSize.width &&
|
||||
cfg.previewSize.height === config.previewSize.height,
|
||||
"Initial preview size = " + cfg.previewSize.width + "x" + cfg.previewSize.height);
|
||||
ok(cfg.pictureSize.width === config.pictureSize.width &&
|
||||
cfg.pictureSize.height === config.pictureSize.height,
|
||||
"Initial picture size = " + cfg.pictureSize.width + "x" + cfg.pictureSize.height);
|
||||
ok(cfg.recorderProfile === config.recorderProfile,
|
||||
"Initial recorder profile = '" + cfg.recorderProfile + "'");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, {}).then(getCamera_onSuccess, onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.mozSrcObject = null;
|
||||
if (Camera.cameraObj) {
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
}
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -17,10 +17,10 @@ const Cr = Components.results;
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
var options = {
|
||||
|
@ -137,7 +137,7 @@ var tests = [
|
|||
next();
|
||||
}
|
||||
var recordingOptions = {
|
||||
profile: 'cif',
|
||||
profile: 'high',
|
||||
rotation: 0
|
||||
};
|
||||
camera.startRecording(recordingOptions,
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,10 +14,10 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
width: 320,
|
||||
height: 240
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var options = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for bad initial configuration</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="text/javascript" src="camera_common.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<video id="viewfinder" width="200" height="200" autoplay></video>
|
||||
<img src="#" alt="This image is going to load" id="testimage"/>
|
||||
<script class="testbody" type="text/javascript;version=1.7">
|
||||
|
||||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var config = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'foobar',
|
||||
};
|
||||
|
||||
var Camera = {
|
||||
cameraObj: null,
|
||||
|
||||
get viewfinder() {
|
||||
return document.getElementById('viewfinder');
|
||||
},
|
||||
|
||||
start: function test_start() {
|
||||
function getCamera_onSuccess(d) {
|
||||
ok(false, "Get camera should have failed");
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function getCamera_onError(e) {
|
||||
ok(true, "Get camera failed as expected: " + JSON.stringify(e));
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
navigator.mozCameras.getCamera(whichCamera, config).then(getCamera_onSuccess, getCamera_onError);
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
window.addEventListener('beforeunload', function() {
|
||||
Camera.viewfinder.mozSrcObject = null;
|
||||
if (Camera.cameraObj) {
|
||||
Camera.cameraObj.release();
|
||||
Camera.cameraObj = null;
|
||||
}
|
||||
});
|
||||
|
||||
Camera.start();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -17,7 +17,7 @@
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=965421
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=965420
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=940424
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -22,7 +22,7 @@ SimpleTest.waitForExplicitFinish();
|
|||
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
|
||||
var initialConfig = {
|
||||
mode: 'picture',
|
||||
recorderProfile: 'cif',
|
||||
recorderProfile: 'high',
|
||||
previewSize: {
|
||||
width: 352,
|
||||
height: 288
|
||||
|
|
|
@ -39,3 +39,5 @@ LOCAL_INCLUDES += [
|
|||
'/dom/base',
|
||||
]
|
||||
|
||||
if not CONFIG['CLANG_CXX']:
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -2991,6 +2991,14 @@ void HTMLMediaElement::Error(uint16_t aErrorCode)
|
|||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_NETWORK ||
|
||||
aErrorCode == nsIDOMMediaError::MEDIA_ERR_ABORTED,
|
||||
"Only use nsIDOMMediaError codes!");
|
||||
|
||||
// Since we have multiple paths calling into DecodeError, e.g.
|
||||
// MediaKeys::Terminated and EMEH264Decoder::Error. We should take the 1st
|
||||
// one only in order not to fire multiple 'error' events.
|
||||
if (mError) {
|
||||
return;
|
||||
}
|
||||
|
||||
mError = new MediaError(this, aErrorCode);
|
||||
DispatchAsyncEvent(NS_LITERAL_STRING("error"));
|
||||
if (mReadyState == nsIDOMHTMLMediaElement::HAVE_NOTHING) {
|
||||
|
|
|
@ -104,6 +104,16 @@ size_t MediaDecoderReader::SizeOfAudioQueueInBytes() const
|
|||
return functor.mSize;
|
||||
}
|
||||
|
||||
size_t MediaDecoderReader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
return mVideoQueue.GetSize();
|
||||
}
|
||||
|
||||
size_t MediaDecoderReader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
return mAudioQueue.GetSize();
|
||||
}
|
||||
|
||||
nsresult MediaDecoderReader::ResetDecode()
|
||||
{
|
||||
nsresult res = NS_OK;
|
||||
|
|
|
@ -188,6 +188,9 @@ public:
|
|||
// the audio queue.
|
||||
size_t SizeOfAudioQueueInBytes() const;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames();
|
||||
virtual size_t SizeOfAudioQueueInFrames();
|
||||
|
||||
// Only used by WebMReader and MediaOmxReader for now, so stub here rather
|
||||
// than in every reader than inherits from MediaDecoderReader.
|
||||
virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength, int64_t aOffset) {}
|
||||
|
|
|
@ -623,6 +623,9 @@ MediaDecoderStateMachine::DecodeVideo()
|
|||
mVideoDecodeStartTime = TimeStamp::Now();
|
||||
}
|
||||
|
||||
SAMPLE_LOG("DecodeVideo() queued=%i, decoder-queued=%o, skip=%i, time=%lld",
|
||||
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame, currentTime);
|
||||
|
||||
mReader->RequestVideoData(skipToNextKeyFrame, currentTime)
|
||||
->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnVideoDecoded,
|
||||
|
@ -669,6 +672,10 @@ MediaDecoderStateMachine::DecodeAudio()
|
|||
mIsAudioPrerolling = false;
|
||||
}
|
||||
}
|
||||
|
||||
SAMPLE_LOG("DecodeAudio() queued=%i, decoder-queued=%o",
|
||||
AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
|
||||
|
||||
mReader->RequestAudioData()->Then(DecodeTaskQueue(), __func__, this,
|
||||
&MediaDecoderStateMachine::OnAudioDecoded,
|
||||
&MediaDecoderStateMachine::OnAudioNotDecoded);
|
||||
|
@ -2760,7 +2767,9 @@ void MediaDecoderStateMachine::RenderVideoFrame(VideoData* aData,
|
|||
return;
|
||||
}
|
||||
|
||||
VERBOSE_LOG("playing video frame %lld", aData->mTime);
|
||||
VERBOSE_LOG("playing video frame %lld (queued=%i, state-machine=%i, decoder-queued=%i)",
|
||||
aData->mTime, VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
|
||||
VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
|
||||
|
||||
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
|
||||
if (container) {
|
||||
|
|
|
@ -26,3 +26,5 @@ LOCAL_INCLUDES += [
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
|
@ -329,8 +329,8 @@ void
|
|||
CDMCallbackProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(mProxy->IsOnGMPThread());
|
||||
|
||||
mProxy->gmp_Terminated();
|
||||
nsRefPtr<nsIRunnable> task = NS_NewRunnableMethod(mProxy, &CDMProxy::Terminated);
|
||||
NS_DispatchToMainThread(task);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -569,11 +569,13 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
|
|||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Terminated()
|
||||
CDMProxy::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
NS_WARNING("CDM terminated");
|
||||
gmp_Shutdown();
|
||||
if (!mKeys.IsNull()) {
|
||||
mKeys->Terminated();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -104,6 +104,9 @@ public:
|
|||
// Main thread only.
|
||||
void Shutdown();
|
||||
|
||||
// Main thread only.
|
||||
void Terminated();
|
||||
|
||||
// Threadsafe.
|
||||
const nsCString& GetNodeId() const;
|
||||
|
||||
|
@ -157,9 +160,6 @@ public:
|
|||
GMPErr aResult,
|
||||
const nsTArray<uint8_t>& aDecryptedData);
|
||||
|
||||
// GMP thread only.
|
||||
void gmp_Terminated();
|
||||
|
||||
CDMCaps& Capabilites();
|
||||
|
||||
// Main thread only.
|
||||
|
|
|
@ -66,6 +66,43 @@ MediaKeys::~MediaKeys()
|
|||
Shutdown();
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CopySessions(const nsAString& aKey,
|
||||
nsRefPtr<MediaKeySession>& aSession,
|
||||
void* aClosure)
|
||||
{
|
||||
KeySessionHashMap* p = static_cast<KeySessionHashMap*>(aClosure);
|
||||
p->Put(aSession->GetSessionId(), aSession);
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CloseSessions(const nsAString& aKey,
|
||||
nsRefPtr<MediaKeySession>& aSession,
|
||||
void* aClosure)
|
||||
{
|
||||
aSession->OnClosed();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::Terminated()
|
||||
{
|
||||
KeySessionHashMap keySessions;
|
||||
// Remove entries during iteration will screw it. Make a copy first.
|
||||
mKeySessions.Enumerate(&CopySessions, &keySessions);
|
||||
keySessions.Enumerate(&CloseSessions, nullptr);
|
||||
keySessions.Clear();
|
||||
MOZ_ASSERT(mKeySessions.Count() == 0);
|
||||
|
||||
// Notify the element about that CDM has terminated.
|
||||
if (mElement) {
|
||||
mElement->DecodeError();
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
MediaKeys::Shutdown()
|
||||
{
|
||||
|
|
|
@ -104,6 +104,10 @@ public:
|
|||
|
||||
void Shutdown();
|
||||
|
||||
// Called by CDMProxy when CDM crashes or shuts down. It is different from
|
||||
// Shutdown which is called from the script/dom side.
|
||||
void Terminated();
|
||||
|
||||
// Returns true if this MediaKeys has been bound to a media element.
|
||||
bool IsBoundToMediaElement() const;
|
||||
|
||||
|
|
|
@ -647,6 +647,26 @@ MP4Reader::PopSample(TrackType aTrack)
|
|||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
return SizeOfQueue(kVideo);
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
return SizeOfQueue(kAudio);
|
||||
}
|
||||
|
||||
size_t
|
||||
MP4Reader::SizeOfQueue(TrackType aTrack)
|
||||
{
|
||||
auto& decoder = GetDecoderData(aTrack);
|
||||
MonitorAutoLock lock(decoder.mMonitor);
|
||||
return decoder.mOutput.Length() + (decoder.mNumSamplesInput - decoder.mNumSamplesOutput);
|
||||
}
|
||||
|
||||
nsresult
|
||||
MP4Reader::ResetDecode()
|
||||
{
|
||||
|
|
|
@ -37,6 +37,9 @@ public:
|
|||
|
||||
virtual nsresult Init(MediaDecoderReader* aCloneDonor) MOZ_OVERRIDE;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
|
||||
|
||||
virtual nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
|
@ -117,6 +120,8 @@ private:
|
|||
bool IsWaitingOnCodecResource();
|
||||
virtual bool IsWaitingOnCDMResource() MOZ_OVERRIDE;
|
||||
|
||||
size_t SizeOfQueue(TrackType aTrack);
|
||||
|
||||
nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
|
||||
nsAutoPtr<PlatformDecoderModule> mPlatform;
|
||||
|
||||
|
|
|
@ -93,6 +93,26 @@ MediaSourceReader::IsWaitingMediaResources()
|
|||
return !mHasEssentialTrackBuffers;
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaSourceReader::SizeOfVideoQueueInFrames()
|
||||
{
|
||||
if (!mVideoReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::SizeOfVideoQueue called with no video reader", this);
|
||||
return 0;
|
||||
}
|
||||
return mVideoReader->SizeOfVideoQueueInFrames();
|
||||
}
|
||||
|
||||
size_t
|
||||
MediaSourceReader::SizeOfAudioQueueInFrames()
|
||||
{
|
||||
if (!mAudioReader) {
|
||||
MSE_DEBUG("MediaSourceReader(%p)::SizeOfAudioQueue called with no audio reader", this);
|
||||
return 0;
|
||||
}
|
||||
return mAudioReader->SizeOfAudioQueueInFrames();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDecoderReader::AudioDataPromise>
|
||||
MediaSourceReader::RequestAudioData()
|
||||
{
|
||||
|
|
|
@ -50,6 +50,9 @@ public:
|
|||
nsRefPtr<VideoDataPromise>
|
||||
RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold) MOZ_OVERRIDE;
|
||||
|
||||
virtual size_t SizeOfVideoQueueInFrames() MOZ_OVERRIDE;
|
||||
virtual size_t SizeOfAudioQueueInFrames() MOZ_OVERRIDE;
|
||||
|
||||
void OnAudioDecoded(AudioData* aSample);
|
||||
void OnAudioNotDecoded(NotDecodedReason aReason);
|
||||
void OnVideoDecoded(VideoData* aSample);
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "SourceBufferResource.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "mozilla/dom/TimeRanges.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -43,6 +44,7 @@ TrackBuffer::TrackBuffer(MediaSourceDecoder* aParentDecoder, const nsACString& a
|
|||
mParser = ContainerParser::CreateForMIMEType(aType);
|
||||
mTaskQueue = new MediaTaskQueue(GetMediaDecodeThreadPool());
|
||||
aParentDecoder->AddTrackBuffer(this);
|
||||
mDecoderPerSegment = Preferences::GetBool("media.mediasource.decoder-per-segment", false);
|
||||
}
|
||||
|
||||
TrackBuffer::~TrackBuffer()
|
||||
|
@ -150,7 +152,8 @@ TrackBuffer::AppendData(const uint8_t* aData, uint32_t aLength)
|
|||
if (mParser->ParseStartAndEndTimestamps(aData, aLength, start, end)) {
|
||||
if (mParser->IsMediaSegmentPresent(aData, aLength) &&
|
||||
mLastEndTimestamp &&
|
||||
!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value())) {
|
||||
(!mParser->TimestampsFuzzyEqual(start, mLastEndTimestamp.value()) ||
|
||||
mDecoderPerSegment)) {
|
||||
MSE_DEBUG("TrackBuffer(%p)::AppendData: Data last=[%lld, %lld] overlaps [%lld, %lld]",
|
||||
this, mLastStartTimestamp, mLastEndTimestamp.value(), start, end);
|
||||
|
||||
|
|
|
@ -162,6 +162,7 @@ private:
|
|||
|
||||
void ContinueShutdown();
|
||||
MediaPromiseHolder<ShutdownPromise> mShutdownPromise;
|
||||
bool mDecoderPerSegment;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -36,3 +36,5 @@ CXXFLAGS += [
|
|||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче