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
This commit is contained in:
Ben Turner 2012-02-24 05:14:37 -08:00
parent 7de7de1cff
commit 10fe365fe0
3 changed files with 122 additions and 66 deletions

View File

@ -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<Telephony*, 2> TelephonyList;
TelephonyList* gTelephonyList;
template <class T>
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<TelephonyCall>
Telephony::CreateNewDialingCall(const nsAString& aNumber)
{
if (mActiveCall) {
// Put the call on hold?
NS_NOTYETIMPLEMENTED("Implement me!");
}
mActiveCall = aCall;
nsRefPtr<TelephonyCall> 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<TelephonyCall> call = CreateNewDialingCall(aNumber);
}
nsresult
Telephony::NotifyCallsChanged(TelephonyCall* aCall)
{
nsRefPtr<CallEvent> 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<TelephonyCall> call =
TelephonyCall::Create(this, aNumber, nsIRadioInterfaceLayer::CALL_STATE_DIALING);
NS_ASSERTION(call, "This should never fail!");
nsRefPtr<TelephonyCall> 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<Telephony> 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<nsIDOMTelephonyCall> 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<TelephonyCall*>(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<TelephonyCall>& 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<nsIURI> documentURI;
nsCOMPtr<nsIURI> 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<nsIURI> documentURI;
rv = originalURI->Clone(getter_AddRefs(documentURI));
NS_ENSURE_SUCCESS(rv, rv);
// Strip the query string (if there is one) before comparing.
nsCOMPtr<nsIURL> 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<nsIIOService> ios = do_GetIOService();
NS_ENSURE_TRUE(ios, NS_ERROR_FAILURE);
nsCCharSeparatedTokenizer tokenizer(whitelist, ',');
while (tokenizer.hasMoreTokens()) {
nsCOMPtr<nsIURI> 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;
}

View File

@ -58,6 +58,7 @@ class Telephony : public nsDOMEventTargetHelper,
nsCOMPtr<nsIRILTelephonyCallback> mRILTelephonyCallback;
NS_DECL_EVENT_HANDLER(incoming)
NS_DECL_EVENT_HANDLER(callschanged)
TelephonyCall* mActiveCall;
nsTArray<nsRefPtr<TelephonyCall> > 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<TelephonyCall>
CreateNewDialingCall(const nsAString& aNumber);
void
SwitchActiveCall(TelephonyCall* aCall);
NoteDialedCallFromOtherInstance(const nsAString& aNumber);
nsresult
NotifyCallsChanged(TelephonyCall* aCall);
class RILTelephonyCallback : public nsIRILTelephonyCallback
{

View File

@ -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;
};