Bug 833385 - Implement HTMLTrackElement and TextTrack. r=bz,Ms2ger
authorRalph Giles <giles@mozilla.com>
Wed, 22 May 2013 00:14:00 +0800
changeset 132713 65d9aab57df9
parent 132712 01d3f80565cc
child 132714 ae2178f76d9a
push id24712
push useremorley@mozilla.com
push date2013-05-23 11:43 +0000
treeherdermozilla-central@8eebe35aae63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, Ms2ger
bugs833385
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 833385 - Implement HTMLTrackElement and TextTrack. r=bz,Ms2ger Add webidl interfaces and implementations of the HTML <track> element and related TextTrack, TextTrackList, TextTrackCue, and TextTrackCueList dom objects. Visibility is controlled by the media.webvtt.enabled pref, which defaults to false. HTMLMediaElement:NewURIFromString() is hoisted to nsGenericHTMLElement so it's available to the track element as well. This patch is primarily work by Dale Karp, David Humphrey and others as Seneca College.
content/base/src/nsGkAtomList.h
content/html/content/public/HTMLMediaElement.h
content/html/content/src/HTMLMediaElement.cpp
content/html/content/src/HTMLTrackElement.cpp
content/html/content/src/HTMLTrackElement.h
content/html/content/src/Makefile.in
content/html/content/src/moz.build
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/media/Makefile.in
content/media/TextTrack.cpp
content/media/TextTrack.h
content/media/TextTrackCue.cpp
content/media/TextTrackCue.h
content/media/TextTrackCueList.cpp
content/media/TextTrackCueList.h
content/media/TextTrackList.cpp
content/media/TextTrackList.h
content/media/moz.build
dom/webidl/HTMLMediaElement.webidl
dom/webidl/HTMLTrackElement.webidl
dom/webidl/TextTrack.webidl
dom/webidl/TextTrackCue.webidl
dom/webidl/TextTrackCueList.webidl
dom/webidl/TextTrackList.webidl
dom/webidl/WebIDL.mk
editor/libeditor/base/nsEditPropertyAtomList.h
editor/libeditor/html/nsHTMLEditUtils.cpp
layout/media/symbols.def.in
modules/libpref/src/init/all.js
parser/htmlparser/public/nsHTMLTagList.h
parser/htmlparser/src/nsElementTable.cpp
parser/htmlparser/src/nsHTMLTags.cpp
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -1870,16 +1870,21 @@ GK_ATOM(onseeking, "onseeking")
 GK_ATOM(onseeked, "onseeked")
 GK_ATOM(ontimeout, "ontimeout")
 GK_ATOM(ontimeupdate, "ontimeupdate")
 GK_ATOM(onended, "onended")
 GK_ATOM(onratechange, "onratechange")
 GK_ATOM(ondurationchange, "ondurationchange")
 GK_ATOM(onvolumechange, "onvolumechange")
 GK_ATOM(onMozAudioAvailable, "onMozAudioAvailable")
+GK_ATOM(onaddtrack, "onaddtrack")
+GK_ATOM(oncuechange, "oncuechange")
+GK_ATOM(onenter, "onenter")
+GK_ATOM(onexit, "onexit")
+GK_ATOM(onremovetrack, "onremovetrack")
 GK_ATOM(loadstart, "loadstart")
 GK_ATOM(suspend, "suspend")
 GK_ATOM(emptied, "emptied")
 GK_ATOM(stalled, "stalled")
 GK_ATOM(play, "play")
 GK_ATOM(pause, "pause")
 GK_ATOM(loadedmetadata, "loadedmetadata")
 GK_ATOM(loadeddata, "loadeddata")
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -23,16 +23,18 @@
 #include "mozilla/Mutex.h"
 #include "mozilla/dom/TimeRanges.h"
 #include "nsIDOMWakeLock.h"
 #include "AudioChannelCommon.h"
 #include "DecoderTraits.h"
 #include "MediaMetadataManager.h"
 #include "AudioChannelAgent.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/TextTrack.h"
+#include "mozilla/dom/TextTrackList.h"
 #include "mozilla/ErrorResult.h"
 
 // Define to output information on decoding and painting framerate
 /* #define DEBUG_FRAME_RATE 1 */
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
@@ -504,16 +506,26 @@ public:
 
   // XPCOM GetMozAudioChannelType() is OK
 
   void SetMozAudioChannelType(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::mozaudiochannel, aValue, aRv);
   }
 
+  TextTrackList* TextTracks() const;
+
+  already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
+                                           const nsAString& aLabel,
+                                           const nsAString& aLanguage);
+
+  void AddTextTrack(TextTrack* aTextTrack) {
+    mTextTracks->AddTextTrack(aTextTrack);
+  }
+
 protected:
   class MediaLoadListener;
   class StreamListener;
 
   virtual void GetItemValueText(nsAString& text);
   virtual void SetItemValueText(const nsAString& text);
 
   class WakeLockBoolWrapper {
@@ -626,21 +638,16 @@ protected:
   /**
    * Execute the initial steps of the load algorithm that ensure existing
    * loads are aborted, the element is emptied, and a new load ID is
    * created.
    */
   void AbortExistingLoads();
 
   /**
-   * Create a URI for the given aURISpec string.
-   */
-  nsresult NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI);
-
-  /**
    * Called when all potential resources are exhausted. Changes network
    * state to NETWORK_NO_SOURCE, and sends error event with code
    * MEDIA_ERR_SRC_NOT_SUPPORTED.
    */
   void NoSupportedMediaSourceError();
 
   /**
    * Attempts to load resources from the <source> children. This is a
@@ -1098,14 +1105,17 @@ protected:
   // Audio Channel Type.
   AudioChannelType mAudioChannelType;
 
   // Is this media element playing?
   bool mPlayingThroughTheAudioChannel;
 
   // An agent used to join audio channel service.
   nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
+
+  // List of our attached text track objects.
+  nsRefPtr<TextTrackList> mTextTracks;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLMediaElement_h
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -407,16 +407,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream);
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlayed);
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTracks);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   if (tmp->mSrcStream) {
     // Need to EndMediaStreamPlayback to clear mSrcStream and make sure everything
     // gets unhooked correctly.
     tmp->EndSrcMediaStreamPlayback();
   }
@@ -425,16 +426,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSourceLoadCandidate)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mOutputStreams[i].mStream);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed);
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTracks);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
@@ -1942,16 +1944,18 @@ HTMLMediaElement::HTMLMediaElement(alrea
     gMediaElementEventsLog = PR_NewLogModule("nsMediaElementEvents");
   }
 #endif
 
   mPaused.SetOuter(this);
 
   RegisterFreezableElement();
   NotifyOwnerDocumentActivityChanged();
+
+  mTextTracks = new TextTrackList(OwnerDoc()->GetParentObject());
 }
 
 HTMLMediaElement::~HTMLMediaElement()
 {
   NS_ASSERTION(!mHasSelfReference,
                "How can we be destroyed if we're still holding a self reference?");
 
   if (mVideoFrameContainer) {
@@ -2675,48 +2679,16 @@ void HTMLMediaElement::EndSrcMediaStream
     stream->ChangeExplicitBlockerCount(-1);
   }
   if (mPausedForInactiveDocumentOrChannel && stream) {
     stream->ChangeExplicitBlockerCount(-1);
   }
   mSrcStream = nullptr;
 }
 
-nsresult HTMLMediaElement::NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI)
-{
-  NS_ENSURE_ARG_POINTER(aURI);
-
-  *aURI = nullptr;
-
-  nsCOMPtr<nsIDocument> doc = OwnerDoc();
-
-  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
-  nsCOMPtr<nsIURI> uri;
-  nsresult rv = nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(uri),
-                                                          aURISpec,
-                                                          doc,
-                                                          baseURI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool equal;
-  if (aURISpec.IsEmpty() &&
-      doc->GetDocumentURI() &&
-      NS_SUCCEEDED(doc->GetDocumentURI()->Equals(uri, &equal)) &&
-      equal) {
-    // It's not possible for a media resource to be embedded in the current
-    // document we extracted aURISpec from, so there's no point returning
-    // the current document URI just to let the caller attempt and fail to
-    // decode it.
-    return NS_ERROR_DOM_INVALID_STATE_ERR;
-  }
-
-  uri.forget(aURI);
-  return NS_OK;
-}
-
 void HTMLMediaElement::ProcessMediaFragmentURI()
 {
   nsMediaFragmentURIParser parser(mLoadingSrc);
 
   if (mDecoder && parser.HasEndTime()) {
     mFragmentEnd = parser.GetEndTime();
   }
 
@@ -3563,16 +3535,24 @@ void HTMLMediaElement::FireTimeUpdate(bo
     mLastCurrentTime = time;
   }
   if (mFragmentEnd >= 0.0 && time >= mFragmentEnd) {
     Pause();
     mFragmentEnd = -1.0;
     mFragmentStart = -1.0;
     mDecoder->SetFragmentEndTime(mFragmentEnd);
   }
+
+  // Update visible text tracks.
+  // Here mTextTracks can be null if the cycle collector has unlinked
+  // us before our parent. In that case UnbindFromTree will call us
+  // when our parent is unlinked.
+  if (mTextTracks) {
+    mTextTracks->Update(time);
+  }
 }
 
 void HTMLMediaElement::GetCurrentSpec(nsCString& aString)
 {
   if (mLoadingSrc) {
     mLoadingSrc->GetSpec(aString);
   } else {
     aString.Truncate();
@@ -3766,10 +3746,25 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
 {
   NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE);
 
   UpdateChannelMuteState(canPlay);
   mPaused.SetCanPlay(canPlay);
   return NS_OK;
 }
 
+/* readonly attribute TextTrackList textTracks; */
+TextTrackList*
+HTMLMediaElement::TextTracks() const
+{
+  return mTextTracks;
+}
+
+already_AddRefed<TextTrack>
+HTMLMediaElement::AddTextTrack(TextTrackKind aKind,
+                               const nsAString& aLabel,
+                               const nsAString& aLanguage)
+{
+  return mTextTracks->AddTextTrack(aKind, aLabel, aLanguage);
+}
+
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLTrackElement.cpp
@@ -0,0 +1,257 @@
+/* -*- 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 "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/dom/HTMLTrackElement.h"
+#include "mozilla/dom/HTMLTrackElementBinding.h"
+#include "mozilla/dom/HTMLUnknownElement.h"
+#include "nsAttrValueInlines.h"
+#include "nsCOMPtr.h"
+#include "nsContentPolicyUtils.h"
+#include "nsContentUtils.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsGenericHTMLElement.h"
+#include "nsGkAtoms.h"
+#include "nsIAsyncVerifyRedirectCallback.h"
+#include "nsICachingChannel.h"
+#include "nsIChannelEventSink.h"
+#include "nsIChannelPolicy.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsIDocument.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIDOMHTMLMediaElement.h"
+#include "nsIFrame.h"
+#include "nsIHttpChannel.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILoadGroup.h"
+#include "nsIObserver.h"
+#include "nsIStreamListener.h"
+#include "nsISupportsImpl.h"
+#include "nsMappedAttributes.h"
+#include "nsNetUtil.h"
+#include "nsRuleData.h"
+#include "nsStyleConsts.h"
+#include "nsThreadUtils.h"
+#include "nsVideoFrame.h"
+#include "webvtt/parser.h"
+
+#ifdef PR_LOGGING
+static PRLogModuleInfo* gTrackElementLog;
+#define LOG(type, msg) PR_LOG(gTrackElementLog, type, msg)
+#else
+#define LOG(type, msg)
+#endif
+
+// Replace the usual NS_IMPL_NS_NEW_HTML_ELEMENT(Track) so
+// we can return an UnknownElement instead when pref'd off.
+nsGenericHTMLElement*
+NS_NewHTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                       mozilla::dom::FromParser aFromParser)
+{
+  if (!mozilla::dom::HTMLTrackElement::IsWebVTTEnabled()) {
+    return mozilla::dom::NewHTMLElementHelper::Create<nsHTMLUnknownElement,
+           mozilla::dom::HTMLUnknownElement>(aNodeInfo);
+  }
+
+  return mozilla::dom::NewHTMLElementHelper::Create<nsHTMLTrackElement,
+         mozilla::dom::HTMLTrackElement>(aNodeInfo);
+}
+
+namespace mozilla {
+namespace dom {
+
+/** HTMLTrackElement */
+HTMLTrackElement::HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  : nsGenericHTMLElement(aNodeInfo)
+  , mReadyState(NONE)
+{
+#ifdef PR_LOGGING
+  if (!gTrackElementLog) {
+    gTrackElementLog = PR_NewLogModule("nsTrackElement");
+  }
+#endif
+
+  SetIsDOMBinding();
+}
+
+HTMLTrackElement::~HTMLTrackElement()
+{
+}
+
+NS_IMPL_ELEMENT_CLONE(HTMLTrackElement)
+
+NS_IMPL_ADDREF_INHERITED(HTMLTrackElement, Element)
+NS_IMPL_RELEASE_INHERITED(HTMLTrackElement, Element)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED_3(HTMLTrackElement, nsGenericHTMLElement,
+                                     mTrack, mChannel, mMediaParent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLTrackElement)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLElement)
+NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
+
+JSObject*
+HTMLTrackElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return HTMLTrackElementBinding::Wrap(aCx, aScope, this);
+}
+
+bool
+HTMLTrackElement::IsWebVTTEnabled()
+{
+  return HTMLTrackElementBinding::PrefEnabled();
+}
+
+TextTrack*
+HTMLTrackElement::Track()
+{
+  if (!mTrack) {
+    // We're expected to always have an internal TextTrack so create
+    // an empty object to return if we don't already have one.
+    mTrack = new TextTrack(OwnerDoc()->GetParentObject());
+  }
+
+  return mTrack;
+}
+
+void
+HTMLTrackElement::DisplayCueText(webvtt_node* head)
+{
+  // TODO: Bug 833382 - Propagate to the LoadListener.
+}
+
+void
+HTMLTrackElement::CreateTextTrack()
+{
+  DOMString label, srcLang;
+  GetSrclang(srcLang);
+  GetLabel(label);
+  mTrack = new TextTrack(OwnerDoc()->GetParentObject(), Kind(), label, srcLang);
+
+  if (mMediaParent) {
+    mMediaParent->AddTextTrack(mTrack);
+  }
+}
+
+TextTrackKind
+HTMLTrackElement::Kind() const
+{
+  const nsAttrValue* value = GetParsedAttr(nsGkAtoms::kind);
+  if (!value) {
+    return TextTrackKind::Subtitles;
+  }
+  return static_cast<TextTrackKind>(value->GetEnumValue());
+}
+
+static EnumEntry
+StringFromKind(TextTrackKind aKind)
+{
+  return TextTrackKindValues::strings[static_cast<int>(aKind)];
+}
+
+void
+HTMLTrackElement::SetKind(TextTrackKind aKind, ErrorResult& aError)
+{
+  const EnumEntry& string = StringFromKind(aKind);
+  nsAutoString kind;
+
+  kind.AssignASCII(string.value, string.length);
+  SetHTMLAttr(nsGkAtoms::kind, kind, aError);
+}
+
+bool
+HTMLTrackElement::ParseAttribute(int32_t aNamespaceID,
+                                 nsIAtom* aAttribute,
+                                 const nsAString& aValue,
+                                 nsAttrValue& aResult)
+{
+  // Map html attribute string values to TextTrackKind enums.
+  static const nsAttrValue::EnumTable kKindTable[] = {
+    { "subtitles", static_cast<int16_t>(TextTrackKind::Subtitles) },
+    { "captions", static_cast<int16_t>(TextTrackKind::Captions) },
+    { "descriptions", static_cast<int16_t>(TextTrackKind::Descriptions) },
+    { "chapters", static_cast<int16_t>(TextTrackKind::Chapters) },
+    { "metadata", static_cast<int16_t>(TextTrackKind::Metadata) },
+    { 0 }
+  };
+
+  if (aNamespaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::kind) {
+    // Case-insensitive lookup, with the first element as the default.
+    return aResult.ParseEnumValue(aValue, kKindTable, false, kKindTable);
+  }
+
+  // Otherwise call the generic implementation.
+  return nsGenericHTMLElement::ParseAttribute(aNamespaceID,
+                                              aAttribute,
+                                              aValue,
+                                              aResult);
+}
+
+nsresult
+HTMLTrackElement::BindToTree(nsIDocument* aDocument,
+                             nsIContent* aParent,
+                             nsIContent* aBindingParent,
+                             bool aCompileEventHandlers)
+{
+  nsresult rv = nsGenericHTMLElement::BindToTree(aDocument,
+                                                 aParent,
+                                                 aBindingParent,
+                                                 aCompileEventHandlers);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!aDocument) {
+    return NS_OK;
+  }
+
+  LOG(PR_LOG_DEBUG, ("Track Element bound to tree."));
+  if (!aParent || !aParent->IsNodeOfType(nsINode::eMEDIA)) {
+    return NS_OK;
+  }
+
+  // Store our parent so we can look up its frame for display.
+  if (!mMediaParent) {
+    mMediaParent = static_cast<HTMLMediaElement*>(aParent);
+
+    HTMLMediaElement* media = static_cast<HTMLMediaElement*>(aParent);
+    // TODO: separate notification for 'alternate' tracks?
+    media->NotifyAddedSource();
+    LOG(PR_LOG_DEBUG, ("Track element sent notification to parent."));
+
+    // TODO: this section needs to become async in bug 833382.
+    // See https://bugzilla.mozilla.org/show_bug.cgi?id=833385#c55.
+
+    // Find our 'src' url
+    nsAutoString src;
+
+    // TODO: we might want to instead call LoadResource() in a
+    // AfterSetAttr, like we do in media element.
+    if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, src)) {
+      nsCOMPtr<nsIURI> uri;
+      nsresult rvTwo = NewURIFromString(src, getter_AddRefs(uri));
+      if (NS_SUCCEEDED(rvTwo)) {
+        LOG(PR_LOG_ALWAYS, ("%p Trying to load from src=%s", this,
+        NS_ConvertUTF16toUTF8(src).get()));
+        // TODO: bug 833382 - dispatch a load request.
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+HTMLTrackElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+  if (mMediaParent && aNullParent) {
+    mMediaParent = nullptr;
+  }
+
+  nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/html/content/src/HTMLTrackElement.h
@@ -0,0 +1,152 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_HTMLTrackElement_h
+#define mozilla_dom_HTMLTrackElement_h
+
+#define WEBVTT_NO_CONFIG_H 1
+#define WEBVTT_STATIC 1
+
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/dom/TextTrack.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsGenericHTMLElement.h"
+#include "nsGkAtoms.h"
+#include "nsIContent.h"
+#include "nsIDocument.h"
+#include "nsIDOMHTMLElement.h"
+#include "nsIDOMEventTarget.h"
+#include "nsIHttpChannel.h"
+#include "webvtt/node.h"
+
+namespace mozilla {
+namespace dom {
+
+class HTMLTrackElement MOZ_FINAL : public nsGenericHTMLElement
+                                 , public nsIDOMHTMLElement
+{
+public:
+  HTMLTrackElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  virtual ~HTMLTrackElement();
+
+  // nsISupports
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLTrackElement,
+                                           nsGenericHTMLElement)
+
+  // nsIDOMNode
+  NS_FORWARD_NSIDOMNODE_TO_NSINODE
+
+  // nsIDOMElement
+  NS_FORWARD_NSIDOMELEMENT_TO_GENERIC
+
+  // nsIDOMHTMLElement
+  NS_FORWARD_NSIDOMHTMLELEMENT_TO_GENERIC
+
+  // HTMLTrackElement WebIDL
+  TextTrackKind Kind() const;
+  void SetKind(TextTrackKind aKind, ErrorResult& aError);
+
+  void GetSrc(DOMString& aSrc) const
+  {
+    GetHTMLURIAttr(nsGkAtoms::src, aSrc);
+  }
+  void SetSrc(const nsAString& aSrc, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::src, aSrc, aError);
+  }
+
+  void GetSrclang(DOMString& aSrclang) const
+  {
+    GetHTMLAttr(nsGkAtoms::srclang, aSrclang);
+  }
+  void SetSrclang(const nsAString& aSrclang, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::srclang, aSrclang, aError);
+  }
+
+  void GetLabel(DOMString& aLabel) const
+  {
+    GetHTMLAttr(nsGkAtoms::label, aLabel);
+  }
+  void SetLabel(const nsAString& aLabel, ErrorResult& aError)
+  {
+    SetHTMLAttr(nsGkAtoms::label, aLabel, aError);
+  }
+
+  bool Default() const
+  {
+    return GetBoolAttr(nsGkAtoms::_default);
+  }
+  void SetDefault(bool aDefault, ErrorResult& aError)
+  {
+    SetHTMLBoolAttr(nsGkAtoms::_default, aDefault, aError);
+  }
+
+  // Constants for numeric readyState property values.
+  enum {
+    NONE = 0U,
+    LOADING = 1U,
+    LOADED = 2U,
+    ERROR = 3U
+  };
+  uint16_t ReadyState() const
+  {
+    return mReadyState;
+  }
+
+  TextTrack* Track();
+
+  virtual nsresult Clone(nsINodeInfo* aNodeInfo, nsINode** aResult) const MOZ_OVERRIDE;
+  virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
+
+  // For Track, ItemValue reflects the src attribute
+  virtual void GetItemValueText(nsAString& aText)
+  {
+    DOMString value;
+    GetSrc(value);
+    aText = value;
+  }
+  virtual void SetItemValueText(const nsAString& aText)
+  {
+    ErrorResult rv;
+    SetSrc(aText, rv);
+  }
+
+  // Override ParseAttribute() to convert kind strings to enum values.
+  virtual bool ParseAttribute(int32_t aNamespaceID,
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult);
+
+  // Override BindToTree() so that we can trigger a load when we become
+  // the child of a media element.
+  virtual nsresult BindToTree(nsIDocument* aDocument,
+                              nsIContent* aParent,
+                              nsIContent* aBindingParent,
+                              bool aCompileEventHandlers) MOZ_OVERRIDE;
+  virtual void UnbindFromTree(bool aDeep, bool aNullParent) MOZ_OVERRIDE;
+
+  void DisplayCueText(webvtt_node* head);
+
+  // Check enabling preference.
+  static bool IsWebVTTEnabled();
+
+protected:
+  virtual JSObject* WrapNode(JSContext* aCx,
+                             JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  nsRefPtr<TextTrack> mTrack;
+  nsCOMPtr<nsIChannel> mChannel;
+  nsRefPtr<HTMLMediaElement> mMediaParent;
+  uint16_t mReadyState;
+
+  void CreateTextTrack();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_HTMLTrackElement_h
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -76,16 +76,17 @@ CPPSRCS		= \
 		HTMLTableCellElement.cpp \
 		HTMLTableColElement.cpp \
 		HTMLTableRowElement.cpp \
 		HTMLTableSectionElement.cpp \
 		HTMLTemplateElement.cpp \
 		HTMLTextAreaElement.cpp \
 		HTMLTimeElement.cpp \
 		HTMLTitleElement.cpp \
+		HTMLTrackElement.cpp \
 		HTMLVideoElement.cpp \
 		HTMLUnknownElement.cpp \
 		MediaError.cpp \
 		TimeRanges.cpp \
 		ValidityState.cpp \
 		nsIConstraintValidation.cpp \
 		nsRadioVisitor.cpp \
 		nsDOMStringMap.cpp \
@@ -113,9 +114,11 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir)/../../../../editor/txmgr/src \
 		-I$(srcdir)/../../../../netwerk/base/src \
 		-I$(srcdir) \
 		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
-DEFINES += -D_IMPL_NS_LAYOUT
+DEFINES		+= \
+		-D_IMPL_NS_LAYOUT \
+		$(NULL)
--- a/content/html/content/src/moz.build
+++ b/content/html/content/src/moz.build
@@ -62,15 +62,16 @@ EXPORTS.mozilla.dom += [
     'HTMLTableColElement.h',
     'HTMLTableElement.h',
     'HTMLTableRowElement.h',
     'HTMLTableSectionElement.h',
     'HTMLTemplateElement.h',
     'HTMLTextAreaElement.h',
     'HTMLTimeElement.h',
     'HTMLTitleElement.h',
+    'HTMLTrackElement.h',
     'HTMLUnknownElement.h',
     'MediaError.h',
     'TimeRanges.h',
     'UndoManager.h',
     'ValidityState.h',
 ]
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3320,8 +3320,42 @@ nsGenericHTMLElement::GetWidthHeightForI
   return size;
 }
 
 bool
 nsGenericHTMLElement::IsEventAttributeName(nsIAtom *aName)
 {
   return nsContentUtils::IsEventAttributeName(aName, EventNameType_HTML);
 }
+
+/**
+ * Construct a URI from a string, as an element.src attribute
+ * would be set to. Helper for the media elements.
+ */
+nsresult
+nsGenericHTMLElement::NewURIFromString(const nsAutoString& aURISpec,
+                                       nsIURI** aURI)
+{
+  NS_ENSURE_ARG_POINTER(aURI);
+
+  *aURI = nullptr;
+
+  nsCOMPtr<nsIDocument> doc = OwnerDoc();
+
+  nsCOMPtr<nsIURI> baseURI = GetBaseURI();
+  nsresult rv = nsContentUtils::NewURIWithDocumentCharset(aURI, aURISpec,
+                                                          doc, baseURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool equal;
+  if (aURISpec.IsEmpty() &&
+      doc->GetDocumentURI() &&
+      NS_SUCCEEDED(doc->GetDocumentURI()->Equals(*aURI, &equal)) &&
+      equal) {
+    // Assume an element can't point to a fragment of its embedding
+    // document. Fail here instead of returning the recursive URI
+    // and waiting for the subsequent load to fail.
+    NS_RELEASE(*aURI);
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  return NS_OK;
+}
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -790,16 +790,23 @@ protected:
   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
                                 const nsAttrValue* aValue, bool aNotify);
 
   virtual nsEventListenerManager*
     GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer);
 
   virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const;
 
+  /**
+   * Create a URI for the given aURISpec string.
+   * Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty
+   * and the document's URI matches the element's base URI.
+   */
+  nsresult NewURIFromString(const nsAutoString& aURISpec, nsIURI** aURI);
+
   void GetHTMLAttr(nsIAtom* aName, nsAString& aResult) const
   {
     GetAttr(kNameSpaceID_None, aName, aResult);
   }
   void GetHTMLAttr(nsIAtom* aName, mozilla::dom::DOMString& aResult) const
   {
     GetAttr(kNameSpaceID_None, aName, aResult);
   }
@@ -1976,16 +1983,17 @@ NS_DECLARE_NS_NEW_HTML_ELEMENT(TableRow)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TableSection)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Tbody)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Template)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(TextArea)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Tfoot)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Thead)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Time)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Title)
+NS_DECLARE_NS_NEW_HTML_ELEMENT(Track)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Unknown)
 NS_DECLARE_NS_NEW_HTML_ELEMENT(Video)
 
 inline nsISupports*
 ToSupports(nsGenericHTMLElement* aHTMLElement)
 {
   return aHTMLElement;
 }
--- a/content/media/Makefile.in
+++ b/content/media/Makefile.in
@@ -30,16 +30,20 @@ CPPSRCS = \
   MediaCache.cpp \
   MediaDecoder.cpp \
   MediaDecoderStateMachine.cpp \
   MediaDecoderReader.cpp \
   MediaResource.cpp \
   MediaStreamGraph.cpp \
   MediaStreamTrack.cpp \
   StreamBuffer.cpp \
+  TextTrack.cpp \
+  TextTrackList.cpp \
+  TextTrackCue.cpp \
+  TextTrackCueList.cpp \
   VideoFrameContainer.cpp \
   VideoSegment.cpp \
   VideoStreamTrack.cpp \
   VideoUtils.cpp \
   $(NULL)
 
 FORCE_STATIC_LIB = 1
 
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrack.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et tw=78: */
+/* 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 "mozilla/dom/TextTrack.h"
+#include "mozilla/dom/TextTrackBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(TextTrack,
+                                        mParent,
+                                        mCueList,
+                                        mActiveCueList)
+
+NS_IMPL_ADDREF_INHERITED(TextTrack, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TextTrack, nsDOMEventTargetHelper)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrack)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+TextTrack::TextTrack(nsISupports* aParent,
+                     TextTrackKind aKind,
+                     const nsAString& aLabel,
+                     const nsAString& aLanguage)
+  : mParent(aParent)
+  , mKind(aKind)
+  , mLabel(aLabel)
+  , mLanguage(aLanguage)
+  , mMode(TextTrackMode::Hidden)
+  , mCueList(new TextTrackCueList(aParent))
+  , mActiveCueList(new TextTrackCueList(aParent))
+{
+  SetIsDOMBinding();
+}
+
+TextTrack::TextTrack(nsISupports* aParent)
+  : mParent(aParent)
+  , mKind(TextTrackKind::Subtitles)
+  , mMode(TextTrackMode::Disabled)
+  , mCueList(new TextTrackCueList(aParent))
+  , mActiveCueList(new TextTrackCueList(aParent))
+{
+  SetIsDOMBinding();
+}
+
+void
+TextTrack::Update(double time)
+{
+  mCueList->Update(time);
+}
+
+JSObject*
+TextTrack::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return TextTrackBinding::Wrap(aCx, aScope, this);
+}
+
+void
+TextTrack::SetMode(TextTrackMode aValue)
+{
+  mMode = aValue;
+}
+
+void
+TextTrack::AddCue(TextTrackCue& aCue)
+{
+  //XXX: If cue exists, remove. Bug 867823.
+  mCueList->AddCue(aCue);
+}
+
+void
+TextTrack::RemoveCue(TextTrackCue& aCue)
+{
+  //XXX: If cue does not exists throw NotFoundError. Bug 867823.
+  mCueList->RemoveCue(aCue);
+}
+
+void
+TextTrack::CueChanged(TextTrackCue& aCue)
+{
+  //XXX: Implement Cue changed. Bug 867823.
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrack.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et tw=78: */
+/* 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_TextTrack_h
+#define mozilla_dom_TextTrack_h
+
+#include "mozilla/dom/TextTrackBinding.h"
+#include "mozilla/dom/TextTrackCue.h"
+#include "mozilla/dom/TextTrackCueList.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsString.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class TextTrackCue;
+class TextTrackCueList;
+
+class TextTrack MOZ_FINAL : public nsDOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TextTrack,
+                                                         nsDOMEventTargetHelper)
+
+  TextTrack(nsISupports* aParent);
+  TextTrack(nsISupports* aParent,
+            TextTrackKind aKind,
+            const nsAString& aLabel,
+            const nsAString& aLanguage);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  nsISupports* GetParentObject() const
+  {
+    return mParent;
+  }
+
+  TextTrackKind Kind() const
+  {
+    return mKind;
+  }
+  void GetLabel(nsAString& aLabel) const
+  {
+    aLabel = mLabel;
+  }
+  void GetLanguage(nsAString& aLanguage) const
+  {
+    aLanguage = mLanguage;
+  }
+  void GetInBandMetadataTrackDispatchType(nsAString& aType) const
+  {
+    aType = mType;
+  }
+
+  TextTrackMode Mode() const
+  {
+    return mMode;
+  }
+  void SetMode(TextTrackMode aValue);
+
+  TextTrackCueList* GetCues() const
+  {
+    if (mMode == TextTrackMode::Disabled) {
+      return nullptr;
+    }
+    return mCueList;
+  }
+
+  TextTrackCueList* GetActiveCues() const
+  {
+    if (mMode == TextTrackMode::Disabled) {
+      return nullptr;
+    }
+    return mActiveCueList;
+  }
+
+  void Update(double time);
+
+  void AddCue(TextTrackCue& aCue);
+  void RemoveCue(TextTrackCue& aCue);
+  void CueChanged(TextTrackCue& aCue);
+
+  IMPL_EVENT_HANDLER(cuechange)
+
+private:
+  nsCOMPtr<nsISupports> mParent;
+
+  TextTrackKind mKind;
+  nsString mLabel;
+  nsString mLanguage;
+  nsString mType;
+  TextTrackMode mMode;
+
+  nsRefPtr<TextTrackCueList> mCueList;
+  nsRefPtr<TextTrackCueList> mActiveCueList;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TextTrack_h
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackCue.cpp
@@ -0,0 +1,102 @@
+/* -*- 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 "mozilla/dom/HTMLTrackElement.h"
+#include "mozilla/dom/TextTrackCue.h"
+#include "mozilla/dom/TextTrackCueBinding.h"
+#include "webvtt/cue.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(TextTrackCue,
+                                        mGlobal,
+                                        mTrack,
+                                        mTrackElement)
+
+NS_IMPL_ADDREF_INHERITED(TextTrackCue, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TextTrackCue, nsDOMEventTargetHelper)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrackCue)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+// Set cue setting defaults based on step 19 & seq.
+// in http://dev.w3.org/html5/webvtt/#parsing
+void
+TextTrackCue::SetDefaultCueSettings()
+{
+  mPosition = 50;
+  mSize = 100;
+  mPauseOnExit = false;
+  mSnapToLines = true;
+  mLine = WEBVTT_AUTO;
+  mAlign = TextTrackCueAlign::Middle;
+}
+
+TextTrackCue::TextTrackCue(nsISupports* aGlobal,
+                           double aStartTime,
+                           double aEndTime,
+                           const nsAString& aText)
+  : mGlobal(aGlobal)
+  , mText(aText)
+  , mStartTime(aStartTime)
+  , mEndTime(aEndTime)
+  , mHead(nullptr)
+{
+  SetDefaultCueSettings();
+  MOZ_ASSERT(aGlobal);
+  SetIsDOMBinding();
+}
+
+TextTrackCue::TextTrackCue(nsISupports* aGlobal,
+                           double aStartTime,
+                           double aEndTime,
+                           const nsAString& aText,
+                           HTMLTrackElement* aTrackElement,
+                           webvtt_node* head)
+  : mGlobal(aGlobal)
+  , mText(aText)
+  , mStartTime(aStartTime)
+  , mEndTime(aEndTime)
+  , mTrackElement(aTrackElement)
+  , mHead(head)
+{
+  // Use the webvtt library's reference counting.
+  webvtt_ref_node(mHead);
+  SetDefaultCueSettings();
+  MOZ_ASSERT(aGlobal);
+  SetIsDOMBinding();
+}
+
+TextTrackCue::~TextTrackCue()
+{
+  if (mHead) {
+    // Release our reference and null mHead.
+    webvtt_release_node(&mHead);
+  }
+}
+
+void
+TextTrackCue::DisplayCue()
+{
+  if (mTrackElement) {
+    mTrackElement->DisplayCueText(mHead);
+  }
+}
+
+JSObject*
+TextTrackCue::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return TextTrackCueBinding::Wrap(aCx, aScope, this);
+}
+
+void
+TextTrackCue::CueChanged()
+{
+  if (mTrack) {
+    mTrack->CueChanged(*this);
+  }
+}
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackCue.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et tw=78: */
+/* 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_TextTrackCue_h
+#define mozilla_dom_TextTrackCue_h
+
+#define WEBVTT_NO_CONFIG_H 1
+#define WEBVTT_STATIC 1
+
+#include "mozilla/dom/DocumentFragment.h"
+#include "mozilla/dom/TextTrack.h"
+#include "mozilla/dom/TextTrackCueBinding.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMEventTargetHelper.h"
+#include "webvtt/node.h"
+
+namespace mozilla {
+namespace dom {
+
+class HTMLTrackElement;
+class TextTrack;
+
+class TextTrackCue MOZ_FINAL : public nsDOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TextTrackCue,
+                                                         nsDOMEventTargetHelper)
+
+  // TextTrackCue WebIDL
+  // See bug 868509 about splitting out the WebVTT-specific interfaces.
+  static already_AddRefed<TextTrackCue>
+  Constructor(GlobalObject& aGlobal,
+              double aStartTime,
+              double aEndTime,
+              const nsAString& aText,
+              ErrorResult& aRv)
+  {
+    nsRefPtr<TextTrackCue> ttcue = new TextTrackCue(aGlobal.Get(), aStartTime,
+                                                    aEndTime, aText);
+    return ttcue.forget();
+  }
+  TextTrackCue(nsISupports* aGlobal, double aStartTime, double aEndTime,
+               const nsAString& aText);
+
+  TextTrackCue(nsISupports* aGlobal, double aStartTime, double aEndTime,
+               const nsAString& aText, HTMLTrackElement* aTrackElement,
+               webvtt_node* head);
+
+  ~TextTrackCue();
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  nsISupports* GetParentObject()
+  {
+    return mGlobal;
+  }
+
+  TextTrack* GetTrack() const
+  {
+    return mTrack;
+  }
+
+  void GetId(nsAString& aId) const
+  {
+    aId = mId;
+  }
+
+  void SetId(const nsAString& aId)
+  {
+    if (mId == aId) {
+      return;
+    }
+
+    mId = aId;
+    CueChanged();
+  }
+
+  double StartTime() const
+  {
+    return mStartTime;
+  }
+
+  void SetStartTime(double aStartTime)
+  {
+    //XXXhumph: validate? bug 868519.
+    if (mStartTime == aStartTime)
+      return;
+
+    mStartTime = aStartTime;
+    CueChanged();
+  }
+
+  double EndTime() const
+  {
+    return mEndTime;
+  }
+
+  void SetEndTime(double aEndTime)
+  {
+    //XXXhumph: validate? bug 868519.
+    if (mEndTime == aEndTime)
+      return;
+
+    mEndTime = aEndTime;
+    CueChanged();
+  }
+
+  bool PauseOnExit()
+  {
+    return mPauseOnExit;
+  }
+
+  void SetPauseOnExit(bool aPauseOnExit)
+  {
+    if (mPauseOnExit == aPauseOnExit)
+      return;
+
+    mPauseOnExit = aPauseOnExit;
+    CueChanged();
+  }
+
+  void GetVertical(nsAString& aVertical)
+  {
+    aVertical = mVertical;
+  }
+
+  void SetVertical(const nsAString& aVertical)
+  {
+    if (mVertical == aVertical)
+      return;
+
+    mVertical = aVertical;
+    CueChanged();
+  }
+
+  bool SnapToLines()
+  {
+    return mSnapToLines;
+  }
+
+  void SetSnapToLines(bool aSnapToLines)
+  {
+    if (mSnapToLines == aSnapToLines)
+      return;
+
+    mSnapToLines = aSnapToLines;
+    CueChanged();
+  }
+
+  double Line() const
+  {
+    return mLine;
+  }
+
+  void SetLine(double aLine)
+  {
+    //XXX: validate? bug 868519.
+    mLine = aLine;
+  }
+
+  int32_t Position() const
+  {
+    return mPosition;
+  }
+
+  void SetPosition(int32_t aPosition)
+  {
+    // XXXhumph: validate? bug 868519.
+    if (mPosition == aPosition)
+      return;
+
+    mPosition = aPosition;
+    CueChanged();
+  }
+
+  int32_t Size() const
+  {
+    return mSize;
+  }
+
+  void SetSize(int32_t aSize)
+  {
+    if (mSize == aSize) {
+      return;
+    }
+
+    if (aSize < 0 || aSize > 100) {
+      //XXX:throw IndexSizeError; bug 868519.
+    }
+
+    mSize = aSize;
+    CueChanged();
+  }
+
+  TextTrackCueAlign Align() const
+  {
+    return mAlign;
+  }
+
+  void SetAlign(TextTrackCueAlign& aAlign)
+  {
+    if (mAlign == aAlign)
+      return;
+
+    mAlign = aAlign;
+    CueChanged();
+  }
+
+  void GetText(nsAString& aText) const
+  {
+    aText = mText;
+  }
+
+  void SetText(const nsAString& aText)
+  {
+    // XXXhumph: validate? bug 868519.
+    if (mText == aText)
+      return;
+
+    mText = aText;
+    CueChanged();
+  }
+
+  already_AddRefed<DocumentFragment> GetCueAsHTML() const
+  {
+    // XXXhumph: todo. Bug 868509.
+    return nullptr;
+  }
+
+  IMPL_EVENT_HANDLER(enter)
+  IMPL_EVENT_HANDLER(exit)
+
+  // Helper functions for implementation.
+  bool
+  operator==(const TextTrackCue& rhs) const
+  {
+    return mId.Equals(rhs.mId);
+  }
+
+  const nsAString& Id() const
+  {
+    return mId;
+  }
+
+  void DisplayCue();
+
+private:
+  void CueChanged();
+  void SetDefaultCueSettings();
+
+  nsCOMPtr<nsISupports> mGlobal;
+  nsString mText;
+  double mStartTime;
+  double mEndTime;
+
+  nsRefPtr<TextTrack> mTrack;
+  nsRefPtr<HTMLTrackElement> mTrackElement;
+  webvtt_node *mHead;
+  nsString mId;
+  int32_t mPosition;
+  int32_t mSize;
+  bool mPauseOnExit;
+  bool mSnapToLines;
+  nsString mVertical;
+  int mLine;
+  TextTrackCueAlign mAlign;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TextTrackCue_h
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackCueList.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "mozilla/dom/TextTrackCueList.h"
+#include "mozilla/dom/TextTrackCueListBinding.h"
+#include "mozilla/dom/TextTrackCue.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(TextTrackCueList, mParent, mList)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TextTrackCueList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TextTrackCueList)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TextTrackCueList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+TextTrackCueList::TextTrackCueList(nsISupports* aParent) : mParent(aParent)
+{
+  SetIsDOMBinding();
+}
+
+void
+TextTrackCueList::Update(double time)
+{
+  const uint32_t length = mList.Length();
+  for (uint32_t i = 0; i < length; i++) {
+    if (time > mList[i]->StartTime() && time < mList[i]->EndTime()) {
+      mList[i]->DisplayCue();
+    }
+  }
+}
+
+JSObject*
+TextTrackCueList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return TextTrackCueListBinding::Wrap(aCx, aScope, this);
+}
+
+TextTrackCue*
+TextTrackCueList::IndexedGetter(uint32_t aIndex, bool& aFound)
+{
+  aFound = aIndex < mList.Length();
+  return aFound ? mList[aIndex] : nullptr;
+}
+
+TextTrackCue*
+TextTrackCueList::GetCueById(const nsAString& aId)
+{
+  if (aId.IsEmpty()) {
+    return nullptr;
+  }
+
+  for (uint32_t i = 0; i < mList.Length(); i++) {
+    if (aId.Equals(mList[i]->Id())) {
+      return mList[i];
+    }
+  }
+  return nullptr;
+}
+
+void
+TextTrackCueList::AddCue(TextTrackCue& cue)
+{
+  mList.AppendElement(&cue);
+}
+
+void
+TextTrackCueList::RemoveCue(TextTrackCue& cue)
+{
+  mList.RemoveElement(&cue);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackCueList.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et tw=78: */
+/* 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_TextTrackCueList_h
+#define mozilla_dom_TextTrackCueList_h
+
+#include "mozilla/dom/TextTrackCue.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class TextTrackCue;
+
+class TextTrackCueList MOZ_FINAL : public nsISupports
+                                 , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TextTrackCueList)
+
+  // TextTrackCueList WebIDL
+  TextTrackCueList(nsISupports* aParent);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  nsISupports* GetParentObject() const
+  {
+    return mParent;
+  }
+
+  uint32_t Length() const
+  {
+    return mList.Length();
+  }
+
+  void Update(double time);
+
+  TextTrackCue* IndexedGetter(uint32_t aIndex, bool& aFound);
+  TextTrackCue* GetCueById(const nsAString& aId);
+
+  void AddCue(TextTrackCue& cue);
+  void RemoveCue(TextTrackCue& cue);
+
+private:
+  nsCOMPtr<nsISupports> mParent;
+
+  nsTArray< nsRefPtr<TextTrackCue> > mList;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TextTrackCueList_h
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackList.cpp
@@ -0,0 +1,55 @@
+/* -*- 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 "mozilla/dom/TextTrackList.h"
+#include "mozilla/dom/TextTrackListBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(TextTrackList, mGlobal, mTextTracks)
+
+NS_IMPL_ADDREF_INHERITED(TextTrackList, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TextTrackList, nsDOMEventTargetHelper)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TextTrackList)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+TextTrackList::TextTrackList(nsISupports* aGlobal) : mGlobal(aGlobal)
+{
+  SetIsDOMBinding();
+}
+
+JSObject*
+TextTrackList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return TextTrackListBinding::Wrap(aCx, aScope, this);
+}
+
+TextTrack*
+TextTrackList::IndexedGetter(uint32_t aIndex, bool& aFound)
+{
+  aFound = aIndex < mTextTracks.Length();
+  return aFound ? mTextTracks[aIndex] : nullptr;
+}
+
+already_AddRefed<TextTrack>
+TextTrackList::AddTextTrack(TextTrackKind aKind,
+                            const nsAString& aLabel,
+                            const nsAString& aLanguage)
+{
+  nsRefPtr<TextTrack> track = new TextTrack(mGlobal, aKind, aLabel, aLanguage);
+  mTextTracks.AppendElement(track);
+  // TODO: dispatch addtrack event
+  return track.forget();
+}
+
+void
+TextTrackList::RemoveTextTrack(const TextTrack& aTrack)
+{
+  mTextTracks.RemoveElement(&aTrack);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/content/media/TextTrackList.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et tw=78: */
+/* 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_TextTrackList_h
+#define mozilla_dom_TextTrackList_h
+
+#include "mozilla/dom/TextTrack.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsDOMEventTargetHelper.h"
+
+namespace mozilla {
+namespace dom {
+
+class TextTrack;
+
+class TextTrackList MOZ_FINAL : public nsDOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(TextTrackList,
+                                                         nsDOMEventTargetHelper)
+
+  TextTrackList(nsISupports* aGlobal);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+
+  nsISupports* GetParentObject() const
+  {
+    return mGlobal;
+  }
+
+  uint32_t Length() const
+  {
+    return mTextTracks.Length();
+  }
+
+  void Update(double time) {
+    uint32_t length = Length(), i;
+    for( i = 0; i < length; i++ ) {
+      mTextTracks[i]->Update(time);
+    }
+  }
+  TextTrack* IndexedGetter(uint32_t aIndex, bool& aFound);
+
+  already_AddRefed<TextTrack> AddTextTrack(TextTrackKind aKind,
+                                           const nsAString& aLabel,
+                                           const nsAString& aLanguage);
+  void AddTextTrack(TextTrack* aTextTrack) {
+    mTextTracks.AppendElement(aTextTrack);
+  }
+
+  void RemoveTextTrack(const TextTrack& aTrack);
+
+  IMPL_EVENT_HANDLER(addtrack)
+  IMPL_EVENT_HANDLER(removetrack)
+
+private:
+  nsCOMPtr<nsISupports> mGlobal;
+  nsTArray< nsRefPtr<TextTrack> > mTextTracks;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TextTrackList_h
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -72,11 +72,14 @@ EXPORTS += [
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'AudioStreamTrack.h',
     'MediaStreamTrack.h',
+    'TextTrack.h',
+    'TextTrackCue.h',
+    'TextTrackCueList.h',
+    'TextTrackList.h',
     'VideoStreamTrack.h',
 ]
-
--- a/dom/webidl/HTMLMediaElement.webidl
+++ b/dom/webidl/HTMLMediaElement.webidl
@@ -82,18 +82,22 @@ interface HTMLMediaElement : HTMLElement
            attribute boolean muted;
   [SetterThrows]
            attribute boolean defaultMuted;
 
   // TODO: Bug 847379
   // tracks
   //readonly attribute AudioTrackList audioTracks;
   //readonly attribute VideoTrackList videoTracks;
-  //readonly attribute TextTrackList textTracks;
-  //TextTrack addTextTrack(DOMString kind, optional DOMString label, optional DOMString language);
+  [Pref="media.webvtt.enabled"]
+  readonly attribute TextTrackList textTracks;
+  [Pref="media.webvtt.enabled"]
+  TextTrack addTextTrack(TextTrackKind kind,
+                         optional DOMString label = "",
+                         optional DOMString language = "");
 };
 
 // Mozilla extensions:
 partial interface HTMLMediaElement {
   attribute MediaStream? mozSrcObject;
   attribute boolean mozPreservesPitch;
   readonly attribute boolean mozAutoplayEnabled;
 
new file mode 100644
--- /dev/null
+++ b/dom/webidl/HTMLTrackElement.webidl
@@ -0,0 +1,30 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#the-track-element
+ */
+
+[Pref="media.webvtt.enabled"]
+interface HTMLTrackElement : HTMLElement {
+  [SetterThrows, Pure]
+  attribute TextTrackKind kind;
+  [SetterThrows, Pure]
+  attribute DOMString src;
+  [SetterThrows, Pure]
+  attribute DOMString srclang;
+  [SetterThrows, Pure]
+  attribute DOMString label;
+  [SetterThrows, Pure]
+  attribute boolean default;
+
+  const unsigned short NONE = 0;
+  const unsigned short LOADING = 1;
+  const unsigned short LOADED = 2;
+  const unsigned short ERROR = 3;
+  readonly attribute unsigned short readyState;
+
+  readonly attribute TextTrack track;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TextTrack.webidl
@@ -0,0 +1,41 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#texttrack
+ */
+
+enum TextTrackKind {
+  "subtitles",
+  "captions",
+  "descriptions",
+  "chapters",
+  "metadata"
+};
+
+enum TextTrackMode {
+  "disabled",
+  "hidden",
+  "showing"
+};
+
+[Pref="media.webvtt.enabled"]
+interface TextTrack : EventTarget {
+  readonly attribute TextTrackKind kind;
+  readonly attribute DOMString label;
+  readonly attribute DOMString language;
+  readonly attribute DOMString inBandMetadataTrackDispatchType;
+
+           attribute TextTrackMode mode;
+
+  readonly attribute TextTrackCueList? cues;
+  readonly attribute TextTrackCueList? activeCues;
+
+  void addCue(TextTrackCue cue);
+  void removeCue(TextTrackCue cue);
+
+  [SetterThrows]
+           attribute EventHandler oncuechange;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TextTrackCue.webidl
@@ -0,0 +1,46 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#texttrackcue
+ */
+
+enum AutoKeyword { "auto" };
+
+/* Non-spec: Request to add this enum to spec
+ * can be found here: https://www.w3.org/Bugs/Public/show_bug.cgi?id=20996 */
+enum TextTrackCueAlign {
+  "start",
+  "middle",
+  "end",
+  "left",
+  "right"
+};
+
+[Constructor(double startTime, double endTime, DOMString text),
+ Pref="media.webvtt.enabled"]
+interface TextTrackCue : EventTarget {
+  readonly attribute TextTrack? track;
+
+  attribute DOMString id;
+  attribute double startTime;
+  attribute double endTime;
+  attribute boolean pauseOnExit;
+  attribute DOMString vertical;
+  attribute boolean snapToLines;
+  // XXXhumph: https://www.w3.org/Bugs/Public/show_bug.cgi?id=20651
+  // attribute (long or AutoKeyword) line;
+  attribute long position;
+  attribute long size;
+  attribute TextTrackCueAlign align;
+  attribute DOMString text;
+  DocumentFragment getCueAsHTML();
+
+  [SetterThrows]
+    attribute EventHandler onenter;
+
+  [SetterThrows]
+    attribute EventHandler onexit;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TextTrackCueList.webidl
@@ -0,0 +1,15 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#texttrackcuelist
+ */
+
+[Pref="media.webvtt.enabled"]
+interface TextTrackCueList {
+  readonly attribute unsigned long length;
+  getter TextTrackCue (unsigned long index);
+  TextTrackCue? getCueById(DOMString id);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/TextTrackList.webidl
@@ -0,0 +1,19 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * http://www.whatwg.org/specs/web-apps/current-work/#texttracklist
+ */
+
+[Pref="media.webvtt.enabled"]
+interface TextTrackList : EventTarget {
+  readonly attribute unsigned long length;
+  getter TextTrack (unsigned long index);
+
+           [SetterThrows]
+           attribute EventHandler onaddtrack;
+           [SetterThrows]
+           attribute EventHandler onremovetrack;
+};
--- a/dom/webidl/WebIDL.mk
+++ b/dom/webidl/WebIDL.mk
@@ -145,16 +145,17 @@ webidl_files = \
   HTMLTableCaptionElement.webidl \
   HTMLTableCellElement.webidl \
   HTMLTableColElement.webidl \
   HTMLTableElement.webidl \
   HTMLTableRowElement.webidl \
   HTMLTableSectionElement.webidl \
   HTMLTemplateElement.webidl \
   HTMLTextAreaElement.webidl \
+  HTMLTrackElement.webidl \
   HTMLTimeElement.webidl \
   HTMLTitleElement.webidl \
   HTMLUListElement.webidl \
   HTMLVideoElement.webidl \
   IDBDatabase.webidl \
   IDBFactory.webidl \
   IDBVersionChangeEvent.webidl \
   ImageData.webidl \
@@ -310,16 +311,20 @@ webidl_files = \
   SVGUseElement.webidl \
   SVGURIReference.webidl \
   SVGViewElement.webidl \
   SVGZoomAndPan.webidl \
   SVGZoomEvent.webidl \
   Text.webidl \
   TextDecoder.webidl \
   TextEncoder.webidl \
+  TextTrack.webidl \
+  TextTrackCue.webidl \
+  TextTrackCueList.webidl \
+  TextTrackList.webidl \
   TimeRanges.webidl \
   Touch.webidl \
   TouchEvent.webidl \
   TransitionEvent.webidl \
   TreeColumns.webidl \
   TreeWalker.webidl \
   UIEvent.webidl \
   URL.webidl \
--- a/editor/libeditor/base/nsEditPropertyAtomList.h
+++ b/editor/libeditor/base/nsEditPropertyAtomList.h
@@ -147,12 +147,13 @@ EDITOR_ATOM(table, "table")
 EDITOR_ATOM(tbody, "tbody")
 EDITOR_ATOM(td, "td")
 EDITOR_ATOM(textarea, "textarea")
 EDITOR_ATOM(tfoot, "tfoot")
 EDITOR_ATOM(thead, "thead")
 EDITOR_ATOM(th, "th")
 EDITOR_ATOM(time, "time")
 EDITOR_ATOM(tr, "tr")
+EDITOR_ATOM(track, "track")
 EDITOR_ATOM(tt, "tt")
 EDITOR_ATOM(ul, "ul")
 EDITOR_ATOM(u, "u")
 EDITOR_ATOM(var, "var")
--- a/editor/libeditor/html/nsHTMLEditUtils.cpp
+++ b/editor/libeditor/html/nsHTMLEditUtils.cpp
@@ -743,16 +743,17 @@ static const nsElementInfo kElements[eHT
   ELEM(textarea, true, false, GROUP_FORMCONTROL, GROUP_LEAF),
   ELEM(tfoot, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
   ELEM(th, true, false, GROUP_TR_CONTENT, GROUP_FLOW_ELEMENT),
   ELEM(thead, true, false, GROUP_NONE, GROUP_TBODY_CONTENT),
   ELEM(template, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(time, true, false, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(title, true, false, GROUP_HEAD_CONTENT, GROUP_LEAF),
   ELEM(tr, true, false, GROUP_TBODY_CONTENT, GROUP_TR_CONTENT),
+  ELEM(track, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(tt, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   ELEM(u, true, true, GROUP_FONTSTYLE, GROUP_INLINE_ELEMENT),
   // XXX Can contain self and ol because editor does sublists illegally.
   ELEM(ul, true, true, GROUP_BLOCK | GROUP_OL_UL,
        GROUP_LI | GROUP_OL_UL),
   ELEM(var, true, true, GROUP_PHRASE, GROUP_INLINE_ELEMENT),
   ELEM(video, false, false, GROUP_NONE, GROUP_NONE),
   ELEM(wbr, false, false, GROUP_NONE, GROUP_NONE),
--- a/layout/media/symbols.def.in
+++ b/layout/media/symbols.def.in
@@ -570,8 +570,14 @@ hb_unicode_funcs_create
 hb_unicode_funcs_get_empty
 hb_unicode_funcs_set_combining_class_func
 hb_unicode_funcs_set_compose_func
 hb_unicode_funcs_set_decompose_func
 hb_unicode_funcs_set_eastasian_width_func
 hb_unicode_funcs_set_general_category_func
 hb_unicode_funcs_set_mirroring_func
 hb_unicode_funcs_set_script_func
+webvtt_string_text
+webvtt_create_parser
+webvtt_delete_parser
+webvtt_parse_chunk
+webvtt_ref_node
+webvtt_release_node
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -197,16 +197,18 @@ pref("media.peerconnection.agc_enabled",
 pref("media.peerconnection.agc", 1);
 pref("media.peerconnection.noise_enabled", false);
 pref("media.peerconnection.noise", 1);
 #else
 #ifdef ANDROID
 pref("media.navigator.enabled", true);
 #endif
 #endif
+// TextTrack support
+pref("media.webvtt.enabled", false);
 
 #ifdef MOZ_WEBSPEECH
 pref("media.webspeech.recognition.enable", false);
 #endif
 
 // Whether to enable Web Audio support
 pref("media.webaudio.enabled", false);
 
--- a/parser/htmlparser/public/nsHTMLTagList.h
+++ b/parser/htmlparser/public/nsHTMLTagList.h
@@ -148,16 +148,17 @@ HTML_TAG(td, TableCell)
 HTML_TAG(textarea, TextArea)
 HTML_TAG(tfoot, TableSection)
 HTML_TAG(th, TableCell)
 HTML_TAG(thead, TableSection)
 HTML_TAG(template, Template)
 HTML_TAG(time, Time)
 HTML_TAG(title, Title)
 HTML_TAG(tr, TableRow)
+HTML_TAG(track, Track)
 HTML_HTMLELEMENT_TAG(tt)
 HTML_HTMLELEMENT_TAG(u)
 HTML_TAG(ul, SharedList)
 HTML_HTMLELEMENT_TAG(var)
 HTML_TAG(video, Video)
 HTML_HTMLELEMENT_TAG(wbr)
 HTML_HTMLELEMENT_TAG(xmp)
 
--- a/parser/htmlparser/src/nsElementTable.cpp
+++ b/parser/htmlparser/src/nsElementTable.cpp
@@ -39,16 +39,17 @@ DECL_TAG_LIST(gBodyParents,{eHTMLTag_htm
 DECL_TAG_LIST(gColParents,{eHTMLTag_table COMMA eHTMLTag_colgroup})
 DECL_TAG_LIST(gFramesetParents,{eHTMLTag_html COMMA eHTMLTag_frameset})
 DECL_TAG_LIST(gLegendParents,{eHTMLTag_fieldset})
 DECL_TAG_LIST(gAreaParent,{eHTMLTag_map})
 DECL_TAG_LIST(gParamParents,{eHTMLTag_applet COMMA eHTMLTag_object})
 DECL_TAG_LIST(gTRParents,{eHTMLTag_tbody COMMA eHTMLTag_tfoot COMMA eHTMLTag_thead COMMA eHTMLTag_table})
 DECL_TAG_LIST(gTREndParents,{eHTMLTag_tbody COMMA eHTMLTag_tfoot COMMA eHTMLTag_thead COMMA eHTMLTag_table COMMA eHTMLTag_applet})
 DECL_TAG_LIST(gSourceParents,{eHTMLTag_video COMMA eHTMLTag_audio})
+DECL_TAG_LIST(gTrackParents,{eHTMLTag_video COMMA eHTMLTag_audio})
 
 //*********************************************************************************************
 //  Next, define the set of taglists for tags with special kids...
 //*********************************************************************************************
 
 DECL_TAG_LIST(gContainsText,{eHTMLTag_text COMMA eHTMLTag_newline COMMA eHTMLTag_whitespace COMMA eHTMLTag_entity})
 DECL_TAG_LIST(gUnknownKids,{eHTMLTag_html COMMA eHTMLTag_frameset})
 
@@ -83,18 +84,18 @@ DECL_TAG_LIST(gMapKids,{eHTMLTag_area})
 DECL_TAG_LIST(gPreKids,{eHTMLTag_hr COMMA eHTMLTag_center})  //note that CENTER is here for backward compatibility; it's not 4.0 spec.
 
 DECL_TAG_LIST(gTableKids,{eHTMLTag_caption COMMA eHTMLTag_col COMMA eHTMLTag_colgroup COMMA eHTMLTag_form COMMA  eHTMLTag_thead COMMA eHTMLTag_tbody COMMA eHTMLTag_tfoot COMMA eHTMLTag_script})// Removed INPUT - Ref. Bug 20087, 25382
   
 DECL_TAG_LIST(gTableElemKids,{eHTMLTag_form COMMA eHTMLTag_noscript COMMA eHTMLTag_script COMMA eHTMLTag_td COMMA eHTMLTag_th COMMA eHTMLTag_tr})
 DECL_TAG_LIST(gTRKids,{eHTMLTag_td COMMA eHTMLTag_th COMMA eHTMLTag_form COMMA eHTMLTag_script})// Removed INPUT - Ref. Bug 20087, 25382 |  Removed MAP to fix 58942
 DECL_TAG_LIST(gTBodyKids,{eHTMLTag_tr COMMA eHTMLTag_form}) // Removed INPUT - Ref. Bug 20087, 25382
 DECL_TAG_LIST(gULKids,{eHTMLTag_li COMMA eHTMLTag_p})
-DECL_TAG_LIST(gVideoKids,{eHTMLTag_source})
-DECL_TAG_LIST(gAudioKids,{eHTMLTag_source})
+DECL_TAG_LIST(gVideoKids,{eHTMLTag_source COMMA eHTMLTag_track})
+DECL_TAG_LIST(gAudioKids,{eHTMLTag_source COMMA eHTMLTag_track})
 
 //*********************************************************************************************
 // The following tag lists are used to define common set of root notes for the HTML elements...
 //*********************************************************************************************
 
 DECL_TAG_LIST(gRootTags,{eHTMLTag_body COMMA eHTMLTag_td COMMA eHTMLTag_table COMMA eHTMLTag_applet COMMA eHTMLTag_select}) // Added SELECT to fix bug 98645
 DECL_TAG_LIST(gTableRootTags,{eHTMLTag_applet COMMA eHTMLTag_body COMMA eHTMLTag_dl COMMA eHTMLTag_ol COMMA eHTMLTag_td COMMA eHTMLTag_th})
 DECL_TAG_LIST(gHTMLRootTags,{eHTMLTag_unknown})
@@ -1225,16 +1226,25 @@ const nsHTMLElement gHTMLElements[] = {
     /*requiredAncestor*/                eHTMLTag_table, eHTMLTag_unknown,
     /*rootnodes,endrootnodes*/          &gTRParents,&gTREndParents,
     /*autoclose starttags and endtags*/ &gTRCloseTags,0,0,0,
     /*parent,incl,exclgroups*/          kNone, kNone, kInlineEntity,
     /*special props, prop-range*/       (kBadContentWatch|kNoStyleLeaksIn|kNoStyleLeaksOut), kNoPropRange,
     /*special parents,kids*/            &gTRParents,&gTRKids,
   },
   {
+    /*tag*/                             eHTMLTag_track,
+    /*req-parent excl-parent*/          eHTMLTag_unknown,eHTMLTag_unknown,
+    /*rootnodes,endrootnodes*/          &gTrackParents,&gTrackParents,
+    /*autoclose starttags and endtags*/ &gPAutoClose, 0, 0,0,
+    /*parent,incl,exclgroups*/          kSpecial, kNone, kNone,
+    /*special props, prop-range*/       kNonContainer,kNoPropRange,
+    /*special parents,kids*/            &gTrackParents,0,
+  },
+  {
     /*tag*/                             eHTMLTag_tt,
     /*req-parent excl-parent*/          eHTMLTag_unknown,eHTMLTag_unknown,
     /*rootnodes,endrootnodes*/          &gRootTags,&gRootTags,
     /*autoclose starttags and endtags*/ 0,0,0,0,
     /*parent,incl,exclgroups*/          kFontStyle, (kSelf|kInlineEntity), kNone,
     /*special props, prop-range*/       0, kDefaultPropRange,
     /*special parents,kids*/            0,0,
   },
--- a/parser/htmlparser/src/nsHTMLTags.cpp
+++ b/parser/htmlparser/src/nsHTMLTags.cpp
@@ -254,16 +254,18 @@ static const PRUnichar sHTMLTagUnicodeNa
 static const PRUnichar sHTMLTagUnicodeName_template[] =
   {'t', 'e', 'm', 'p', 'l', 'a', 't', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_time[] =
   {'t', 'i', 'm', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_title[] =
   {'t', 'i', 't', 'l', 'e', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_tr[] =
   {'t', 'r', '\0'};
+static const PRUnichar sHTMLTagUnicodeName_track[] =
+  {'t', 'r', 'a', 'c', 'k', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_tt[] =
   {'t', 't', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_u[] =
   {'u', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_ul[] =
   {'u', 'l', '\0'};
 static const PRUnichar sHTMLTagUnicodeName_var[] =
   {'v', 'a', 'r', '\0'};