gecko/content/media/WebVTTLoadListener.cpp
Caitlin Potter a8991d3096 Bug 881475 - Drop cues with malformed timestamps. r=rillian
The parsing specification requires the UA to drop
cues containing malformed cue-times.
(http://dev.w3.org/html5/webvtt/#dfn-collect-a-webvtt-timestamp)

The OnReportError callback has been modified to notify the parser
that we need need to drop such cues which are deemed 'malformed'
according to the timestamp parsing rules.
2013-06-10 21:10:30 -04:00

261 lines
7.4 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "WebVTTLoadListener.h"
#include "mozilla/dom/TextTrackCue.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "VideoUtils.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_1(WebVTTLoadListener, mElement)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebVTTLoadListener)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamListener)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebVTTLoadListener)
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebVTTLoadListener)
#ifdef PR_LOGGING
PRLogModuleInfo* gTextTrackLog;
# define LOG(...) PR_LOG(gTextTrackLog, PR_LOG_DEBUG, (__VA_ARGS__))
#else
# define LOG(msg)
#endif
WebVTTLoadListener::WebVTTLoadListener(HTMLTrackElement* aElement)
: mElement(aElement)
{
MOZ_ASSERT(mElement, "Must pass an element to the callback");
#ifdef PR_LOGGING
if (!gTextTrackLog) {
gTextTrackLog = PR_NewLogModule("TextTrack");
}
#endif
LOG("WebVTTLoadListener created.");
}
WebVTTLoadListener::~WebVTTLoadListener()
{
LOG("WebVTTLoadListener destroyed.");
}
nsresult
WebVTTLoadListener::LoadResource()
{
if (!HTMLTrackElement::IsWebVTTEnabled()) {
NS_WARNING("WebVTT support disabled."
" See media.webvtt.enabled in about:config. ");
return NS_ERROR_FAILURE;
}
LOG("Loading text track resource.");
webvtt_parser_t* parser = nullptr;
webvtt_status status = webvtt_create_parser(&OnParsedCueWebVTTCallBack,
&OnReportErrorWebVTTCallBack,
this, &parser);
if (status != WEBVTT_SUCCESS) {
NS_ENSURE_TRUE(status != WEBVTT_OUT_OF_MEMORY,
NS_ERROR_OUT_OF_MEMORY);
NS_ENSURE_TRUE(status != WEBVTT_INVALID_PARAM,
NS_ERROR_INVALID_ARG);
return NS_ERROR_FAILURE;
}
mParser.own(parser);
NS_ENSURE_TRUE(mParser != nullptr, NS_ERROR_FAILURE);
return NS_OK;
}
NS_IMETHODIMP
WebVTTLoadListener::OnStartRequest(nsIRequest* aRequest,
nsISupports* aContext)
{
return NS_OK;
}
NS_IMETHODIMP
WebVTTLoadListener::OnStopRequest(nsIRequest* aRequest,
nsISupports* aContext,
nsresult aStatus)
{
webvtt_finish_parsing(mParser);
return NS_OK;
}
NS_IMETHODIMP
WebVTTLoadListener::OnDataAvailable(nsIRequest* aRequest,
nsISupports* aContext,
nsIInputStream* aStream,
uint64_t aOffset,
uint32_t aCount)
{
uint32_t count = aCount;
while (count > 0) {
uint32_t read;
nsresult rv = aStream->ReadSegments(ParseChunk, this, count, &read);
NS_ENSURE_SUCCESS(rv, rv);
if (!read) {
return NS_ERROR_FAILURE;
}
count -= read;
}
return NS_OK;
}
NS_IMETHODIMP
WebVTTLoadListener::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
nsIChannel* aNewChannel,
uint32_t aFlags,
nsIAsyncVerifyRedirectCallback* cb)
{
if (mElement) {
mElement->OnChannelRedirect(aOldChannel, aNewChannel, aFlags);
}
return NS_OK;
}
NS_IMETHODIMP
WebVTTLoadListener::GetInterface(const nsIID &aIID,
void** aResult)
{
return QueryInterface(aIID, aResult);
}
NS_METHOD
WebVTTLoadListener::ParseChunk(nsIInputStream* aInStream, void* aClosure,
const char* aFromSegment, uint32_t aToOffset,
uint32_t aCount, uint32_t* aWriteCount)
{
WebVTTLoadListener* loadListener = static_cast<WebVTTLoadListener*>(aClosure);
if (WEBVTT_FAILED(webvtt_parse_chunk(loadListener->mParser, aFromSegment,
aCount))) {
LOG("Unable to parse chunk of WEBVTT text. Aborting.");
*aWriteCount = 0;
return NS_ERROR_FAILURE;
}
*aWriteCount = aCount;
return NS_OK;
}
void
WebVTTLoadListener::OnParsedCue(webvtt_cue* aCue)
{
const char* text = webvtt_string_text(&aCue->body);
nsRefPtr<TextTrackCue> textTrackCue =
new TextTrackCue(mElement->OwnerDoc()->GetParentObject(),
SECONDS_TO_MS(aCue->from), SECONDS_TO_MS(aCue->until),
NS_ConvertUTF8toUTF16(text), mElement,
aCue->node_head);
text = webvtt_string_text(&aCue->id);
textTrackCue->SetId(NS_ConvertUTF8toUTF16(text));
textTrackCue->SetSnapToLines(aCue->snap_to_lines);
textTrackCue->SetSize(aCue->settings.size);
textTrackCue->SetPosition(aCue->settings.position);
textTrackCue->SetLine(aCue->settings.line);
nsAutoString vertical;
switch (aCue->settings.vertical) {
case WEBVTT_VERTICAL_LR:
vertical = NS_LITERAL_STRING("lr");
break;
case WEBVTT_VERTICAL_RL:
vertical = NS_LITERAL_STRING("rl");
break;
case WEBVTT_HORIZONTAL:
// TODO: https://bugzilla.mozilla.org/show_bug.cgi?id=865407
// Will be handled in the processing model.
break;
}
textTrackCue->SetVertical(vertical);
TextTrackCueAlign align;
switch (aCue->settings.align) {
case WEBVTT_ALIGN_START:
align = TextTrackCueAlign::Start;
break;
case WEBVTT_ALIGN_MIDDLE:
align = TextTrackCueAlign::Middle;
case WEBVTT_ALIGN_END:
align = TextTrackCueAlign::End;
case WEBVTT_ALIGN_LEFT:
align = TextTrackCueAlign::Left;
break;
case WEBVTT_ALIGN_RIGHT:
align = TextTrackCueAlign::Right;
break;
default:
align = TextTrackCueAlign::Start;
break;
}
textTrackCue->SetAlign(align);
mElement->mTrack->AddCue(*textTrackCue);
}
int
WebVTTLoadListener::OnReportError(uint32_t aLine, uint32_t aCol,
webvtt_error aError)
{
#ifdef PR_LOGGING
// Get source webvtt file to display in the log
DOMString wideFile;
mElement->GetSrc(wideFile);
NS_ConvertUTF16toUTF8 file(wideFile);
const char* error = "parser error";
if (aError >= 0) {
error = webvtt_strerror(aError);
}
LOG("error: %s(%d:%d) - %s\n", file.get(), aLine, aCol, error);
#endif
switch(aError) {
// Errors which should result in dropped cues
// if the return value is negative:
case WEBVTT_MALFORMED_TIMESTAMP:
return -1;
// By default, we can safely ignore other errors
// or else parsing the document will be aborted regardless
// of the return value.
default:
return 0;
}
}
void WEBVTT_CALLBACK
WebVTTLoadListener::OnParsedCueWebVTTCallBack(void* aUserData, webvtt_cue* aCue)
{
WebVTTLoadListener* self = static_cast<WebVTTLoadListener*>(aUserData);
self->OnParsedCue(aCue);
}
int WEBVTT_CALLBACK
WebVTTLoadListener::OnReportErrorWebVTTCallBack(void* aUserData, uint32_t aLine,
uint32_t aCol,
webvtt_error aError)
{
WebVTTLoadListener* self = static_cast<WebVTTLoadListener*>(aUserData);
return self->OnReportError(aLine, aCol, aError);
}
} // namespace dom
} // namespace mozilla