dom/speakermanager/SpeakerManager.cpp
author Seth Fowler <seth@mozilla.com>
Tue, 17 Dec 2013 14:04:32 -0800
changeset 171152 4ed374e9a2902a17361b4d0ec4b32f2c74046eb9
parent 167603 5e6f5a0ef9cdb952564d1ff5f610b42da6af6bb2
child 182897 8b872592e44140baa088e3af474eaca9eb1d2701
permissions -rw-r--r--
Bug 862909 (Part 3) - Assert that we own the decoding mutex in CurrentStatusTracker. r=tn

/* 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 "SpeakerManager.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMEventListener.h"
#include "SpeakerManagerService.h"
#include "nsIPermissionManager.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIDocShell.h"
#include "nsDOMEvent.h"
#include "AudioChannelService.h"

using namespace mozilla::dom;

NS_IMPL_QUERY_INTERFACE_INHERITED1(SpeakerManager, nsDOMEventTargetHelper,
                                   nsIDOMEventListener)
NS_IMPL_ADDREF_INHERITED(SpeakerManager, nsDOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SpeakerManager, nsDOMEventTargetHelper)

SpeakerManager::SpeakerManager()
  : mForcespeaker(false)
  , mVisible(false)
{
  SetIsDOMBinding();
  SpeakerManagerService *service =
    SpeakerManagerService::GetSpeakerManagerService();
  if (service) {
    service->RegisterSpeakerManager(this);
  }
}

SpeakerManager::~SpeakerManager()
{
  SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
  if (service) {
    service->UnRegisterSpeakerManager(this);
  }
  nsCOMPtr<EventTarget> target = do_QueryInterface(GetOwner());
  NS_ENSURE_TRUE_VOID(target);

  target->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
                                    this,
                                    /* useCapture = */ true);
}

bool
SpeakerManager::Speakerforced()
{
  // If a background app calls forcespeaker=true that doesn't change anything.
  // 'speakerforced' remains false everywhere.
  if (mForcespeaker && !mVisible) {
    return false;
  }
  SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
  if (service) {
    return service->GetSpeakerStatus();
  }
  return false;
}

bool
SpeakerManager::Forcespeaker()
{
  return mForcespeaker;
}

void
SpeakerManager::SetForcespeaker(bool aEnable)
{
  SpeakerManagerService *service = SpeakerManagerService::GetSpeakerManagerService();
  if (service) {
    service->ForceSpeaker(aEnable, mVisible);
  }
  mForcespeaker = aEnable;
}

void
SpeakerManager::DispatchSimpleEvent(const nsAString& aStr)
{
  NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  nsresult rv = CheckInnerWindowCorrectness();
  if (NS_FAILED(rv)) {
    return;
  }

  nsCOMPtr<nsIDOMEvent> event;
  rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
  if (NS_FAILED(rv)) {
    NS_WARNING("Failed to create the error event!!!");
    return;
  }
  rv = event->InitEvent(aStr, false, false);

  if (NS_FAILED(rv)) {
    NS_WARNING("Failed to init the error event!!!");
    return;
  }

  event->SetTrusted(true);

  rv = DispatchDOMEvent(nullptr, event, nullptr, nullptr);
  if (NS_FAILED(rv)) {
    NS_ERROR("Failed to dispatch the event!!!");
    return;
  }
}

void
SpeakerManager::Init(nsPIDOMWindow* aWindow)
{
  BindToOwner(aWindow);

  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
  NS_ENSURE_TRUE_VOID(docshell);
  docshell->GetIsActive(&mVisible);

  nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(GetOwner());
  NS_ENSURE_TRUE_VOID(target);

  target->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
                                 this,
                                 /* useCapture = */ true,
                                 /* wantsUntrusted = */ false);
}

nsPIDOMWindow*
SpeakerManager::GetParentObject() const
{
  return GetOwner();
}

/* static */ already_AddRefed<SpeakerManager>
SpeakerManager::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
{
  nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
  if (!sgo) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
  if (!ownerWindow) {
    aRv.Throw(NS_ERROR_FAILURE);
    return nullptr;
  }

  nsCOMPtr<nsIPermissionManager> permMgr =
    do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
  NS_ENSURE_TRUE(permMgr, nullptr);

  uint32_t permission = nsIPermissionManager::DENY_ACTION;
  nsresult rv =
    permMgr->TestPermissionFromWindow(ownerWindow, "speaker-control",
                                      &permission);
  NS_ENSURE_SUCCESS(rv, nullptr);

  if (permission != nsIPermissionManager::ALLOW_ACTION) {
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    return nullptr;
  }

  nsRefPtr<SpeakerManager> object = new SpeakerManager();
  object->Init(ownerWindow);
  return object.forget();
}

JSObject*
SpeakerManager::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
  return MozSpeakerManagerBinding::Wrap(aCx, aScope, this);
}

NS_IMETHODIMP
SpeakerManager::HandleEvent(nsIDOMEvent* aEvent)
{
  nsAutoString type;
  aEvent->GetType(type);

  if (!type.EqualsLiteral("visibilitychange")) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIDocShell> docshell = do_GetInterface(GetOwner());
  NS_ENSURE_TRUE(docshell, NS_ERROR_FAILURE);
  docshell->GetIsActive(&mVisible);

  // If an app that has called forcespeaker=true is switched
  // from the background to the foreground 'speakerforced'
  // switches to true in all apps. I.e. the app doesn't have to
  // call forcespeaker=true again when it comes into foreground.
  SpeakerManagerService *service =
    SpeakerManagerService::GetSpeakerManagerService();
  if (service && mVisible && mForcespeaker) {
    service->ForceSpeaker(mForcespeaker, mVisible);
  }
  // If an application that has called forcespeaker=true, but no audio is
  // currently playing in the app itself, if application switch to
  // the background, we switch 'speakerforced' to false.
  if (!mVisible && mForcespeaker) {
    AudioChannelService* audioChannelService =
      AudioChannelService::GetAudioChannelService();
    if (audioChannelService && !audioChannelService->AnyAudioChannelIsActive()) {
      service->ForceSpeaker(false, mVisible);
    }
  }
  return NS_OK;
}

void
SpeakerManager::SetAudioChannelActive(bool isActive)
{
  if (!isActive && !mVisible) {
    SpeakerManagerService *service =
      SpeakerManagerService::GetSpeakerManagerService();
    if (service) {
      service->ForceSpeaker(false, mVisible);
    }
  }
}