From 10fe365fe086e353e878181bba90312079cc3cea Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Fri, 24 Feb 2012 05:14:37 -0800 Subject: [PATCH] Bug 717451 - 'Telephony: Keep telephony objects on multiple pages in sync'. r=sicking. --HG-- extra : transplant_source : %15%BB%92%8E%A38A%80%9E%A40T%E5BCi%BE%ACK%F7 --- dom/telephony/Telephony.cpp | 164 ++++++++++++++++++++---------- dom/telephony/Telephony.h | 16 ++- dom/telephony/nsIDOMTelephony.idl | 8 +- 3 files changed, 122 insertions(+), 66 deletions(-) diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index 3c7317c8d6e6..6bec4e5237cd 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -41,13 +41,16 @@ #include "nsIDocument.h" #include "nsIURI.h" +#include "nsIURL.h" #include "nsPIDOMWindow.h" #include "jsapi.h" #include "mozilla/Preferences.h" +#include "nsCharSeparatedTokenizer.h" #include "nsContentUtils.h" #include "nsDOMClassInfo.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsNetUtil.h" #include "nsServiceManagerUtils.h" #include "SystemWorkerManager.h" @@ -61,6 +64,10 @@ using mozilla::Preferences; namespace { +typedef nsAutoTArray TelephonyList; + +TelephonyList* gTelephonyList; + template inline nsresult nsTArrayToJSArray(JSContext* aCx, JSObject* aGlobal, @@ -110,6 +117,16 @@ nsTArrayToJSArray(JSContext* aCx, JSObject* aGlobal, } // anonymous namespace +Telephony::Telephony() +: mActiveCall(nsnull), mCallsArray(nsnull), mRooted(false) +{ + if (!gTelephonyList) { + gTelephonyList = new TelephonyList(); + } + + gTelephonyList->AppendElement(this); +} + Telephony::~Telephony() { if (mRIL && mRILTelephonyCallback) { @@ -119,6 +136,17 @@ Telephony::~Telephony() if (mRooted) { NS_DROP_JS_OBJECTS(this, Telephony); } + + NS_ASSERTION(gTelephonyList, "This should never be null!"); + NS_ASSERTION(gTelephonyList->Contains(this), "Should be in the list!"); + + if (gTelephonyList->Length() == 1) { + delete gTelephonyList; + gTelephonyList = nsnull; + } + else { + gTelephonyList->RemoveElement(this); + } } // static @@ -150,14 +178,37 @@ Telephony::Create(nsPIDOMWindow* aOwner, nsIRadioInterfaceLayer* aRIL) return telephony.forget(); } -void -Telephony::SwitchActiveCall(TelephonyCall* aCall) +already_AddRefed +Telephony::CreateNewDialingCall(const nsAString& aNumber) { - if (mActiveCall) { - // Put the call on hold? - NS_NOTYETIMPLEMENTED("Implement me!"); - } - mActiveCall = aCall; + nsRefPtr call = + TelephonyCall::Create(this, aNumber, + nsIRadioInterfaceLayer::CALL_STATE_DIALING); + NS_ASSERTION(call, "This should never fail!"); + + NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); + + return call.forget(); +} + +void +Telephony::NoteDialedCallFromOtherInstance(const nsAString& aNumber) +{ + // We don't need to hang on to this call object, it is held alive by mCalls. + nsRefPtr call = CreateNewDialingCall(aNumber); +} + +nsresult +Telephony::NotifyCallsChanged(TelephonyCall* aCall) +{ + nsRefPtr event = CallEvent::Create(aCall); + NS_ASSERTION(event, "This should never fail!"); + + nsresult rv = + event->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("callschanged")); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; } NS_IMPL_CYCLE_COLLECTION_CLASS(Telephony) @@ -166,6 +217,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Telephony, nsDOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(incoming) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(callschanged) for (PRUint32 index = 0; index < tmp->mCalls.Length(); index++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCalls[i]"); cb.NoteXPCOMChild(tmp->mCalls[index]->ToISupports()); @@ -180,6 +232,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Telephony, nsDOMEventTargetHelper) NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(incoming) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(callschanged) tmp->mCalls.Clear(); tmp->mActiveCall = nsnull; tmp->mCallsArray = nsnull; @@ -216,11 +269,16 @@ Telephony::Dial(const nsAString& aNumber, nsIDOMTelephonyCall** aResult) nsresult rv = mRIL->Dial(aNumber); NS_ENSURE_SUCCESS(rv, rv); - nsRefPtr call = - TelephonyCall::Create(this, aNumber, nsIRadioInterfaceLayer::CALL_STATE_DIALING); - NS_ASSERTION(call, "This should never fail!"); + nsRefPtr call = CreateNewDialingCall(aNumber); - NS_ASSERTION(mCalls.Contains(call), "Should have auto-added new call!"); + // Notify other telephony objects that we just dialed. + for (PRUint32 index = 0; index < gTelephonyList->Length(); index++) { + Telephony*& telephony = gTelephonyList->ElementAt(index); + if (telephony != this) { + nsRefPtr kungFuDeathGrip = telephony; + telephony->NoteDialedCallFromOtherInstance(aNumber); + } + } call.forget(aResult); return NS_OK; @@ -279,32 +337,6 @@ Telephony::GetActive(jsval* aActive) return NS_OK; } -NS_IMETHODIMP -Telephony::SetActive(const jsval& aActive) -{ - if (aActive.isObject()) { - nsIXPConnect* xpc = nsContentUtils::XPConnect(); - NS_ASSERTION(xpc, "This should never be null!"); - - nsISupports* native = - xpc->GetNativeOfWrapper(mScriptContext->GetNativeContext(), - &aActive.toObject()); - - nsCOMPtr call = do_QueryInterface(native); - if (call) { - // See if this call has the same telephony object. Otherwise we can't use - // it. - TelephonyCall* concreteCall = static_cast(call.get()); - if (this == concreteCall->mTelephony) { - SwitchActiveCall(concreteCall); - return NS_OK; - } - } - } - - return NS_ERROR_INVALID_ARG; -} - NS_IMETHODIMP Telephony::GetCalls(jsval* aCalls) { @@ -354,15 +386,8 @@ Telephony::StopTone() return NS_OK; } -NS_IMETHODIMP -Telephony::SendTones(const nsAString& aTones, PRUint32 aToneDuration, - PRUint32 aIntervalDuration) -{ - NS_NOTYETIMPLEMENTED("Implement me!"); - return NS_ERROR_NOT_IMPLEMENTED; -} - NS_IMPL_EVENT_HANDLER(Telephony, incoming) +NS_IMPL_EVENT_HANDLER(Telephony, callschanged) NS_IMETHODIMP Telephony::CallStateChanged(PRUint32 aCallIndex, PRUint16 aCallState, @@ -378,7 +403,8 @@ Telephony::CallStateChanged(PRUint32 aCallIndex, PRUint16 aCallState, nsRefPtr& tempCall = mCalls[index]; if (tempCall->CallIndex() == kOutgoingPlaceholderCallIndex) { NS_ASSERTION(!outgoingCall, "More than one outgoing call not supported!"); - NS_ASSERTION(tempCall->CallState() == nsIRadioInterfaceLayer::CALL_STATE_DIALING, + NS_ASSERTION(tempCall->CallState() == + nsIRadioInterfaceLayer::CALL_STATE_DIALING, "Something really wrong here!"); // Stash this for later, we may need it if aCallIndex doesn't match one of // our other calls. @@ -407,7 +433,7 @@ Telephony::CallStateChanged(PRUint32 aCallIndex, PRUint16 aCallState, // See if this should replace our current active call. if (aCallState == nsIRadioInterfaceLayer::CALL_STATE_CONNECTED) { - SwitchActiveCall(modifiedCall); + mActiveCall = modifiedCall; } return NS_OK; @@ -486,22 +512,48 @@ NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony) // Do security checks. We assume that chrome is always allowed and we also // allow a single page specified by preferences. if (!nsContentUtils::IsSystemPrincipal(document->NodePrincipal())) { - nsCOMPtr documentURI; + nsCOMPtr originalURI; nsresult rv = - document->NodePrincipal()->GetURI(getter_AddRefs(documentURI)); + document->NodePrincipal()->GetURI(getter_AddRefs(originalURI)); NS_ENSURE_SUCCESS(rv, rv); - nsCString documentURL; - rv = documentURI->GetSpec(documentURL); + nsCOMPtr documentURI; + rv = originalURI->Clone(getter_AddRefs(documentURI)); NS_ENSURE_SUCCESS(rv, rv); + // Strip the query string (if there is one) before comparing. + nsCOMPtr documentURL = do_QueryInterface(documentURI); + if (documentURL) { + rv = documentURL->SetQuery(EmptyCString()); + NS_ENSURE_SUCCESS(rv, rv); + } + + bool allowed = false; + // The pref may not exist but in that case we deny access just as we do if // the url doesn't match. - nsCString phoneAppURL; - if (NS_FAILED(Preferences::GetCString(DOM_TELEPHONY_APP_PHONE_URL_PREF, - &phoneAppURL)) || - !phoneAppURL.Equals(documentURL, - nsCaseInsensitiveCStringComparator())) { + nsCString whitelist; + if (NS_SUCCEEDED(Preferences::GetCString(DOM_TELEPHONY_APP_PHONE_URL_PREF, + &whitelist))) { + nsCOMPtr ios = do_GetIOService(); + NS_ENSURE_TRUE(ios, NS_ERROR_FAILURE); + + nsCCharSeparatedTokenizer tokenizer(whitelist, ','); + while (tokenizer.hasMoreTokens()) { + nsCOMPtr uri; + if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(uri), tokenizer.nextToken(), + nsnull, nsnull, ios))) { + rv = documentURI->EqualsExceptRef(uri, &allowed); + NS_ENSURE_SUCCESS(rv, rv); + + if (allowed) { + break; + } + } + } + } + + if (!allowed) { *aTelephony = nsnull; return NS_OK; } diff --git a/dom/telephony/Telephony.h b/dom/telephony/Telephony.h index 3e3f3dbd90d3..c374331f51ba 100644 --- a/dom/telephony/Telephony.h +++ b/dom/telephony/Telephony.h @@ -58,6 +58,7 @@ class Telephony : public nsDOMEventTargetHelper, nsCOMPtr mRILTelephonyCallback; NS_DECL_EVENT_HANDLER(incoming) + NS_DECL_EVENT_HANDLER(callschanged) TelephonyCall* mActiveCall; nsTArray > mCalls; @@ -99,6 +100,7 @@ public: NS_ASSERTION(!mCalls.Contains(aCall), "Already know about this one!"); mCalls.AppendElement(aCall); mCallsArray = nsnull; + NotifyCallsChanged(aCall); } void @@ -107,6 +109,7 @@ public: NS_ASSERTION(mCalls.Contains(aCall), "Didn't know about this one!"); mCalls.RemoveElement(aCall); mCallsArray = nsnull; + NotifyCallsChanged(aCall); } nsIRadioInterfaceLayer* @@ -128,14 +131,17 @@ public: } private: - Telephony() - : mActiveCall(nsnull), mCallsArray(nsnull), mRooted(false) - { } - + Telephony(); ~Telephony(); + already_AddRefed + CreateNewDialingCall(const nsAString& aNumber); + void - SwitchActiveCall(TelephonyCall* aCall); + NoteDialedCallFromOtherInstance(const nsAString& aNumber); + + nsresult + NotifyCallsChanged(TelephonyCall* aCall); class RILTelephonyCallback : public nsIRILTelephonyCallback { diff --git a/dom/telephony/nsIDOMTelephony.idl b/dom/telephony/nsIDOMTelephony.idl index 4f5cdb124411..f4baa79e2064 100644 --- a/dom/telephony/nsIDOMTelephony.idl +++ b/dom/telephony/nsIDOMTelephony.idl @@ -43,7 +43,7 @@ interface nsIDOMEventListener; interface nsIDOMTelephonyCall; -[scriptable, builtinclass, uuid(047be0d8-a9cd-49aa-8948-2f60ff3a7a18)] +[scriptable, builtinclass, uuid(0de46b73-be83-4970-ad15-45f92cb0902a)] interface nsIDOMTelephony : nsIDOMEventTarget { nsIDOMTelephonyCall dial(in DOMString number); @@ -53,16 +53,14 @@ interface nsIDOMTelephony : nsIDOMEventTarget // The call that is "active", i.e. receives microphone input and tones // generated via startTone. - attribute jsval active; + readonly attribute jsval active; // Array of all calls that are currently connected. readonly attribute jsval calls; void startTone(in DOMString tone); void stopTone(); - void sendTones(in DOMString tones, - [optional] in unsigned long toneDuration, - [optional] in unsigned long intervalDuration); attribute nsIDOMEventListener onincoming; + attribute nsIDOMEventListener oncallschanged; };