diff -r 000000000000 -r 6474c204b198 dom/telephony/TelephonyCallGroup.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dom/telephony/TelephonyCallGroup.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,311 @@ +/* -*- 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 "TelephonyCallGroup.h" +#include "mozilla/dom/TelephonyCallGroupBinding.h" + +#include "CallEvent.h" +#include "CallsList.h" +#include "mozilla/dom/CallGroupErrorEvent.h" +#include "Telephony.h" + +using namespace mozilla::dom; +using mozilla::ErrorResult; + +TelephonyCallGroup::TelephonyCallGroup(nsPIDOMWindow* aOwner) + : DOMEventTargetHelper(aOwner) + , mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN) +{ +} + +TelephonyCallGroup::~TelephonyCallGroup() +{ +} + +// static +already_AddRefed +TelephonyCallGroup::Create(Telephony* aTelephony) +{ + NS_ASSERTION(aTelephony, "Null telephony!"); + + nsRefPtr group = + new TelephonyCallGroup(aTelephony->GetOwner()); + + group->mTelephony = aTelephony; + group->mCallsList = new CallsList(aTelephony, group); + + return group.forget(); +} + +JSObject* +TelephonyCallGroup::WrapObject(JSContext* aCx) +{ + return TelephonyCallGroupBinding::Wrap(aCx, this); +} + +void +TelephonyCallGroup::AddCall(TelephonyCall* aCall) +{ + NS_ASSERTION(!mCalls.Contains(aCall), "Already know about this one!"); + mCalls.AppendElement(aCall); + aCall->ChangeGroup(this); + NotifyCallsChanged(aCall); +} + +void +TelephonyCallGroup::RemoveCall(TelephonyCall* aCall) +{ + NS_ASSERTION(mCalls.Contains(aCall), "Didn't know about this one!"); + mCalls.RemoveElement(aCall); + aCall->ChangeGroup(nullptr); + NotifyCallsChanged(aCall); +} + +nsresult +TelephonyCallGroup::NotifyError(const nsAString& aName, const nsAString& aMessage) +{ + CallGroupErrorEventInit init; + init.mBubbles = false; + init.mCancelable = false; + init.mName = aName; + init.mMessage = aMessage; + + nsRefPtr event = + CallGroupErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init); + + return DispatchTrustedEvent(event); +} + +void +TelephonyCallGroup::ChangeState(uint16_t aCallState) +{ + if (mCallState == aCallState) { + return; + } + + nsString stateString; + switch (aCallState) { + case nsITelephonyProvider::CALL_STATE_UNKNOWN: + break; + case nsITelephonyProvider::CALL_STATE_CONNECTED: + stateString.AssignLiteral("connected"); + break; + case nsITelephonyProvider::CALL_STATE_HOLDING: + stateString.AssignLiteral("holding"); + break; + case nsITelephonyProvider::CALL_STATE_HELD: + stateString.AssignLiteral("held"); + break; + case nsITelephonyProvider::CALL_STATE_RESUMING: + stateString.AssignLiteral("resuming"); + break; + default: + NS_NOTREACHED("Unknown state!"); + } + + mState = stateString; + mCallState = aCallState; + + nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to dispatch specific event!"); + } + if (!stateString.IsEmpty()) { + // This can change if the statechange handler called back here... Need to + // figure out something smarter. + if (mCallState == aCallState) { + rv = DispatchCallEvent(stateString, nullptr); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to dispatch specific event!"); + } + } + } + + for (uint32_t index = 0; index < mCalls.Length(); index++) { + nsRefPtr call = mCalls[index]; + call->ChangeState(aCallState); + + MOZ_ASSERT(call->CallState() == aCallState); + } +} + +nsresult +TelephonyCallGroup::NotifyCallsChanged(TelephonyCall* aCall) +{ + return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall); +} + +nsresult +TelephonyCallGroup::DispatchCallEvent(const nsAString& aType, + TelephonyCall* aCall) +{ + nsRefPtr event = CallEvent::Create(this, aType, aCall, false, false); + return DispatchTrustedEvent(event); +} + +bool +TelephonyCallGroup::CanConference(const TelephonyCall& aCall, + TelephonyCall* aSecondCall) +{ + if (!aCall.Mergeable()) { + return false; + } + + if (!aSecondCall) { + MOZ_ASSERT(!mCalls.IsEmpty()); + + return (mCallState == nsITelephonyProvider::CALL_STATE_CONNECTED && + aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD) || + (mCallState == nsITelephonyProvider::CALL_STATE_HELD && + aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED); + } + + MOZ_ASSERT(mCallState == nsITelephonyProvider::CALL_STATE_UNKNOWN); + + if (aCall.ServiceId() != aSecondCall->ServiceId()) { + return false; + } + + if (!aSecondCall->Mergeable()) { + return false; + } + + return (aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED && + aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_HELD) || + (aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD && + aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED); +} + +already_AddRefed +TelephonyCallGroup::GetCall(uint32_t aServiceId, uint32_t aCallIndex) +{ + nsRefPtr call; + + for (uint32_t index = 0; index < mCalls.Length(); index++) { + nsRefPtr& tempCall = mCalls[index]; + if (tempCall->ServiceId() == aServiceId && + tempCall->CallIndex() == aCallIndex) { + call = tempCall; + break; + } + } + + return call.forget(); +} + +NS_IMPL_CYCLE_COLLECTION_CLASS(TelephonyCallGroup) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TelephonyCallGroup, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TelephonyCallGroup, + DOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTelephony) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCallGroup) +NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) + +NS_IMPL_ADDREF_INHERITED(TelephonyCallGroup, DOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(TelephonyCallGroup, DOMEventTargetHelper) + +// WebIDL +already_AddRefed +TelephonyCallGroup::Calls() const +{ + nsRefPtr list = mCallsList; + return list.forget(); +} + +void +TelephonyCallGroup::Add(TelephonyCall& aCall, + ErrorResult& aRv) +{ + if (!CanConference(aCall, nullptr)) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + aRv = mTelephony->Provider()->ConferenceCall(aCall.ServiceId()); +} + +void +TelephonyCallGroup::Add(TelephonyCall& aCall, + TelephonyCall& aSecondCall, + ErrorResult& aRv) +{ + if (!CanConference(aCall, &aSecondCall)) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + aRv = mTelephony->Provider()->ConferenceCall(aCall.ServiceId()); +} + +void +TelephonyCallGroup::Remove(TelephonyCall& aCall, ErrorResult& aRv) +{ + if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { + NS_WARNING("Remove call from a non-connected call group. Ignore!"); + return; + } + + uint32_t serviceId = aCall.ServiceId(); + uint32_t callIndex = aCall.CallIndex(); + + nsRefPtr call; + + call = GetCall(serviceId, callIndex); + if (call) { + aRv = mTelephony->Provider()->SeparateCall(serviceId, callIndex); + } else { + NS_WARNING("Didn't have this call. Ignore!"); + } +} + +void +TelephonyCallGroup::Hold(ErrorResult& aRv) +{ + if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { + NS_WARNING("Hold non-connected call ignored!"); + return; + } + + MOZ_ASSERT(!mCalls.IsEmpty()); + + nsresult rv = mTelephony->Provider()->HoldConference(mCalls[0]->ServiceId()); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + ChangeState(nsITelephonyProvider::CALL_STATE_HOLDING); +} + +void +TelephonyCallGroup::Resume(ErrorResult& aRv) +{ + if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) { + NS_WARNING("Resume non-held call ignored!"); + return; + } + + MOZ_ASSERT(!mCalls.IsEmpty()); + + nsresult rv = mTelephony->Provider()->ResumeConference(mCalls[0]->ServiceId()); + if (NS_FAILED(rv)) { + aRv.Throw(rv); + return; + } + + ChangeState(nsITelephonyProvider::CALL_STATE_RESUMING); +}