michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "TelephonyCallGroup.h" michael@0: #include "mozilla/dom/TelephonyCallGroupBinding.h" michael@0: michael@0: #include "CallEvent.h" michael@0: #include "CallsList.h" michael@0: #include "mozilla/dom/CallGroupErrorEvent.h" michael@0: #include "Telephony.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: using mozilla::ErrorResult; michael@0: michael@0: TelephonyCallGroup::TelephonyCallGroup(nsPIDOMWindow* aOwner) michael@0: : DOMEventTargetHelper(aOwner) michael@0: , mCallState(nsITelephonyProvider::CALL_STATE_UNKNOWN) michael@0: { michael@0: } michael@0: michael@0: TelephonyCallGroup::~TelephonyCallGroup() michael@0: { michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: TelephonyCallGroup::Create(Telephony* aTelephony) michael@0: { michael@0: NS_ASSERTION(aTelephony, "Null telephony!"); michael@0: michael@0: nsRefPtr group = michael@0: new TelephonyCallGroup(aTelephony->GetOwner()); michael@0: michael@0: group->mTelephony = aTelephony; michael@0: group->mCallsList = new CallsList(aTelephony, group); michael@0: michael@0: return group.forget(); michael@0: } michael@0: michael@0: JSObject* michael@0: TelephonyCallGroup::WrapObject(JSContext* aCx) michael@0: { michael@0: return TelephonyCallGroupBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::AddCall(TelephonyCall* aCall) michael@0: { michael@0: NS_ASSERTION(!mCalls.Contains(aCall), "Already know about this one!"); michael@0: mCalls.AppendElement(aCall); michael@0: aCall->ChangeGroup(this); michael@0: NotifyCallsChanged(aCall); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::RemoveCall(TelephonyCall* aCall) michael@0: { michael@0: NS_ASSERTION(mCalls.Contains(aCall), "Didn't know about this one!"); michael@0: mCalls.RemoveElement(aCall); michael@0: aCall->ChangeGroup(nullptr); michael@0: NotifyCallsChanged(aCall); michael@0: } michael@0: michael@0: nsresult michael@0: TelephonyCallGroup::NotifyError(const nsAString& aName, const nsAString& aMessage) michael@0: { michael@0: CallGroupErrorEventInit init; michael@0: init.mBubbles = false; michael@0: init.mCancelable = false; michael@0: init.mName = aName; michael@0: init.mMessage = aMessage; michael@0: michael@0: nsRefPtr event = michael@0: CallGroupErrorEvent::Constructor(this, NS_LITERAL_STRING("error"), init); michael@0: michael@0: return DispatchTrustedEvent(event); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::ChangeState(uint16_t aCallState) michael@0: { michael@0: if (mCallState == aCallState) { michael@0: return; michael@0: } michael@0: michael@0: nsString stateString; michael@0: switch (aCallState) { michael@0: case nsITelephonyProvider::CALL_STATE_UNKNOWN: michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_CONNECTED: michael@0: stateString.AssignLiteral("connected"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_HOLDING: michael@0: stateString.AssignLiteral("holding"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_HELD: michael@0: stateString.AssignLiteral("held"); michael@0: break; michael@0: case nsITelephonyProvider::CALL_STATE_RESUMING: michael@0: stateString.AssignLiteral("resuming"); michael@0: break; michael@0: default: michael@0: NS_NOTREACHED("Unknown state!"); michael@0: } michael@0: michael@0: mState = stateString; michael@0: mCallState = aCallState; michael@0: michael@0: nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch specific event!"); michael@0: } michael@0: if (!stateString.IsEmpty()) { michael@0: // This can change if the statechange handler called back here... Need to michael@0: // figure out something smarter. michael@0: if (mCallState == aCallState) { michael@0: rv = DispatchCallEvent(stateString, nullptr); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to dispatch specific event!"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: for (uint32_t index = 0; index < mCalls.Length(); index++) { michael@0: nsRefPtr call = mCalls[index]; michael@0: call->ChangeState(aCallState); michael@0: michael@0: MOZ_ASSERT(call->CallState() == aCallState); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: TelephonyCallGroup::NotifyCallsChanged(TelephonyCall* aCall) michael@0: { michael@0: return DispatchCallEvent(NS_LITERAL_STRING("callschanged"), aCall); michael@0: } michael@0: michael@0: nsresult michael@0: TelephonyCallGroup::DispatchCallEvent(const nsAString& aType, michael@0: TelephonyCall* aCall) michael@0: { michael@0: nsRefPtr event = CallEvent::Create(this, aType, aCall, false, false); michael@0: return DispatchTrustedEvent(event); michael@0: } michael@0: michael@0: bool michael@0: TelephonyCallGroup::CanConference(const TelephonyCall& aCall, michael@0: TelephonyCall* aSecondCall) michael@0: { michael@0: if (!aCall.Mergeable()) { michael@0: return false; michael@0: } michael@0: michael@0: if (!aSecondCall) { michael@0: MOZ_ASSERT(!mCalls.IsEmpty()); michael@0: michael@0: return (mCallState == nsITelephonyProvider::CALL_STATE_CONNECTED && michael@0: aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD) || michael@0: (mCallState == nsITelephonyProvider::CALL_STATE_HELD && michael@0: aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED); michael@0: } michael@0: michael@0: MOZ_ASSERT(mCallState == nsITelephonyProvider::CALL_STATE_UNKNOWN); michael@0: michael@0: if (aCall.ServiceId() != aSecondCall->ServiceId()) { michael@0: return false; michael@0: } michael@0: michael@0: if (!aSecondCall->Mergeable()) { michael@0: return false; michael@0: } michael@0: michael@0: return (aCall.CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED && michael@0: aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_HELD) || michael@0: (aCall.CallState() == nsITelephonyProvider::CALL_STATE_HELD && michael@0: aSecondCall->CallState() == nsITelephonyProvider::CALL_STATE_CONNECTED); michael@0: } michael@0: michael@0: already_AddRefed michael@0: TelephonyCallGroup::GetCall(uint32_t aServiceId, uint32_t aCallIndex) michael@0: { michael@0: nsRefPtr call; michael@0: michael@0: for (uint32_t index = 0; index < mCalls.Length(); index++) { michael@0: nsRefPtr& tempCall = mCalls[index]; michael@0: if (tempCall->ServiceId() == aServiceId && michael@0: tempCall->CallIndex() == aCallIndex) { michael@0: call = tempCall; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return call.forget(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(TelephonyCallGroup) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TelephonyCallGroup, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCalls) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallsList) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTelephony) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TelephonyCallGroup, michael@0: DOMEventTargetHelper) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCalls) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mCallsList) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mTelephony) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TelephonyCallGroup) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(TelephonyCallGroup, DOMEventTargetHelper) michael@0: NS_IMPL_RELEASE_INHERITED(TelephonyCallGroup, DOMEventTargetHelper) michael@0: michael@0: // WebIDL michael@0: already_AddRefed michael@0: TelephonyCallGroup::Calls() const michael@0: { michael@0: nsRefPtr list = mCallsList; michael@0: return list.forget(); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::Add(TelephonyCall& aCall, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!CanConference(aCall, nullptr)) { michael@0: aRv.Throw(NS_ERROR_NOT_AVAILABLE); michael@0: return; michael@0: } michael@0: michael@0: aRv = mTelephony->Provider()->ConferenceCall(aCall.ServiceId()); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::Add(TelephonyCall& aCall, michael@0: TelephonyCall& aSecondCall, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (!CanConference(aCall, &aSecondCall)) { michael@0: aRv.Throw(NS_ERROR_NOT_AVAILABLE); michael@0: return; michael@0: } michael@0: michael@0: aRv = mTelephony->Provider()->ConferenceCall(aCall.ServiceId()); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::Remove(TelephonyCall& aCall, ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { michael@0: NS_WARNING("Remove call from a non-connected call group. Ignore!"); michael@0: return; michael@0: } michael@0: michael@0: uint32_t serviceId = aCall.ServiceId(); michael@0: uint32_t callIndex = aCall.CallIndex(); michael@0: michael@0: nsRefPtr call; michael@0: michael@0: call = GetCall(serviceId, callIndex); michael@0: if (call) { michael@0: aRv = mTelephony->Provider()->SeparateCall(serviceId, callIndex); michael@0: } else { michael@0: NS_WARNING("Didn't have this call. Ignore!"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::Hold(ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_CONNECTED) { michael@0: NS_WARNING("Hold non-connected call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(!mCalls.IsEmpty()); michael@0: michael@0: nsresult rv = mTelephony->Provider()->HoldConference(mCalls[0]->ServiceId()); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: ChangeState(nsITelephonyProvider::CALL_STATE_HOLDING); michael@0: } michael@0: michael@0: void michael@0: TelephonyCallGroup::Resume(ErrorResult& aRv) michael@0: { michael@0: if (mCallState != nsITelephonyProvider::CALL_STATE_HELD) { michael@0: NS_WARNING("Resume non-held call ignored!"); michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(!mCalls.IsEmpty()); michael@0: michael@0: nsresult rv = mTelephony->Provider()->ResumeConference(mCalls[0]->ServiceId()); michael@0: if (NS_FAILED(rv)) { michael@0: aRv.Throw(rv); michael@0: return; michael@0: } michael@0: michael@0: ChangeState(nsITelephonyProvider::CALL_STATE_RESUMING); michael@0: }