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
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'};