Bug 469247. Implement 'canPlayType' API for video/audio elements. r=doublec,r+sr=bzbarsky

--HG--
extra : rebase_source : 06b14571546a763ee4f4b8641e8582bfff2caf8d
This commit is contained in:
Robert O'Callahan 2008-12-17 15:11:07 +13:00
Родитель ba9db4ebe4
Коммит 1d4a6fd073
18 изменённых файлов: 398 добавлений и 42 удалений

Просмотреть файл

@ -59,6 +59,7 @@
#include "nsIDOMEvent.h"
#include "nsTArray.h"
#include "nsTextFragment.h"
#include "nsReadableUtils.h"
struct nsNativeKeyEvent; // Don't include nsINativeKeyBindings.h here: it will force strange compilation error!
@ -112,6 +113,7 @@ class nsIXTFService;
#ifdef IBMBIDI
class nsIBidiKeyboard;
#endif
class nsIMIMEHeaderParam;
extern const char kLoadAsData[];
@ -1696,4 +1698,20 @@ inline NS_HIDDEN_(PRBool) NS_FloatIsFinite(jsdouble f) {
} \
}
class nsContentTypeParser {
public:
nsContentTypeParser(const nsAString& aString);
~nsContentTypeParser();
nsresult GetParameter(const char* aParameterName, nsAString& aResult);
nsresult GetType(nsAString& aResult)
{
return GetParameter(nsnull, aResult);
}
private:
NS_ConvertUTF16toUTF8 mString;
nsIMIMEHeaderParam* mService;
};
#endif /* nsContentUtils_h___ */

Просмотреть файл

@ -161,6 +161,7 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_XTFSERVICE_CID);
#include "nsIOfflineCacheUpdate.h"
#include "nsCPrefetchService.h"
#include "nsIChromeRegistry.h"
#include "nsIMIMEHeaderParam.h"
#ifdef IBMBIDI
#include "nsIBidiKeyboard.h"
@ -4522,3 +4523,22 @@ nsSameOriginChecker::GetInterface(const nsIID & aIID, void **aResult)
return QueryInterface(aIID, aResult);
}
nsContentTypeParser::nsContentTypeParser(const nsAString& aString)
: mString(aString), mService(nsnull)
{
CallGetService("@mozilla.org/network/mime-hdrparam;1", &mService);
}
nsContentTypeParser::~nsContentTypeParser()
{
NS_IF_RELEASE(mService);
}
nsresult
nsContentTypeParser::GetParameter(const char* aParameterName, nsAString& aResult)
{
NS_ENSURE_TRUE(mService, NS_ERROR_FAILURE);
return mService->GetParameter(mString, aParameterName,
EmptyCString(), PR_FALSE, nsnull,
aResult);
}

Просмотреть файл

@ -44,7 +44,6 @@
#include "nsScriptLoader.h"
#include "nsIDOMCharacterData.h"
#include "nsParserUtils.h"
#include "nsIMIMEHeaderParam.h"
#include "nsICharsetConverterManager.h"
#include "nsIUnicodeDecoder.h"
#include "nsIContent.h"
@ -331,16 +330,10 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
// If type exists, it trumps the deprecated 'language='
aElement->GetScriptType(type);
if (!type.IsEmpty()) {
nsCOMPtr<nsIMIMEHeaderParam> mimeHdrParser =
do_GetService("@mozilla.org/network/mime-hdrparam;1");
NS_ENSURE_TRUE(mimeHdrParser, NS_ERROR_FAILURE);
NS_ConvertUTF16toUTF8 typeAndParams(type);
nsContentTypeParser parser(type);
nsAutoString mimeType;
rv = mimeHdrParser->GetParameter(typeAndParams, nsnull,
EmptyCString(), PR_FALSE, nsnull,
mimeType);
rv = parser.GetType(mimeType);
NS_ENSURE_SUCCESS(rv, rv);
// Javascript keeps the fast path, optimized for most-likely type
@ -379,9 +372,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
if (typeID != nsIProgrammingLanguage::UNKNOWN) {
// Get the version string, and ensure the language supports it.
nsAutoString versionName;
rv = mimeHdrParser->GetParameter(typeAndParams, "version",
EmptyCString(), PR_FALSE, nsnull,
versionName);
rv = parser.GetParameter("version", versionName);
if (NS_FAILED(rv)) {
// no version attribute - version remains 0.
if (rv != NS_ERROR_INVALID_ARG)
@ -404,10 +395,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
// Some js specifics yet to be abstracted.
if (typeID == nsIProgrammingLanguage::JAVASCRIPT) {
nsAutoString value;
rv = mimeHdrParser->GetParameter(typeAndParams, "e4x",
EmptyCString(), PR_FALSE, nsnull,
value);
rv = parser.GetParameter("e4x", value);
if (NS_FAILED(rv)) {
if (rv != NS_ERROR_INVALID_ARG)
return rv;

Просмотреть файл

@ -159,8 +159,14 @@ public:
void Thaw();
// Returns true if we can handle this MIME type in a <video> or <audio>
// element
static PRBool CanHandleMediaType(const char* aMIMEType);
// element.
// If it returns true, then it also returns a null-terminated list
// of supported codecs in *aSupportedCodecs, and a null-terminated list
// of codecs that *may* be supported in *aMaybeSupportedCodecs. These
// lists should not be freed, they area static data.
static PRBool CanHandleMediaType(const char* aMIMEType,
const char*** aSupportedCodecs,
const char*** aMaybeSupportedCodecs);
/**
* Initialize data for available media types

Просмотреть файл

@ -55,6 +55,7 @@
#include "nsXPCOMStrings.h"
#include "prlock.h"
#include "nsThreadUtils.h"
#include "nsContentUtils.h"
#include "nsIScriptSecurityManager.h"
#include "nsIXPConnect.h"
@ -68,6 +69,7 @@
#include "nsIDOMProgressEvent.h"
#include "nsHTMLMediaError.h"
#include "nsICategoryManager.h"
#include "nsCommaSeparatedTokenizer.h"
#ifdef MOZ_OGG
#include "nsOggDecoder.h"
@ -592,12 +594,24 @@ void nsHTMLMediaElement::UnbindFromTree(PRBool aDeep,
}
#ifdef MOZ_OGG
// See http://www.rfc-editor.org/rfc/rfc5334.txt for the definitions
// of Ogg media types and codec types
static const char gOggTypes[][16] = {
"video/ogg",
"audio/ogg",
"application/ogg"
};
static const char* gOggCodecs[] = {
"vorbis",
"theora",
nsnull
};
static const char* gOggMaybeCodecs[] = {
nsnull
};
static PRBool IsOggEnabled()
{
return nsContentUtils::GetBoolPref("media.ogg.enabled");
@ -616,6 +630,9 @@ static PRBool IsOggType(const nsACString& aType)
#endif
#ifdef MOZ_WAVE
// See http://www.rfc-editor.org/rfc/rfc2361.txt for the definitions
// of WAVE media types and codec types. However, the audio/vnd.wave
// MIME type described there is not used.
static const char gWaveTypes[][16] = {
"audio/x-wav",
"audio/wav",
@ -623,6 +640,16 @@ static const char gWaveTypes[][16] = {
"audio/x-pn-wav"
};
static const char* gWaveCodecs[] = {
"1", // Microsoft PCM Format
nsnull
};
static const char* gWaveMaybeCodecs[] = {
"0", // Microsoft Unknown Wave Format
nsnull
};
static PRBool IsWaveEnabled()
{
return nsContentUtils::GetBoolPref("media.wave.enabled");
@ -641,19 +668,99 @@ static PRBool IsWaveType(const nsACString& aType)
#endif
/* static */
PRBool nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType)
PRBool nsHTMLMediaElement::CanHandleMediaType(const char* aMIMEType,
const char*** aCodecList,
const char*** aMaybeCodecList)
{
#ifdef MOZ_OGG
if (IsOggType(nsDependentCString(aMIMEType)))
if (IsOggType(nsDependentCString(aMIMEType))) {
*aCodecList = gOggCodecs;
*aMaybeCodecList = gOggMaybeCodecs;
return PR_TRUE;
}
#endif
#ifdef MOZ_WAVE
if (IsWaveType(nsDependentCString(aMIMEType)))
if (IsWaveType(nsDependentCString(aMIMEType))) {
*aCodecList = gWaveCodecs;
*aMaybeCodecList = gWaveMaybeCodecs;
return PR_TRUE;
}
#endif
return PR_FALSE;
}
static PRBool
CodecListContains(const char** aCodecs, const nsAString& aCodec)
{
for (PRInt32 i = 0; aCodecs[i]; ++i) {
if (aCodec.EqualsASCII(aCodecs[i]))
return PR_TRUE;
}
return PR_FALSE;
}
enum CanPlayStatus {
CANPLAY_NO,
CANPLAY_MAYBE,
CANPLAY_YES
};
static CanPlayStatus GetCanPlay(const nsAString& aType)
{
nsContentTypeParser parser(aType);
nsAutoString mimeType;
nsresult rv = parser.GetType(mimeType);
if (NS_FAILED(rv))
return CANPLAY_NO;
NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
const char** supportedCodecs;
const char** maybeSupportedCodecs;
if (!nsHTMLMediaElement::CanHandleMediaType(mimeTypeUTF8.get(),
&supportedCodecs, &maybeSupportedCodecs))
return CANPLAY_NO;
nsAutoString codecs;
rv = parser.GetParameter("codecs", codecs);
if (NS_FAILED(rv))
// Parameter not found or whatever
return CANPLAY_MAYBE;
CanPlayStatus result = CANPLAY_YES;
// See http://www.rfc-editor.org/rfc/rfc4281.txt for the description
// of the 'codecs' parameter
nsCommaSeparatedTokenizer tokenizer(codecs);
PRBool expectMoreTokens = PR_FALSE;
while (tokenizer.hasMoreTokens()) {
const nsSubstring& token = tokenizer.nextToken();
if (CodecListContains(maybeSupportedCodecs, token)) {
result = CANPLAY_MAYBE;
} else if (!CodecListContains(supportedCodecs, token)) {
// Totally unsupported codec
return CANPLAY_NO;
}
expectMoreTokens = tokenizer.lastTokenEndedWithComma();
}
if (expectMoreTokens) {
// Last codec name was empty
return CANPLAY_NO;
}
return result;
}
NS_IMETHODIMP
nsHTMLMediaElement::CanPlayType(const nsAString& aType, nsAString& aResult)
{
switch (GetCanPlay(aType)) {
case CANPLAY_NO: aResult.AssignLiteral("no"); break;
case CANPLAY_YES: aResult.AssignLiteral("probably"); break;
default:
case CANPLAY_MAYBE: aResult.AssignLiteral("maybe"); break;
}
return NS_OK;
}
/* static */
void nsHTMLMediaElement::InitMediaTypes()
{
@ -796,9 +903,8 @@ nsresult nsHTMLMediaElement::PickMediaElement(nsIURI** aURI)
nsAutoString src;
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
if (source->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type)) {
if (CanHandleMediaType(NS_ConvertUTF16toUTF8(type).get())) {
if (GetCanPlay(type) != CANPLAY_NO)
return NewURIFromString(src, aURI);
}
} else if (i + 1 == count) {
// The last source element is permitted to omit the type
// attribute, in which case we need to open the URI and examine

Просмотреть файл

@ -44,7 +44,10 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = \
can_play_type_ogg.js \
can_play_type_wave.js \
test_autoplay.html \
test_can_play_type.html \
test_constants.html \
test_controls.html \
test_currentTime.html \
@ -59,6 +62,7 @@ ifdef MOZ_OGG
_TEST_FILES += \
test_bug448534.html \
test_bug461281.html \
test_can_play_type_ogg.html \
test_duration1.html \
test_ended1.html \
test_ended2.html \
@ -78,11 +82,16 @@ _TEST_FILES += \
bug461281.ogg \
seek.ogg \
$(NULL)
else
_TEST_FILES += \
test_can_play_type_no_ogg.html \
$(NULL)
endif
ifdef MOZ_WAVE
_TEST_FILES += \
test_bug463162.xhtml \
test_can_play_type_wave.html \
test_wav_8bit.html \
test_wav_ended1.html \
test_wav_seek3.html \
@ -101,6 +110,10 @@ _TEST_FILES += \
r11025_u8_c1.wav \
r11025_u8_c1_trunc.wav \
$(NULL)
else
_TEST_FILES += \
test_can_play_type_no_wave.html \
$(NULL)
endif
ifdef MOZ_OGG

Просмотреть файл

@ -0,0 +1,22 @@
function check_ogg(v, enabled) {
function check(type, expected) {
is(v.canPlayType(type), enabled ? expected : "no", type);
}
// Ogg types
check("video/ogg", "maybe");
check("audio/ogg", "maybe");
check("application/ogg", "maybe");
// Supported Ogg codecs
check("audio/ogg; codecs=vorbis", "probably");
check("video/ogg; codecs=vorbis", "probably");
check("video/ogg; codecs=vorbis,theora", "probably");
check("video/ogg; codecs=\"vorbis, theora\"", "probably");
check("video/ogg; codecs=theora", "probably");
// Unsupported Ogg codecs
check("video/ogg; codecs=xyz", "no");
check("video/ogg; codecs=xyz,vorbis", "no");
check("video/ogg; codecs=vorbis,xyz", "no");
}

Просмотреть файл

@ -0,0 +1,30 @@
function check_wave(v, enabled) {
function check(type, expected) {
is(v.canPlayType(type), enabled ? expected : "no", type);
}
// Wave types
check("audio/wave", "maybe");
check("audio/wav", "maybe");
check("audio/x-wav", "maybe");
check("audio/x-pn-wav", "maybe");
// Supported Wave codecs
check("audio/wave; codecs=1", "probably");
// "no codecs" should be supported, I guess
check("audio/wave; codecs=", "probably");
check("audio/wave; codecs=\"\"", "probably");
// Maybe-supported Wave codecs
check("audio/wave; codecs=0", "maybe");
check("audio/wave; codecs=\"0, 1\"", "maybe");
// Unsupported Wave codecs
check("audio/wave; codecs=2", "no");
check("audio/wave; codecs=xyz,0", "no");
check("audio/wave; codecs=0,xyz", "no");
check("audio/wave; codecs=\"xyz, 1\"", "no");
// empty codec names
check("audio/wave; codecs=,", "no");
check("audio/wave; codecs=\"0, 1,\"", "no");
}

Просмотреть файл

@ -0,0 +1,38 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
-->
<head>
<title>Test for Bug 469247</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
a Bug 469247</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
<script type="application/javascript">
var v = document.getElementById('v');
function check(type, expected) {
is(v.canPlayType(type), expected, type);
}
// Invalid types
check("foo/bar", "no");
check("", "no");
check("!!!", "no");
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
-->
<head>
<title>Test for Bug 469247: Ogg backend disabled</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
a Bug 469247</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
<script src="can_play_type_ogg.js"></script>
check_ogg(document.getElementById('v'), false);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
-->
<head>
<title>Test for Bug 469247: WAVE backend disabled</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
a Bug 469247</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
<script src="can_play_type_wave.js"></script>
check_wave(document.getElementById('v'), false);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
-->
<head>
<title>Test for Bug 469247: Ogg backend</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
a Bug 469247</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
<script src="can_play_type_ogg.js"></script>
<script>
check_ogg(document.getElementById('v'), true);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -0,0 +1,28 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=469247
-->
<head>
<title>Test for Bug 469247: WAVE backend</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469247">Mozill
a Bug 469247</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<video id="v"></video>
<pre id="test">
<script src="can_play_type_wave.js"></script>
<script>
check_wave(document.getElementById('v'), true);
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -74,7 +74,6 @@
#include "nsNetUtil.h"
#include "nsRDFCID.h"
#include "nsParserUtils.h"
#include "nsIMIMEHeaderParam.h"
#include "nsXPIDLString.h"
#include "nsReadableUtils.h"
#include "nsXULElement.h"
@ -983,16 +982,10 @@ XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
src.Assign(aAttributes[1]);
}
else if (key.EqualsLiteral("type")) {
nsCOMPtr<nsIMIMEHeaderParam> mimeHdrParser =
do_GetService("@mozilla.org/network/mime-hdrparam;1");
NS_ENSURE_TRUE(mimeHdrParser, NS_ERROR_FAILURE);
NS_ConvertUTF16toUTF8 typeAndParams(aAttributes[1]);
nsDependentString str(aAttributes[1]);
nsContentTypeParser parser(str);
nsAutoString mimeType;
rv = mimeHdrParser->GetParameter(typeAndParams, nsnull,
EmptyCString(), PR_FALSE, nsnull,
mimeType);
rv = parser.GetType(mimeType);
if (NS_FAILED(rv)) {
if (rv == NS_ERROR_INVALID_ARG) {
// Might as well bail out now instead of setting langID to
@ -1043,9 +1036,7 @@ XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
if (langID != nsIProgrammingLanguage::UNKNOWN) {
// Get the version string, and ensure the language supports it.
nsAutoString versionName;
rv = mimeHdrParser->GetParameter(typeAndParams, "version",
EmptyCString(), PR_FALSE, nsnull,
versionName);
rv = parser.GetParameter("version", versionName);
if (NS_FAILED(rv)) {
if (rv != NS_ERROR_INVALID_ARG)
return rv;
@ -1071,9 +1062,7 @@ XULContentSinkImpl::OpenScript(const PRUnichar** aAttributes,
version |= JSVERSION_HAS_XML;
nsAutoString value;
rv = mimeHdrParser->GetParameter(typeAndParams, "e4x",
EmptyCString(), PR_FALSE, nsnull,
value);
rv = parser.GetParameter("e4x", value);
if (NS_FAILED(rv)) {
if (rv != NS_ERROR_INVALID_ARG)
return rv;

Просмотреть файл

@ -71,6 +71,7 @@ interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
const unsigned short NETWORK_LOADED = 3;
readonly attribute unsigned short networkState;
void load();
DOMString canPlayType(in DOMString type);
// ready state
const unsigned short HAVE_NOTHING = 0;

Просмотреть файл

@ -267,7 +267,10 @@ nsContentDLF::CreateInstance(const char* aCommand,
}
#ifdef MOZ_MEDIA
if (nsHTMLMediaElement::CanHandleMediaType(aContentType)) {
const char** supportedCodecs;
const char** maybeSupportedCodecs;
if (nsHTMLMediaElement::CanHandleMediaType(aContentType,
&supportedCodecs, &maybeSupportedCodecs)) {
return CreateDocument(aCommand,
aChannel, aLoadGroup,
aContainer, kVideoDocumentCID,

Просмотреть файл

@ -1066,6 +1066,7 @@ if [ "$MOZ_MEDIA" ]; then
content/media/video/Makefile
content/media/video/public/Makefile
content/media/video/src/Makefile
content/media/video/test/Makefile
"
fi

Просмотреть файл

@ -80,6 +80,11 @@ public:
return mIter != mEnd;
}
PRBool lastTokenEndedWithComma()
{
return mLastTokenEndedWithComma;
}
/**
* Returns the next token.
*/
@ -101,9 +106,10 @@ public:
++mIter;
}
}
mLastTokenEndedWithComma = mIter != mEnd;
// Skip comma
if (mIter != mEnd) {
if (mLastTokenEndedWithComma) {
NS_ASSERTION(*mIter == ',', "Ended loop too soon");
++mIter;
@ -117,6 +123,7 @@ public:
private:
nsSubstring::const_char_iterator mIter, mEnd;
PRPackedBool mLastTokenEndedWithComma;
PRBool isWhitespace(PRUnichar aChar)
{