Bug 998872 - [Stingray] TV Manager API. Part 3 - TV manager, TV tuner, TV source. r=ehsan
authorSean Lin <selin@mozilla.com>
Fri, 22 Aug 2014 12:08:34 +0800
changeset 212871 544aa604483f62f0b99fa3112c4df3ec47b6a0e8
parent 212870 1593d48890a0abfbb6ee7a1f49ba2fb7c88ebcc0
child 212872 edf60abe62a5a7da09c9caed36ab49bb65a10558
push id27734
push userryanvm@gmail.com
push dateWed, 29 Oct 2014 20:15:52 +0000
treeherdermozilla-central@8345ae427a3f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs998872
milestone36.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 998872 - [Stingray] TV Manager API. Part 3 - TV manager, TV tuner, TV source. r=ehsan
dom/base/Navigator.cpp
dom/tv/TVListeners.cpp
dom/tv/TVManager.cpp
dom/tv/TVManager.h
dom/tv/TVServiceCallbacks.cpp
dom/tv/TVServiceCallbacks.h
dom/tv/TVServiceFactory.cpp
dom/tv/TVServiceFactory.h
dom/tv/TVSource.cpp
dom/tv/TVSource.h
dom/tv/TVTuner.cpp
dom/tv/TVTuner.h
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -1571,17 +1571,17 @@ Navigator::GetMozTelephony(ErrorResult& 
 
 TVManager*
 Navigator::GetTv()
 {
   if (!mTVManager) {
     if (!mWindow) {
       return nullptr;
     }
-    mTVManager = new TVManager(mWindow);
+    mTVManager = TVManager::Create(mWindow);
   }
 
   return mTVManager;
 }
 
 #ifdef MOZ_B2G
 already_AddRefed<Promise>
 Navigator::GetMobileIdAssertion(const MobileIdOptions& aOptions,
--- a/dom/tv/TVListeners.cpp
+++ b/dom/tv/TVListeners.cpp
@@ -48,55 +48,47 @@ TVSourceListener::UnregisterSource(TVSou
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanned(const nsAString& aTunerId,
                                        const nsAString& aSourceType,
                                        nsITVChannelData* aChannelData)
 {
   nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-
-  // TODO Notify the source in follow-up patches.
-
+  source->NotifyChannelScanned(aChannelData);
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanComplete(const nsAString& aTunerId,
                                             const nsAString& aSourceType)
 {
   nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-
-  // TODO Notify the source in follow-up patches.
-
+  source->NotifyChannelScanComplete();
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyChannelScanStopped(const nsAString& aTunerId,
                                            const nsAString& aSourceType)
 {
   nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-
-  // TODO Notify the source in follow-up patches.
-
+  source->NotifyChannelScanStopped();
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVSourceListener::NotifyEITBroadcasted(const nsAString& aTunerId,
                                        const nsAString& aSourceType,
                                        nsITVChannelData* aChannelData,
                                        nsITVProgramData** aProgramDataList,
                                        const uint32_t aCount)
 {
   nsRefPtr<TVSource> source = GetSource(aTunerId, aSourceType);
-
-  // TODO Notify the source in follow-up patches.
-
+  source->NotifyEITBroadcasted(aChannelData, aProgramDataList, aCount);
   return NS_OK;
 }
 
 already_AddRefed<TVSource>
 TVSourceListener::GetSource(const nsAString& aTunerId,
                             const nsAString& aSourceType)
 {
   nsRefPtrHashtable<nsStringHashKey, TVSource>* tunerSources = nullptr;
--- a/dom/tv/TVManager.cpp
+++ b/dom/tv/TVManager.cpp
@@ -1,62 +1,123 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/Promise.h"
 #include "mozilla/dom/TVManagerBinding.h"
+#include "mozilla/dom/TVServiceCallbacks.h"
+#include "mozilla/dom/TVServiceFactory.h"
+#include "mozilla/dom/TVTuner.h"
+#include "nsITVService.h"
+#include "nsServiceManagerUtils.h"
 #include "TVManager.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(TVManager)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TVManager,
-                                                  DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TVManager,
-                                                DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVManager, DOMEventTargetHelper, mTVService,
+                                   mTuners, mPendingGetTunersPromises)
 
 NS_IMPL_ADDREF_INHERITED(TVManager, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVManager, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVManager)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVManager::TVManager(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
+  , mIsReady(false)
 {
 }
 
 TVManager::~TVManager()
 {
 }
 
+/* static */ already_AddRefed<TVManager>
+TVManager::Create(nsPIDOMWindow* aWindow)
+{
+  nsRefPtr<TVManager> manager = new TVManager(aWindow);
+  return (manager->Init()) ? manager.forget() : nullptr;
+}
+
+bool
+TVManager::Init()
+{
+  mTVService = TVServiceFactory::AutoCreateTVService();
+  NS_ENSURE_TRUE(mTVService, false);
+
+  nsCOMPtr<nsITVServiceCallback> callback = new TVServiceTunerGetterCallback(this);
+  nsresult rv = mTVService->GetTuners(callback);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  return true;
+}
+
 /* virtual */ JSObject*
 TVManager::WrapObject(JSContext* aCx)
 {
   return TVManagerBinding::Wrap(aCx, this);
 }
 
+nsresult
+TVManager::SetTuners(const nsTArray<nsRefPtr<TVTuner>>& aTuners)
+{
+  // Should be called only when TV Manager hasn't been ready yet.
+  if (mIsReady) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  mTuners = aTuners;
+  mIsReady = true;
+
+  // Resolve pending promises.
+  uint32_t length = mPendingGetTunersPromises.Length();
+  for(uint32_t i = 0; i < length; i++) {
+    mPendingGetTunersPromises[i]->MaybeResolve(mTuners);
+  }
+  mPendingGetTunersPromises.Clear();
+  return NS_OK;
+}
+
+void
+TVManager::RejectPendingGetTunersPromises(nsresult aRv)
+{
+  // Reject pending promises.
+  uint32_t length = mPendingGetTunersPromises.Length();
+  for(uint32_t i = 0; i < length; i++) {
+    mPendingGetTunersPromises[i]->MaybeReject(aRv);
+  }
+  mPendingGetTunersPromises.Clear();
+}
+
+nsresult
+TVManager::DispatchTVEvent(nsIDOMEvent* aEvent)
+{
+  return DispatchTrustedEvent(aEvent);
+}
+
 already_AddRefed<Promise>
 TVManager::GetTuners(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  // Keep track of the promise when the manager hasn't been ready yet.
+  if (mIsReady) {
+    promise->MaybeResolve(mTuners);
+  } else {
+    mPendingGetTunersPromises.AppendElement(promise);
+  }
 
   return promise.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVManager.h
+++ b/dom/tv/TVManager.h
@@ -1,41 +1,58 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_TVManager_h__
-#define mozilla_dom_TVManager_h__
+#ifndef mozilla_dom_TVManager_h
+#define mozilla_dom_TVManager_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 
+class nsITVService;
+
 namespace mozilla {
 namespace dom {
 
 class Promise;
+class TVTuner;
 
 class TVManager MOZ_FINAL : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVManager, DOMEventTargetHelper)
 
-  explicit TVManager(nsPIDOMWindow* aWindow);
+  static already_AddRefed<TVManager> Create(nsPIDOMWindow* aWindow);
 
   // WebIDL (internal functions)
 
   virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
 
+  nsresult SetTuners(const nsTArray<nsRefPtr<TVTuner>>& aTuners);
+
+  void RejectPendingGetTunersPromises(nsresult aRv);
+
+  nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
+
   // WebIDL (public APIs)
 
   already_AddRefed<Promise> GetTuners(ErrorResult& aRv);
 
 private:
+  explicit TVManager(nsPIDOMWindow* aWindow);
+
   ~TVManager();
 
+  bool Init();
+
+  nsCOMPtr<nsITVService> mTVService;
+  nsTArray<nsRefPtr<TVTuner>> mTuners;
+  bool mIsReady;
+  nsTArray<nsRefPtr<Promise>> mPendingGetTunersPromises;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_TVManager_h__
+#endif // mozilla_dom_TVManager_h
--- a/dom/tv/TVServiceCallbacks.cpp
+++ b/dom/tv/TVServiceCallbacks.cpp
@@ -3,16 +3,17 @@
 /* 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/Promise.h"
 #include "mozilla/dom/TVManager.h"
 #include "mozilla/dom/TVSource.h"
 #include "mozilla/dom/TVTuner.h"
+#include "nsArrayUtils.h"
 #include "TVServiceCallbacks.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
  * Implementation of TVServiceSourceSetterCallback
  */
@@ -46,17 +47,21 @@ TVServiceSourceSetterCallback::~TVServic
 TVServiceSourceSetterCallback::NotifySuccess(nsIArray* aDataList)
 {
   // |aDataList| is expected to be null for setter callbacks.
   if (aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Update correspondent fields in |mTuner| in follow-up patches.
+  nsresult rv = mTuner->SetCurrentSource(mSourceType);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mPromise->MaybeReject(rv);
+    return rv;
+  }
 
   mPromise->MaybeResolve(JS::UndefinedHandleValue);
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceSourceSetterCallback::NotifyError(uint16_t aErrorCode)
 {
@@ -111,17 +116,17 @@ TVServiceChannelScanCallback::~TVService
 TVServiceChannelScanCallback::NotifySuccess(nsIArray* aDataList)
 {
   // |aDataList| is expected to be null for setter callbacks.
   if (aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Update correspondent fields in |mSource| in follow-up patches.
+  mSource->SetIsScanning(mIsScanning);
 
   mPromise->MaybeResolve(JS::UndefinedHandleValue);
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelScanCallback::NotifyError(uint16_t aErrorCode)
 {
@@ -179,23 +184,36 @@ TVServiceChannelSetterCallback::NotifySu
   // |aDataList| is expected to be with only one element.
   if (!aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
   uint32_t length;
   nsresult rv = aDataList->GetLength(&length);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
   if (length != 1) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Update correspondent fields in |mSource| in follow-up patches.
+  nsCOMPtr<nsITVChannelData> channelData = do_QueryElementAt(aDataList, 0);
+  if (NS_WARN_IF(!channelData)) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
+
+  rv = mSource->SetCurrentChannel(channelData);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
 
   mPromise->MaybeResolve(JS::UndefinedHandleValue);
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelSetterCallback::NotifyError(uint16_t aErrorCode)
 {
@@ -216,69 +234,82 @@ TVServiceChannelSetterCallback::NotifyEr
   return NS_ERROR_ILLEGAL_VALUE;
 }
 
 
 /*
  * Implementation of TVServiceTunerGetterCallback
  */
 
-NS_IMPL_CYCLE_COLLECTION(TVServiceTunerGetterCallback, mManager, mPromise)
+NS_IMPL_CYCLE_COLLECTION(TVServiceTunerGetterCallback, mManager)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TVServiceTunerGetterCallback)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TVServiceTunerGetterCallback)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TVServiceTunerGetterCallback)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITVServiceCallback)
 NS_INTERFACE_MAP_END
 
-TVServiceTunerGetterCallback::TVServiceTunerGetterCallback(TVManager* aManager,
-                                                           Promise* aPromise)
+TVServiceTunerGetterCallback::TVServiceTunerGetterCallback(TVManager* aManager)
   : mManager(aManager)
-  , mPromise(aPromise)
 {
   MOZ_ASSERT(mManager);
-  MOZ_ASSERT(mPromise);
 }
 
 TVServiceTunerGetterCallback::~TVServiceTunerGetterCallback()
 {
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceTunerGetterCallback::NotifySuccess(nsIArray* aDataList)
 {
   if (!aDataList) {
-    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    mManager->RejectPendingGetTunersPromises(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Implement in follow-up patches.
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<nsRefPtr<TVTuner>> tuners(length);
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsITVTunerData> tunerData = do_QueryElementAt(aDataList, i);
+    if (NS_WARN_IF(!tunerData)) {
+      continue;
+    }
+
+    nsRefPtr<TVTuner> tuner = TVTuner::Create(mManager->GetOwner(), tunerData);
+    NS_ENSURE_TRUE(tuner, NS_ERROR_DOM_ABORT_ERR);
+
+    tuners.AppendElement(tuner);
+  }
+  mManager->SetTuners(tuners);
 
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceTunerGetterCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
   case nsITVServiceCallback::TV_ERROR_FAILURE:
   case nsITVServiceCallback::TV_ERROR_INVALID_ARG:
-    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    mManager->RejectPendingGetTunersPromises(NS_ERROR_DOM_ABORT_ERR);
     return NS_OK;
   case nsITVServiceCallback::TV_ERROR_NO_SIGNAL:
-    mPromise->MaybeReject(NS_ERROR_DOM_NETWORK_ERR);
+    mManager->RejectPendingGetTunersPromises(NS_ERROR_DOM_NETWORK_ERR);
     return NS_OK;
   case nsITVServiceCallback::TV_ERROR_NOT_SUPPORTED:
-    mPromise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    mManager->RejectPendingGetTunersPromises(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return NS_OK;
   }
 
-  mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  mManager->RejectPendingGetTunersPromises(NS_ERROR_DOM_ABORT_ERR);
   return NS_ERROR_ILLEGAL_VALUE;
 }
 
 
 /*
  * Implementation of TVServiceChannelGetterCallback
  */
 
@@ -308,17 +339,36 @@ TVServiceChannelGetterCallback::~TVServi
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelGetterCallback::NotifySuccess(nsIArray* aDataList)
 {
   if (!aDataList) {
     mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Implement in follow-up patches.
+  uint32_t length;
+  nsresult rv = aDataList->GetLength(&length);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+    return rv;
+  }
+
+  nsTArray<nsRefPtr<TVChannel>> channels(length);
+  for (uint32_t i = 0; i < length; i++) {
+    nsCOMPtr<nsITVChannelData> channelData = do_QueryElementAt(aDataList, i);
+    if (NS_WARN_IF(!channelData)) {
+      mPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return NS_ERROR_DOM_ABORT_ERR;
+    }
+
+    nsRefPtr<TVChannel> channel = new TVChannel(mSource->GetOwner());
+    channels.AppendElement(channel);
+  }
+
+  mPromise->MaybeResolve(channels);
 
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceChannelGetterCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
@@ -369,17 +419,17 @@ TVServiceProgramGetterCallback::~TVServi
 
 /* virtual */ NS_IMETHODIMP
 TVServiceProgramGetterCallback::NotifySuccess(nsIArray* aDataList)
 {
   if (!aDataList) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  // TODO Store the results to IndexedDB in follow-up patches.
+  // TODO Store the results to MozStorage in follow-up patches.
 
   return NS_OK;
 }
 
 /* virtual */ NS_IMETHODIMP
 TVServiceProgramGetterCallback::NotifyError(uint16_t aErrorCode)
 {
   switch (aErrorCode) {
--- a/dom/tv/TVServiceCallbacks.h
+++ b/dom/tv/TVServiceCallbacks.h
@@ -79,24 +79,22 @@ private:
 
 class TVServiceTunerGetterCallback MOZ_FINAL : public nsITVServiceCallback
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSITVSERVICECALLBACK
   NS_DECL_CYCLE_COLLECTION_CLASS(TVServiceTunerGetterCallback)
 
-  TVServiceTunerGetterCallback(TVManager* aManager,
-                               Promise* aPromise);
+  explicit TVServiceTunerGetterCallback(TVManager* aManager);
 
 private:
   ~TVServiceTunerGetterCallback();
 
   nsRefPtr<TVManager> mManager;
-  nsRefPtr<Promise> mPromise;
 };
 
 class TVServiceChannelGetterCallback MOZ_FINAL : public nsITVServiceCallback
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSITVSERVICECALLBACK
   NS_DECL_CYCLE_COLLECTION_CLASS(TVServiceChannelGetterCallback)
--- a/dom/tv/TVServiceFactory.cpp
+++ b/dom/tv/TVServiceFactory.cpp
@@ -12,16 +12,34 @@
 
 namespace mozilla {
 namespace dom {
 
 /* static */ already_AddRefed<FakeTVService>
 TVServiceFactory::CreateFakeTVService()
 {
   nsRefPtr<FakeTVService> service = new FakeTVService();
-  nsresult rv = service->SetSourceListener(new TVSourceListener());
-  NS_ENSURE_SUCCESS(rv, nullptr);
+  return service.forget();
+}
+
+/* static */ already_AddRefed<nsITVService>
+TVServiceFactory::AutoCreateTVService()
+{
+  nsresult rv;
+  nsCOMPtr<nsITVService> service = do_CreateInstance(TV_SERVICE_CONTRACTID);
+  if (!service) {
+    // Fallback to the fake service.
+    service = do_CreateInstance(FAKE_TV_SERVICE_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+  }
+
+  rv = service->SetSourceListener(new TVSourceListener());
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
 
   return service.forget();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVServiceFactory.h
+++ b/dom/tv/TVServiceFactory.h
@@ -15,14 +15,16 @@ namespace mozilla {
 namespace dom {
 
 class FakeTVService;
 
 class TVServiceFactory
 {
 public:
   static already_AddRefed<FakeTVService> CreateFakeTVService();
+
+  static already_AddRefed<nsITVService> AutoCreateTVService();
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_TVServiceFactory_h
--- a/dom/tv/TVSource.cpp
+++ b/dom/tv/TVSource.cpp
@@ -1,138 +1,408 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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/Promise.h"
+#include "mozilla/dom/TVChannel.h"
+#include "mozilla/dom/TVCurrentChannelChangedEvent.h"
+#include "mozilla/dom/TVEITBroadcastedEvent.h"
+#include "mozilla/dom/TVListeners.h"
+#include "mozilla/dom/TVScanningStateChangedEvent.h"
+#include "mozilla/dom/TVServiceCallbacks.h"
+#include "mozilla/dom/TVServiceFactory.h"
+#include "mozilla/dom/TVTuner.h"
+#include "mozilla/dom/TVUtils.h"
+#include "nsITVService.h"
+#include "nsServiceManagerUtils.h"
 #include "TVSource.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(TVSource)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TVSource,
                                                   DOMEventTargetHelper)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTVService)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTuner)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCurrentChannel)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TVSource,
                                                 DOMEventTargetHelper)
+  tmp->Shutdown();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTVService)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mTuner)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCurrentChannel)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_ADDREF_INHERITED(TVSource, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVSource, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVSource)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-TVSource::TVSource(nsPIDOMWindow* aWindow)
+TVSource::TVSource(nsPIDOMWindow* aWindow,
+                   TVSourceType aType,
+                   TVTuner* aTuner)
   : DOMEventTargetHelper(aWindow)
+  , mTuner(aTuner)
+  , mType(aType)
+  , mIsScanning(false)
 {
+  MOZ_ASSERT(mTuner);
 }
 
 TVSource::~TVSource()
 {
+  Shutdown();
+}
+
+/* static */ already_AddRefed<TVSource>
+TVSource::Create(nsPIDOMWindow* aWindow,
+                 TVSourceType aType,
+                 TVTuner* aTuner)
+{
+  nsRefPtr<TVSource> source = new TVSource(aWindow, aType, aTuner);
+  return (source->Init()) ? source.forget() : nullptr;
+}
+
+bool
+TVSource::Init()
+{
+  mTVService = TVServiceFactory::AutoCreateTVService();
+  NS_ENSURE_TRUE(mTVService, false);
+
+  nsCOMPtr<nsITVSourceListener> sourceListener;
+  mTVService->GetSourceListener(getter_AddRefs(sourceListener));
+  NS_ENSURE_TRUE(sourceListener, false);
+  (static_cast<TVSourceListener*>(sourceListener.get()))->RegisterSource(this);
+
+  return true;
+}
+
+void
+TVSource::Shutdown()
+{
+  if (!mTVService) {
+    return;
+  }
+
+  nsCOMPtr<nsITVSourceListener> sourceListener;
+  mTVService->GetSourceListener(getter_AddRefs(sourceListener));
+  if (!sourceListener) {
+    return;
+  }
+  (static_cast<TVSourceListener*>(sourceListener.get()))->UnregisterSource(this);
 }
 
 /* virtual */ JSObject*
 TVSource::WrapObject(JSContext* aCx)
 {
   return TVSourceBinding::Wrap(aCx, this);
 }
 
+nsresult
+TVSource::SetCurrentChannel(nsITVChannelData* aChannelData)
+{
+  if (!aChannelData) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsString newChannelNumber;
+  nsresult rv = aChannelData->GetNumber(newChannelNumber);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (newChannelNumber.IsEmpty()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (mCurrentChannel) {
+    nsString currentChannelNumber;
+    mCurrentChannel->GetNumber(currentChannelNumber);
+    if (newChannelNumber.Equals(currentChannelNumber)) {
+      // No actual change.
+      return NS_OK;
+    }
+  }
+
+  // TODO Use channel data to initialize TVChannel in follow-up patches.
+  mCurrentChannel = new TVChannel(GetOwner());
+
+  return DispatchCurrentChannelChangedEvent(mCurrentChannel);
+}
+
+nsresult
+TVSource::UnsetCurrentChannel()
+{
+  mCurrentChannel = nullptr;
+  return DispatchCurrentChannelChangedEvent(mCurrentChannel);
+}
+
+void
+TVSource::SetIsScanning(bool aIsScanning)
+{
+  mIsScanning = aIsScanning;
+}
+
+nsresult
+TVSource::DispatchTVEvent(nsIDOMEvent* aEvent)
+{
+  return DispatchTrustedEvent(aEvent);
+}
+
 already_AddRefed<Promise>
 TVSource::GetChannels(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  // The operation is prohibited when the source is scanning channels.
+  if (mIsScanning) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  nsString tunerId;
+  mTuner->GetId(tunerId);
+
+  nsCOMPtr<nsITVServiceCallback> callback =
+    new TVServiceChannelGetterCallback(this, promise);
+  nsresult rv =
+    mTVService->GetChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
-TVSource::SetCurrentChannel(const nsAString& aChannelNumber, ErrorResult& aRv)
+TVSource::SetCurrentChannel(const nsAString& aChannelNumber,
+                            ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  // The operation is prohibited when the source is scanning channels.
+  if (mIsScanning) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  nsString tunerId;
+  mTuner->GetId(tunerId);
+
+  nsCOMPtr<nsITVServiceCallback> callback =
+    new TVServiceChannelSetterCallback(this, promise, aChannelNumber);
+  nsresult rv =
+    mTVService->SetChannel(tunerId, ToTVSourceTypeStr(mType), aChannelNumber, callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TVSource::StartScanning(const TVStartScanningOptions& aOptions,
                         ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  nsString tunerId;
+  mTuner->GetId(tunerId);
+
+  bool isRescanned = aOptions.mIsRescanned.WasPassed() &&
+                     aOptions.mIsRescanned.Value();
+
+  if (isRescanned) {
+    nsresult rv = mTVService->ClearScannedChannelsCache();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return promise.forget();
+    }
+
+    rv = DispatchScanningStateChangedEvent(TVScanningState::Cleared, nullptr);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+      return promise.forget();
+    }
+  }
+
+  // |SetIsScanning(bool)| should be called once |notifySuccess()| of this
+  // callback is invoked.
+  nsCOMPtr<nsITVServiceCallback> callback =
+    new TVServiceChannelScanCallback(this, promise, true);
+  nsresult rv =
+    mTVService->StartScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TVSource::StopScanning(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  nsString tunerId;
+  mTuner->GetId(tunerId);
+
+  // |SetIsScanning(bool)| should be called once |notifySuccess()| of this
+  // callback is invoked.
+  nsCOMPtr<nsITVServiceCallback> callback =
+    new TVServiceChannelScanCallback(this, promise, false);
+  nsresult rv =
+    mTVService->StopScanningChannels(tunerId, ToTVSourceTypeStr(mType), callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
 
   return promise.forget();
 }
 
 already_AddRefed<TVTuner>
 TVSource::Tuner() const
 {
-  // TODO Implement in follow-up patches.
-  return nullptr;
+  nsRefPtr<TVTuner> tuner = mTuner;
+  return tuner.forget();
 }
 
 TVSourceType
 TVSource::Type() const
 {
-  // TODO Implement in follow-up patches.
-  return TVSourceType::Dvb_t;
+  return mType;
 }
 
 bool
 TVSource::IsScanning() const
 {
-  // TODO Implement in follow-up patches.
-  return false;
+  return mIsScanning;
 }
 
 already_AddRefed<TVChannel>
 TVSource::GetCurrentChannel() const
 {
-  // TODO Implement in follow-up patches.
-  return nullptr;
+  nsRefPtr<TVChannel> currentChannel = mCurrentChannel;
+  return currentChannel.forget();
+}
+
+nsresult
+TVSource::NotifyChannelScanned(nsITVChannelData* aChannelData)
+{
+  // TODO Use channel data to initialize TVChannel in follow-up patches.
+  nsRefPtr<TVChannel> channel = new TVChannel(GetOwner());
+  return DispatchScanningStateChangedEvent(TVScanningState::Scanned, channel);
+}
+
+nsresult
+TVSource::NotifyChannelScanComplete()
+{
+  SetIsScanning(false);
+  return DispatchScanningStateChangedEvent(TVScanningState::Completed, nullptr);
+}
+
+nsresult
+TVSource::NotifyChannelScanStopped()
+{
+  SetIsScanning(false);
+  return DispatchScanningStateChangedEvent(TVScanningState::Stopped, nullptr);
+}
+
+nsresult
+TVSource::NotifyEITBroadcasted(nsITVChannelData* aChannelData,
+                               nsITVProgramData** aProgramDataList,
+                               uint32_t aCount)
+{
+  // TODO Use channel data to initialize TVChannel in follow-up patches.
+  nsRefPtr<TVChannel> channel = new TVChannel(GetOwner());
+  Sequence<OwningNonNull<TVProgram>> programs;
+  for (uint32_t i = 0; i < aCount; i++) {
+    // TODO Use program data to initialize TVProgram in follow-up patches.
+    nsRefPtr<TVProgram> program = new TVProgram(GetOwner());
+    *programs.AppendElement() = program;
+  }
+  return DispatchEITBroadcastedEvent(programs);
+}
+
+nsresult
+TVSource::DispatchCurrentChannelChangedEvent(TVChannel* aChannel)
+{
+  TVCurrentChannelChangedEventInit init;
+  init.mChannel = aChannel;
+  nsCOMPtr<nsIDOMEvent> event =
+    TVCurrentChannelChangedEvent::Constructor(this,
+                                              NS_LITERAL_STRING("currentchannelchanged"),
+                                              init);
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethodWithArg<nsCOMPtr<nsIDOMEvent>>(this,
+                                                       &TVSource::DispatchTVEvent,
+                                                       event);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVSource::DispatchScanningStateChangedEvent(TVScanningState aState,
+                                            TVChannel* aChannel)
+{
+  TVScanningStateChangedEventInit init;
+  init.mState = aState;
+  init.mChannel = aChannel;
+  nsCOMPtr<nsIDOMEvent> event =
+    TVScanningStateChangedEvent::Constructor(this,
+                                             NS_LITERAL_STRING("scanningstatechanged"),
+                                             init);
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethodWithArg<nsCOMPtr<nsIDOMEvent>>(this,
+                                                       &TVSource::DispatchTVEvent,
+                                                       event);
+  return NS_DispatchToCurrentThread(runnable);
+}
+
+nsresult
+TVSource::DispatchEITBroadcastedEvent(const Sequence<OwningNonNull<TVProgram>>& aPrograms)
+{
+  TVEITBroadcastedEventInit init;
+  init.mPrograms = aPrograms;
+  nsCOMPtr<nsIDOMEvent> event =
+    TVEITBroadcastedEvent::Constructor(this,
+                                       NS_LITERAL_STRING("eitbroadcasted"),
+                                       init);
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethodWithArg<nsCOMPtr<nsIDOMEvent>>(this,
+                                                       &TVSource::DispatchTVEvent,
+                                                       event);
+  return NS_DispatchToCurrentThread(runnable);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVSource.h
+++ b/dom/tv/TVSource.h
@@ -1,40 +1,68 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_TVSource_h__
-#define mozilla_dom_TVSource_h__
+#ifndef mozilla_dom_TVSource_h
+#define mozilla_dom_TVSource_h
 
 #include "mozilla/DOMEventTargetHelper.h"
+// Include TVScanningStateChangedEventBinding.h since enum TVScanningState can't
+// be forward declared.
+#include "mozilla/dom/TVScanningStateChangedEventBinding.h"
 // Include TVSourceBinding.h since enum TVSourceType can't be forward declared.
 #include "mozilla/dom/TVSourceBinding.h"
 
+class nsITVChannelData;
+class nsITVProgramData;
+class nsITVService;
+
 namespace mozilla {
 namespace dom {
 
 class Promise;
 class TVChannel;
+class TVProgram;
 class TVTuner;
 
 class TVSource MOZ_FINAL : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVSource, DOMEventTargetHelper)
 
-  explicit TVSource(nsPIDOMWindow* aWindow);
+  static already_AddRefed<TVSource> Create(nsPIDOMWindow* aWindow,
+                                           TVSourceType aType,
+                                           TVTuner* aTuner);
 
   // WebIDL (internal functions)
 
   virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
 
+  nsresult SetCurrentChannel(nsITVChannelData* aChannelData);
+
+  nsresult UnsetCurrentChannel();
+
+  void SetIsScanning(bool aIsScanning);
+
+  nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
+
+  nsresult NotifyChannelScanned(nsITVChannelData* aChannelData);
+
+  nsresult NotifyChannelScanComplete();
+
+  nsresult NotifyChannelScanStopped();
+
+  nsresult NotifyEITBroadcasted(nsITVChannelData* aChannelData,
+                                nsITVProgramData** aProgramDataList,
+                                uint32_t aCount);
+
   // WebIDL (public APIs)
 
   already_AddRefed<Promise> GetChannels(ErrorResult& aRv);
 
   already_AddRefed<Promise> SetCurrentChannel(const nsAString& aChannelNumber,
                                               ErrorResult& aRv);
 
   already_AddRefed<Promise> StartScanning(const TVStartScanningOptions& aOptions,
@@ -50,16 +78,36 @@ public:
 
   already_AddRefed<TVChannel> GetCurrentChannel() const;
 
   IMPL_EVENT_HANDLER(currentchannelchanged);
   IMPL_EVENT_HANDLER(eitbroadcasted);
   IMPL_EVENT_HANDLER(scanningstatechanged);
 
 private:
+  TVSource(nsPIDOMWindow* aWindow,
+           TVSourceType aType,
+           TVTuner* aTuner);
+
   ~TVSource();
 
+  bool Init();
+
+  void Shutdown();
+
+  nsresult DispatchCurrentChannelChangedEvent(TVChannel* aChannel);
+
+  nsresult DispatchScanningStateChangedEvent(TVScanningState aState,
+                                             TVChannel* aChannel);
+
+  nsresult DispatchEITBroadcastedEvent(const Sequence<OwningNonNull<TVProgram>>& aPrograms);
+
+  nsCOMPtr<nsITVService> mTVService;
+  nsRefPtr<TVTuner> mTuner;
+  nsRefPtr<TVChannel> mCurrentChannel;
+  TVSourceType mType;
+  bool mIsScanning;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_TVSource_h__
+#endif // mozilla_dom_TVSource_h
--- a/dom/tv/TVTuner.cpp
+++ b/dom/tv/TVTuner.cpp
@@ -1,104 +1,222 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "DOMMediaStream.h"
 #include "mozilla/dom/Promise.h"
+#include "mozilla/dom/TVCurrentSourceChangedEvent.h"
+#include "mozilla/dom/TVServiceCallbacks.h"
+#include "mozilla/dom/TVServiceFactory.h"
+#include "mozilla/dom/TVSource.h"
+#include "mozilla/dom/TVUtils.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITVService.h"
+#include "nsServiceManagerUtils.h"
 #include "TVTuner.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_CLASS(TVTuner)
-
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TVTuner,
-                                                  DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TVTuner,
-                                                DOMEventTargetHelper)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_INHERITED(TVTuner, DOMEventTargetHelper,
+                                   mTVService, mStream, mCurrentSource, mSources)
 
 NS_IMPL_ADDREF_INHERITED(TVTuner, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(TVTuner, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TVTuner)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 TVTuner::TVTuner(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
 }
 
 TVTuner::~TVTuner()
 {
 }
 
+/* static */ already_AddRefed<TVTuner>
+TVTuner::Create(nsPIDOMWindow* aWindow,
+                nsITVTunerData* aData)
+{
+  nsRefPtr<TVTuner> tuner = new TVTuner(aWindow);
+  return (tuner->Init(aData)) ? tuner.forget() : nullptr;
+}
+
+bool
+TVTuner::Init(nsITVTunerData* aData)
+{
+  NS_ENSURE_TRUE(aData, false);
+
+  nsresult rv = aData->GetId(mId);
+  NS_ENSURE_SUCCESS(rv, false);
+  if (NS_WARN_IF(mId.IsEmpty())) {
+    return false;
+  }
+
+  uint32_t count;
+  char** supportedSourceTypes;
+  rv = aData->GetSupportedSourceTypes(&count, &supportedSourceTypes);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  for (uint32_t i = 0; i < count; i++) {
+    TVSourceType sourceType = ToTVSourceType(supportedSourceTypes[i]);
+    if (NS_WARN_IF(sourceType == TVSourceType::EndGuard_)) {
+      continue;
+    }
+
+    // Generate the source instance based on the supported source type.
+    nsRefPtr<TVSource> source = TVSource::Create(GetOwner(), sourceType, this);
+    if (NS_WARN_IF(!source)) {
+      continue;
+    }
+
+    mSupportedSourceTypes.AppendElement(sourceType);
+    mSources.AppendElement(source);
+  }
+  NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(count, supportedSourceTypes);
+
+  mTVService = TVServiceFactory::AutoCreateTVService();
+  NS_ENSURE_TRUE(mTVService, false);
+
+  return true;
+}
+
 /* virtual */ JSObject*
 TVTuner::WrapObject(JSContext* aCx)
 {
   return TVTunerBinding::Wrap(aCx, this);
 }
 
+nsresult
+TVTuner::SetCurrentSource(TVSourceType aSourceType)
+{
+  ErrorResult error;
+  if (mCurrentSource) {
+    if (aSourceType == mCurrentSource->Type()) {
+      // No actual change.
+      return NS_OK;
+    }
+
+    // No need to stay tuned for non-current sources.
+    nsresult rv = mCurrentSource->UnsetCurrentChannel();
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+
+  for (uint32_t i = 0; i < mSources.Length(); i++) {
+    if (aSourceType == mSources[i]->Type()) {
+      mCurrentSource = mSources[i];
+      break;
+    }
+  }
+
+  nsresult rv = InitMediaStream();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  return DispatchCurrentSourceChangedEvent(mCurrentSource);
+}
+
+nsresult
+TVTuner::DispatchTVEvent(nsIDOMEvent* aEvent)
+{
+  return DispatchTrustedEvent(aEvent);
+}
+
 void
 TVTuner::GetSupportedSourceTypes(nsTArray<TVSourceType>& aSourceTypes,
                                  ErrorResult& aRv) const
 {
-  // TODO Implement in follow-up patches.
+  aSourceTypes = mSupportedSourceTypes;
 }
 
 already_AddRefed<Promise>
 TVTuner::GetSources(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  promise->MaybeResolve(mSources);
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TVTuner::SetCurrentSource(const TVSourceType aSourceType, ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetOwner());
   MOZ_ASSERT(global);
 
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  // TODO Resolve/reject the promise in follow-up patches.
+  // |SetCurrentSource(const TVSourceType)| will be called once |notifySuccess|
+  // of the callback is invoked.
+  nsCOMPtr<nsITVServiceCallback> callback =
+    new TVServiceSourceSetterCallback(this, promise, aSourceType);
+  nsresult rv = mTVService->SetSource(mId, ToTVSourceTypeStr(aSourceType), callback);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
+  }
 
   return promise.forget();
 }
 
 void
 TVTuner::GetId(nsAString& aId) const
 {
-  // TODO Implement in follow-up patches.
+  aId = mId;
 }
 
 already_AddRefed<TVSource>
 TVTuner::GetCurrentSource() const
 {
-  // TODO Implement in follow-up patches.
-  return nullptr;
+  nsRefPtr<TVSource> currentSource = mCurrentSource;
+  return currentSource.forget();
 }
 
 already_AddRefed<DOMMediaStream>
 TVTuner::GetStream() const
 {
-  // TODO Implement in follow-up patches.
-  return nullptr;
+  nsRefPtr<DOMMediaStream> stream = mStream;
+  return stream.forget();
+}
+
+nsresult
+TVTuner::InitMediaStream()
+{
+  // TODO Instantiate |mStream| when bug 987498 is done.
+
+  return NS_OK;
+}
+
+nsresult
+TVTuner::DispatchCurrentSourceChangedEvent(TVSource* aSource)
+{
+  TVCurrentSourceChangedEventInit init;
+  init.mSource = aSource;
+  nsCOMPtr<nsIDOMEvent> event =
+    TVCurrentSourceChangedEvent::Constructor(this,
+                                             NS_LITERAL_STRING("currentsourcechanged"),
+                                             init);
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethodWithArg<nsCOMPtr<nsIDOMEvent>>(this,
+                                                       &TVTuner::DispatchTVEvent,
+                                                       event);
+  return NS_DispatchToCurrentThread(runnable);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/tv/TVTuner.h
+++ b/dom/tv/TVTuner.h
@@ -1,42 +1,50 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_TVTuner_h__
-#define mozilla_dom_TVTuner_h__
+#ifndef mozilla_dom_TVTuner_h
+#define mozilla_dom_TVTuner_h
 
 #include "mozilla/DOMEventTargetHelper.h"
 // Include TVTunerBinding.h since enum TVSourceType can't be forward declared.
 #include "mozilla/dom/TVTunerBinding.h"
 
+class nsITVService;
+class nsITVTunerData;
+
 namespace mozilla {
 
 class DOMMediaStream;
 
 namespace dom {
 
 class Promise;
 class TVSource;
 
 class TVTuner MOZ_FINAL : public DOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TVTuner, DOMEventTargetHelper)
 
-  explicit TVTuner(nsPIDOMWindow* aWindow);
+  static already_AddRefed<TVTuner> Create(nsPIDOMWindow* aWindow,
+                                          nsITVTunerData* aData);
 
   // WebIDL (internal functions)
 
   virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
 
+  nsresult SetCurrentSource(TVSourceType aSourceType);
+
+  nsresult DispatchTVEvent(nsIDOMEvent* aEvent);
+
   // WebIDL (public APIs)
 
   void GetSupportedSourceTypes(nsTArray<TVSourceType>& aSourceTypes,
                                ErrorResult& aRv) const;
 
   already_AddRefed<Promise> GetSources(ErrorResult& aRv);
 
   already_AddRefed<Promise> SetCurrentSource(const TVSourceType aSourceType,
@@ -46,16 +54,30 @@ public:
 
   already_AddRefed<TVSource> GetCurrentSource() const;
 
   already_AddRefed<DOMMediaStream> GetStream() const;
 
   IMPL_EVENT_HANDLER(currentsourcechanged);
 
 private:
+  explicit TVTuner(nsPIDOMWindow* aWindow);
+
   ~TVTuner();
 
+  bool Init(nsITVTunerData* aData);
+
+  nsresult InitMediaStream();
+
+  nsresult DispatchCurrentSourceChangedEvent(TVSource* aSource);
+
+  nsCOMPtr<nsITVService> mTVService;
+  nsRefPtr<DOMMediaStream> mStream;
+  nsRefPtr<TVSource> mCurrentSource;
+  nsTArray<nsRefPtr<TVSource>> mSources;
+  nsString mId;
+  nsTArray<TVSourceType> mSupportedSourceTypes;
 };
 
 } // namespace dom
 } // namespace mozilla
 
-#endif // mozilla_dom_TVTuner_h__
+#endif // mozilla_dom_TVTuner_h