mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Merge m-c to mozilla-inbound
This commit is contained in:
commit
4b66ac5e67
@ -12,7 +12,6 @@
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsIAccessibleRelation.h"
|
||||
#include "nsIAccessibleTable.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "RootAccessible.h"
|
||||
#include "nsIAccessibleValue.h"
|
||||
#include "nsMai.h"
|
||||
@ -21,7 +20,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "prprf.h"
|
||||
#include "nsStateMap.h"
|
||||
#include "mozilla/a11y/Platform.h"
|
||||
#include "Relation.h"
|
||||
#include "RootAccessible.h"
|
||||
#include "States.h"
|
||||
@ -135,13 +133,9 @@ struct MaiAtkObject
|
||||
* The AccessibleWrap whose properties and features are exported
|
||||
* via this object instance.
|
||||
*/
|
||||
uintptr_t accWrap;
|
||||
AccessibleWrap* accWrap;
|
||||
};
|
||||
|
||||
// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a
|
||||
// proxy.
|
||||
static const uintptr_t IS_PROXY = 1;
|
||||
|
||||
struct MaiAtkObjectClass
|
||||
{
|
||||
AtkObjectClass parent_class;
|
||||
@ -254,7 +248,7 @@ AccessibleWrap::ShutdownAtkObject()
|
||||
{
|
||||
if (mAtkObject) {
|
||||
if (IS_MAI_OBJECT(mAtkObject)) {
|
||||
MAI_ATK_OBJECT(mAtkObject)->accWrap = 0;
|
||||
MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr;
|
||||
}
|
||||
SetMaiHyperlink(nullptr);
|
||||
g_object_unref(mAtkObject);
|
||||
@ -588,7 +582,8 @@ initializeCB(AtkObject *aAtkObj, gpointer aData)
|
||||
ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
|
||||
|
||||
/* initialize object */
|
||||
MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast<uintptr_t>(aData);
|
||||
MAI_ATK_OBJECT(aAtkObj)->accWrap =
|
||||
static_cast<AccessibleWrap*>(aData);
|
||||
}
|
||||
|
||||
void
|
||||
@ -596,7 +591,7 @@ finalizeCB(GObject *aObj)
|
||||
{
|
||||
if (!IS_MAI_OBJECT(aObj))
|
||||
return;
|
||||
NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == 0, "AccWrap NOT null");
|
||||
NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null");
|
||||
|
||||
// call parent finalize function
|
||||
// finalize of GObjectClass will unref the accessible parent if has
|
||||
@ -668,25 +663,17 @@ getDescriptionCB(AtkObject *aAtkObj)
|
||||
AtkRole
|
||||
getRoleCB(AtkObject *aAtkObj)
|
||||
{
|
||||
if (aAtkObj->role != ATK_ROLE_INVALID)
|
||||
return aAtkObj->role;
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
a11y::role role;
|
||||
if (!accWrap) {
|
||||
ProxyAccessible* proxy = GetProxy(aAtkObj);
|
||||
if (!proxy)
|
||||
return ATK_ROLE_INVALID;
|
||||
if (!accWrap)
|
||||
return ATK_ROLE_INVALID;
|
||||
|
||||
role = proxy->Role();
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
|
||||
"Does not support nsIAccessibleText when it should");
|
||||
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
|
||||
"Does not support nsIAccessibleText when it should");
|
||||
#endif
|
||||
|
||||
role = accWrap->Role();
|
||||
}
|
||||
if (aAtkObj->role != ATK_ROLE_INVALID)
|
||||
return aAtkObj->role;
|
||||
|
||||
#define ROLE(geckoRole, stringRole, atkRole, macRole, \
|
||||
msaaRole, ia2Role, nameRule) \
|
||||
@ -694,7 +681,7 @@ getRoleCB(AtkObject *aAtkObj)
|
||||
aAtkObj->role = atkRole; \
|
||||
break;
|
||||
|
||||
switch (role) {
|
||||
switch (accWrap->Role()) {
|
||||
#include "RoleMap.h"
|
||||
default:
|
||||
MOZ_CRASH("Unknown role.");
|
||||
@ -959,13 +946,7 @@ AccessibleWrap*
|
||||
GetAccessibleWrap(AtkObject* aAtkObj)
|
||||
{
|
||||
NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
|
||||
|
||||
// Make sure its native is an AccessibleWrap not a proxy.
|
||||
if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY)
|
||||
return nullptr;
|
||||
|
||||
AccessibleWrap* accWrap =
|
||||
reinterpret_cast<AccessibleWrap*>(MAI_ATK_OBJECT(aAtkObj)->accWrap);
|
||||
AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap;
|
||||
|
||||
// Check if the accessible was deconstructed.
|
||||
if (!accWrap)
|
||||
@ -980,49 +961,6 @@ GetAccessibleWrap(AtkObject* aAtkObj)
|
||||
return accWrap;
|
||||
}
|
||||
|
||||
ProxyAccessible*
|
||||
GetProxy(AtkObject* aObj)
|
||||
{
|
||||
if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY))
|
||||
return nullptr;
|
||||
|
||||
return reinterpret_cast<ProxyAccessible*>(MAI_ATK_OBJECT(aObj)->accWrap
|
||||
& ~IS_PROXY);
|
||||
}
|
||||
|
||||
static uint16_t
|
||||
GetInterfacesForProxy(ProxyAccessible* aProxy)
|
||||
{
|
||||
return MAI_INTERFACE_COMPONENT;
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyCreated(ProxyAccessible* aProxy)
|
||||
{
|
||||
GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
|
||||
NS_ASSERTION(type, "why don't we have a type!");
|
||||
|
||||
AtkObject* obj =
|
||||
reinterpret_cast<AtkObject *>
|
||||
(g_object_new(type, nullptr));
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
atk_object_initialize(obj, aProxy);
|
||||
obj->role = ATK_ROLE_INVALID;
|
||||
obj->layer = ATK_LAYER_INVALID;
|
||||
aProxy->SetWrapper(reinterpret_cast<uintptr_t>(obj) | IS_PROXY);
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyDestroyed(ProxyAccessible* aProxy)
|
||||
{
|
||||
auto obj = reinterpret_cast<MaiAtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
|
||||
obj->accWrap = 0;
|
||||
g_object_unref(obj);
|
||||
aProxy->SetWrapper(0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
||||
{
|
||||
|
@ -97,7 +97,7 @@ private:
|
||||
|
||||
static EAvailableAtkSignals gAvailableAtkSignals;
|
||||
|
||||
uint16_t CreateMaiInterfaces();
|
||||
uint16_t CreateMaiInterfaces(void);
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
|
@ -35,7 +35,6 @@ LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/other-licenses/atk-1.0',
|
||||
|
@ -13,12 +13,6 @@
|
||||
|
||||
#include "AccessibleWrap.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
class ProxyAccessible;
|
||||
}
|
||||
}
|
||||
|
||||
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
|
||||
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
MAI_TYPE_ATK_OBJECT, MaiAtkObject))
|
||||
@ -35,7 +29,6 @@ class ProxyAccessible;
|
||||
GType mai_atk_object_get_type(void);
|
||||
GType mai_util_get_type();
|
||||
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
|
||||
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
|
||||
|
||||
extern int atkMajorVersion, atkMinorVersion;
|
||||
|
||||
|
@ -232,8 +232,6 @@ public:
|
||||
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
|
||||
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
|
||||
|
||||
Accessible* Parent() const { return mParent; }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
nsRefPtr<Accessible> mParent;
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "ApplicationAccessible.h"
|
||||
#include "ARIAMap.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "RootAccessibleWrap.h"
|
||||
|
||||
@ -28,8 +27,6 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@ -421,12 +418,6 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
|
||||
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
|
||||
ApplicationAcc());
|
||||
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
|
||||
docAcc->SetIPCDoc(ipcDoc);
|
||||
auto contentChild = dom::ContentChild::GetSingleton();
|
||||
contentChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
|
||||
}
|
||||
} else {
|
||||
parentDocAcc->BindChildDocument(docAcc);
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class DocAccessible;
|
||||
class DocAccessibleParent;
|
||||
|
||||
/**
|
||||
* Manage the document accessible life cycle.
|
||||
@ -66,25 +65,6 @@ public:
|
||||
RemoveListeners(aDocument);
|
||||
}
|
||||
|
||||
/*
|
||||
* Notification that a top level document in a content process has gone away.
|
||||
*/
|
||||
void RemoteDocShutdown(DocAccessibleParent* aDoc)
|
||||
{
|
||||
DebugOnly<bool> result = mRemoteDocuments.RemoveElement(aDoc);
|
||||
MOZ_ASSERT(result, "Why didn't we find the document!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify of a new top level document in a content process.
|
||||
*/
|
||||
void RemoteDocAdded(DocAccessibleParent* aDoc)
|
||||
{
|
||||
MOZ_ASSERT(!mRemoteDocuments.Contains(aDoc),
|
||||
"How did we already have the doc!");
|
||||
mRemoteDocuments.AppendElement(aDoc);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsProcessingRefreshDriverNotification() const;
|
||||
#endif
|
||||
@ -164,11 +144,6 @@ private:
|
||||
#endif
|
||||
|
||||
DocAccessibleHashtable mDocAccessibleCache;
|
||||
|
||||
/*
|
||||
* The list of remote top level documents.
|
||||
*/
|
||||
nsTArray<DocAccessibleParent*> mRemoteDocuments;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsTextEquivUtils.h"
|
||||
#ifdef A11Y_LOG
|
||||
@ -556,15 +555,5 @@ EventQueue::ProcessEventQueue()
|
||||
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
DocAccessibleChild* ipcDoc = mDocument->IPCDoc();
|
||||
if (event->mEventType == nsIAccessibleEvent::EVENT_SHOW)
|
||||
ipcDoc->ShowEvent(downcast_accEvent(event));
|
||||
else if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
|
||||
ipcDoc->SendHideEvent(reinterpret_cast<uintptr_t>(event->GetAccessible()));
|
||||
else
|
||||
ipcDoc->SendEvent(event->GetEventType());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,11 +6,9 @@
|
||||
#include "NotificationController.h"
|
||||
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
#include "TextUpdater.h"
|
||||
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
@ -219,19 +217,8 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
if (ownerContent) {
|
||||
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
|
||||
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
|
||||
if (mDocument->AppendChildDocument(childDoc)) {
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc);
|
||||
childDoc->SetIPCDoc(ipcDoc);
|
||||
auto contentChild = dom::ContentChild::GetSingleton();
|
||||
DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
|
||||
uint64_t id = reinterpret_cast<uintptr_t>(outerDocAcc->UniqueID());
|
||||
contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc,
|
||||
id);
|
||||
}
|
||||
|
||||
if (mDocument->AppendChildDocument(childDoc))
|
||||
continue;
|
||||
}
|
||||
|
||||
outerDocAcc->RemoveChild(childDoc);
|
||||
}
|
||||
|
@ -7,8 +7,6 @@
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class ProxyAccessible;
|
||||
|
||||
enum EPlatformDisabledState {
|
||||
ePlatformIsForceEnabled = -1,
|
||||
ePlatformIsEnabled = 0,
|
||||
@ -49,17 +47,6 @@ void PlatformInit();
|
||||
*/
|
||||
void PlatformShutdown();
|
||||
|
||||
/**
|
||||
* called when a new ProxyAccessible is created, so the platform may setup a
|
||||
* wrapper for it, or take other action.
|
||||
*/
|
||||
void ProxyCreated(ProxyAccessible*);
|
||||
|
||||
/**
|
||||
* Called just before a ProxyAccessible is destroyed so its wrapper can be
|
||||
* disposed of and other action taken.
|
||||
*/
|
||||
void ProxyDestroyed(ProxyAccessible*);
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -783,9 +783,7 @@ enum Role {
|
||||
/**
|
||||
* Represent a keyboard or keypad key (ARIA role "key").
|
||||
*/
|
||||
KEY = 129,
|
||||
|
||||
LAST_ROLE = KEY
|
||||
KEY = 129
|
||||
};
|
||||
|
||||
} // namespace role
|
||||
|
@ -60,7 +60,6 @@ if CONFIG['A11Y_LOG']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/dom/xbl',
|
||||
@ -94,5 +93,3 @@ FINAL_LIBRARY = 'xul'
|
||||
|
||||
if CONFIG['MOZ_ENABLE_GTK']:
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccIterator.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "HTMLImageMapAccessible.h"
|
||||
#include "nsAccCache.h"
|
||||
#include "nsAccessiblePivot.h"
|
||||
@ -84,7 +83,7 @@ DocAccessible::
|
||||
mScrollPositionChangedTicks(0),
|
||||
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
|
||||
mVirtualCursor(nullptr),
|
||||
mPresShell(aPresShell), mIPCDoc(nullptr)
|
||||
mPresShell(aPresShell)
|
||||
{
|
||||
mGenericTypes |= eDocument;
|
||||
mStateFlags |= eNotNodeMapEntry;
|
||||
@ -474,12 +473,6 @@ DocAccessible::Shutdown()
|
||||
|
||||
mChildDocuments.Clear();
|
||||
|
||||
// XXX thinking about ordering?
|
||||
if (XRE_GetProcessType() != GeckoProcessType_Default) {
|
||||
DocAccessibleChild::Send__delete__(mIPCDoc);
|
||||
MOZ_ASSERT(!mIPCDoc);
|
||||
}
|
||||
|
||||
if (mVirtualCursor) {
|
||||
mVirtualCursor->RemoveObserver(this);
|
||||
mVirtualCursor = nullptr;
|
||||
@ -1443,13 +1436,6 @@ DocAccessible::DoInitialUpdate()
|
||||
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
|
||||
ParentDocument()->FireDelayedEvent(reorderEvent);
|
||||
}
|
||||
|
||||
uint32_t childCount = ChildCount();
|
||||
for (uint32_t i = 0; i < childCount; i++) {
|
||||
Accessible* child = GetChildAt(i);
|
||||
nsRefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
|
||||
FireDelayedEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -33,7 +33,6 @@ namespace a11y {
|
||||
|
||||
class DocManager;
|
||||
class NotificationController;
|
||||
class DocAccessibleChild;
|
||||
class RelatedAccIterator;
|
||||
template<class Class, class Arg>
|
||||
class TNotification;
|
||||
@ -520,20 +519,6 @@ protected:
|
||||
*/
|
||||
bool IsLoadEventTarget() const;
|
||||
|
||||
/**
|
||||
* If this document is in a content process return the object responsible for
|
||||
* communicating with the main process for it.
|
||||
*/
|
||||
DocAccessibleChild* IPCDoc() const { return mIPCDoc; }
|
||||
|
||||
/*
|
||||
* Set the object responsible for communicating with the main process on
|
||||
* behalf of this document.
|
||||
*/
|
||||
void SetIPCDoc(DocAccessibleChild* aIPCDoc) { mIPCDoc = aIPCDoc; }
|
||||
|
||||
friend class DocAccessibleChild;
|
||||
|
||||
/**
|
||||
* Used to fire scrolling end event after page scroll.
|
||||
*
|
||||
@ -657,9 +642,6 @@ protected:
|
||||
private:
|
||||
|
||||
nsIPresShell* mPresShell;
|
||||
|
||||
// Exclusively owned by IPDL so don't manually delete it!
|
||||
DocAccessibleChild* mIPCDoc;
|
||||
};
|
||||
|
||||
inline DocAccessible*
|
||||
|
@ -28,7 +28,6 @@ UNIFIED_SOURCES += [
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/content/base/src',
|
||||
@ -55,5 +54,3 @@ else:
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
@ -1,40 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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 "DocAccessibleChild.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
void
|
||||
SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
|
||||
{
|
||||
uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
|
||||
uint32_t role = aRoot->Role();
|
||||
uint32_t childCount = aRoot->ChildCount();
|
||||
|
||||
nsString name;
|
||||
aRoot->Name(name);
|
||||
aTree.AppendElement(AccessibleData(id, role, childCount, name));
|
||||
for (uint32_t i = 0; i < childCount; i++)
|
||||
SerializeTree(aRoot->GetChildAt(i), aTree);
|
||||
}
|
||||
|
||||
void
|
||||
DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
|
||||
{
|
||||
Accessible* parent = aShowEvent->Parent();
|
||||
uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
|
||||
uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
|
||||
nsTArray<AccessibleData> shownTree;
|
||||
ShowEventData data(parentID, idxInParent, shownTree);
|
||||
SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
|
||||
SendShowEvent(data);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_a11y_DocAccessibleChild_h
|
||||
#define mozilla_a11y_DocAccessibleChild_h
|
||||
|
||||
#include "mozilla/a11y/DocAccessible.h"
|
||||
#include "mozilla/a11y/PDocAccessibleChild.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
class AccShowEvent;
|
||||
|
||||
/*
|
||||
* These objects handle content side communication for an accessible document,
|
||||
* and their lifetime is the same as the document they represent.
|
||||
*/
|
||||
class DocAccessibleChild : public PDocAccessibleChild
|
||||
{
|
||||
public:
|
||||
DocAccessibleChild(DocAccessible* aDoc) :
|
||||
mDoc(aDoc)
|
||||
{ MOZ_COUNT_CTOR(DocAccessibleChild); }
|
||||
~DocAccessibleChild()
|
||||
{
|
||||
mDoc->SetIPCDoc(nullptr);
|
||||
MOZ_COUNT_DTOR(DocAccessibleChild);
|
||||
}
|
||||
|
||||
void ShowEvent(AccShowEvent* aShowEvent);
|
||||
|
||||
private:
|
||||
DocAccessible* mDoc;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,112 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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 "DocAccessibleParent.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "mozilla/a11y/Platform.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
bool
|
||||
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
|
||||
{
|
||||
if (aData.NewTree().IsEmpty()) {
|
||||
NS_ERROR("no children being added");
|
||||
return false;
|
||||
}
|
||||
|
||||
ProxyAccessible* parent = nullptr;
|
||||
if (aData.ID()) {
|
||||
ProxyEntry* e = mAccessibles.GetEntry(aData.ID());
|
||||
if (e)
|
||||
parent = e->mProxy;
|
||||
} else {
|
||||
parent = this;
|
||||
}
|
||||
|
||||
// XXX This should really never happen, but sometimes we fail to fire the
|
||||
// required show events.
|
||||
if (!parent) {
|
||||
NS_ERROR("adding child to unknown accessible");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t newChildIdx = aData.Idx();
|
||||
if (newChildIdx > parent->ChildrenCount()) {
|
||||
NS_ERROR("invalid index to add child at");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
|
||||
MOZ_ASSERT(consumed == aData.NewTree().Length());
|
||||
for (uint32_t i = 0; i < consumed; i++) {
|
||||
uint64_t id = aData.NewTree()[i].ID();
|
||||
MOZ_ASSERT(mAccessibles.GetEntry(id));
|
||||
}
|
||||
|
||||
return consumed;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
|
||||
const nsTArray<a11y::AccessibleData>& aNewTree,
|
||||
uint32_t aIdx, uint32_t aIdxInParent)
|
||||
{
|
||||
if (aNewTree.Length() <= aIdx) {
|
||||
NS_ERROR("bad index in serialized tree!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const AccessibleData& newChild = aNewTree[aIdx];
|
||||
if (newChild.Role() > roles::LAST_ROLE) {
|
||||
NS_ERROR("invalid role");
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto role = static_cast<a11y::role>(newChild.Role());
|
||||
ProxyAccessible* newProxy =
|
||||
new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name());
|
||||
aParent->AddChildAt(aIdxInParent, newProxy);
|
||||
mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
|
||||
ProxyCreated(newProxy);
|
||||
|
||||
uint32_t accessibles = 1;
|
||||
uint32_t kids = newChild.ChildrenCount();
|
||||
for (uint32_t i = 0; i < kids; i++) {
|
||||
uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
|
||||
if (!consumed)
|
||||
return 0;
|
||||
|
||||
accessibles += consumed;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(newProxy->ChildrenCount() == kids);
|
||||
|
||||
return accessibles;
|
||||
}
|
||||
|
||||
bool
|
||||
DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
|
||||
{
|
||||
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
|
||||
if (!rootEntry) {
|
||||
NS_ERROR("invalid root being removed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
ProxyAccessible* root = rootEntry->mProxy;
|
||||
if (!root) {
|
||||
NS_ERROR("invalid root being removed!");
|
||||
return true;
|
||||
}
|
||||
|
||||
root->Shutdown();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_a11y_DocAccessibleParent_h
|
||||
#define mozilla_a11y_DocAccessibleParent_h
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "mozilla/a11y/PDocAccessibleParent.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/*
|
||||
* These objects live in the main process and comunicate with and represent
|
||||
* an accessible document in a content process.
|
||||
*/
|
||||
class DocAccessibleParent : public ProxyAccessible,
|
||||
public PDocAccessibleParent
|
||||
{
|
||||
public:
|
||||
DocAccessibleParent() :
|
||||
mParentDoc(nullptr)
|
||||
{ MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
|
||||
~DocAccessibleParent()
|
||||
{
|
||||
MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
|
||||
MOZ_ASSERT(mChildDocs.Length() == 0);
|
||||
MOZ_ASSERT(!mParentDoc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a message from a document in a child process notifies the main
|
||||
* process it is firing an event.
|
||||
*/
|
||||
virtual bool RecvEvent(const uint32_t& aType) MOZ_OVERRIDE
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE;
|
||||
virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
|
||||
{
|
||||
MOZ_ASSERT(mChildDocs.IsEmpty(),
|
||||
"why wheren't the child docs destroyed already?");
|
||||
mParentDoc ? mParentDoc->RemoveChildDoc(this)
|
||||
: GetAccService()->RemoteDocShutdown(this);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the main processes representation of the parent document (if any)
|
||||
* of the document this object represents.
|
||||
*/
|
||||
DocAccessibleParent* Parent() const { return mParentDoc; }
|
||||
|
||||
/*
|
||||
* Called when a document in a content process notifies the main process of a
|
||||
* new child document.
|
||||
*/
|
||||
bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID)
|
||||
{
|
||||
ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy;
|
||||
if (!outerDoc)
|
||||
return false;
|
||||
|
||||
aChildDoc->mParent = outerDoc;
|
||||
outerDoc->SetChildDoc(aChildDoc);
|
||||
mChildDocs.AppendElement(aChildDoc);
|
||||
aChildDoc->mParentDoc = this;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when the document in the content process this object represents
|
||||
* notifies the main process a child document has been removed.
|
||||
*/
|
||||
void RemoveChildDoc(DocAccessibleParent* aChildDoc)
|
||||
{
|
||||
aChildDoc->mParent->SetChildDoc(nullptr);
|
||||
mChildDocs.RemoveElement(aChildDoc);
|
||||
aChildDoc->mParentDoc = nullptr;
|
||||
MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
|
||||
}
|
||||
|
||||
void RemoveAccessible(ProxyAccessible* aAccessible)
|
||||
{
|
||||
MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
|
||||
mAccessibles.RemoveEntry(aAccessible->ID());
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class ProxyEntry : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
ProxyEntry(const void*) : mProxy(nullptr) {}
|
||||
ProxyEntry(ProxyEntry&& aOther) :
|
||||
mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
|
||||
~ProxyEntry() { delete mProxy; }
|
||||
|
||||
typedef uint64_t KeyType;
|
||||
typedef const void* KeyTypePointer;
|
||||
|
||||
bool KeyEquals(const void* aKey) const
|
||||
{ return mProxy->ID() == (uint64_t)aKey; }
|
||||
|
||||
static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; }
|
||||
|
||||
static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; }
|
||||
|
||||
enum { ALLOW_MEMMOVE = true };
|
||||
|
||||
ProxyAccessible* mProxy;
|
||||
};
|
||||
|
||||
uint32_t AddSubtree(ProxyAccessible* aParent,
|
||||
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
|
||||
uint32_t aIdxInParent);
|
||||
|
||||
nsTArray<DocAccessibleParent*> mChildDocs;
|
||||
DocAccessibleParent* mParentDoc;
|
||||
|
||||
/*
|
||||
* Conceptually this is a map from IDs to proxies, but we store the ID in the
|
||||
* proxy object so we can't use a real map.
|
||||
*/
|
||||
nsTHashtable<ProxyEntry> mAccessibles;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,44 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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 protocol PContent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
struct AccessibleData
|
||||
{
|
||||
uint64_t ID;
|
||||
uint32_t Role;
|
||||
uint32_t ChildrenCount;
|
||||
nsString Name;
|
||||
};
|
||||
|
||||
struct ShowEventData
|
||||
{
|
||||
uint64_t ID;
|
||||
uint32_t Idx;
|
||||
AccessibleData[] NewTree;
|
||||
};
|
||||
|
||||
protocol PDocAccessible
|
||||
{
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
__delete__();
|
||||
|
||||
/*
|
||||
* Notify the parent process the document in the child process is firing an
|
||||
* event.
|
||||
*/
|
||||
Event(uint32_t type);
|
||||
ShowEvent(ShowEventData data);
|
||||
HideEvent(uint64_t aRootID);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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 "ProxyAccessible.h"
|
||||
#include "DocAccessibleParent.h"
|
||||
#include "mozilla/a11y/Platform.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
void
|
||||
ProxyAccessible::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(!mOuterDoc);
|
||||
|
||||
uint32_t childCount = mChildren.Length();
|
||||
for (uint32_t idx = 0; idx < childCount; idx++)
|
||||
mChildren[idx]->Shutdown();
|
||||
|
||||
mChildren.Clear();
|
||||
ProxyDestroyed(this);
|
||||
mDoc->RemoveAccessible(this);
|
||||
}
|
||||
|
||||
void
|
||||
ProxyAccessible::SetChildDoc(DocAccessibleParent* aParent)
|
||||
{
|
||||
if (aParent) {
|
||||
MOZ_ASSERT(mChildren.IsEmpty());
|
||||
mChildren.AppendElement(aParent);
|
||||
mOuterDoc = true;
|
||||
} else {
|
||||
MOZ_ASSERT(mChildren.Length() == 1);
|
||||
mChildren.Clear();
|
||||
mOuterDoc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=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/. */
|
||||
|
||||
#ifndef mozilla_a11y_ProxyAccessible_h
|
||||
#define mozilla_a11y_ProxyAccessible_h
|
||||
|
||||
#include "mozilla/a11y/Role.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessibleParent;
|
||||
|
||||
class ProxyAccessible
|
||||
{
|
||||
public:
|
||||
|
||||
ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
|
||||
DocAccessibleParent* aDoc, role aRole,
|
||||
const nsString& aName) :
|
||||
mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName)
|
||||
{
|
||||
MOZ_COUNT_CTOR(ProxyAccessible);
|
||||
}
|
||||
~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); }
|
||||
|
||||
void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
|
||||
{ mChildren.InsertElementAt(aIdx, aChild); }
|
||||
|
||||
uint32_t ChildrenCount() const { return mChildren.Length(); }
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void SetChildDoc(DocAccessibleParent*);
|
||||
|
||||
/**
|
||||
* Get the role of the accessible we're proxying.
|
||||
*/
|
||||
role Role() const { return mRole; }
|
||||
|
||||
/**
|
||||
* Allow the platform to store a pointers worth of data on us.
|
||||
*/
|
||||
uintptr_t GetWrapper() const { return mWrapper; }
|
||||
void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
|
||||
|
||||
/*
|
||||
* Return the ID of the accessible being proxied.
|
||||
*/
|
||||
uint64_t ID() const { return mID; }
|
||||
|
||||
protected:
|
||||
ProxyAccessible() :
|
||||
mParent(nullptr), mDoc(nullptr) { MOZ_COUNT_CTOR(ProxyAccessible); }
|
||||
|
||||
protected:
|
||||
ProxyAccessible* mParent;
|
||||
|
||||
private:
|
||||
nsTArray<ProxyAccessible*> mChildren;
|
||||
DocAccessibleParent* mDoc;
|
||||
uintptr_t mWrapper;
|
||||
uint64_t mID;
|
||||
role mRole : 31;
|
||||
bool mOuterDoc : 1;
|
||||
nsString mName;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
IPDL_SOURCES += ['PDocAccessible.ipdl']
|
||||
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'DocAccessibleChild.h',
|
||||
'DocAccessibleParent.h',
|
||||
'ProxyAccessible.h'
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'DocAccessibleChild.cpp',
|
||||
'DocAccessibleParent.cpp',
|
||||
'ProxyAccessible.cpp'
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../generic',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
@ -33,15 +33,6 @@ PlatformShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,7 @@ elif toolkit == 'cocoa':
|
||||
else:
|
||||
DIRS += ['other']
|
||||
|
||||
DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
|
||||
DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom']
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
DIRS += ['xul']
|
||||
|
@ -18,13 +18,3 @@ void
|
||||
a11y::PlatformShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
@ -34,12 +34,3 @@ a11y::PlatformShutdown()
|
||||
nsWinUtils::ShutdownWindowEmulation();
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyCreated(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
a11y::ProxyDestroyed(ProxyAccessible*)
|
||||
{
|
||||
}
|
||||
|
@ -19,7 +19,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
|
@ -22,7 +22,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -22,7 +22,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "7f097f40e32ecba580890ce1d3df2a493641bdec",
|
||||
"revision": "aa3ab2d389dce3ba351a897b4ae56f1fe9e1780d",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
@ -16,6 +16,12 @@
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/out/target/product/hamachi/*.mar"
|
||||
],
|
||||
"zip_files": [
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
"MOZILLA_OFFICIAL": "1",
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -21,7 +21,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -22,7 +22,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
|
@ -16,7 +16,11 @@
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
"{workdir}/.config",
|
||||
"{workdir}/sources.xml"
|
||||
"{workdir}/sources.xml",
|
||||
"{workdir}/profile.sh",
|
||||
["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
|
||||
["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
|
||||
["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
|
||||
],
|
||||
"env": {
|
||||
"VARIANT": "user",
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="a23d2c490b39c4699c9375e25c4acdf396a2fa85"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -1613,6 +1613,7 @@ pref("loop.retry_delay.limit", 300000);
|
||||
pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
|
||||
pref("loop.feedback.product", "Loop");
|
||||
pref("loop.debug.loglevel", "Error");
|
||||
pref("loop.debug.dispatcher", false);
|
||||
pref("loop.debug.websocket", false);
|
||||
pref("loop.debug.sdk", false);
|
||||
|
||||
|
@ -20,24 +20,22 @@ let gUpdater = {
|
||||
// Find all sites that remain in the grid.
|
||||
let sites = this._findRemainingSites(links);
|
||||
|
||||
let self = this;
|
||||
|
||||
// Remove sites that are no longer in the grid.
|
||||
this._removeLegacySites(sites, function () {
|
||||
this._removeLegacySites(sites, () => {
|
||||
// Freeze all site positions so that we can move their DOM nodes around
|
||||
// without any visual impact.
|
||||
self._freezeSitePositions(sites);
|
||||
this._freezeSitePositions(sites);
|
||||
|
||||
// Move the sites' DOM nodes to their new position in the DOM. This will
|
||||
// have no visual effect as all the sites have been frozen and will
|
||||
// remain in their current position.
|
||||
self._moveSiteNodes(sites);
|
||||
this._moveSiteNodes(sites);
|
||||
|
||||
// Now it's time to animate the sites actually moving to their new
|
||||
// positions.
|
||||
self._rearrangeSites(sites, function () {
|
||||
this._rearrangeSites(sites, () => {
|
||||
// Try to fill empty cells and finish.
|
||||
self._fillEmptyCells(links, aCallback);
|
||||
this._fillEmptyCells(links, aCallback);
|
||||
|
||||
// Update other pages that might be open to keep them synced.
|
||||
gAllPages.update(gPage);
|
||||
|
@ -30,8 +30,14 @@
|
||||
<script type="text/javascript" src="loop/shared/js/mixins.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/views.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/feedbackApiClient.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/actions.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/validate.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/conversationStore.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||
<script type="text/javascript" src="loop/shared/js/websocket.js"></script>
|
||||
<script type="text/javascript" src="loop/js/client.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversationViews.js"></script>
|
||||
<script type="text/javascript" src="loop/js/conversation.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -15,6 +15,12 @@ loop.Client = (function($) {
|
||||
// The expected properties to be returned from the GET /calls request.
|
||||
var expectedCallProperties = ["calls"];
|
||||
|
||||
// THe expected properties to be returned from the POST /calls request.
|
||||
var expectedPostCallProperties = [
|
||||
"apiKey", "callId", "progressURL",
|
||||
"sessionId", "sessionToken", "websocketToken"
|
||||
];
|
||||
|
||||
/**
|
||||
* Loop server client.
|
||||
*
|
||||
@ -209,6 +215,44 @@ loop.Client = (function($) {
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up an outgoing call, getting the relevant data from the server.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on successful registration, non-null otherwise.
|
||||
* - result an object of the obtained data for starting the call, if successful
|
||||
*
|
||||
* @param {Array} calleeIds an array of emails and phone numbers.
|
||||
* @param {String} callType the type of call.
|
||||
* @param {Function} cb Callback(err, result)
|
||||
*/
|
||||
setupOutgoingCall: function(calleeIds, callType, cb) {
|
||||
this.mozLoop.hawkRequest(this.mozLoop.LOOP_SESSION_TYPE.FXA,
|
||||
"/calls", "POST", {
|
||||
calleeId: calleeIds,
|
||||
callType: callType
|
||||
},
|
||||
function (err, responseText) {
|
||||
if (err) {
|
||||
this._failureHandler(cb, err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var postData = JSON.parse(responseText);
|
||||
|
||||
var outgoingCallData = this._validate(postData,
|
||||
expectedPostCallProperties);
|
||||
|
||||
cb(null, outgoingCallData);
|
||||
} catch (err) {
|
||||
console.log("Error requesting call info", err);
|
||||
cb(err);
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a value to a telemetry histogram, ignoring errors.
|
||||
*
|
||||
|
@ -11,8 +11,9 @@ var loop = loop || {};
|
||||
loop.conversation = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedViews = loop.shared.views,
|
||||
sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||
|
||||
var IncomingCallView = React.createClass({displayName: 'IncomingCallView',
|
||||
|
||||
@ -109,26 +110,23 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var btnClassAccept = "btn btn-accept";
|
||||
var btnClassDecline = "btn btn-error btn-decline";
|
||||
var conversationPanelClass = "incoming-call";
|
||||
var dropdownMenuClassesDecline = React.addons.classSet({
|
||||
"native-dropdown-menu": true,
|
||||
"conversation-window-dropdown": true,
|
||||
"visually-hidden": !this.state.showDeclineMenu
|
||||
});
|
||||
return (
|
||||
React.DOM.div({className: conversationPanelClass},
|
||||
React.DOM.div({className: "call-window"},
|
||||
React.DOM.h2(null, mozL10n.get("incoming_call_title2")),
|
||||
React.DOM.div({className: "btn-group incoming-call-action-group"},
|
||||
React.DOM.div({className: "btn-group call-action-group"},
|
||||
|
||||
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"}),
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"}),
|
||||
|
||||
React.DOM.div({className: "btn-chevron-menu-group"},
|
||||
React.DOM.div({className: "btn-group-chevron"},
|
||||
React.DOM.div({className: "btn-group"},
|
||||
|
||||
React.DOM.button({className: btnClassDecline,
|
||||
React.DOM.button({className: "btn btn-error btn-decline",
|
||||
onClick: this._handleDecline},
|
||||
mozL10n.get("incoming_call_cancel_button")
|
||||
),
|
||||
@ -146,11 +144,11 @@ loop.conversation = (function(mozL10n) {
|
||||
)
|
||||
),
|
||||
|
||||
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"}),
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"}),
|
||||
|
||||
AcceptCallButton({mode: this._answerModeProps()}),
|
||||
|
||||
React.DOM.div({className: "fx-embedded-incoming-call-button-spacer"})
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"})
|
||||
|
||||
)
|
||||
)
|
||||
@ -370,8 +368,10 @@ loop.conversation = (function(mozL10n) {
|
||||
websocketToken: this.props.conversation.get("websocketToken"),
|
||||
callId: this.props.conversation.get("callId"),
|
||||
});
|
||||
this._websocket.promiseConnect().then(function() {
|
||||
this.setState({callStatus: "incoming"});
|
||||
this._websocket.promiseConnect().then(function(progressStatus) {
|
||||
this.setState({
|
||||
callStatus: progressStatus === "terminated" ? "close" : "incoming"
|
||||
});
|
||||
}.bind(this), function() {
|
||||
this._handleSessionError();
|
||||
return;
|
||||
@ -489,6 +489,55 @@ loop.conversation = (function(mozL10n) {
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Master controller view for handling if incoming or outgoing calls are
|
||||
* in progress, and hence, which view to display.
|
||||
*/
|
||||
var ConversationControllerView = React.createClass({displayName: 'ConversationControllerView',
|
||||
propTypes: {
|
||||
// XXX Old types required for incoming call view.
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
|
||||
// XXX New types for OutgoingConversationView
|
||||
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change:outgoing", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// Don't display anything, until we know what type of call we are.
|
||||
if (this.state.outgoing === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.outgoing) {
|
||||
return (OutgoingConversationView({
|
||||
store: this.props.store}
|
||||
));
|
||||
}
|
||||
|
||||
return (IncomingConversationView({
|
||||
client: this.props.client,
|
||||
conversation: this.props.conversation,
|
||||
notifications: this.props.notifications,
|
||||
sdk: this.props.sdk}
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel initialisation.
|
||||
*/
|
||||
@ -509,36 +558,57 @@ loop.conversation = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var client = new loop.Client();
|
||||
var conversationStore = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
|
||||
// XXX For now key this on the pref, but this should really be
|
||||
// set by the information from the mozLoop API when we can get it (bug 1072323).
|
||||
var outgoingEmail = navigator.mozLoop.getLoopCharPref("outgoingemail");
|
||||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
var conversation = new sharedModels.ConversationModel(
|
||||
{}, // Model attributes
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
|
||||
// Obtain the callId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationHash();
|
||||
var callId;
|
||||
if (locationHash) {
|
||||
callId = locationHash.match(/\#incoming\/(.*)/)[1]
|
||||
conversation.set("callId", callId);
|
||||
}
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(conversation.get("callId"));
|
||||
});
|
||||
|
||||
// Obtain the callId and pass it to the conversation
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationHash();
|
||||
if (locationHash) {
|
||||
conversation.set("callId", locationHash.match(/\#incoming\/(.*)/)[1]);
|
||||
}
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
|
||||
React.renderComponent(IncomingConversationView({
|
||||
React.renderComponent(ConversationControllerView({
|
||||
store: conversationStore,
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
notifications: notifications,
|
||||
sdk: window.OT}
|
||||
), document.querySelector('#main'));
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
callId: callId,
|
||||
calleeId: outgoingEmail
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
ConversationControllerView: ConversationControllerView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
IncomingCallView: IncomingCallView,
|
||||
init: init
|
||||
|
@ -11,8 +11,9 @@ var loop = loop || {};
|
||||
loop.conversation = (function(mozL10n) {
|
||||
"use strict";
|
||||
|
||||
var sharedViews = loop.shared.views,
|
||||
sharedModels = loop.shared.models;
|
||||
var sharedViews = loop.shared.views;
|
||||
var sharedModels = loop.shared.models;
|
||||
var OutgoingConversationView = loop.conversationViews.OutgoingConversationView;
|
||||
|
||||
var IncomingCallView = React.createClass({
|
||||
|
||||
@ -109,26 +110,23 @@ loop.conversation = (function(mozL10n) {
|
||||
|
||||
render: function() {
|
||||
/* jshint ignore:start */
|
||||
var btnClassAccept = "btn btn-accept";
|
||||
var btnClassDecline = "btn btn-error btn-decline";
|
||||
var conversationPanelClass = "incoming-call";
|
||||
var dropdownMenuClassesDecline = React.addons.classSet({
|
||||
"native-dropdown-menu": true,
|
||||
"conversation-window-dropdown": true,
|
||||
"visually-hidden": !this.state.showDeclineMenu
|
||||
});
|
||||
return (
|
||||
<div className={conversationPanelClass}>
|
||||
<div className="call-window">
|
||||
<h2>{mozL10n.get("incoming_call_title2")}</h2>
|
||||
<div className="btn-group incoming-call-action-group">
|
||||
<div className="btn-group call-action-group">
|
||||
|
||||
<div className="fx-embedded-incoming-call-button-spacer"></div>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
|
||||
<div className="btn-chevron-menu-group">
|
||||
<div className="btn-group-chevron">
|
||||
<div className="btn-group">
|
||||
|
||||
<button className={btnClassDecline}
|
||||
<button className="btn btn-error btn-decline"
|
||||
onClick={this._handleDecline}>
|
||||
{mozL10n.get("incoming_call_cancel_button")}
|
||||
</button>
|
||||
@ -146,11 +144,11 @@ loop.conversation = (function(mozL10n) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="fx-embedded-incoming-call-button-spacer"></div>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
|
||||
<AcceptCallButton mode={this._answerModeProps()} />
|
||||
|
||||
<div className="fx-embedded-incoming-call-button-spacer"></div>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@ -370,8 +368,10 @@ loop.conversation = (function(mozL10n) {
|
||||
websocketToken: this.props.conversation.get("websocketToken"),
|
||||
callId: this.props.conversation.get("callId"),
|
||||
});
|
||||
this._websocket.promiseConnect().then(function() {
|
||||
this.setState({callStatus: "incoming"});
|
||||
this._websocket.promiseConnect().then(function(progressStatus) {
|
||||
this.setState({
|
||||
callStatus: progressStatus === "terminated" ? "close" : "incoming"
|
||||
});
|
||||
}.bind(this), function() {
|
||||
this._handleSessionError();
|
||||
return;
|
||||
@ -489,6 +489,55 @@ loop.conversation = (function(mozL10n) {
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Master controller view for handling if incoming or outgoing calls are
|
||||
* in progress, and hence, which view to display.
|
||||
*/
|
||||
var ConversationControllerView = React.createClass({
|
||||
propTypes: {
|
||||
// XXX Old types required for incoming call view.
|
||||
client: React.PropTypes.instanceOf(loop.Client).isRequired,
|
||||
conversation: React.PropTypes.instanceOf(sharedModels.ConversationModel)
|
||||
.isRequired,
|
||||
notifications: React.PropTypes.instanceOf(sharedModels.NotificationCollection)
|
||||
.isRequired,
|
||||
sdk: React.PropTypes.object.isRequired,
|
||||
|
||||
// XXX New types for OutgoingConversationView
|
||||
store: React.PropTypes.instanceOf(loop.store.ConversationStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change:outgoing", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// Don't display anything, until we know what type of call we are.
|
||||
if (this.state.outgoing === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (this.state.outgoing) {
|
||||
return (<OutgoingConversationView
|
||||
store={this.props.store}
|
||||
/>);
|
||||
}
|
||||
|
||||
return (<IncomingConversationView
|
||||
client={this.props.client}
|
||||
conversation={this.props.conversation}
|
||||
notifications={this.props.notifications}
|
||||
sdk={this.props.sdk}
|
||||
/>);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Panel initialisation.
|
||||
*/
|
||||
@ -509,36 +558,57 @@ loop.conversation = (function(mozL10n) {
|
||||
}
|
||||
});
|
||||
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
|
||||
var dispatcher = new loop.Dispatcher();
|
||||
var client = new loop.Client();
|
||||
var conversationStore = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
|
||||
// XXX For now key this on the pref, but this should really be
|
||||
// set by the information from the mozLoop API when we can get it (bug 1072323).
|
||||
var outgoingEmail = navigator.mozLoop.getLoopCharPref("outgoingemail");
|
||||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
var conversation = new sharedModels.ConversationModel(
|
||||
{}, // Model attributes
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
var notifications = new sharedModels.NotificationCollection();
|
||||
|
||||
// Obtain the callId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationHash();
|
||||
var callId;
|
||||
if (locationHash) {
|
||||
callId = locationHash.match(/\#incoming\/(.*)/)[1]
|
||||
conversation.set("callId", callId);
|
||||
}
|
||||
|
||||
window.addEventListener("unload", function(event) {
|
||||
// Handle direct close of dialog box via [x] control.
|
||||
navigator.mozLoop.releaseCallData(conversation.get("callId"));
|
||||
});
|
||||
|
||||
// Obtain the callId and pass it to the conversation
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
var locationHash = helper.locationHash();
|
||||
if (locationHash) {
|
||||
conversation.set("callId", locationHash.match(/\#incoming\/(.*)/)[1]);
|
||||
}
|
||||
document.body.classList.add(loop.shared.utils.getTargetPlatform());
|
||||
|
||||
React.renderComponent(<IncomingConversationView
|
||||
React.renderComponent(<ConversationControllerView
|
||||
store={conversationStore}
|
||||
client={client}
|
||||
conversation={conversation}
|
||||
notifications={notifications}
|
||||
sdk={window.OT}
|
||||
/>, document.querySelector('#main'));
|
||||
|
||||
dispatcher.dispatch(new loop.shared.actions.GatherCallData({
|
||||
callId: callId,
|
||||
calleeId: outgoingEmail
|
||||
}));
|
||||
}
|
||||
|
||||
return {
|
||||
ConversationControllerView: ConversationControllerView,
|
||||
IncomingConversationView: IncomingConversationView,
|
||||
IncomingCallView: IncomingCallView,
|
||||
init: init
|
||||
|
126
browser/components/loop/content/js/conversationViews.js
Normal file
126
browser/components/loop/content/js/conversationViews.js
Normal file
@ -0,0 +1,126 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.conversationViews = (function(mozL10n) {
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
|
||||
/**
|
||||
* Displays details of the incoming/outgoing conversation
|
||||
* (name, link, audio/video type etc).
|
||||
*
|
||||
* Allows the view to be extended with different buttons and progress
|
||||
* via children properties.
|
||||
*/
|
||||
var ConversationDetailView = React.createClass({displayName: 'ConversationDetailView',
|
||||
propTypes: {
|
||||
calleeId: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = this.props.calleeId;
|
||||
|
||||
return (
|
||||
React.DOM.div({className: "call-window"},
|
||||
React.DOM.h2(null, this.props.calleeId),
|
||||
React.DOM.div(null, this.props.children)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View for pending conversations. Displays a cancel button and appropriate
|
||||
* pending/ringing strings.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({displayName: 'PendingConversationView',
|
||||
propTypes: {
|
||||
callState: React.PropTypes.string,
|
||||
calleeId: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var pendingStateString;
|
||||
if (this.props.callState === CALL_STATES.ALERTING) {
|
||||
pendingStateString = mozL10n.get("call_progress_ringing_description");
|
||||
} else {
|
||||
pendingStateString = mozL10n.get("call_progress_connecting_description");
|
||||
}
|
||||
|
||||
return (
|
||||
ConversationDetailView({calleeId: this.props.calleeId},
|
||||
|
||||
React.DOM.p({className: "btn-label"}, pendingStateString),
|
||||
|
||||
React.DOM.div({className: "btn-group call-action-group"},
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"}),
|
||||
React.DOM.button({className: "btn btn-cancel"},
|
||||
mozL10n.get("initiate_call_cancel_button")
|
||||
),
|
||||
React.DOM.div({className: "fx-embedded-call-button-spacer"})
|
||||
)
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({displayName: 'CallFailedView',
|
||||
render: function() {
|
||||
return (
|
||||
React.DOM.div({className: "call-window"},
|
||||
React.DOM.h2(null, mozL10n.get("generic_failure_title"))
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Master View Controller for outgoing calls. This manages
|
||||
* the different views that need displaying.
|
||||
*/
|
||||
var OutgoingConversationView = React.createClass({displayName: 'OutgoingConversationView',
|
||||
propTypes: {
|
||||
store: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.callState === CALL_STATES.TERMINATED) {
|
||||
return (CallFailedView(null));
|
||||
}
|
||||
|
||||
return (PendingConversationView({
|
||||
callState: this.state.callState,
|
||||
calleeId: this.state.calleeId}
|
||||
))
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
PendingConversationView: PendingConversationView,
|
||||
ConversationDetailView: ConversationDetailView,
|
||||
CallFailedView: CallFailedView,
|
||||
OutgoingConversationView: OutgoingConversationView
|
||||
};
|
||||
|
||||
})(document.mozL10n || navigator.mozL10n);
|
126
browser/components/loop/content/js/conversationViews.jsx
Normal file
126
browser/components/loop/content/js/conversationViews.jsx
Normal file
@ -0,0 +1,126 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
/* 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/. */
|
||||
|
||||
/* global loop:true, React */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.conversationViews = (function(mozL10n) {
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
|
||||
/**
|
||||
* Displays details of the incoming/outgoing conversation
|
||||
* (name, link, audio/video type etc).
|
||||
*
|
||||
* Allows the view to be extended with different buttons and progress
|
||||
* via children properties.
|
||||
*/
|
||||
var ConversationDetailView = React.createClass({
|
||||
propTypes: {
|
||||
calleeId: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
document.title = this.props.calleeId;
|
||||
|
||||
return (
|
||||
<div className="call-window">
|
||||
<h2>{this.props.calleeId}</h2>
|
||||
<div>{this.props.children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* View for pending conversations. Displays a cancel button and appropriate
|
||||
* pending/ringing strings.
|
||||
*/
|
||||
var PendingConversationView = React.createClass({
|
||||
propTypes: {
|
||||
callState: React.PropTypes.string,
|
||||
calleeId: React.PropTypes.string,
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var pendingStateString;
|
||||
if (this.props.callState === CALL_STATES.ALERTING) {
|
||||
pendingStateString = mozL10n.get("call_progress_ringing_description");
|
||||
} else {
|
||||
pendingStateString = mozL10n.get("call_progress_connecting_description");
|
||||
}
|
||||
|
||||
return (
|
||||
<ConversationDetailView calleeId={this.props.calleeId}>
|
||||
|
||||
<p className="btn-label">{pendingStateString}</p>
|
||||
|
||||
<div className="btn-group call-action-group">
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
<button className="btn btn-cancel">
|
||||
{mozL10n.get("initiate_call_cancel_button")}
|
||||
</button>
|
||||
<div className="fx-embedded-call-button-spacer"></div>
|
||||
</div>
|
||||
|
||||
</ConversationDetailView>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Call failed view. Displayed when a call fails.
|
||||
*/
|
||||
var CallFailedView = React.createClass({
|
||||
render: function() {
|
||||
return (
|
||||
<div className="call-window">
|
||||
<h2>{mozL10n.get("generic_failure_title")}</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Master View Controller for outgoing calls. This manages
|
||||
* the different views that need displaying.
|
||||
*/
|
||||
var OutgoingConversationView = React.createClass({
|
||||
propTypes: {
|
||||
store: React.PropTypes.instanceOf(
|
||||
loop.store.ConversationStore).isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return this.props.store.attributes;
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
this.props.store.on("change", function() {
|
||||
this.setState(this.props.store.attributes);
|
||||
}, this);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (this.state.callState === CALL_STATES.TERMINATED) {
|
||||
return (<CallFailedView />);
|
||||
}
|
||||
|
||||
return (<PendingConversationView
|
||||
callState={this.state.callState}
|
||||
calleeId={this.state.calleeId}
|
||||
/>)
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
PendingConversationView: PendingConversationView,
|
||||
ConversationDetailView: ConversationDetailView,
|
||||
CallFailedView: CallFailedView,
|
||||
OutgoingConversationView: OutgoingConversationView
|
||||
};
|
||||
|
||||
})(document.mozL10n || navigator.mozL10n);
|
@ -223,14 +223,14 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Incoming call */
|
||||
/* General Call (incoming or outgoing). */
|
||||
|
||||
/*
|
||||
* Height matches the height of the docked window
|
||||
* but the UI breaks when you pop out
|
||||
* Bug 1040985
|
||||
*/
|
||||
.incoming-call {
|
||||
.call-window {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@ -238,26 +238,27 @@
|
||||
min-height: 230px;
|
||||
}
|
||||
|
||||
.incoming-call-action-group {
|
||||
.call-action-group {
|
||||
display: flex;
|
||||
padding: 2.5em 0 0 0;
|
||||
width: 100%;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.incoming-call-action-group > .btn {
|
||||
.call-action-group > .btn {
|
||||
margin-left: .5em;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.incoming-call-action-group .btn-group-chevron,
|
||||
.incoming-call-action-group .btn-group {
|
||||
.call-action-group .btn-group-chevron,
|
||||
.call-action-group .btn-group {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* XXX Once we get the incoming call avatar, bug 1047435, the H2 should
|
||||
* disappear from our markup, and we should remove this rule entirely.
|
||||
*/
|
||||
.incoming-call h2 {
|
||||
.call-window h2 {
|
||||
font-size: 1.5em;
|
||||
font-weight: normal;
|
||||
|
||||
@ -266,7 +267,7 @@
|
||||
margin: 0.83em 0;
|
||||
}
|
||||
|
||||
.fx-embedded-incoming-call-button-spacer {
|
||||
.fx-embedded-call-button-spacer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
}
|
||||
|
78
browser/components/loop/content/shared/js/actions.js
Normal file
78
browser/components/loop/content/shared/js/actions.js
Normal file
@ -0,0 +1,78 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global loop:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.shared = loop.shared || {};
|
||||
loop.shared.actions = (function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Actions are events that are triggered by the user, e.g. clicking a button,
|
||||
* or by an async event, e.g. status received.
|
||||
*
|
||||
* They should be dispatched to stores via the dispatcher.
|
||||
*/
|
||||
|
||||
function Action(name, schema, values) {
|
||||
var validatedData = new loop.validate.Validator(schema || {})
|
||||
.validate(values || {});
|
||||
for (var prop in validatedData)
|
||||
this[prop] = validatedData[prop];
|
||||
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Action.define = function(name, schema) {
|
||||
return Action.bind(null, name, schema);
|
||||
};
|
||||
|
||||
return {
|
||||
/**
|
||||
* Used to trigger gathering of initial call data.
|
||||
*/
|
||||
GatherCallData: Action.define("gatherCallData", {
|
||||
// XXX This may change when bug 1072323 is implemented.
|
||||
// Optional: Specify the calleeId for an outgoing call
|
||||
calleeId: [String, null],
|
||||
// Specify the callId for an incoming call.
|
||||
callId: [String, null]
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to cancel call setup.
|
||||
*/
|
||||
CancelCall: Action.define("cancelCall", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used to initiate connecting of a call with the relevant
|
||||
* sessionData.
|
||||
*/
|
||||
ConnectCall: Action.define("connectCall", {
|
||||
// This object contains the necessary details for the
|
||||
// connection of the websocket, and the SDK
|
||||
sessionData: Object
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used for notifying of connection progress state changes.
|
||||
* The connection refers to the overall connection flow as indicated
|
||||
* on the websocket.
|
||||
*/
|
||||
ConnectionProgress: Action.define("connectionProgress", {
|
||||
// The new connection state
|
||||
state: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used for notifying of connection failures.
|
||||
*/
|
||||
ConnectionFailure: Action.define("connectionFailure", {
|
||||
// A string relating to the reason the connection failed.
|
||||
reason: String
|
||||
})
|
||||
};
|
||||
})();
|
244
browser/components/loop/content/shared/js/conversationStore.js
Normal file
244
browser/components/loop/content/shared/js/conversationStore.js
Normal file
@ -0,0 +1,244 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global loop:true */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.store = (function() {
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
|
||||
var CALL_STATES = {
|
||||
// The initial state of the view.
|
||||
INIT: "init",
|
||||
// The store is gathering the call data from the server.
|
||||
GATHER: "gather",
|
||||
// The websocket has connected to the server and is waiting
|
||||
// for the other peer to connect to the websocket.
|
||||
CONNECTING: "connecting",
|
||||
// The websocket has received information that we're now alerting
|
||||
// the peer.
|
||||
ALERTING: "alerting",
|
||||
// The call was terminated due to an issue during connection.
|
||||
TERMINATED: "terminated"
|
||||
};
|
||||
|
||||
|
||||
var ConversationStore = Backbone.Model.extend({
|
||||
defaults: {
|
||||
// The current state of the call
|
||||
callState: CALL_STATES.INIT,
|
||||
// The reason if a call was terminated
|
||||
callStateReason: undefined,
|
||||
// The error information, if there was a failure
|
||||
error: undefined,
|
||||
// True if the call is outgoing, false if not, undefined if unknown
|
||||
outgoing: undefined,
|
||||
// The id of the person being called for outgoing calls
|
||||
calleeId: undefined,
|
||||
// The call type for the call.
|
||||
// XXX Don't hard-code, this comes from the data in bug 1072323
|
||||
callType: sharedUtils.CALL_TYPES.AUDIO_VIDEO,
|
||||
|
||||
// Call Connection information
|
||||
// The call id from the loop-server
|
||||
callId: undefined,
|
||||
// The connection progress url to connect the websocket
|
||||
progressURL: undefined,
|
||||
// The websocket token that allows connection to the progress url
|
||||
websocketToken: undefined,
|
||||
// SDK API key
|
||||
apiKey: undefined,
|
||||
// SDK session ID
|
||||
sessionId: undefined,
|
||||
// SDK session token
|
||||
sessionToken: undefined
|
||||
},
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Options:
|
||||
* - {loop.Dispatcher} dispatcher The dispatcher for dispatching actions and
|
||||
* registering to consume actions.
|
||||
* - {Object} client A client object for communicating with the server.
|
||||
*
|
||||
* @param {Object} attributes Attributes object.
|
||||
* @param {Object} options Options object.
|
||||
*/
|
||||
initialize: function(attributes, options) {
|
||||
options = options || {};
|
||||
|
||||
if (!options.dispatcher) {
|
||||
throw new Error("Missing option dispatcher");
|
||||
}
|
||||
if (!options.client) {
|
||||
throw new Error("Missing option client");
|
||||
}
|
||||
|
||||
this.client = options.client;
|
||||
this.dispatcher = options.dispatcher;
|
||||
|
||||
this.dispatcher.register(this, [
|
||||
"connectionFailure",
|
||||
"connectionProgress",
|
||||
"gatherCallData",
|
||||
"connectCall"
|
||||
]);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the connection failure action, setting the state to
|
||||
* terminated.
|
||||
*
|
||||
* @param {sharedActions.ConnectionFailure} actionData The action data.
|
||||
*/
|
||||
connectionFailure: function(actionData) {
|
||||
this.set({
|
||||
callState: CALL_STATES.TERMINATED,
|
||||
callStateReason: actionData.reason
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the connection progress action, setting the next state
|
||||
* appropriately.
|
||||
*
|
||||
* @param {sharedActions.ConnectionProgress} actionData The action data.
|
||||
*/
|
||||
connectionProgress: function(actionData) {
|
||||
// XXX Turn this into a state machine?
|
||||
if (actionData.state === "alerting" &&
|
||||
(this.get("callState") === CALL_STATES.CONNECTING ||
|
||||
this.get("callState") === CALL_STATES.GATHER)) {
|
||||
this.set({
|
||||
callState: CALL_STATES.ALERTING
|
||||
});
|
||||
}
|
||||
if (actionData.state === "connecting" &&
|
||||
this.get("callState") === CALL_STATES.GATHER) {
|
||||
this.set({
|
||||
callState: CALL_STATES.CONNECTING
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the gather call data action, setting the state
|
||||
* and starting to get the appropriate data for the type of call.
|
||||
*
|
||||
* @param {sharedActions.GatherCallData} actionData The action data.
|
||||
*/
|
||||
gatherCallData: function(actionData) {
|
||||
this.set({
|
||||
calleeId: actionData.calleeId,
|
||||
outgoing: !!actionData.calleeId,
|
||||
callId: actionData.callId,
|
||||
callState: CALL_STATES.GATHER
|
||||
});
|
||||
|
||||
if (this.get("outgoing")) {
|
||||
this._setupOutgoingCall();
|
||||
} // XXX Else, other types aren't supported yet.
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the connect call action, this saves the appropriate
|
||||
* data and starts the connection for the websocket to notify the
|
||||
* server of progress.
|
||||
*
|
||||
* @param {sharedActions.ConnectCall} actionData The action data.
|
||||
*/
|
||||
connectCall: function(actionData) {
|
||||
this.set(actionData.sessionData);
|
||||
this._connectWebSocket();
|
||||
},
|
||||
|
||||
/**
|
||||
* Obtains the outgoing call data from the server and handles the
|
||||
* result.
|
||||
*/
|
||||
_setupOutgoingCall: function() {
|
||||
// XXX For now, we only have one calleeId, so just wrap that in an array.
|
||||
this.client.setupOutgoingCall([this.get("calleeId")],
|
||||
this.get("callType"),
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
console.error("Failed to get outgoing call data", err);
|
||||
this.dispatcher.dispatch(
|
||||
new sharedActions.ConnectionFailure({reason: "setup"}));
|
||||
return;
|
||||
}
|
||||
|
||||
// Success, dispatch a new action.
|
||||
this.dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: result}));
|
||||
}.bind(this)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets up and connects the websocket to the server. The websocket
|
||||
* deals with sending and obtaining status via the server about the
|
||||
* setup of the call.
|
||||
*/
|
||||
_connectWebSocket: function() {
|
||||
this._websocket = new loop.CallConnectionWebSocket({
|
||||
url: this.get("progressURL"),
|
||||
callId: this.get("callId"),
|
||||
websocketToken: this.get("websocketToken")
|
||||
});
|
||||
|
||||
this._websocket.promiseConnect().then(
|
||||
function() {
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionProgress({
|
||||
// This is the websocket call state, i.e. waiting for the
|
||||
// other end to connect to the server.
|
||||
state: "connecting"
|
||||
}));
|
||||
}.bind(this),
|
||||
function(error) {
|
||||
console.error("Websocket failed to connect", error);
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: "websocket-setup"
|
||||
}));
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this._websocket.on("progress", this._handleWebSocketProgress, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to handle any progressed received from the websocket. This will
|
||||
* dispatch new actions so that the data can be handled appropriately.
|
||||
*/
|
||||
_handleWebSocketProgress: function(progressData) {
|
||||
var action;
|
||||
|
||||
switch(progressData.state) {
|
||||
case "terminated":
|
||||
action = new sharedActions.ConnectionFailure({
|
||||
reason: progressData.reason
|
||||
});
|
||||
break;
|
||||
case "alerting":
|
||||
action = new sharedActions.ConnectionProgress({
|
||||
state: progressData.state
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.warn("Received unexpected state in _handleWebSocketProgress", progressData.state);
|
||||
return;
|
||||
}
|
||||
|
||||
this.dispatcher.dispatch(action);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
CALL_STATES: CALL_STATES,
|
||||
ConversationStore: ConversationStore
|
||||
};
|
||||
})();
|
84
browser/components/loop/content/shared/js/dispatcher.js
Normal file
84
browser/components/loop/content/shared/js/dispatcher.js
Normal file
@ -0,0 +1,84 @@
|
||||
/* 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/. */
|
||||
|
||||
/* global loop:true */
|
||||
|
||||
/**
|
||||
* The dispatcher for actions. This dispatches actions to stores registered
|
||||
* for those actions.
|
||||
*
|
||||
* If stores need to perform async operations for actions, they should return
|
||||
* straight away, and set up a new action for the changes if necessary.
|
||||
*
|
||||
* It is an error if a returned promise rejects - they should always pass.
|
||||
*/
|
||||
var loop = loop || {};
|
||||
loop.Dispatcher = (function() {
|
||||
|
||||
function Dispatcher() {
|
||||
this._eventData = {};
|
||||
this._actionQueue = [];
|
||||
this._debug = loop.shared.utils.getBoolPreference("debug.dispatcher");
|
||||
}
|
||||
|
||||
Dispatcher.prototype = {
|
||||
/**
|
||||
* Register a store to receive notifications of specific actions.
|
||||
*
|
||||
* @param {Object} store The store object to register
|
||||
* @param {Array} eventTypes An array of action names
|
||||
*/
|
||||
register: function(store, eventTypes) {
|
||||
eventTypes.forEach(function(type) {
|
||||
if (this._eventData.hasOwnProperty(type)) {
|
||||
this._eventData[type].push(store);
|
||||
} else {
|
||||
this._eventData[type] = [store];
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches an action to all registered stores.
|
||||
*/
|
||||
dispatch: function(action) {
|
||||
// Always put it on the queue, to make it simpler.
|
||||
this._actionQueue.push(action);
|
||||
this._dispatchNextAction();
|
||||
},
|
||||
|
||||
/**
|
||||
* Dispatches the next action in the queue if one is not already active.
|
||||
*/
|
||||
_dispatchNextAction: function() {
|
||||
if (!this._actionQueue.length || this._active) {
|
||||
return;
|
||||
}
|
||||
|
||||
var action = this._actionQueue.shift();
|
||||
var type = action.name;
|
||||
|
||||
var registeredStores = this._eventData[type];
|
||||
if (!registeredStores) {
|
||||
console.warn("No stores registered for event type ", type);
|
||||
return;
|
||||
}
|
||||
|
||||
this._active = true;
|
||||
|
||||
if (this._debug) {
|
||||
console.log("[Dispatcher] Dispatching action", action);
|
||||
}
|
||||
|
||||
registeredStores.forEach(function(store) {
|
||||
store[type](action);
|
||||
});
|
||||
|
||||
this._active = false;
|
||||
this._dispatchNextAction();
|
||||
}
|
||||
};
|
||||
|
||||
return Dispatcher;
|
||||
})();
|
@ -9,6 +9,14 @@ loop.shared = loop.shared || {};
|
||||
loop.shared.utils = (function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Call types used for determining if a call is audio/video or audio-only.
|
||||
*/
|
||||
var CALL_TYPES = {
|
||||
AUDIO_VIDEO: "audio-video",
|
||||
AUDIO_ONLY: "audio"
|
||||
};
|
||||
|
||||
/**
|
||||
* Used for adding different styles to the panel
|
||||
* @returns {String} Corresponds to the client platform
|
||||
@ -77,6 +85,7 @@ loop.shared.utils = (function() {
|
||||
};
|
||||
|
||||
return {
|
||||
CALL_TYPES: CALL_TYPES,
|
||||
Helper: Helper,
|
||||
getTargetPlatform: getTargetPlatform,
|
||||
getBoolPreference: getBoolPreference
|
||||
|
127
browser/components/loop/content/shared/js/validate.js
Normal file
127
browser/components/loop/content/shared/js/validate.js
Normal file
@ -0,0 +1,127 @@
|
||||
/* 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/. */
|
||||
|
||||
/* jshint unused:false */
|
||||
|
||||
var loop = loop || {};
|
||||
loop.validate = (function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Computes the difference between two arrays.
|
||||
*
|
||||
* @param {Array} arr1 First array
|
||||
* @param {Array} arr2 Second array
|
||||
* @return {Array} Array difference
|
||||
*/
|
||||
function difference(arr1, arr2) {
|
||||
return arr1.filter(function(item) {
|
||||
return arr2.indexOf(item) === -1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type name of an object or constructor. Fallback to "unknown"
|
||||
* when it fails.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {String}
|
||||
*/
|
||||
function typeName(obj) {
|
||||
if (obj === null)
|
||||
return "null";
|
||||
if (typeof obj === "function")
|
||||
return obj.name || obj.toString().match(/^function\s?([^\s(]*)/)[1];
|
||||
if (typeof obj.constructor === "function")
|
||||
return typeName(obj.constructor);
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple typed values validator.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} schema Validation schema
|
||||
*/
|
||||
function Validator(schema) {
|
||||
this.schema = schema || {};
|
||||
}
|
||||
|
||||
Validator.prototype = {
|
||||
/**
|
||||
* Validates all passed values against declared dependencies.
|
||||
*
|
||||
* @param {Object} values The values object
|
||||
* @return {Object} The validated values object
|
||||
* @throws {TypeError} If validation fails
|
||||
*/
|
||||
validate: function(values) {
|
||||
this._checkRequiredProperties(values);
|
||||
this._checkRequiredTypes(values);
|
||||
return values;
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if any of Object values matches any of current dependency type
|
||||
* requirements.
|
||||
*
|
||||
* @param {Object} values The values object
|
||||
* @throws {TypeError}
|
||||
*/
|
||||
_checkRequiredTypes: function(values) {
|
||||
Object.keys(this.schema).forEach(function(name) {
|
||||
var types = this.schema[name];
|
||||
types = Array.isArray(types) ? types : [types];
|
||||
if (!this._dependencyMatchTypes(values[name], types)) {
|
||||
throw new TypeError("invalid dependency: " + name +
|
||||
"; expected " + types.map(typeName).join(", ") +
|
||||
", got " + typeName(values[name]));
|
||||
}
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if a values object owns the required keys defined in dependencies.
|
||||
* Values attached to these properties shouldn't be null nor undefined.
|
||||
*
|
||||
* @param {Object} values The values object
|
||||
* @throws {TypeError} If any dependency is missing.
|
||||
*/
|
||||
_checkRequiredProperties: function(values) {
|
||||
var definedProperties = Object.keys(values).filter(function(name) {
|
||||
return typeof values[name] !== "undefined";
|
||||
});
|
||||
var diff = difference(Object.keys(this.schema), definedProperties);
|
||||
if (diff.length > 0)
|
||||
throw new TypeError("missing required " + diff.join(", "));
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if a given value matches any of the provided type requirements.
|
||||
*
|
||||
* @param {Object} value The value to check
|
||||
* @param {Array} types The list of types to check the value against
|
||||
* @return {Boolean}
|
||||
* @throws {TypeError} If the value doesn't match any types.
|
||||
*/
|
||||
_dependencyMatchTypes: function(value, types) {
|
||||
return types.some(function(Type) {
|
||||
/*jshint eqeqeq:false*/
|
||||
try {
|
||||
return typeof Type === "undefined" || // skip checking
|
||||
Type === null && value === null || // null type
|
||||
value.constructor == Type || // native type
|
||||
Type.prototype.isPrototypeOf(value) || // custom type
|
||||
typeName(value) === typeName(Type); // type string eq.
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
Validator: Validator
|
||||
};
|
||||
})();
|
@ -99,10 +99,13 @@ loop.CallConnectionWebSocket = (function() {
|
||||
* Internal function called to resolve the connection promise.
|
||||
*
|
||||
* It will log an error if no promise is found.
|
||||
*
|
||||
* @param {String} progressState The current state of progress of the
|
||||
* websocket.
|
||||
*/
|
||||
_completeConnection: function() {
|
||||
_completeConnection: function(progressState) {
|
||||
if (this.connectDetails && this.connectDetails.resolve) {
|
||||
this.connectDetails.resolve();
|
||||
this.connectDetails.resolve(progressState);
|
||||
this._clearConnectionFlags();
|
||||
return;
|
||||
}
|
||||
@ -227,7 +230,7 @@ loop.CallConnectionWebSocket = (function() {
|
||||
|
||||
switch(msg.messageType) {
|
||||
case "hello":
|
||||
this._completeConnection();
|
||||
this._completeConnection(msg.state);
|
||||
break;
|
||||
case "progress":
|
||||
this.trigger("progress:" + msg.state);
|
||||
|
@ -16,6 +16,7 @@ browser.jar:
|
||||
content/browser/loop/js/otconfig.js (content/js/otconfig.js)
|
||||
content/browser/loop/js/panel.js (content/js/panel.js)
|
||||
content/browser/loop/js/contacts.js (content/js/contacts.js)
|
||||
content/browser/loop/js/conversationViews.js (content/js/conversationViews.js)
|
||||
|
||||
# Shared styles
|
||||
content/browser/loop/shared/css/reset.css (content/shared/css/reset.css)
|
||||
@ -52,11 +53,15 @@ browser.jar:
|
||||
content/browser/loop/shared/img/icons-16x16.svg (content/shared/img/icons-16x16.svg)
|
||||
|
||||
# Shared scripts
|
||||
content/browser/loop/shared/js/actions.js (content/shared/js/actions.js)
|
||||
content/browser/loop/shared/js/conversationStore.js (content/shared/js/conversationStore.js)
|
||||
content/browser/loop/shared/js/dispatcher.js (content/shared/js/dispatcher.js)
|
||||
content/browser/loop/shared/js/feedbackApiClient.js (content/shared/js/feedbackApiClient.js)
|
||||
content/browser/loop/shared/js/models.js (content/shared/js/models.js)
|
||||
content/browser/loop/shared/js/mixins.js (content/shared/js/mixins.js)
|
||||
content/browser/loop/shared/js/views.js (content/shared/js/views.js)
|
||||
content/browser/loop/shared/js/utils.js (content/shared/js/utils.js)
|
||||
content/browser/loop/shared/js/validate.js (content/shared/js/validate.js)
|
||||
content/browser/loop/shared/js/websocket.js (content/shared/js/websocket.js)
|
||||
|
||||
# Shared libs
|
||||
|
@ -253,5 +253,70 @@ describe("loop.Client", function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#setupOutgoingCall", function() {
|
||||
var calleeIds, callType;
|
||||
|
||||
beforeEach(function() {
|
||||
calleeIds = [
|
||||
"fakeemail", "fake phone"
|
||||
];
|
||||
callType = "audio";
|
||||
});
|
||||
|
||||
it("should make a POST call to /calls", function() {
|
||||
client.setupOutgoingCall(calleeIds, callType);
|
||||
|
||||
sinon.assert.calledOnce(hawkRequestStub);
|
||||
sinon.assert.calledWith(hawkRequestStub,
|
||||
mozLoop.LOOP_SESSION_TYPE.FXA,
|
||||
"/calls",
|
||||
"POST",
|
||||
{ calleeId: calleeIds, callType: callType }
|
||||
);
|
||||
});
|
||||
|
||||
it("should call the callback if the request is successful", function() {
|
||||
var requestData = {
|
||||
apiKey: "fake",
|
||||
callId: "fakeCall",
|
||||
progressURL: "fakeurl",
|
||||
sessionId: "12345678",
|
||||
sessionToken: "15263748",
|
||||
websocketToken: "13572468"
|
||||
};
|
||||
|
||||
hawkRequestStub.callsArgWith(4, null, JSON.stringify(requestData));
|
||||
|
||||
client.setupOutgoingCall(calleeIds, callType, callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithExactly(callback, null, requestData);
|
||||
});
|
||||
|
||||
it("should send an error when the request fails", function() {
|
||||
hawkRequestStub.callsArgWith(4, fakeErrorRes);
|
||||
|
||||
client.setupOutgoingCall(calleeIds, callType, callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithExactly(callback, sinon.match(function(err) {
|
||||
return /400.*invalid token/.test(err.message);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should send an error if the data is not valid", function() {
|
||||
// Sets up the hawkRequest stub to trigger the callback with
|
||||
// an error
|
||||
hawkRequestStub.callsArgWith(4, null, "{}");
|
||||
|
||||
client.setupOutgoingCall(calleeIds, callType, callback);
|
||||
|
||||
sinon.assert.calledOnce(callback);
|
||||
sinon.assert.calledWithMatch(callback, sinon.match(function(err) {
|
||||
return /Invalid data received/.test(err.message);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1,131 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.conversationViews", function () {
|
||||
var sandbox, oldTitle, view;
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
oldTitle = document.title;
|
||||
sandbox.stub(document.mozL10n, "get", function(x) {
|
||||
return x;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
document.title = oldTitle;
|
||||
view = undefined;
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("ConversationDetailView", function() {
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversationViews.ConversationDetailView(props));
|
||||
}
|
||||
|
||||
it("should set the document title to the calledId", function() {
|
||||
mountTestComponent({calleeId: "mrsmith"});
|
||||
|
||||
expect(document.title).eql("mrsmith");
|
||||
});
|
||||
|
||||
it("should set display the calledId", function() {
|
||||
view = mountTestComponent({calleeId: "mrsmith"});
|
||||
|
||||
expect(TestUtils.findRenderedDOMComponentWithTag(
|
||||
view, "h2").props.children).eql("mrsmith");
|
||||
});
|
||||
});
|
||||
|
||||
describe("PendingConversationView", function() {
|
||||
function mountTestComponent(props) {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversationViews.PendingConversationView(props));
|
||||
}
|
||||
|
||||
it("should set display connecting string when the state is not alerting",
|
||||
function() {
|
||||
view = mountTestComponent({
|
||||
callState: CALL_STATES.CONNECTING,
|
||||
calleeId: "mrsmith"
|
||||
});
|
||||
|
||||
var label = TestUtils.findRenderedDOMComponentWithClass(
|
||||
view, "btn-label").props.children;
|
||||
|
||||
expect(label).to.have.string("connecting");
|
||||
});
|
||||
|
||||
it("should set display ringing string when the state is alerting",
|
||||
function() {
|
||||
view = mountTestComponent({
|
||||
callState: CALL_STATES.ALERTING,
|
||||
calleeId: "mrsmith"
|
||||
});
|
||||
|
||||
var label = TestUtils.findRenderedDOMComponentWithClass(
|
||||
view, "btn-label").props.children;
|
||||
|
||||
expect(label).to.have.string("ringing");
|
||||
});
|
||||
});
|
||||
|
||||
describe("OutgoingConversationView", function() {
|
||||
var store;
|
||||
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversationViews.OutgoingConversationView({
|
||||
store: store
|
||||
}));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
store = new loop.store.ConversationStore({}, {
|
||||
dispatcher: new loop.Dispatcher(),
|
||||
client: {}
|
||||
});
|
||||
});
|
||||
|
||||
it("should render the CallFailedView when the call state is 'terminated'",
|
||||
function() {
|
||||
store.set({callState: CALL_STATES.TERMINATED});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.conversationViews.CallFailedView);
|
||||
});
|
||||
|
||||
it("should render the PendingConversationView when the call state is connecting",
|
||||
function() {
|
||||
store.set({callState: CALL_STATES.CONNECTING});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.conversationViews.PendingConversationView);
|
||||
});
|
||||
|
||||
it("should update the rendered views when the state is changed.",
|
||||
function() {
|
||||
store.set({callState: CALL_STATES.CONNECTING});
|
||||
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.conversationViews.PendingConversationView);
|
||||
|
||||
store.set({callState: CALL_STATES.TERMINATED});
|
||||
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.conversationViews.CallFailedView);
|
||||
});
|
||||
});
|
||||
});
|
@ -41,7 +41,7 @@ describe("loop.conversation", function() {
|
||||
return "en-US";
|
||||
},
|
||||
setLoopCharPref: sinon.stub(),
|
||||
getLoopCharPref: sinon.stub(),
|
||||
getLoopCharPref: sinon.stub().returns(null),
|
||||
getLoopBoolPref: sinon.stub(),
|
||||
getCallData: sinon.stub(),
|
||||
releaseCallData: sinon.stub(),
|
||||
@ -68,8 +68,6 @@ describe("loop.conversation", function() {
|
||||
});
|
||||
|
||||
describe("#init", function() {
|
||||
var oldTitle;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox.stub(React, "renderComponent");
|
||||
sandbox.stub(document.mozL10n, "initialize");
|
||||
@ -77,6 +75,11 @@ describe("loop.conversation", function() {
|
||||
sandbox.stub(loop.shared.models.ConversationModel.prototype,
|
||||
"initialize");
|
||||
|
||||
sandbox.stub(loop.Dispatcher.prototype, "dispatch");
|
||||
|
||||
sandbox.stub(loop.shared.utils.Helper.prototype,
|
||||
"locationHash").returns("#incoming/42");
|
||||
|
||||
window.OT = {
|
||||
overrideGuidStorage: sinon.stub()
|
||||
};
|
||||
@ -94,17 +97,78 @@ describe("loop.conversation", function() {
|
||||
navigator.mozLoop);
|
||||
});
|
||||
|
||||
it("should create the IncomingConversationView", function() {
|
||||
it("should create the ConversationControllerView", function() {
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(React.renderComponent);
|
||||
sinon.assert.calledWith(React.renderComponent,
|
||||
sinon.match(function(value) {
|
||||
return TestUtils.isDescriptorOfType(value,
|
||||
loop.conversation.IncomingConversationView);
|
||||
loop.conversation.ConversationControllerView);
|
||||
}));
|
||||
});
|
||||
|
||||
it("should trigger a gatherCallData action", function() {
|
||||
loop.conversation.init();
|
||||
|
||||
sinon.assert.calledOnce(loop.Dispatcher.prototype.dispatch);
|
||||
sinon.assert.calledWithExactly(loop.Dispatcher.prototype.dispatch,
|
||||
new loop.shared.actions.GatherCallData({
|
||||
calleeId: null,
|
||||
callId: "42"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("ConversationControllerView", function() {
|
||||
var store, conversation, client, ccView, oldTitle, dispatcher;
|
||||
|
||||
function mountTestComponent() {
|
||||
return TestUtils.renderIntoDocument(
|
||||
loop.conversation.ConversationControllerView({
|
||||
client: client,
|
||||
conversation: conversation,
|
||||
notifications: notifications,
|
||||
sdk: {},
|
||||
store: store
|
||||
}));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
oldTitle = document.title;
|
||||
client = new loop.Client();
|
||||
conversation = new loop.shared.models.ConversationModel({}, {
|
||||
sdk: {}
|
||||
});
|
||||
dispatcher = new loop.Dispatcher();
|
||||
store = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
ccView = undefined;
|
||||
document.title = oldTitle;
|
||||
});
|
||||
|
||||
it("should display the OutgoingConversationView for outgoing calls", function() {
|
||||
store.set({outgoing: true});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ccView,
|
||||
loop.conversationViews.OutgoingConversationView);
|
||||
});
|
||||
|
||||
it("should display the IncomingConversationView for incoming calls", function() {
|
||||
store.set({outgoing: false});
|
||||
|
||||
ccView = mountTestComponent();
|
||||
|
||||
TestUtils.findRenderedComponentWithType(ccView,
|
||||
loop.conversation.IncomingConversationView);
|
||||
});
|
||||
});
|
||||
|
||||
describe("IncomingConversationView", function() {
|
||||
@ -231,7 +295,7 @@ describe("loop.conversation", function() {
|
||||
|
||||
it("should set the state to incoming on success", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect();
|
||||
resolveWebSocketConnect("incoming");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("incoming");
|
||||
@ -239,6 +303,17 @@ describe("loop.conversation", function() {
|
||||
});
|
||||
});
|
||||
|
||||
it("should set the state to close on success if the progress " +
|
||||
"state is terminated", function(done) {
|
||||
icView = mountTestComponent();
|
||||
resolveWebSocketConnect("terminated");
|
||||
|
||||
promise.then(function () {
|
||||
expect(icView.state.callStatus).eql("close");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should display an error if the websocket failed to connect", function(done) {
|
||||
sandbox.stub(notifications, "errorL10n");
|
||||
|
||||
|
@ -34,11 +34,16 @@
|
||||
<!-- App scripts -->
|
||||
<script src="../../content/shared/js/utils.js"></script>
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../content/shared/js/conversationStore.js"></script>
|
||||
<script src="../../content/shared/js/models.js"></script>
|
||||
<script src="../../content/shared/js/mixins.js"></script>
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/actions.js"></script>
|
||||
<script src="../../content/shared/js/validate.js"></script>
|
||||
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||
<script src="../../content/js/client.js"></script>
|
||||
<script src="../../content/js/conversationViews.js"></script>
|
||||
<script src="../../content/js/conversation.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="../../content/js/contacts.js"></script>
|
||||
<script src="../../content/js/panel.js"></script>
|
||||
@ -47,6 +52,7 @@
|
||||
<script src="client_test.js"></script>
|
||||
<script src="conversation_test.js"></script>
|
||||
<script src="panel_test.js"></script>
|
||||
<script src="conversationViews_test.js"></script>
|
||||
<script>
|
||||
// Stop the default init functions running to avoid conflicts in tests
|
||||
document.removeEventListener('DOMContentLoaded', loop.panel.init);
|
||||
|
321
browser/components/loop/test/shared/conversationStore_test.js
Normal file
321
browser/components/loop/test/shared/conversationStore_test.js
Normal file
@ -0,0 +1,321 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.ConversationStore", function () {
|
||||
"use strict";
|
||||
|
||||
var CALL_STATES = loop.store.CALL_STATES;
|
||||
var sharedActions = loop.shared.actions;
|
||||
var sharedUtils = loop.shared.utils;
|
||||
var sandbox, dispatcher, client, store, fakeSessionData;
|
||||
var connectPromise, resolveConnectPromise, rejectConnectPromise;
|
||||
|
||||
function checkFailures(done, f) {
|
||||
try {
|
||||
f();
|
||||
done();
|
||||
} catch (err) {
|
||||
done(err);
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
||||
dispatcher = new loop.Dispatcher();
|
||||
client = {
|
||||
setupOutgoingCall: sinon.stub()
|
||||
};
|
||||
store = new loop.store.ConversationStore({}, {
|
||||
client: client,
|
||||
dispatcher: dispatcher
|
||||
});
|
||||
fakeSessionData = {
|
||||
apiKey: "fakeKey",
|
||||
callId: "142536",
|
||||
sessionId: "321456",
|
||||
sessionToken: "341256",
|
||||
websocketToken: "543216",
|
||||
progressURL: "fakeURL"
|
||||
};
|
||||
|
||||
var dummySocket = {
|
||||
close: sinon.spy(),
|
||||
send: sinon.spy()
|
||||
};
|
||||
|
||||
connectPromise = new Promise(function(resolve, reject) {
|
||||
resolveConnectPromise = resolve;
|
||||
rejectConnectPromise = reject;
|
||||
});
|
||||
|
||||
sandbox.stub(loop.CallConnectionWebSocket.prototype,
|
||||
"promiseConnect").returns(connectPromise);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("#initialize", function() {
|
||||
it("should throw an error if the dispatcher is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.ConversationStore({}, {client: client});
|
||||
}).to.Throw(/dispatcher/);
|
||||
});
|
||||
|
||||
it("should throw an error if the client is missing", function() {
|
||||
expect(function() {
|
||||
new loop.store.ConversationStore({}, {dispatcher: dispatcher});
|
||||
}).to.Throw(/client/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectionFailure", function() {
|
||||
it("should set the state to 'terminated'", function() {
|
||||
store.set({callState: CALL_STATES.ALERTING});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionFailure({reason: "fake"}));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.TERMINATED);
|
||||
expect(store.get("callStateReason")).eql("fake");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectionProgress", function() {
|
||||
describe("progress: connecting", function() {
|
||||
it("should change the state from 'gather' to 'connecting'", function() {
|
||||
store.set({callState: CALL_STATES.GATHER});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionProgress({state: "connecting"}));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.CONNECTING);
|
||||
});
|
||||
});
|
||||
|
||||
describe("progress: alerting", function() {
|
||||
it("should set the state from 'gather' to 'alerting'", function() {
|
||||
store.set({callState: CALL_STATES.GATHER});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionProgress({state: "alerting"}));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
||||
});
|
||||
|
||||
it("should set the state from 'connecting' to 'alerting'", function() {
|
||||
store.set({callState: CALL_STATES.CONNECTING});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectionProgress({state: "alerting"}));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.ALERTING);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#gatherCallData", function() {
|
||||
beforeEach(function() {
|
||||
store.set({callState: CALL_STATES.INIT});
|
||||
});
|
||||
|
||||
it("should set the state to 'gather'", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
calleeId: "",
|
||||
callId: "76543218"
|
||||
}));
|
||||
|
||||
expect(store.get("callState")).eql(CALL_STATES.GATHER);
|
||||
});
|
||||
|
||||
it("should save the basic call information", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData({
|
||||
calleeId: "fake",
|
||||
callId: "123456"
|
||||
}));
|
||||
|
||||
expect(store.get("calleeId")).eql("fake");
|
||||
expect(store.get("callId")).eql("123456");
|
||||
expect(store.get("outgoing")).eql(true);
|
||||
});
|
||||
|
||||
describe("outgoing calls", function() {
|
||||
var outgoingCallData;
|
||||
|
||||
beforeEach(function() {
|
||||
outgoingCallData = {
|
||||
calleeId: "fake",
|
||||
callId: "135246"
|
||||
};
|
||||
});
|
||||
|
||||
it("should request the outgoing call data", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
|
||||
sinon.assert.calledOnce(client.setupOutgoingCall);
|
||||
sinon.assert.calledWith(client.setupOutgoingCall,
|
||||
["fake"], sharedUtils.CALL_TYPES.AUDIO_VIDEO);
|
||||
});
|
||||
|
||||
describe("server response handling", function() {
|
||||
beforeEach(function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should dispatch a connect call action on success", function() {
|
||||
var callData = {
|
||||
apiKey: "fakeKey"
|
||||
};
|
||||
|
||||
client.setupOutgoingCall.callsArgWith(2, null, callData);
|
||||
|
||||
store.gatherCallData(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectCall"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("sessionData", callData));
|
||||
});
|
||||
|
||||
it("should dispatch a connection failure action on failure", function() {
|
||||
client.setupOutgoingCall.callsArgWith(2, {});
|
||||
|
||||
store.gatherCallData(
|
||||
new sharedActions.GatherCallData(outgoingCallData));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectionFailure"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("reason", "setup"));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#connectCall", function() {
|
||||
it("should save the call session data", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||
|
||||
expect(store.get("apiKey")).eql("fakeKey");
|
||||
expect(store.get("callId")).eql("142536");
|
||||
expect(store.get("sessionId")).eql("321456");
|
||||
expect(store.get("sessionToken")).eql("341256");
|
||||
expect(store.get("websocketToken")).eql("543216");
|
||||
expect(store.get("progressURL")).eql("fakeURL");
|
||||
});
|
||||
|
||||
it("should initialize the websocket", function() {
|
||||
sandbox.stub(loop, "CallConnectionWebSocket").returns({
|
||||
promiseConnect: function() { return connectPromise; },
|
||||
on: sinon.spy()
|
||||
});
|
||||
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||
|
||||
sinon.assert.calledOnce(loop.CallConnectionWebSocket);
|
||||
sinon.assert.calledWithExactly(loop.CallConnectionWebSocket, {
|
||||
url: "fakeURL",
|
||||
callId: "142536",
|
||||
websocketToken: "543216"
|
||||
});
|
||||
});
|
||||
|
||||
it("should connect the websocket to the server", function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||
|
||||
sinon.assert.calledOnce(store._websocket.promiseConnect);
|
||||
});
|
||||
|
||||
describe("WebSocket connection result", function() {
|
||||
beforeEach(function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should dispatch a connection progress action on success", function(done) {
|
||||
resolveConnectPromise();
|
||||
|
||||
connectPromise.then(function() {
|
||||
checkFailures(done, function() {
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectionProgress"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("state", "connecting"));
|
||||
});
|
||||
}, function() {
|
||||
done(new Error("Promise should have been resolve, not rejected"));
|
||||
});
|
||||
});
|
||||
|
||||
it("should dispatch a connection failure action on failure", function(done) {
|
||||
rejectConnectPromise();
|
||||
|
||||
connectPromise.then(function() {
|
||||
done(new Error("Promise should have been rejected, not resolved"));
|
||||
}, function() {
|
||||
checkFailures(done, function() {
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectionFailure"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("reason", "websocket-setup"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("Websocket progress", function() {
|
||||
beforeEach(function() {
|
||||
dispatcher.dispatch(
|
||||
new sharedActions.ConnectCall({sessionData: fakeSessionData}));
|
||||
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
});
|
||||
|
||||
it("should dispatch a connection failure action on 'terminate'", function() {
|
||||
store._websocket.trigger("progress", {state: "terminated", reason: "reject"});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectionFailure"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("reason", "reject"));
|
||||
});
|
||||
|
||||
it("should dispatch a connection progress action on 'alerting'", function() {
|
||||
store._websocket.trigger("progress", {state: "alerting"});
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
// Can't use instanceof here, as that matches any action
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("name", "connectionProgress"));
|
||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||
sinon.match.hasOwn("state", "alerting"));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
140
browser/components/loop/test/shared/dispatcher_test.js
Normal file
140
browser/components/loop/test/shared/dispatcher_test.js
Normal file
@ -0,0 +1,140 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("loop.Dispatcher", function () {
|
||||
"use strict";
|
||||
|
||||
var sharedActions = loop.shared.actions;
|
||||
var dispatcher, sandbox;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
dispatcher = new loop.Dispatcher();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
sandbox.restore();
|
||||
});
|
||||
|
||||
describe("#register", function() {
|
||||
it("should register a store against an action name", function() {
|
||||
var object = { fake: true };
|
||||
|
||||
dispatcher.register(object, ["gatherCallData"]);
|
||||
|
||||
expect(dispatcher._eventData["gatherCallData"][0]).eql(object);
|
||||
});
|
||||
|
||||
it("should register multiple store against an action name", function() {
|
||||
var object1 = { fake: true };
|
||||
var object2 = { fake2: true };
|
||||
|
||||
dispatcher.register(object1, ["gatherCallData"]);
|
||||
dispatcher.register(object2, ["gatherCallData"]);
|
||||
|
||||
expect(dispatcher._eventData["gatherCallData"][0]).eql(object1);
|
||||
expect(dispatcher._eventData["gatherCallData"][1]).eql(object2);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#dispatch", function() {
|
||||
var gatherStore1, gatherStore2, cancelStore1, connectStore1;
|
||||
var gatherAction, cancelAction, connectAction, resolveCancelStore1;
|
||||
|
||||
beforeEach(function() {
|
||||
gatherAction = new sharedActions.GatherCallData({
|
||||
callId: "42",
|
||||
calleeId: null
|
||||
});
|
||||
|
||||
cancelAction = new sharedActions.CancelCall();
|
||||
connectAction = new sharedActions.ConnectCall({
|
||||
sessionData: {}
|
||||
});
|
||||
|
||||
gatherStore1 = {
|
||||
gatherCallData: sinon.stub()
|
||||
};
|
||||
gatherStore2 = {
|
||||
gatherCallData: sinon.stub()
|
||||
};
|
||||
cancelStore1 = {
|
||||
cancelCall: sinon.stub()
|
||||
};
|
||||
connectStore1 = {
|
||||
connectCall: function() {}
|
||||
};
|
||||
|
||||
dispatcher.register(gatherStore1, ["gatherCallData"]);
|
||||
dispatcher.register(gatherStore2, ["gatherCallData"]);
|
||||
dispatcher.register(cancelStore1, ["cancelCall"]);
|
||||
dispatcher.register(connectStore1, ["connectCall"]);
|
||||
});
|
||||
|
||||
it("should dispatch an action to the required object", function() {
|
||||
dispatcher.dispatch(cancelAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore1.gatherCallData);
|
||||
|
||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
||||
sinon.assert.calledWithExactly(cancelStore1.cancelCall, cancelAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore2.gatherCallData);
|
||||
});
|
||||
|
||||
it("should dispatch actions to multiple objects", function() {
|
||||
dispatcher.dispatch(gatherAction);
|
||||
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledWithExactly(gatherStore1.gatherCallData, gatherAction);
|
||||
|
||||
sinon.assert.notCalled(cancelStore1.cancelCall);
|
||||
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
sinon.assert.calledWithExactly(gatherStore2.gatherCallData, gatherAction);
|
||||
});
|
||||
|
||||
it("should dispatch multiple actions", function() {
|
||||
dispatcher.dispatch(cancelAction);
|
||||
dispatcher.dispatch(gatherAction);
|
||||
|
||||
sinon.assert.calledOnce(cancelStore1.cancelCall);
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
});
|
||||
|
||||
describe("Queued actions", function() {
|
||||
beforeEach(function() {
|
||||
// Restore the stub, so that we can easily add a function to be
|
||||
// returned. Unfortunately, sinon doesn't make this easy.
|
||||
sandbox.stub(connectStore1, "connectCall", function() {
|
||||
dispatcher.dispatch(gatherAction);
|
||||
|
||||
sinon.assert.notCalled(gatherStore1.gatherCallData);
|
||||
sinon.assert.notCalled(gatherStore2.gatherCallData);
|
||||
});
|
||||
});
|
||||
|
||||
it("should not dispatch an action if the previous action hasn't finished", function() {
|
||||
// Dispatch the first action. The action handler dispatches the second
|
||||
// action - see the beforeEach above.
|
||||
dispatcher.dispatch(connectAction);
|
||||
|
||||
sinon.assert.calledOnce(connectStore1.connectCall);
|
||||
});
|
||||
|
||||
it("should dispatch an action when the previous action finishes", function() {
|
||||
// Dispatch the first action. The action handler dispatches the second
|
||||
// action - see the beforeEach above.
|
||||
dispatcher.dispatch(connectAction);
|
||||
|
||||
sinon.assert.calledOnce(connectStore1.connectCall);
|
||||
// These should be called, because the dispatcher synchronously queues actions.
|
||||
sinon.assert.calledOnce(gatherStore1.gatherCallData);
|
||||
sinon.assert.calledOnce(gatherStore2.gatherCallData);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -39,6 +39,10 @@
|
||||
<script src="../../content/shared/js/views.js"></script>
|
||||
<script src="../../content/shared/js/websocket.js"></script>
|
||||
<script src="../../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../../content/shared/js/validate.js"></script>
|
||||
<script src="../../content/shared/js/actions.js"></script>
|
||||
<script src="../../content/shared/js/dispatcher.js"></script>
|
||||
<script src="../../content/shared/js/conversationStore.js"></script>
|
||||
|
||||
<!-- Test scripts -->
|
||||
<script src="models_test.js"></script>
|
||||
@ -47,6 +51,9 @@
|
||||
<script src="views_test.js"></script>
|
||||
<script src="websocket_test.js"></script>
|
||||
<script src="feedbackApiClient_test.js"></script>
|
||||
<script src="validate_test.js"></script>
|
||||
<script src="dispatcher_test.js"></script>
|
||||
<script src="conversationStore_test.js"></script>
|
||||
<script>
|
||||
mocha.run(function () {
|
||||
$("#mocha").append("<p id='complete'>Complete.</p>");
|
||||
|
82
browser/components/loop/test/shared/validate_test.js
Normal file
82
browser/components/loop/test/shared/validate_test.js
Normal file
@ -0,0 +1,82 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/*global chai, validate */
|
||||
|
||||
var expect = chai.expect;
|
||||
|
||||
describe("Validator", function() {
|
||||
"use strict";
|
||||
|
||||
// test helpers
|
||||
function create(dependencies, values) {
|
||||
var validator = new loop.validate.Validator(dependencies);
|
||||
return validator.validate.bind(validator, values);
|
||||
}
|
||||
|
||||
// test types
|
||||
function X(){}
|
||||
function Y(){}
|
||||
|
||||
describe("#validate", function() {
|
||||
it("should check for a single required dependency when no option passed",
|
||||
function() {
|
||||
expect(create({x: Number}, {}))
|
||||
.to.Throw(TypeError, /missing required x$/);
|
||||
});
|
||||
|
||||
it("should check for a missing required dependency, undefined passed",
|
||||
function() {
|
||||
expect(create({x: Number}, {x: undefined}))
|
||||
.to.Throw(TypeError, /missing required x$/);
|
||||
});
|
||||
|
||||
it("should check for multiple missing required dependencies", function() {
|
||||
expect(create({x: Number, y: String}, {}))
|
||||
.to.Throw(TypeError, /missing required x, y$/);
|
||||
});
|
||||
|
||||
it("should check for required dependency types", function() {
|
||||
expect(create({x: Number}, {x: "woops"})).to.Throw(
|
||||
TypeError, /invalid dependency: x; expected Number, got String$/);
|
||||
});
|
||||
|
||||
it("should check for a dependency to match at least one of passed types",
|
||||
function() {
|
||||
expect(create({x: [X, Y]}, {x: 42})).to.Throw(
|
||||
TypeError, /invalid dependency: x; expected X, Y, got Number$/);
|
||||
expect(create({x: [X, Y]}, {x: new Y()})).to.not.Throw();
|
||||
});
|
||||
|
||||
it("should skip type check if required dependency type is undefined",
|
||||
function() {
|
||||
expect(create({x: undefined}, {x: /whatever/})).not.to.Throw();
|
||||
});
|
||||
|
||||
it("should check for a String dependency", function() {
|
||||
expect(create({foo: String}, {foo: 42})).to.Throw(
|
||||
TypeError, /invalid dependency: foo/);
|
||||
});
|
||||
|
||||
it("should check for a Number dependency", function() {
|
||||
expect(create({foo: Number}, {foo: "x"})).to.Throw(
|
||||
TypeError, /invalid dependency: foo/);
|
||||
});
|
||||
|
||||
it("should check for a custom constructor dependency", function() {
|
||||
expect(create({foo: X}, {foo: null})).to.Throw(
|
||||
TypeError, /invalid dependency: foo; expected X, got null$/);
|
||||
});
|
||||
|
||||
it("should check for a native constructor dependency", function() {
|
||||
expect(create({foo: mozRTCSessionDescription}, {foo: "x"}))
|
||||
.to.Throw(TypeError,
|
||||
/invalid dependency: foo; expected mozRTCSessionDescription/);
|
||||
});
|
||||
|
||||
it("should check for a null dependency", function() {
|
||||
expect(create({foo: null}, {foo: "x"})).to.Throw(
|
||||
TypeError, /invalid dependency: foo; expected null, got String$/);
|
||||
});
|
||||
});
|
||||
});
|
@ -128,8 +128,11 @@ describe("loop.CallConnectionWebSocket", function() {
|
||||
data: '{"messageType":"hello", "state":"init"}'
|
||||
});
|
||||
|
||||
promise.then(function() {
|
||||
promise.then(function(state) {
|
||||
expect(state).eql("init");
|
||||
done();
|
||||
}, function() {
|
||||
done(new Error("shouldn't have rejected the promise"));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -32,10 +32,13 @@
|
||||
<script src="../content/shared/libs/lodash-2.4.1.js"></script>
|
||||
<script src="../content/shared/libs/backbone-1.1.2.js"></script>
|
||||
<script src="../content/shared/js/feedbackApiClient.js"></script>
|
||||
<script src="../content/shared/js/conversationStore.js"></script>
|
||||
<script src="../content/shared/js/utils.js"></script>
|
||||
<script src="../content/shared/js/models.js"></script>
|
||||
<script src="../content/shared/js/mixins.js"></script>
|
||||
<script src="../content/shared/js/views.js"></script>
|
||||
<script src="../content/shared/js/websocket.js"></script>
|
||||
<script src="../content/js/conversationViews.js"></script>
|
||||
<script src="../content/js/client.js"></script>
|
||||
<script src="../standalone/content/js/webapp.js"></script>
|
||||
<script type="text/javascript;version=1.8" src="../content/js/contacts.js"></script>
|
||||
|
@ -138,8 +138,8 @@
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.incoming-call-action-group .btn-group-chevron,
|
||||
.incoming-call-action-group .btn-group {
|
||||
.call-action-group .btn-group-chevron,
|
||||
.call-action-group .btn-group {
|
||||
/* Prevent box overflow due to long string */
|
||||
max-width: 120px;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
var PanelView = loop.panel.PanelView;
|
||||
// 1.2. Conversation Window
|
||||
var IncomingCallView = loop.conversation.IncomingCallView;
|
||||
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
||||
|
||||
// 2. Standalone webapp
|
||||
var HomeView = loop.webapp.HomeView;
|
||||
@ -63,6 +64,12 @@
|
||||
});
|
||||
mockConversationModel.startSession = noop;
|
||||
|
||||
var mockWebSocket = new loop.CallConnectionWebSocket({
|
||||
url: "fake",
|
||||
callId: "fakeId",
|
||||
websocketToken: "fakeToken"
|
||||
});
|
||||
|
||||
var notifications = new loop.shared.models.NotificationCollection();
|
||||
var errNotifications = new loop.shared.models.NotificationCollection();
|
||||
errNotifications.error("Error!");
|
||||
@ -223,12 +230,21 @@
|
||||
Section({name: "PendingConversationView"},
|
||||
Example({summary: "Pending conversation view (connecting)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView(null)
|
||||
PendingConversationView({websocket: mockWebSocket})
|
||||
)
|
||||
),
|
||||
Example({summary: "Pending conversation view (ringing)", dashed: "true"},
|
||||
React.DOM.div({className: "standalone"},
|
||||
PendingConversationView({callState: "ringing"})
|
||||
PendingConversationView({websocket: mockWebSocket, callState: "ringing"})
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
Section({name: "PendingConversationView (Desktop)"},
|
||||
Example({summary: "Connecting", dashed: "true",
|
||||
style: {width: "260px", height: "265px"}},
|
||||
React.DOM.div({className: "fx-embedded"},
|
||||
DesktopPendingConversationView({callState: "gather", calleeId: "Mr Smith"})
|
||||
)
|
||||
)
|
||||
),
|
||||
@ -446,6 +462,9 @@
|
||||
React.renderComponent(App(null), body);
|
||||
|
||||
_renderComponentsInIframes();
|
||||
|
||||
// Put the title back, in case views changed it.
|
||||
document.title = "Loop UI Components Showcase";
|
||||
});
|
||||
|
||||
})();
|
||||
|
@ -15,6 +15,7 @@
|
||||
var PanelView = loop.panel.PanelView;
|
||||
// 1.2. Conversation Window
|
||||
var IncomingCallView = loop.conversation.IncomingCallView;
|
||||
var DesktopPendingConversationView = loop.conversationViews.PendingConversationView;
|
||||
|
||||
// 2. Standalone webapp
|
||||
var HomeView = loop.webapp.HomeView;
|
||||
@ -63,6 +64,12 @@
|
||||
});
|
||||
mockConversationModel.startSession = noop;
|
||||
|
||||
var mockWebSocket = new loop.CallConnectionWebSocket({
|
||||
url: "fake",
|
||||
callId: "fakeId",
|
||||
websocketToken: "fakeToken"
|
||||
});
|
||||
|
||||
var notifications = new loop.shared.models.NotificationCollection();
|
||||
var errNotifications = new loop.shared.models.NotificationCollection();
|
||||
errNotifications.error("Error!");
|
||||
@ -223,12 +230,21 @@
|
||||
<Section name="PendingConversationView">
|
||||
<Example summary="Pending conversation view (connecting)" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView />
|
||||
<PendingConversationView websocket={mockWebSocket}/>
|
||||
</div>
|
||||
</Example>
|
||||
<Example summary="Pending conversation view (ringing)" dashed="true">
|
||||
<div className="standalone">
|
||||
<PendingConversationView callState="ringing"/>
|
||||
<PendingConversationView websocket={mockWebSocket} callState="ringing"/>
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
|
||||
<Section name="PendingConversationView (Desktop)">
|
||||
<Example summary="Connecting" dashed="true"
|
||||
style={{width: "260px", height: "265px"}}>
|
||||
<div className="fx-embedded">
|
||||
<DesktopPendingConversationView callState={"gather"} calleeId="Mr Smith" />
|
||||
</div>
|
||||
</Example>
|
||||
</Section>
|
||||
@ -446,6 +462,9 @@
|
||||
React.renderComponent(<App />, body);
|
||||
|
||||
_renderComponentsInIframes();
|
||||
|
||||
// Put the title back, in case views changed it.
|
||||
document.title = "Loop UI Components Showcase";
|
||||
});
|
||||
|
||||
})();
|
||||
|
@ -796,6 +796,7 @@ GK_ATOM(onMozMousePixelScroll, "onMozMousePixelScroll")
|
||||
GK_ATOM(onMozScrolledAreaChanged, "onMozScrolledAreaChanged")
|
||||
GK_ATOM(onmoznetworkupload, "onmoznetworkupload")
|
||||
GK_ATOM(onmoznetworkdownload, "onmoznetworkdownload")
|
||||
GK_ATOM(onnewrdsgroup, "onnewrdsgroup")
|
||||
GK_ATOM(onnoupdate, "onnoupdate")
|
||||
GK_ATOM(onobsolete, "onobsolete")
|
||||
GK_ATOM(ononline, "ononline")
|
||||
@ -812,11 +813,16 @@ GK_ATOM(onpairingconfirmationreq, "onpairingconfirmationreq")
|
||||
GK_ATOM(onpairingconsentreq, "onpairingconsentreq")
|
||||
GK_ATOM(onpaste, "onpaste")
|
||||
GK_ATOM(onpendingchange, "onpendingchange")
|
||||
GK_ATOM(onpichange, "onpichange")
|
||||
GK_ATOM(onpopuphidden, "onpopuphidden")
|
||||
GK_ATOM(onpopuphiding, "onpopuphiding")
|
||||
GK_ATOM(onpopupshowing, "onpopupshowing")
|
||||
GK_ATOM(onpopupshown, "onpopupshown")
|
||||
GK_ATOM(onpschange, "onpschange")
|
||||
GK_ATOM(onptychange, "onptychange")
|
||||
GK_ATOM(onradiostatechange, "onradiostatechange")
|
||||
GK_ATOM(onrdsdisabled, "onrdsdisabled")
|
||||
GK_ATOM(onrdsenabled, "onrdsenabled")
|
||||
GK_ATOM(onreaderror, "onreaderror")
|
||||
GK_ATOM(onreadsuccess, "onreadsuccess")
|
||||
GK_ATOM(onready, "onready")
|
||||
@ -831,6 +837,7 @@ GK_ATOM(onrequestmediaplaystatus, "onrequestmediaplaystatus")
|
||||
GK_ATOM(onreset, "onreset")
|
||||
GK_ATOM(onresuming, "onresuming")
|
||||
GK_ATOM(onresize, "onresize")
|
||||
GK_ATOM(onrtchange, "onrtchange")
|
||||
GK_ATOM(onscostatuschanged, "onscostatuschanged")
|
||||
GK_ATOM(onscroll, "onscroll")
|
||||
GK_ATOM(onselect, "onselect")
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/ipc/BlobChild.h"
|
||||
|
||||
#include "BluetoothChild.h"
|
||||
#include "MainThreadUtils.h"
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "mozilla/dom/ContentChild.h"
|
||||
#include "mozilla/dom/PFMRadioChild.h"
|
||||
#include "mozilla/dom/FMRadioService.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "DOMRequest.h"
|
||||
#include "nsDOMClassInfo.h"
|
||||
#include "nsIDocShell.h"
|
||||
@ -105,6 +106,7 @@ NS_IMPL_ISUPPORTS_INHERITED0(FMRadioRequest, DOMRequest)
|
||||
|
||||
FMRadio::FMRadio()
|
||||
: mHeadphoneState(SWITCH_STATE_OFF)
|
||||
, mRdsGroupMask(0)
|
||||
, mAudioChannelAgentEnabled(false)
|
||||
, mHasInternalAntenna(false)
|
||||
, mIsShutdown(false)
|
||||
@ -220,6 +222,28 @@ FMRadio::Notify(const FMRadioEventType& aType)
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("disabled"));
|
||||
}
|
||||
break;
|
||||
case RDSEnabledChanged:
|
||||
if (RdsEnabled()) {
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("rdsenabled"));
|
||||
} else {
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("rdsdisabled"));
|
||||
}
|
||||
break;
|
||||
case PIChanged:
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("pichange"));
|
||||
break;
|
||||
case PSChanged:
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("pschange"));
|
||||
break;
|
||||
case RadiotextChanged:
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("rtchange"));
|
||||
break;
|
||||
case PTYChanged:
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("ptychange"));
|
||||
break;
|
||||
case NewRDSGroup:
|
||||
DispatchTrustedEvent(NS_LITERAL_STRING("newrdsgroup"));
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
@ -232,6 +256,12 @@ FMRadio::Enabled()
|
||||
return IFMRadioService::Singleton()->IsEnabled();
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadio::RdsEnabled()
|
||||
{
|
||||
return IFMRadioService::Singleton()->IsRDSEnabled();
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadio::AntennaAvailable() const
|
||||
{
|
||||
@ -265,6 +295,69 @@ FMRadio::ChannelWidth() const
|
||||
return IFMRadioService::Singleton()->GetChannelWidth();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
FMRadio::RdsGroupMask() const
|
||||
{
|
||||
return mRdsGroupMask;
|
||||
}
|
||||
|
||||
void
|
||||
FMRadio::SetRdsGroupMask(uint32_t aRdsGroupMask)
|
||||
{
|
||||
mRdsGroupMask = aRdsGroupMask;
|
||||
IFMRadioService::Singleton()->SetRDSGroupMask(aRdsGroupMask);
|
||||
}
|
||||
|
||||
Nullable<unsigned short>
|
||||
FMRadio::GetPi() const
|
||||
{
|
||||
return IFMRadioService::Singleton()->GetPi();
|
||||
}
|
||||
|
||||
Nullable<uint8_t>
|
||||
FMRadio::GetPty() const
|
||||
{
|
||||
return IFMRadioService::Singleton()->GetPty();
|
||||
}
|
||||
|
||||
void
|
||||
FMRadio::GetPs(DOMString& aPsname) const
|
||||
{
|
||||
if (!IFMRadioService::Singleton()->GetPs(aPsname)) {
|
||||
aPsname.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FMRadio::GetRt(DOMString& aRadiotext) const
|
||||
{
|
||||
if (!IFMRadioService::Singleton()->GetRt(aRadiotext)) {
|
||||
aRadiotext.SetNull();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FMRadio::GetRdsgroup(JSContext* cx, JS::MutableHandle<JSObject*> retval)
|
||||
{
|
||||
uint64_t group;
|
||||
if (!IFMRadioService::Singleton()->GetRdsgroup(group)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSObject *rdsgroup = Uint16Array::Create(cx, this, 4);
|
||||
uint16_t *data = JS_GetUint16ArrayData(rdsgroup);
|
||||
data[3] = group & 0xFFFF;
|
||||
group >>= 16;
|
||||
data[2] = group & 0xFFFF;
|
||||
group >>= 16;
|
||||
data[1] = group & 0xFFFF;
|
||||
group >>= 16;
|
||||
data[0] = group & 0xFFFF;
|
||||
|
||||
JS::ExposeObjectToActiveJS(rdsgroup);
|
||||
retval.set(rdsgroup);
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
FMRadio::Enable(double aFrequency)
|
||||
{
|
||||
@ -350,6 +443,32 @@ FMRadio::CancelSeek()
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
FMRadio::EnableRDS()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
|
||||
IFMRadioService::Singleton()->EnableRDS(r);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<DOMRequest>
|
||||
FMRadio::DisableRDS()
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<FMRadioRequest> r = new FMRadioRequest(win, this);
|
||||
FMRadioService::Singleton()->DisableRDS(r);
|
||||
return r.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FMRadio::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
|
@ -55,6 +55,8 @@ public:
|
||||
|
||||
static bool Enabled();
|
||||
|
||||
bool RdsEnabled();
|
||||
|
||||
bool AntennaAvailable() const;
|
||||
|
||||
Nullable<double> GetFrequency() const;
|
||||
@ -65,6 +67,20 @@ public:
|
||||
|
||||
double ChannelWidth() const;
|
||||
|
||||
uint32_t RdsGroupMask() const;
|
||||
|
||||
void SetRdsGroupMask(uint32_t aRdsGroupMask);
|
||||
|
||||
Nullable<unsigned short> GetPi() const;
|
||||
|
||||
Nullable<uint8_t> GetPty() const;
|
||||
|
||||
void GetPs(DOMString& aPsname) const;
|
||||
|
||||
void GetRt(DOMString& aRadiotext) const;
|
||||
|
||||
void GetRdsgroup(JSContext* cx, JS::MutableHandle<JSObject*> retval);
|
||||
|
||||
already_AddRefed<DOMRequest> Enable(double aFrequency);
|
||||
|
||||
already_AddRefed<DOMRequest> Disable();
|
||||
@ -77,10 +93,21 @@ public:
|
||||
|
||||
already_AddRefed<DOMRequest> CancelSeek();
|
||||
|
||||
already_AddRefed<DOMRequest> EnableRDS();
|
||||
|
||||
already_AddRefed<DOMRequest> DisableRDS();
|
||||
|
||||
IMPL_EVENT_HANDLER(enabled);
|
||||
IMPL_EVENT_HANDLER(disabled);
|
||||
IMPL_EVENT_HANDLER(rdsenabled);
|
||||
IMPL_EVENT_HANDLER(rdsdisabled);
|
||||
IMPL_EVENT_HANDLER(antennaavailablechange);
|
||||
IMPL_EVENT_HANDLER(frequencychange);
|
||||
IMPL_EVENT_HANDLER(pichange);
|
||||
IMPL_EVENT_HANDLER(ptychange);
|
||||
IMPL_EVENT_HANDLER(pschange);
|
||||
IMPL_EVENT_HANDLER(rtchange);
|
||||
IMPL_EVENT_HANDLER(newrdsgroup);
|
||||
|
||||
// nsIDOMEventListener
|
||||
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
|
||||
@ -92,6 +119,7 @@ private:
|
||||
void EnableAudioChannelAgent();
|
||||
|
||||
hal::SwitchState mHeadphoneState;
|
||||
uint32_t mRdsGroupMask;
|
||||
bool mAudioChannelAgentEnabled;
|
||||
bool mHasInternalAntenna;
|
||||
bool mIsShutdown;
|
||||
|
@ -30,7 +30,13 @@ BEGIN_FMRADIO_NAMESPACE
|
||||
enum FMRadioEventType
|
||||
{
|
||||
FrequencyChanged,
|
||||
EnabledChanged
|
||||
EnabledChanged,
|
||||
RDSEnabledChanged,
|
||||
PIChanged,
|
||||
PSChanged,
|
||||
PTYChanged,
|
||||
RadiotextChanged,
|
||||
NewRDSGroup
|
||||
};
|
||||
|
||||
typedef mozilla::Observer<FMRadioEventType> FMRadioEventObserver;
|
||||
|
@ -26,6 +26,8 @@
|
||||
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
|
||||
#define SETTING_KEY_AIRPLANEMODE_ENABLED "airplaneMode.enabled"
|
||||
|
||||
#define DOM_PARSED_RDS_GROUPS ((0x2 << 30) | (0x3 << 4) | (0x3 << 0))
|
||||
|
||||
using namespace mozilla::hal;
|
||||
using mozilla::Preferences;
|
||||
|
||||
@ -49,9 +51,26 @@ FMRadioService::FMRadioService()
|
||||
, mState(Disabled)
|
||||
, mHasReadAirplaneModeSetting(false)
|
||||
, mAirplaneModeEnabled(false)
|
||||
, mRDSEnabled(false)
|
||||
, mPendingRequest(nullptr)
|
||||
, mObserverList(FMRadioEventObserverList())
|
||||
, mRDSGroupMask(0)
|
||||
, mLastPI(0)
|
||||
, mPI(0)
|
||||
, mPTY(0)
|
||||
, mPISet(false)
|
||||
, mPTYSet(false)
|
||||
, mRDSLock("FMRadioService::mRDSLock")
|
||||
, mPSNameState(0)
|
||||
, mRadiotextAB(false)
|
||||
, mRDSGroupSet(false)
|
||||
, mPSNameSet(false)
|
||||
, mRadiotextSet(false)
|
||||
{
|
||||
memset(mPSName, 0, sizeof(mPSName));
|
||||
memset(mRadiotext, 0, sizeof(mRadiotext));
|
||||
memset(mTempPSName, 0, sizeof(mTempPSName));
|
||||
memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
|
||||
|
||||
// Read power state and frequency from Hal.
|
||||
mEnabled = IsFMRadioOn();
|
||||
@ -110,10 +129,12 @@ FMRadioService::FMRadioService()
|
||||
}
|
||||
|
||||
RegisterFMRadioObserver(this);
|
||||
RegisterFMRadioRDSObserver(this);
|
||||
}
|
||||
|
||||
FMRadioService::~FMRadioService()
|
||||
{
|
||||
UnregisterFMRadioRDSObserver(this);
|
||||
UnregisterFMRadioObserver(this);
|
||||
}
|
||||
|
||||
@ -277,6 +298,21 @@ private:
|
||||
FMRadioSeekDirection mDirection;
|
||||
};
|
||||
|
||||
class NotifyRunnable MOZ_FINAL : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NotifyRunnable(FMRadioEventType aType) : mType(aType) { }
|
||||
|
||||
NS_IMETHOD Run()
|
||||
{
|
||||
FMRadioService::Singleton()->NotifyFMRadioEvent(mType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
FMRadioEventType mType;
|
||||
};
|
||||
|
||||
void
|
||||
FMRadioService::TransitionState(const FMRadioResponseType& aResponse,
|
||||
FMRadioState aState)
|
||||
@ -374,6 +410,13 @@ FMRadioService::IsEnabled() const
|
||||
return IsFMRadioOn();
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioService::IsRDSEnabled() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
return mRDSEnabled;
|
||||
}
|
||||
|
||||
double
|
||||
FMRadioService::GetFrequency() const
|
||||
{
|
||||
@ -407,6 +450,54 @@ FMRadioService::GetChannelWidth() const
|
||||
return mChannelWidthInKHz / 1000.0;
|
||||
}
|
||||
|
||||
Nullable<unsigned short>
|
||||
FMRadioService::GetPi() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
if (!mPISet) {
|
||||
return Nullable<unsigned short>();
|
||||
}
|
||||
return Nullable<unsigned short>(mPI);
|
||||
}
|
||||
|
||||
Nullable<uint8_t>
|
||||
FMRadioService::GetPty() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
if (!mPTYSet) {
|
||||
return Nullable<uint8_t>();
|
||||
}
|
||||
return Nullable<uint8_t>(mPTY);
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioService::GetPs(nsString& aPSName)
|
||||
{
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
if (mPSNameSet) {
|
||||
aPSName = nsString(mPSName);
|
||||
}
|
||||
return mPSNameSet;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioService::GetRt(nsString& aRadiotext)
|
||||
{
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
if (mRadiotextSet) {
|
||||
aRadiotext = nsString(mRadiotext);
|
||||
}
|
||||
return mRadiotextSet;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioService::GetRdsgroup(uint64_t& aRDSGroup)
|
||||
{
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
aRDSGroup = mRDSGroup;
|
||||
return mRDSGroupSet;
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::Enable(double aFrequencyInMHz,
|
||||
FMRadioReplyRunnable* aReplyRunnable)
|
||||
@ -679,6 +770,47 @@ FMRadioService::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::SetRDSGroupMask(uint32_t aRDSGroupMask)
|
||||
{
|
||||
mRDSGroupMask = aRDSGroupMask;
|
||||
if (IsFMRadioOn()) {
|
||||
hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
MOZ_ASSERT(aReplyRunnable);
|
||||
|
||||
mRDSEnabled = true;
|
||||
if (IsFMRadioOn()) {
|
||||
hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
|
||||
}
|
||||
|
||||
aReplyRunnable->SetReply(SuccessResponse());
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RDSEnabledChanged));
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::DisableRDS(FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
||||
MOZ_ASSERT(aReplyRunnable);
|
||||
|
||||
mRDSEnabled = false;
|
||||
if (IsFMRadioOn()) {
|
||||
hal::DisableRDS();
|
||||
}
|
||||
|
||||
aReplyRunnable->SetReply(SuccessResponse());
|
||||
NS_DispatchToMainThread(aReplyRunnable);
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RDSEnabledChanged));
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FMRadioService::Observe(nsISupports* aSubject,
|
||||
const char* aTopic,
|
||||
@ -760,10 +892,18 @@ FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
|
||||
// The frequency was changed from '0' to some meaningful number, so we
|
||||
// should send the `FrequencyChanged` event manually.
|
||||
NotifyFMRadioEvent(FrequencyChanged);
|
||||
|
||||
if (mRDSEnabled) {
|
||||
hal::EnableRDS(mRDSGroupMask | DOM_PARSED_RDS_GROUPS);
|
||||
}
|
||||
break;
|
||||
case FM_RADIO_OPERATION_DISABLE:
|
||||
MOZ_ASSERT(mState == Disabling);
|
||||
|
||||
mPISet = false;
|
||||
mPTYSet = false;
|
||||
memset(mPSName, 0, sizeof(mPSName));
|
||||
memset(mRadiotext, 0, sizeof(mRadiotext));
|
||||
TransitionState(SuccessResponse(), Disabled);
|
||||
UpdatePowerState();
|
||||
break;
|
||||
@ -785,6 +925,274 @@ FMRadioService::Notify(const FMRadioOperationInformation& aInfo)
|
||||
}
|
||||
}
|
||||
|
||||
/* This is defined by the RDS standard */
|
||||
static const uint16_t sRDSToUnicodeMap[256] = {
|
||||
// The lower half differs from ASCII in 0x1F, 0x24, 0x5E, 0x7E
|
||||
// Most control characters are replaced with 0x20 (space)
|
||||
// 0x0-
|
||||
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
|
||||
0x0020, 0x0009, 0x000A, 0x000B, 0x0020, 0x00D0, 0x0020, 0x0020,
|
||||
|
||||
// 0x1-
|
||||
0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020,
|
||||
0x0020, 0x0020, 0x0020, 0x001B, 0x0020, 0x0020, 0x0020, 0x00AD,
|
||||
|
||||
// 0x2-
|
||||
0x0020, 0x0021, 0x0022, 0x0023, 0x00A4, 0x0025, 0x0026, 0x0027,
|
||||
0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
|
||||
|
||||
// 0x3-
|
||||
0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
|
||||
0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
|
||||
|
||||
// 0x4-
|
||||
0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047,
|
||||
0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F,
|
||||
|
||||
// 0x5-
|
||||
0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057,
|
||||
0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x2015, 0x005F,
|
||||
|
||||
// 0x6-
|
||||
0x2551, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067,
|
||||
0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
|
||||
|
||||
// 0x7-
|
||||
0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077,
|
||||
0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x00AF, 0x007F,
|
||||
|
||||
// 0x8-
|
||||
0x00E1, 0x00E0, 0x00E9, 0x00E8, 0x00ED, 0x00EC, 0x00F3, 0x00F2,
|
||||
0x00FA, 0x00F9, 0x00D1, 0x00C7, 0x015E, 0x00DF, 0x00A1, 0x0132,
|
||||
|
||||
// 0x9-
|
||||
0x00E2, 0x00E4, 0x00EA, 0x00EB, 0x00EE, 0x00EF, 0x00F4, 0x00F6,
|
||||
0x00FB, 0x00FC, 0x00F1, 0x00E7, 0x015F, 0x011F, 0x0131, 0x0133,
|
||||
|
||||
// 0xA-
|
||||
0x00AA, 0x03B1, 0x00A9, 0x2030, 0x011E, 0x011B, 0x0148, 0x0151,
|
||||
0x03C0, 0x20AC, 0x00A3, 0x0024, 0x2190, 0x2191, 0x2192, 0x2193,
|
||||
|
||||
// 0xB-
|
||||
0x00BA, 0x00B9, 0x00B2, 0x00B3, 0x00B1, 0x0130, 0x0144, 0x0171,
|
||||
0x03BC, 0x00BF, 0x00F7, 0x00B0, 0x00BC, 0x00BD, 0x00BE, 0x00A7,
|
||||
|
||||
// 0xC-
|
||||
0x00C1, 0x00C0, 0x00C9, 0x00C8, 0x00CD, 0x00CC, 0x00D3, 0x00D2,
|
||||
0x00DA, 0x00D9, 0x0158, 0x010C, 0x0160, 0x017D, 0x00D0, 0x013F,
|
||||
|
||||
// 0xD-
|
||||
0x00C2, 0x00C4, 0x00CA, 0x00CB, 0x00CE, 0x00CF, 0x00D4, 0x00D6,
|
||||
0x00DB, 0x00DC, 0x0159, 0x010D, 0x0161, 0x017E, 0x0111, 0x0140,
|
||||
|
||||
// 0xE-
|
||||
0x00C3, 0x00C5, 0x00C6, 0x0152, 0x0177, 0x00DD, 0x00D5, 0x00D8,
|
||||
0x00DE, 0x014A, 0x0154, 0x0106, 0x015A, 0x0179, 0x0166, 0x00F0,
|
||||
|
||||
// 0xF-
|
||||
0x00E3, 0x00E5, 0x00E6, 0x0153, 0x0175, 0x00FD, 0x00F5, 0x00F8,
|
||||
0x00FE, 0x014B, 0x0155, 0x0107, 0x015B, 0x017A, 0x0167, 0x0020,
|
||||
};
|
||||
|
||||
void
|
||||
FMRadioService::Notify(const FMRadioRDSGroup& aRDSGroup)
|
||||
{
|
||||
uint16_t blocks[4];
|
||||
blocks[0] = aRDSGroup.blockA();
|
||||
blocks[1] = aRDSGroup.blockB();
|
||||
blocks[2] = aRDSGroup.blockC();
|
||||
blocks[3] = aRDSGroup.blockD();
|
||||
|
||||
/* Bit 11 in block B determines whether this is a type B group. */
|
||||
uint16_t lastPI = blocks[1] & (1 << 11) ? blocks[2] : mLastPI;
|
||||
|
||||
/* Update PI if it's not set or if we get two PI with the new value. */
|
||||
if ((mPI != blocks[0] && lastPI == blocks[0]) || !mPISet) {
|
||||
mPI = blocks[0];
|
||||
if (!mPISet) {
|
||||
mPSNameState = 0;
|
||||
mRadiotextState = 0;
|
||||
memset(mTempPSName, 0, sizeof(mTempPSName));
|
||||
memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
|
||||
}
|
||||
mPISet = true;
|
||||
NS_DispatchToMainThread(new NotifyRunnable(PIChanged));
|
||||
}
|
||||
mLastPI = blocks[0];
|
||||
|
||||
/* PTY is also updated using the same logic as PI */
|
||||
uint16_t pty = (blocks[1] >> 5) & 0x1F;
|
||||
if ((mPTY != pty && pty == mLastPTY) || !mPTYSet) {
|
||||
mPTY = pty;
|
||||
mPTYSet = true;
|
||||
NS_DispatchToMainThread(new NotifyRunnable(PTYChanged));
|
||||
}
|
||||
mLastPTY = pty;
|
||||
|
||||
uint16_t grouptype = blocks[1] >> 11;
|
||||
switch (grouptype) {
|
||||
case 0: // 0a
|
||||
case 1: // 0b
|
||||
{
|
||||
uint16_t segmentAddr = (blocks[1] & 0x3);
|
||||
// mPSNameState is a bitmask that lets us ensure all segments
|
||||
// are received before updating the PS name.
|
||||
if (!segmentAddr) {
|
||||
mPSNameState = 1;
|
||||
} else {
|
||||
mPSNameState |= 1 << segmentAddr;
|
||||
}
|
||||
|
||||
uint16_t offset = segmentAddr << 1;
|
||||
mTempPSName[offset] = sRDSToUnicodeMap[blocks[3] >> 8];
|
||||
mTempPSName[offset + 1] = sRDSToUnicodeMap[blocks[3] & 0xFF];
|
||||
|
||||
if (mPSNameState != 0xF) {
|
||||
break;
|
||||
}
|
||||
|
||||
mPSNameState = 0;
|
||||
if (memcmp(mTempPSName, mPSName, sizeof(mTempPSName))) {
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
mPSNameSet = true;
|
||||
memcpy(mPSName, mTempPSName, sizeof(mTempPSName));
|
||||
NS_DispatchToMainThread(new NotifyRunnable(PSChanged));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: // 2a Radiotext
|
||||
{
|
||||
uint16_t segmentAddr = (blocks[1] & 0xF);
|
||||
bool textAB = blocks[1] & (1 << 5);
|
||||
if (textAB != mRadiotextAB) {
|
||||
mRadiotextState = 0;
|
||||
memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
|
||||
mRadiotextAB = textAB;
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
memset(mRadiotext, 0, sizeof(mRadiotext));
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
|
||||
}
|
||||
|
||||
// mRadiotextState is a bitmask that lets us ensure all segments
|
||||
// are received before updating the radiotext.
|
||||
if (!segmentAddr) {
|
||||
mRadiotextState = 1;
|
||||
} else {
|
||||
mRadiotextState |= 1 << segmentAddr;
|
||||
}
|
||||
|
||||
uint8_t segment[4];
|
||||
segment[0] = blocks[2] >> 8;
|
||||
segment[1] = blocks[2] & 0xFF;
|
||||
segment[2] = blocks[3] >> 8;
|
||||
segment[3] = blocks[3] & 0xFF;
|
||||
|
||||
uint16_t offset = segmentAddr << 2;
|
||||
bool done = false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (segment[i] == '\r') {
|
||||
mTempRadiotext[offset++] = 0;
|
||||
done = true;
|
||||
} else {
|
||||
mTempRadiotext[offset++] = sRDSToUnicodeMap[segment[i]];
|
||||
}
|
||||
}
|
||||
if (offset == 64) {
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (!done ||
|
||||
(mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
|
||||
!memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
|
||||
break;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
mRadiotextSet = true;
|
||||
memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
|
||||
break;
|
||||
}
|
||||
case 5: // 2b Radiotext
|
||||
{
|
||||
uint16_t segmentAddr = (blocks[1] & 0xF);
|
||||
bool textAB = blocks[1] & (1 << 5);
|
||||
if (textAB != mRadiotextAB) {
|
||||
mRadiotextState = 0;
|
||||
memset(mTempRadiotext, 0, sizeof(mTempRadiotext));
|
||||
mRadiotextAB = textAB;
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
memset(mRadiotext, 0, sizeof(mRadiotext));
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
|
||||
}
|
||||
|
||||
if (!segmentAddr) {
|
||||
mRadiotextState = 1;
|
||||
} else {
|
||||
mRadiotextState |= 1 << segmentAddr;
|
||||
}
|
||||
uint8_t segment[2];
|
||||
segment[0] = blocks[3] >> 8;
|
||||
segment[1] = blocks[3] & 0xFF;
|
||||
|
||||
uint16_t offset = segmentAddr << 1;
|
||||
bool done = false;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
if (segment[i] == '\r') {
|
||||
mTempRadiotext[offset++] = 0;
|
||||
done = true;
|
||||
} else {
|
||||
mTempRadiotext[offset++] = sRDSToUnicodeMap[segment[i]];
|
||||
}
|
||||
}
|
||||
if (offset == 32) {
|
||||
done = true;
|
||||
}
|
||||
|
||||
if (!done ||
|
||||
(mRadiotextState + 1) != (1 << ((blocks[1] & 0xF) + 1)) ||
|
||||
!memcmp(mTempRadiotext, mRadiotext, sizeof(mTempRadiotext))) {
|
||||
break;
|
||||
}
|
||||
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
mRadiotextSet = true;
|
||||
memcpy(mRadiotext, mTempRadiotext, sizeof(mTempRadiotext));
|
||||
NS_DispatchToMainThread(new NotifyRunnable(RadiotextChanged));
|
||||
break;
|
||||
}
|
||||
case 31: // 15b Fast Tuning and Switching
|
||||
{
|
||||
uint16_t secondPty = (blocks[3] >> 5) & 0x1F;
|
||||
if (pty == mPTY || pty != secondPty) {
|
||||
break;
|
||||
}
|
||||
mPTY = pty;
|
||||
NS_DispatchToMainThread(new NotifyRunnable(PTYChanged));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Only notify users of raw RDS groups that they're interested in.
|
||||
// We always receive DOM_PARSED_RDS_GROUPS when RDS is enabled.
|
||||
if (!(mRDSGroupMask & (1 << grouptype))) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t newgroup = blocks[0];
|
||||
newgroup <<= 16;
|
||||
newgroup |= blocks[1];
|
||||
newgroup <<= 16;
|
||||
newgroup |= blocks[2];
|
||||
newgroup <<= 16;
|
||||
newgroup |= blocks[3];
|
||||
|
||||
MutexAutoLock lock(mRDSLock);
|
||||
mRDSGroup = newgroup;
|
||||
mRDSGroupSet = true;
|
||||
NS_DispatchToMainThread(new NotifyRunnable(NewRDSGroup));
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioService::UpdatePowerState()
|
||||
{
|
||||
@ -802,6 +1210,13 @@ FMRadioService::UpdateFrequency()
|
||||
if (mPendingFrequencyInKHz != frequency) {
|
||||
mPendingFrequencyInKHz = frequency;
|
||||
NotifyFMRadioEvent(FrequencyChanged);
|
||||
mPISet = false;
|
||||
mPTYSet = false;
|
||||
memset(mPSName, 0, sizeof(mPSName));
|
||||
memset(mRadiotext, 0, sizeof(mRadiotext));
|
||||
mRDSGroupSet = false;
|
||||
mPSNameSet = false;
|
||||
mRadiotextSet = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,9 +7,11 @@
|
||||
#ifndef mozilla_dom_fmradioservice_h__
|
||||
#define mozilla_dom_fmradioservice_h__
|
||||
|
||||
#include "mozilla/dom/Nullable.h"
|
||||
#include "mozilla/dom/PFMRadioRequest.h"
|
||||
#include "FMRadioCommon.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -95,10 +97,16 @@ protected:
|
||||
|
||||
public:
|
||||
virtual bool IsEnabled() const = 0;
|
||||
virtual bool IsRDSEnabled() const = 0;
|
||||
virtual double GetFrequency() const = 0;
|
||||
virtual double GetFrequencyUpperBound() const = 0;
|
||||
virtual double GetFrequencyLowerBound() const = 0;
|
||||
virtual double GetChannelWidth() const = 0;
|
||||
virtual Nullable<unsigned short> GetPi() const = 0;
|
||||
virtual Nullable<uint8_t> GetPty() const = 0;
|
||||
virtual bool GetPs(nsString& aPsname) = 0;
|
||||
virtual bool GetRt(nsString& aRadiotext) = 0;
|
||||
virtual bool GetRdsgroup(uint64_t& aRDSGroup) = 0;
|
||||
|
||||
virtual void Enable(double aFrequency, FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
virtual void Disable(FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
@ -106,6 +114,9 @@ public:
|
||||
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
|
||||
FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) = 0;
|
||||
virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) = 0;
|
||||
|
||||
/**
|
||||
* Register handler to receive the FM Radio events, including:
|
||||
@ -138,11 +149,13 @@ enum FMRadioState
|
||||
|
||||
class FMRadioService MOZ_FINAL : public IFMRadioService
|
||||
, public hal::FMRadioObserver
|
||||
, public hal::FMRadioRDSObserver
|
||||
, public nsIObserver
|
||||
{
|
||||
friend class ReadAirplaneModeSettingTask;
|
||||
friend class EnableRunnable;
|
||||
friend class DisableRunnable;
|
||||
friend class NotifyRunnable;
|
||||
|
||||
public:
|
||||
static FMRadioService* Singleton();
|
||||
@ -151,10 +164,16 @@ public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
virtual bool IsEnabled() const MOZ_OVERRIDE;
|
||||
virtual bool IsRDSEnabled() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequency() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequencyUpperBound() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequencyLowerBound() const MOZ_OVERRIDE;
|
||||
virtual double GetChannelWidth() const MOZ_OVERRIDE;
|
||||
virtual Nullable<unsigned short> GetPi() const MOZ_OVERRIDE;
|
||||
virtual Nullable<uint8_t> GetPty() const MOZ_OVERRIDE;
|
||||
virtual bool GetPs(nsString& aPsname) MOZ_OVERRIDE;
|
||||
virtual bool GetRt(nsString& aRadiotext) MOZ_OVERRIDE;
|
||||
virtual bool GetRdsgroup(uint64_t& aRDSGroup) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Enable(double aFrequency,
|
||||
FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
@ -164,6 +183,9 @@ public:
|
||||
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
|
||||
FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) MOZ_OVERRIDE;
|
||||
virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
|
||||
virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
|
||||
@ -172,6 +194,8 @@ public:
|
||||
|
||||
/* FMRadioObserver */
|
||||
void Notify(const hal::FMRadioOperationInformation& aInfo) MOZ_OVERRIDE;
|
||||
/* FMRadioRDSObserver */
|
||||
void Notify(const hal::FMRadioRDSGroup& aRDSGroup) MOZ_OVERRIDE;
|
||||
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
@ -197,6 +221,7 @@ private:
|
||||
|
||||
bool mHasReadAirplaneModeSetting;
|
||||
bool mAirplaneModeEnabled;
|
||||
bool mRDSEnabled;
|
||||
|
||||
uint32_t mUpperBoundInKHz;
|
||||
uint32_t mLowerBoundInKHz;
|
||||
@ -209,6 +234,30 @@ private:
|
||||
FMRadioEventObserverList mObserverList;
|
||||
|
||||
static StaticRefPtr<FMRadioService> sFMRadioService;
|
||||
|
||||
uint32_t mRDSGroupMask;
|
||||
|
||||
uint16_t mLastPI;
|
||||
uint16_t mLastPTY;
|
||||
Atomic<uint32_t> mPI;
|
||||
Atomic<uint32_t> mPTY;
|
||||
Atomic<bool> mPISet;
|
||||
Atomic<bool> mPTYSet;
|
||||
|
||||
/* Protects mPSName, mRadiotext, and mRDSGroup */
|
||||
Mutex mRDSLock;
|
||||
char16_t mPSName[9];
|
||||
char16_t mRadiotext[65];
|
||||
uint64_t mRDSGroup;
|
||||
|
||||
uint8_t mPSNameState;
|
||||
uint16_t mRadiotextState;
|
||||
uint16_t mTempPSName[8];
|
||||
uint16_t mTempRadiotext[64];
|
||||
bool mRadiotextAB;
|
||||
bool mRDSGroupSet;
|
||||
bool mPSNameSet;
|
||||
bool mRadiotextSet;
|
||||
};
|
||||
|
||||
END_FMRADIO_NAMESPACE
|
||||
|
@ -16,7 +16,13 @@ StaticAutoPtr<FMRadioChild> FMRadioChild::sFMRadioChild;
|
||||
|
||||
FMRadioChild::FMRadioChild()
|
||||
: mEnabled(false)
|
||||
, mRDSEnabled(false)
|
||||
, mRDSGroupSet(false)
|
||||
, mPSNameSet(false)
|
||||
, mRadiotextSet(false)
|
||||
, mFrequency(0)
|
||||
, mRDSGroup(0)
|
||||
, mRDSGroupMask(0)
|
||||
, mObserverList(FMRadioEventObserverList())
|
||||
{
|
||||
MOZ_COUNT_CTOR(FMRadioChild);
|
||||
@ -44,6 +50,12 @@ FMRadioChild::IsEnabled() const
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::IsRDSEnabled() const
|
||||
{
|
||||
return mRDSEnabled;
|
||||
}
|
||||
|
||||
double
|
||||
FMRadioChild::GetFrequency() const
|
||||
{
|
||||
@ -69,6 +81,43 @@ FMRadioChild::GetChannelWidth() const
|
||||
return mChannelWidth;
|
||||
}
|
||||
|
||||
Nullable<unsigned short>
|
||||
FMRadioChild::GetPi() const
|
||||
{
|
||||
return mPI;
|
||||
}
|
||||
|
||||
Nullable<uint8_t>
|
||||
FMRadioChild::GetPty() const
|
||||
{
|
||||
return mPTY;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::GetPs(nsString& aPSName)
|
||||
{
|
||||
if (mPSNameSet) {
|
||||
aPSName = mPSName;
|
||||
}
|
||||
return mPSNameSet;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::GetRt(nsString& aRadiotext)
|
||||
{
|
||||
if (mRadiotextSet) {
|
||||
aRadiotext = mRadiotext;
|
||||
}
|
||||
return mRadiotextSet;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::GetRdsgroup(uint64_t& aRDSGroup)
|
||||
{
|
||||
aRDSGroup = mRDSGroup;
|
||||
return mRDSGroupSet;
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioChild::Enable(double aFrequency, FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
@ -101,6 +150,25 @@ FMRadioChild::CancelSeek(FMRadioReplyRunnable* aReplyRunnable)
|
||||
SendRequest(aReplyRunnable, CancelSeekRequestArgs());
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioChild::SetRDSGroupMask(uint32_t aRDSGroupMask)
|
||||
{
|
||||
mRDSGroupMask = aRDSGroupMask;
|
||||
SendSetRDSGroupMask(aRDSGroupMask);
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioChild::EnableRDS(FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
SendRequest(aReplyRunnable, EnableRDSArgs());
|
||||
}
|
||||
|
||||
void
|
||||
FMRadioChild::DisableRDS(FMRadioReplyRunnable* aReplyRunnable)
|
||||
{
|
||||
SendRequest(aReplyRunnable, DisableRDSArgs());
|
||||
}
|
||||
|
||||
inline void
|
||||
FMRadioChild::NotifyFMRadioEvent(FMRadioEventType aType)
|
||||
{
|
||||
@ -132,6 +200,26 @@ FMRadioChild::RecvNotifyFrequencyChanged(const double& aFrequency)
|
||||
{
|
||||
mFrequency = aFrequency;
|
||||
NotifyFMRadioEvent(FrequencyChanged);
|
||||
|
||||
if (!mPI.IsNull()) {
|
||||
mPI.SetNull();
|
||||
NotifyFMRadioEvent(PIChanged);
|
||||
}
|
||||
if (!mPTY.IsNull()) {
|
||||
mPTY.SetNull();
|
||||
NotifyFMRadioEvent(PTYChanged);
|
||||
}
|
||||
if (mPSNameSet) {
|
||||
mPSNameSet = false;
|
||||
mPSName.Truncate();
|
||||
NotifyFMRadioEvent(PSChanged);
|
||||
}
|
||||
if (mRadiotextSet) {
|
||||
mRadiotextSet = false;
|
||||
mRadiotext.Truncate();
|
||||
NotifyFMRadioEvent(RadiotextChanged);
|
||||
}
|
||||
mRDSGroupSet = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -141,10 +229,85 @@ FMRadioChild::RecvNotifyEnabledChanged(const bool& aEnabled,
|
||||
{
|
||||
mEnabled = aEnabled;
|
||||
mFrequency = aFrequency;
|
||||
if (!mEnabled) {
|
||||
mPI.SetNull();
|
||||
mPTY.SetNull();
|
||||
mPSName.Truncate();
|
||||
mRadiotext.Truncate();
|
||||
mRDSGroupSet = false;
|
||||
mPSNameSet = false;
|
||||
mRadiotextSet = false;
|
||||
}
|
||||
NotifyFMRadioEvent(EnabledChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyRDSEnabledChanged(const bool& aEnabled)
|
||||
{
|
||||
mRDSEnabled = aEnabled;
|
||||
NotifyFMRadioEvent(RDSEnabledChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyPIChanged(const bool& aValid,
|
||||
const uint16_t& aCode)
|
||||
{
|
||||
if (aValid) {
|
||||
mPI.SetValue(aCode);
|
||||
} else {
|
||||
mPI.SetNull();
|
||||
}
|
||||
NotifyFMRadioEvent(PIChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyPTYChanged(const bool& aValid,
|
||||
const uint8_t& aPTY)
|
||||
{
|
||||
if (aValid) {
|
||||
mPTY.SetValue(aPTY);
|
||||
} else {
|
||||
mPTY.SetNull();
|
||||
}
|
||||
NotifyFMRadioEvent(PTYChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyPSChanged(const nsString& aPSName)
|
||||
{
|
||||
mPSNameSet = true;
|
||||
mPSName = aPSName;
|
||||
NotifyFMRadioEvent(PSChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyRadiotextChanged(const nsString& aRadiotext)
|
||||
{
|
||||
mRadiotextSet = true;
|
||||
mRadiotext = aRadiotext;
|
||||
NotifyFMRadioEvent(RadiotextChanged);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::RecvNotifyNewRDSGroup(const uint64_t& aGroup)
|
||||
{
|
||||
uint16_t grouptype = (aGroup >> 43) & 0x1F;
|
||||
if (!(mRDSGroupMask & (1 << grouptype))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
mRDSGroupSet = true;
|
||||
mRDSGroup = aGroup;
|
||||
NotifyFMRadioEvent(NewRDSGroup);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioChild::Recv__delete__()
|
||||
{
|
||||
|
@ -34,10 +34,16 @@ public:
|
||||
|
||||
/* IFMRadioService */
|
||||
virtual bool IsEnabled() const MOZ_OVERRIDE;
|
||||
virtual bool IsRDSEnabled() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequency() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequencyUpperBound() const MOZ_OVERRIDE;
|
||||
virtual double GetFrequencyLowerBound() const MOZ_OVERRIDE;
|
||||
virtual double GetChannelWidth() const MOZ_OVERRIDE;
|
||||
virtual Nullable<unsigned short> GetPi() const MOZ_OVERRIDE;
|
||||
virtual Nullable<uint8_t> GetPty() const MOZ_OVERRIDE;
|
||||
virtual bool GetPs(nsString& aPSName) MOZ_OVERRIDE;
|
||||
virtual bool GetRt(nsString& aRadiotext) MOZ_OVERRIDE;
|
||||
virtual bool GetRdsgroup(uint64_t& aRDSGroup) MOZ_OVERRIDE;
|
||||
|
||||
virtual void Enable(double aFrequency,
|
||||
FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
@ -47,6 +53,9 @@ public:
|
||||
virtual void Seek(mozilla::hal::FMRadioSeekDirection aDirection,
|
||||
FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void CancelSeek(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void SetRDSGroupMask(uint32_t aRDSGroupMask) MOZ_OVERRIDE;
|
||||
virtual void EnableRDS(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
virtual void DisableRDS(FMRadioReplyRunnable* aReplyRunnable) MOZ_OVERRIDE;
|
||||
|
||||
virtual void AddObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
|
||||
virtual void RemoveObserver(FMRadioEventObserver* aObserver) MOZ_OVERRIDE;
|
||||
@ -64,6 +73,26 @@ public:
|
||||
RecvNotifyEnabledChanged(const bool& aEnabled,
|
||||
const double& aFrequency) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyRDSEnabledChanged(const bool& aEnabled) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyPIChanged(const bool& aValid,
|
||||
const uint16_t& aCode) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyPTYChanged(const bool& aValid,
|
||||
const uint8_t& aPTY) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyPSChanged(const nsString& aPSName) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyRadiotextChanged(const nsString& aRadiotext) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvNotifyNewRDSGroup(const uint64_t& aGroup) MOZ_OVERRIDE;
|
||||
|
||||
virtual PFMRadioRequestChild*
|
||||
AllocPFMRadioRequestChild(const FMRadioRequestArgs& aArgs) MOZ_OVERRIDE;
|
||||
|
||||
@ -78,10 +107,20 @@ private:
|
||||
inline void NotifyFMRadioEvent(FMRadioEventType aType);
|
||||
|
||||
bool mEnabled;
|
||||
bool mRDSEnabled;
|
||||
bool mRDSGroupSet;
|
||||
bool mPSNameSet;
|
||||
bool mRadiotextSet;
|
||||
double mFrequency;
|
||||
double mUpperBound;
|
||||
double mLowerBound;
|
||||
double mChannelWidth;
|
||||
Nullable<unsigned short> mPI;
|
||||
Nullable<uint8_t> mPTY;
|
||||
nsAutoString mPSName;
|
||||
nsAutoString mRadiotext;
|
||||
uint64_t mRDSGroup;
|
||||
uint32_t mRDSGroupMask;
|
||||
|
||||
FMRadioEventObserverList mObserverList;
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "FMRadioParent.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "FMRadioRequestParent.h"
|
||||
#include "FMRadioService.h"
|
||||
|
||||
@ -69,6 +70,12 @@ FMRadioParent::AllocPFMRadioRequestParent(const FMRadioRequestArgs& aArgs)
|
||||
case FMRadioRequestArgs::TCancelSeekRequestArgs:
|
||||
IFMRadioService::Singleton()->CancelSeek(requestParent);
|
||||
break;
|
||||
case FMRadioRequestArgs::TEnableRDSArgs:
|
||||
IFMRadioService::Singleton()->EnableRDS(requestParent);
|
||||
break;
|
||||
case FMRadioRequestArgs::TDisableRDSArgs:
|
||||
IFMRadioService::Singleton()->DisableRDS(requestParent);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH();
|
||||
}
|
||||
@ -98,6 +105,43 @@ FMRadioParent::Notify(const FMRadioEventType& aType)
|
||||
IFMRadioService::Singleton()->IsEnabled(),
|
||||
IFMRadioService::Singleton()->GetFrequency());
|
||||
break;
|
||||
case RDSEnabledChanged:
|
||||
unused << SendNotifyRDSEnabledChanged(
|
||||
IFMRadioService::Singleton()->IsRDSEnabled());
|
||||
break;
|
||||
case PIChanged: {
|
||||
Nullable<unsigned short> pi =
|
||||
IFMRadioService::Singleton()->GetPi();
|
||||
unused << SendNotifyPIChanged(!pi.IsNull(),
|
||||
pi.IsNull() ? 0 : pi.Value());
|
||||
break;
|
||||
}
|
||||
case PTYChanged: {
|
||||
Nullable<uint8_t> pty = IFMRadioService::Singleton()->GetPty();
|
||||
unused << SendNotifyPTYChanged(!pty.IsNull(),
|
||||
pty.IsNull() ? 0 : pty.Value());
|
||||
break;
|
||||
}
|
||||
case PSChanged: {
|
||||
nsAutoString psname;
|
||||
IFMRadioService::Singleton()->GetPs(psname);
|
||||
unused << SendNotifyPSChanged(psname);
|
||||
break;
|
||||
}
|
||||
case RadiotextChanged: {
|
||||
nsAutoString radiotext;
|
||||
IFMRadioService::Singleton()->GetRt(radiotext);
|
||||
unused << SendNotifyRadiotextChanged(radiotext);
|
||||
break;
|
||||
}
|
||||
case NewRDSGroup: {
|
||||
uint64_t group;
|
||||
DebugOnly<bool> rdsgroupset =
|
||||
IFMRadioService::Singleton()->GetRdsgroup(group);
|
||||
MOZ_ASSERT(rdsgroupset);
|
||||
unused << SendNotifyNewRDSGroup(group);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
NS_RUNTIMEABORT("not reached");
|
||||
break;
|
||||
@ -111,5 +155,12 @@ FMRadioParent::RecvEnableAudio(const bool& aAudioEnabled)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
FMRadioParent::RecvSetRDSGroupMask(const uint32_t& aRDSGroupMask)
|
||||
{
|
||||
IFMRadioService::Singleton()->SetRDSGroupMask(aRDSGroupMask);
|
||||
return true;
|
||||
}
|
||||
|
||||
END_FMRADIO_NAMESPACE
|
||||
|
||||
|
@ -39,6 +39,9 @@ public:
|
||||
|
||||
virtual bool
|
||||
RecvEnableAudio(const bool& aAudioEnabled) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool
|
||||
RecvSetRDSGroupMask(const uint32_t& aRDSGroupMask) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
END_FMRADIO_NAMESPACE
|
||||
|
@ -34,6 +34,14 @@ struct CancelSeekRequestArgs
|
||||
{
|
||||
};
|
||||
|
||||
struct EnableRDSArgs
|
||||
{
|
||||
};
|
||||
|
||||
struct DisableRDSArgs
|
||||
{
|
||||
};
|
||||
|
||||
union FMRadioRequestArgs
|
||||
{
|
||||
EnableRequestArgs;
|
||||
@ -41,6 +49,8 @@ union FMRadioRequestArgs
|
||||
SetFrequencyRequestArgs;
|
||||
SeekRequestArgs;
|
||||
CancelSeekRequestArgs;
|
||||
EnableRDSArgs;
|
||||
DisableRDSArgs;
|
||||
};
|
||||
|
||||
struct StatusInfo
|
||||
@ -66,6 +76,30 @@ child:
|
||||
* Sent when the power state of FM radio HW is changed.
|
||||
*/
|
||||
NotifyEnabledChanged(bool enabled, double frequency);
|
||||
/**
|
||||
* Sent when RDS is enabled or disabled.
|
||||
*/
|
||||
NotifyRDSEnabledChanged(bool enabled);
|
||||
/**
|
||||
* Sent when we have a new PI code.
|
||||
*/
|
||||
NotifyPIChanged(bool valid, uint16_t code);
|
||||
/**
|
||||
* Sent when we have a new PTY
|
||||
*/
|
||||
NotifyPTYChanged(bool valid, uint8_t pty);
|
||||
/**
|
||||
* Sent when we have a new PS name.
|
||||
*/
|
||||
NotifyPSChanged(nsString psname);
|
||||
/**
|
||||
* Sent when we have new radiotext.
|
||||
*/
|
||||
NotifyRadiotextChanged(nsString radiotext);
|
||||
/**
|
||||
* Sent when a full RDS group is received.
|
||||
*/
|
||||
NotifyNewRDSGroup(uint64_t data);
|
||||
|
||||
__delete__();
|
||||
|
||||
@ -91,6 +125,11 @@ parent:
|
||||
* Enable/Disable audio
|
||||
*/
|
||||
EnableAudio(bool audioEnabled);
|
||||
|
||||
/**
|
||||
* Set RDS group mask
|
||||
*/
|
||||
SetRDSGroupMask(uint32_t groupMask);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include "TabChild.h"
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/a11y/DocAccessibleChild.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/ContentBridgeChild.h"
|
||||
#include "mozilla/dom/ContentBridgeParent.h"
|
||||
@ -708,20 +707,6 @@ ContentChild::InitXPCOM()
|
||||
InitOnContentProcessCreated();
|
||||
}
|
||||
|
||||
a11y::PDocAccessibleChild*
|
||||
ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
|
||||
{
|
||||
MOZ_ASSERT(false, "should never call this!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
|
||||
{
|
||||
delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
|
||||
return true;
|
||||
}
|
||||
|
||||
PMemoryReportRequestChild*
|
||||
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
|
||||
const bool &aAnonymize,
|
||||
|
@ -380,8 +380,6 @@ public:
|
||||
const uint64_t& aID,
|
||||
const bool& aIsForApp,
|
||||
const bool& aIsForBrowser) MOZ_OVERRIDE;
|
||||
virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
|
||||
|
@ -30,8 +30,6 @@
|
||||
#include "CrashReporterParent.h"
|
||||
#include "IHistory.h"
|
||||
#include "mozIApplication.h"
|
||||
#include "mozilla/a11y/DocAccessibleParent.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/dom/DataStoreService.h"
|
||||
#include "mozilla/dom/DOMStorageIPC.h"
|
||||
@ -2702,34 +2700,6 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
a11y::PDocAccessibleParent*
|
||||
ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&)
|
||||
{
|
||||
return new a11y::DocAccessibleParent();
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
|
||||
{
|
||||
delete static_cast<a11y::DocAccessibleParent*>(aParent);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID)
|
||||
{
|
||||
auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
|
||||
if (aParentDoc) {
|
||||
MOZ_ASSERT(aParentID);
|
||||
auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
|
||||
return parentDoc->AddChildDoc(doc, aParentID);
|
||||
} else {
|
||||
MOZ_ASSERT(!aParentID);
|
||||
GetAccService()->RemoteDocAdded(doc);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PCompositorParent*
|
||||
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
|
||||
base::ProcessId aOtherProcess)
|
||||
|
@ -670,11 +670,6 @@ private:
|
||||
int32_t* aSliceRefCnt,
|
||||
bool* aResult) MOZ_OVERRIDE;
|
||||
|
||||
virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) MOZ_OVERRIDE;
|
||||
virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) MOZ_OVERRIDE;
|
||||
virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
|
||||
PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) MOZ_OVERRIDE;
|
||||
|
||||
// If you add strong pointers to cycle collected objects here, be sure to
|
||||
// release these objects in ShutDownProcess. See the comment there for more
|
||||
// details.
|
||||
|
@ -14,7 +14,6 @@ include protocol PCompositor;
|
||||
include protocol PContentBridge;
|
||||
include protocol PCycleCollectWithLogs;
|
||||
include protocol PCrashReporter;
|
||||
include protocol PDocAccessible;
|
||||
include protocol PExternalHelperApp;
|
||||
include protocol PDeviceStorageRequest;
|
||||
include protocol PFileDescriptorSet;
|
||||
@ -333,7 +332,6 @@ intr protocol PContent
|
||||
manages PCellBroadcast;
|
||||
manages PCrashReporter;
|
||||
manages PCycleCollectWithLogs;
|
||||
manages PDocAccessible;
|
||||
manages PDeviceStorageRequest;
|
||||
manages PFileSystemRequest;
|
||||
manages PExternalHelperApp;
|
||||
@ -490,14 +488,6 @@ child:
|
||||
OnAppThemeChanged();
|
||||
|
||||
parent:
|
||||
/**
|
||||
* Tell the parent process a new accessible document has been created.
|
||||
* aParentDoc is the accessible document it was created in if any, and
|
||||
* aParentAcc is the id of the accessible in that document the new document
|
||||
* is a child of.
|
||||
*/
|
||||
PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
|
||||
|
||||
/**
|
||||
* Tell the content process some attributes of itself. This is
|
||||
* among the first information queried by content processes after
|
||||
|
@ -482,7 +482,7 @@ NetworkManager.prototype = {
|
||||
return Promise.reject("Invalid network interface.");
|
||||
}
|
||||
|
||||
return this.resolveHostname(host)
|
||||
return this.resolveHostname(network, host)
|
||||
.then((ipAddresses) => this._updateRoutes(true,
|
||||
ipAddresses,
|
||||
network.name,
|
||||
@ -494,7 +494,7 @@ NetworkManager.prototype = {
|
||||
return Promise.reject("Invalid network interface.");
|
||||
}
|
||||
|
||||
return this.resolveHostname(host)
|
||||
return this.resolveHostname(network, host)
|
||||
.then((ipAddresses) => this._updateRoutes(false,
|
||||
ipAddresses,
|
||||
network.name,
|
||||
@ -594,7 +594,7 @@ NetworkManager.prototype = {
|
||||
// The override was just set, so reconfigure the network.
|
||||
if (this.active != this._overriddenActive) {
|
||||
this.active = this._overriddenActive;
|
||||
gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
|
||||
this._setDefaultRouteAndDNS(this.active, oldActive);
|
||||
Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null);
|
||||
}
|
||||
return;
|
||||
@ -605,7 +605,7 @@ NetworkManager.prototype = {
|
||||
this.active.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED &&
|
||||
this.active.type == this._preferredNetworkType) {
|
||||
debug("Active network is already our preferred type.");
|
||||
gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
|
||||
this._setDefaultRouteAndDNS(this.active, oldActive);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -641,10 +641,10 @@ NetworkManager.prototype = {
|
||||
}
|
||||
// Don't set default route on secondary APN
|
||||
if (this.isNetworkTypeSecondaryMobile(this.active.type)) {
|
||||
gNetworkService.setDNS(this.active);
|
||||
gNetworkService.setDNS(this.active, function() {});
|
||||
} else {
|
||||
#endif // MOZ_B2G_RIL
|
||||
gNetworkService.setDefaultRouteAndDNS(this.active, oldActive);
|
||||
this._setDefaultRouteAndDNS(this.active, oldActive);
|
||||
#ifdef MOZ_B2G_RIL
|
||||
}
|
||||
#endif
|
||||
@ -659,7 +659,7 @@ NetworkManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
resolveHostname: function(hostname) {
|
||||
resolveHostname: function(network, hostname) {
|
||||
// Sanity check for null, undefined and empty string... etc.
|
||||
if (!hostname) {
|
||||
return Promise.reject(new Error("hostname is empty: " + hostname));
|
||||
@ -694,8 +694,18 @@ NetworkManager.prototype = {
|
||||
deferred.resolve(retval);
|
||||
};
|
||||
|
||||
// Bug 1058282 - Explicitly request ipv4 to get around 8.8.8.8 probe at
|
||||
// http://androidxref.com/4.3_r2.1/xref/bionic/libc/netbsd/net/getaddrinfo.c#1923
|
||||
//
|
||||
// Whenever MMS connection is the only network interface, there is no
|
||||
// default route so that any ip probe will fail.
|
||||
let flags = 0;
|
||||
if (network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
|
||||
flags |= Ci.nsIDNSService.RESOLVE_DISABLE_IPV6;
|
||||
}
|
||||
|
||||
// TODO: Bug 992772 - Resolve the hostname with specified networkInterface.
|
||||
gDNSService.asyncResolve(hostname, 0, onLookupComplete, Services.tm.mainThread);
|
||||
gDNSService.asyncResolve(hostname, flags, onLookupComplete, Services.tm.mainThread);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
@ -1297,7 +1307,15 @@ NetworkManager.prototype = {
|
||||
this.wantConnectionEvent = null;
|
||||
|
||||
callback.call(this);
|
||||
}
|
||||
},
|
||||
|
||||
_setDefaultRouteAndDNS: function(network, oldInterface) {
|
||||
gNetworkService.setDefaultRoute(network, oldInterface, function(success) {
|
||||
gNetworkService.setDNS(network, function(result) {
|
||||
gNetworkService.setNetworkProxy(network);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
let CaptivePortalDetectionHelper = (function() {
|
||||
|
@ -188,7 +188,6 @@ NetworkService.prototype = {
|
||||
};
|
||||
|
||||
params.report = true;
|
||||
params.isAsync = true;
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
if (!isError(result.resultCode)) {
|
||||
@ -210,7 +209,6 @@ NetworkService.prototype = {
|
||||
};
|
||||
|
||||
params.report = true;
|
||||
params.isAsync = true;
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
if (!isError(result.resultCode)) {
|
||||
@ -230,7 +228,6 @@ NetworkService.prototype = {
|
||||
};
|
||||
|
||||
params.report = true;
|
||||
params.isAsync = true;
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback(result);
|
||||
@ -247,7 +244,6 @@ NetworkService.prototype = {
|
||||
};
|
||||
|
||||
params.report = true;
|
||||
params.isAsync = true;
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
if (isError(result.resultCode)) {
|
||||
@ -277,8 +273,8 @@ NetworkService.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
setDNS: function(networkInterface) {
|
||||
if(DEBUG) debug("Going DNS to " + networkInterface.name);
|
||||
setDNS: function(networkInterface, callback) {
|
||||
if (DEBUG) debug("Going DNS to " + networkInterface.name);
|
||||
let dnses = networkInterface.getDnses();
|
||||
let options = {
|
||||
cmd: "setDNS",
|
||||
@ -286,23 +282,23 @@ NetworkService.prototype = {
|
||||
domain: "mozilla." + networkInterface.name + ".doman",
|
||||
dnses: dnses
|
||||
};
|
||||
this.controlMessage(options);
|
||||
this.controlMessage(options, function(result) {
|
||||
callback.setDnsResult(result.success ? null : result.reason);
|
||||
});
|
||||
},
|
||||
|
||||
setDefaultRouteAndDNS: function(network, oldInterface) {
|
||||
if(DEBUG) debug("Going to change route and DNS to " + network.name);
|
||||
setDefaultRoute: function(network, oldInterface, callback) {
|
||||
if (DEBUG) debug("Going to change default route to " + network.name);
|
||||
let gateways = network.getGateways();
|
||||
let dnses = network.getDnses();
|
||||
let options = {
|
||||
cmd: "setDefaultRouteAndDNS",
|
||||
cmd: "setDefaultRoute",
|
||||
ifname: network.name,
|
||||
oldIfname: (oldInterface && oldInterface !== network) ? oldInterface.name : null,
|
||||
gateways: gateways,
|
||||
domain: "mozilla." + network.name + ".doman",
|
||||
dnses: dnses
|
||||
gateways: gateways
|
||||
};
|
||||
this.controlMessage(options);
|
||||
this.setNetworkProxy(network);
|
||||
this.controlMessage(options, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
removeDefaultRoute: function(network) {
|
||||
@ -415,7 +411,6 @@ NetworkService.prototype = {
|
||||
}
|
||||
|
||||
config.cmd = "setDhcpServer";
|
||||
config.isAsync = true;
|
||||
config.enabled = enabled;
|
||||
|
||||
this.controlMessage(config, function setDhcpServerResult(response) {
|
||||
@ -437,7 +432,6 @@ NetworkService.prototype = {
|
||||
config.cmd = "setWifiTethering";
|
||||
|
||||
// The callback function in controlMessage may not be fired immediately.
|
||||
config.isAsync = true;
|
||||
this.controlMessage(config, function setWifiTetheringResult(data) {
|
||||
let code = data.resultCode;
|
||||
let reason = data.resultReason;
|
||||
@ -458,7 +452,6 @@ NetworkService.prototype = {
|
||||
setUSBTethering: function(enable, config, callback) {
|
||||
config.cmd = "setUSBTethering";
|
||||
// The callback function in controlMessage may not be fired immediately.
|
||||
config.isAsync = true;
|
||||
this.controlMessage(config, function setUsbTetheringResult(data) {
|
||||
let code = data.resultCode;
|
||||
let reason = data.resultReason;
|
||||
@ -491,7 +484,6 @@ NetworkService.prototype = {
|
||||
}
|
||||
|
||||
// The callback function in controlMessage may not be fired immediately.
|
||||
params.isAsync = true;
|
||||
//this._usbTetheringAction = TETHERING_STATE_ONGOING;
|
||||
this.controlMessage(params, function(data) {
|
||||
callback.enableUsbRndisResult(data.result, data.enable);
|
||||
@ -501,7 +493,6 @@ NetworkService.prototype = {
|
||||
updateUpStream: function(previous, current, callback) {
|
||||
let params = {
|
||||
cmd: "updateUpStream",
|
||||
isAsync: true,
|
||||
preInternalIfname: previous.internalIfname,
|
||||
preExternalIfname: previous.externalIfname,
|
||||
curInternalIfname: current.internalIfname,
|
||||
@ -516,6 +507,66 @@ NetworkService.prototype = {
|
||||
});
|
||||
},
|
||||
|
||||
configureInterface: function(config, callback) {
|
||||
let params = {
|
||||
cmd: "configureInterface",
|
||||
ifname: config.ifname,
|
||||
ipaddr: config.ipaddr,
|
||||
mask: config.mask,
|
||||
gateway_long: config.gateway,
|
||||
dns1_long: config.dns1,
|
||||
dns2_long: config.dns2,
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
dhcpRequest: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "dhcpRequest",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.dhcpRequestResult(!result.error, result.error ? null : result);
|
||||
});
|
||||
},
|
||||
|
||||
enableInterface: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "enableInterface",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
disableInterface: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "disableInterface",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
resetConnections: function(interfaceName, callback) {
|
||||
let params = {
|
||||
cmd: "resetConnections",
|
||||
ifname: interfaceName
|
||||
};
|
||||
|
||||
this.controlMessage(params, function(result) {
|
||||
callback.nativeCommandResult(!result.error);
|
||||
});
|
||||
},
|
||||
|
||||
shutdown: false,
|
||||
|
||||
observe: function observe(aSubject, aTopic, aData) {
|
||||
|
@ -101,7 +101,14 @@ typedef Tuple3<NetdCommand*, CommandChain*, CommandCallback> QueueData;
|
||||
#define GET_CURRENT_CALLBACK (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].c)
|
||||
#define GET_CURRENT_COMMAND (gCommandQueue.IsEmpty() ? nullptr : gCommandQueue[0].a->mData)
|
||||
|
||||
#define CNT_OF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
|
||||
// A macro for native function call return value check.
|
||||
// For native function call, non-zero return value means failure.
|
||||
#define RETURN_IF_FAILED(rv) do { \
|
||||
if (SUCCESS != rv) { \
|
||||
return rv; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
|
||||
static NetworkUtils* gNetworkUtils;
|
||||
static nsTArray<QueueData> gCommandQueue;
|
||||
@ -393,6 +400,40 @@ void NetworkUtils::next(CommandChain* aChain, bool aError, NetworkResultOptions&
|
||||
(*f)(aChain, next, aResult);
|
||||
}
|
||||
|
||||
CommandResult::CommandResult(int32_t aResultCode)
|
||||
: mIsPending(false)
|
||||
{
|
||||
// This is usually not a netd command. We treat the return code
|
||||
// typical linux convention, which uses 0 to indicate success.
|
||||
mResult.mError = (aResultCode == SUCCESS ? false : true);
|
||||
mResult.mResultCode = aResultCode;
|
||||
if (aResultCode != SUCCESS) {
|
||||
// The returned value is sometimes negative, make sure we pass a positive
|
||||
// error number to strerror.
|
||||
enum { STRERROR_R_BUF_SIZE = 1024, };
|
||||
char strerrorBuf[STRERROR_R_BUF_SIZE];
|
||||
strerror_r(abs(aResultCode), strerrorBuf, STRERROR_R_BUF_SIZE);
|
||||
mResult.mReason = NS_ConvertUTF8toUTF16(strerrorBuf);
|
||||
}
|
||||
mResult.mRet = true;
|
||||
}
|
||||
|
||||
CommandResult::CommandResult(const mozilla::dom::NetworkResultOptions& aResult)
|
||||
: mResult(aResult)
|
||||
, mIsPending(false)
|
||||
{
|
||||
}
|
||||
|
||||
CommandResult::CommandResult(const Pending&)
|
||||
: mIsPending(true)
|
||||
{
|
||||
}
|
||||
|
||||
bool CommandResult::isPending() const
|
||||
{
|
||||
return mIsPending;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send command to netd.
|
||||
*/
|
||||
@ -1030,14 +1071,19 @@ NetworkUtils::~NetworkUtils()
|
||||
#define GET_CHAR(prop) NS_ConvertUTF16toUTF8(aOptions.prop).get()
|
||||
#define GET_FIELD(prop) aOptions.prop
|
||||
|
||||
// Hoist this type definition to global to avoid template
|
||||
// instantiation error on gcc 4.4 used by ICS emulator.
|
||||
typedef CommandResult (NetworkUtils::*CommandHandler)(NetworkParams&);
|
||||
struct CommandHandlerEntry
|
||||
{
|
||||
const char* mCommandName;
|
||||
CommandHandler mCommandHandler;
|
||||
};
|
||||
|
||||
void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
{
|
||||
typedef int32_t (NetworkUtils::*CommandHandler)(NetworkParams&);
|
||||
|
||||
const static struct {
|
||||
const char* mCommandName;
|
||||
CommandHandler mCommandHandler;
|
||||
} COMMAND_HANDLER_TABLE[] = {
|
||||
const static CommandHandlerEntry
|
||||
COMMAND_HANDLER_TABLE[] = {
|
||||
|
||||
// For command 'testCommand', BUILD_ENTRY(testCommand) will generate
|
||||
// {"testCommand", NetworkUtils::testCommand}
|
||||
@ -1045,7 +1091,7 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
|
||||
BUILD_ENTRY(removeNetworkRoute),
|
||||
BUILD_ENTRY(setDNS),
|
||||
BUILD_ENTRY(setDefaultRouteAndDNS),
|
||||
BUILD_ENTRY(setDefaultRoute),
|
||||
BUILD_ENTRY(removeDefaultRoute),
|
||||
BUILD_ENTRY(addHostRoute),
|
||||
BUILD_ENTRY(removeHostRoute),
|
||||
@ -1061,13 +1107,18 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
BUILD_ENTRY(setUSBTethering),
|
||||
BUILD_ENTRY(enableUsbRndis),
|
||||
BUILD_ENTRY(updateUpStream),
|
||||
BUILD_ENTRY(configureInterface),
|
||||
BUILD_ENTRY(dhcpRequest),
|
||||
BUILD_ENTRY(enableInterface),
|
||||
BUILD_ENTRY(disableInterface),
|
||||
BUILD_ENTRY(resetConnections),
|
||||
|
||||
#undef BUILD_ENTRY
|
||||
};
|
||||
|
||||
// Loop until we find the command name which matches aOptions.mCmd.
|
||||
CommandHandler handler = nullptr;
|
||||
for (size_t i = 0; i < CNT_OF_ARRAY(COMMAND_HANDLER_TABLE); i++) {
|
||||
for (size_t i = 0; i < mozilla::ArrayLength(COMMAND_HANDLER_TABLE); i++) {
|
||||
if (aOptions.mCmd.EqualsASCII(COMMAND_HANDLER_TABLE[i].mCommandName)) {
|
||||
handler = COMMAND_HANDLER_TABLE[i].mCommandHandler;
|
||||
break;
|
||||
@ -1080,25 +1131,19 @@ void NetworkUtils::ExecuteCommand(NetworkParams aOptions)
|
||||
return;
|
||||
}
|
||||
|
||||
// Command matches! Dispatch to the handler.
|
||||
int32_t ret = 0;
|
||||
ret = (this->*handler)(aOptions);
|
||||
|
||||
if (!aOptions.mIsAsync) {
|
||||
// The requested command is synchronous, which implies the actual result
|
||||
// from netd is not important to the client. So, just notify the
|
||||
// registered callback.
|
||||
NetworkResultOptions result;
|
||||
result.mError = ret == SUCCESS ? false : true;
|
||||
result.mResultCode = ret;
|
||||
if (ret != SUCCESS) {
|
||||
// The returned value is sometimes negative, make sure we pass a positive
|
||||
// error number to strerror.
|
||||
result.mReason = NS_ConvertUTF8toUTF16(strerror(abs(ret)));
|
||||
}
|
||||
|
||||
result.mRet = true;
|
||||
postMessage(aOptions, result);
|
||||
// The handler would return one of the following 3 values
|
||||
// to be wrapped to CommandResult:
|
||||
//
|
||||
// 1) |int32_t| for mostly synchronous native function calls.
|
||||
// 2) |NetworkResultOptions| to populate additional results. (e.g. dhcpRequest)
|
||||
// 3) |CommandResult::Pending| to indicate the result is not
|
||||
// obtained yet.
|
||||
//
|
||||
// If the handler returns "Pending", the handler should take the
|
||||
// responsibility for posting result to main thread.
|
||||
CommandResult commandResult = (this->*handler)(aOptions);
|
||||
if (!commandResult.isPending()) {
|
||||
postMessage(aOptions, commandResult.mResult);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1183,7 +1228,7 @@ void NetworkUtils::onNetdMessage(NetdCommand* aCommand)
|
||||
/**
|
||||
* Start/Stop DHCP server.
|
||||
*/
|
||||
int32_t NetworkUtils::setDhcpServer(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setDhcpServer(NetworkParams& aOptions)
|
||||
{
|
||||
if (aOptions.mEnabled) {
|
||||
aOptions.mWifiStartIp = aOptions.mStartIp;
|
||||
@ -1196,13 +1241,13 @@ int32_t NetworkUtils::setDhcpServer(NetworkParams& aOptions)
|
||||
} else {
|
||||
RUN_CHAIN(aOptions, sStopDhcpServerChain, setDhcpServerFail)
|
||||
}
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set DNS servers for given network interface.
|
||||
*/
|
||||
int32_t NetworkUtils::setDNS(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setDNS(NetworkParams& aOptions)
|
||||
{
|
||||
uint32_t length = aOptions.mDnses.Length();
|
||||
|
||||
@ -1234,23 +1279,116 @@ int32_t NetworkUtils::setDNS(NetworkParams& aOptions)
|
||||
// DNS needs to be set through netd since JellyBean (4.3).
|
||||
if (SDK_VERSION >= 18) {
|
||||
RUN_CHAIN(aOptions, sSetDnsChain, setDnsFail)
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::configureInterface(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
return mNetUtils->do_ifc_configure(
|
||||
autoIfname.get(),
|
||||
aOptions.mIpaddr,
|
||||
aOptions.mMask,
|
||||
aOptions.mGateway_long,
|
||||
aOptions.mDns1_long,
|
||||
aOptions.mDns2_long
|
||||
);
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::dhcpRequest(NetworkParams& aOptions) {
|
||||
mozilla::dom::NetworkResultOptions result;
|
||||
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
char ipaddr[PROPERTY_VALUE_MAX];
|
||||
char gateway[PROPERTY_VALUE_MAX];
|
||||
uint32_t prefixLength;
|
||||
char dns1[PROPERTY_VALUE_MAX];
|
||||
char dns2[PROPERTY_VALUE_MAX];
|
||||
char server[PROPERTY_VALUE_MAX];
|
||||
uint32_t lease;
|
||||
char vendorinfo[PROPERTY_VALUE_MAX];
|
||||
int32_t ret = mNetUtils->do_dhcp_do_request(autoIfname.get(),
|
||||
ipaddr,
|
||||
gateway,
|
||||
&prefixLength,
|
||||
dns1,
|
||||
dns2,
|
||||
server,
|
||||
&lease,
|
||||
vendorinfo);
|
||||
|
||||
RETURN_IF_FAILED(ret);
|
||||
|
||||
result.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr);
|
||||
result.mGateway_str = NS_ConvertUTF8toUTF16(gateway);
|
||||
result.mDns1_str = NS_ConvertUTF8toUTF16(dns1);
|
||||
result.mDns2_str = NS_ConvertUTF8toUTF16(dns2);
|
||||
result.mServer_str = NS_ConvertUTF8toUTF16(server);
|
||||
result.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo);
|
||||
result.mLease = lease;
|
||||
result.mMask = makeMask(prefixLength);
|
||||
|
||||
uint32_t inet4; // only support IPv4 for now.
|
||||
|
||||
#define INET_PTON(var, field) \
|
||||
PR_BEGIN_MACRO \
|
||||
inet_pton(AF_INET, var, &inet4); \
|
||||
result.field = inet4; \
|
||||
PR_END_MACRO
|
||||
|
||||
INET_PTON(ipaddr, mIpaddr);
|
||||
INET_PTON(gateway, mGateway);
|
||||
|
||||
if (dns1[0] != '\0') {
|
||||
INET_PTON(dns1, mDns1);
|
||||
}
|
||||
|
||||
if (dns2[0] != '\0') {
|
||||
INET_PTON(dns2, mDns2);
|
||||
}
|
||||
|
||||
INET_PTON(server, mServer);
|
||||
|
||||
char inet_str[64];
|
||||
if (inet_ntop(AF_INET, &result.mMask, inet_str, sizeof(inet_str))) {
|
||||
result.mMask_str = NS_ConvertUTF8toUTF16(inet_str);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::enableInterface(NetworkParams& aOptions) {
|
||||
return mNetUtils->do_ifc_enable(
|
||||
NS_ConvertUTF16toUTF8(aOptions.mIfname).get());
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::disableInterface(NetworkParams& aOptions) {
|
||||
return mNetUtils->do_ifc_disable(
|
||||
NS_ConvertUTF16toUTF8(aOptions.mIfname).get());
|
||||
}
|
||||
|
||||
CommandResult NetworkUtils::resetConnections(NetworkParams& aOptions) {
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
return mNetUtils->do_ifc_reset_connections(
|
||||
NS_ConvertUTF16toUTF8(aOptions.mIfname).get(),
|
||||
RESET_ALL_ADDRESSES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set default route and DNS servers for given network interface.
|
||||
*/
|
||||
int32_t NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setDefaultRoute(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
|
||||
if (!aOptions.mOldIfname.IsEmpty()) {
|
||||
// Remove IPv4's default route.
|
||||
mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname));
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(GET_CHAR(mOldIfname)));
|
||||
// Remove IPv6's default route.
|
||||
mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL);
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mOldIfname), "::", 0, NULL));
|
||||
}
|
||||
|
||||
uint32_t length = aOptions.mGateways.Length();
|
||||
@ -1264,9 +1402,9 @@ int32_t NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions)
|
||||
}
|
||||
|
||||
if (type == AF_INET6) {
|
||||
mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, autoGateway.get());
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, autoGateway.get()));
|
||||
} else { /* type == AF_INET */
|
||||
mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(autoGateway.get()));
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(autoGateway.get())));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1283,20 +1421,19 @@ int32_t NetworkUtils::setDefaultRouteAndDNS(NetworkParams& aOptions)
|
||||
}
|
||||
|
||||
if (type == AF_INET6) {
|
||||
mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway);
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway));
|
||||
} else { /* type == AF_INET */
|
||||
mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(gateway));
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(gateway)));
|
||||
}
|
||||
}
|
||||
|
||||
setDNS(aOptions);
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove default route for given network interface.
|
||||
*/
|
||||
int32_t NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
{
|
||||
uint32_t length = aOptions.mGateways.Length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
@ -1307,9 +1444,9 @@ int32_t NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
return EAFNOSUPPORT;
|
||||
}
|
||||
|
||||
mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
|
||||
type == AF_INET ? "0.0.0.0" : "::",
|
||||
0, autoGateway.get());
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(GET_CHAR(mIfname),
|
||||
type == AF_INET ? "0.0.0.0" : "::",
|
||||
0, autoGateway.get()));
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
@ -1318,7 +1455,7 @@ int32_t NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
|
||||
/**
|
||||
* Add host route for given network interface.
|
||||
*/
|
||||
int32_t NetworkUtils::addHostRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::addHostRoute(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
|
||||
@ -1342,7 +1479,7 @@ int32_t NetworkUtils::addHostRoute(NetworkParams& aOptions)
|
||||
/**
|
||||
* Remove host route for given network interface.
|
||||
*/
|
||||
int32_t NetworkUtils::removeHostRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::removeHostRoute(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoHostname(aOptions.mIp);
|
||||
@ -1366,12 +1503,12 @@ int32_t NetworkUtils::removeHostRoute(NetworkParams& aOptions)
|
||||
/**
|
||||
* Remove the routes associated with the named interface.
|
||||
*/
|
||||
int32_t NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::removeHostRoutes(NetworkParams& aOptions)
|
||||
{
|
||||
return mNetUtils->do_ifc_remove_host_routes(GET_CHAR(mIfname));
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
{
|
||||
NS_ConvertUTF16toUTF8 autoIfname(aOptions.mIfname);
|
||||
NS_ConvertUTF16toUTF8 autoIp(aOptions.mIp);
|
||||
@ -1406,10 +1543,10 @@ int32_t NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
}
|
||||
|
||||
// Remove default route.
|
||||
mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL);
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), "::", 0, NULL));
|
||||
|
||||
// Remove subnet route.
|
||||
mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL);
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), subnetStr, prefixLength, NULL));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
@ -1422,12 +1559,12 @@ int32_t NetworkUtils::removeNetworkRoute(NetworkParams& aOptions)
|
||||
addr.s_addr = subnet;
|
||||
const char* dst = inet_ntoa(addr);
|
||||
|
||||
mNetUtils->do_ifc_remove_default_route(autoIfname.get());
|
||||
mNetUtils->do_ifc_remove_route(autoIfname.get(), dst, prefixLength, gateway);
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_default_route(autoIfname.get()));
|
||||
RETURN_IF_FAILED(mNetUtils->do_ifc_remove_route(autoIfname.get(), dst, prefixLength, gateway));
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::addSecondaryRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::addSecondaryRoute(NetworkParams& aOptions)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1,
|
||||
@ -1441,7 +1578,7 @@ int32_t NetworkUtils::addSecondaryRoute(NetworkParams& aOptions)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions)
|
||||
{
|
||||
char command[MAX_COMMAND_SIZE];
|
||||
snprintf(command, MAX_COMMAND_SIZE - 1,
|
||||
@ -1455,41 +1592,41 @@ int32_t NetworkUtils::removeSecondaryRoute(NetworkParams& aOptions)
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
{
|
||||
DEBUG("setNetworkInterfaceAlarms: %s", GET_CHAR(mIfname));
|
||||
RUN_CHAIN(aOptions, sNetworkInterfaceSetAlarmChain, networkInterfaceAlarmFail);
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::enableNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
{
|
||||
DEBUG("enableNetworkInterfaceAlarm: %s", GET_CHAR(mIfname));
|
||||
RUN_CHAIN(aOptions, sNetworkInterfaceEnableAlarmChain, networkInterfaceAlarmFail);
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::disableNetworkInterfaceAlarm(NetworkParams& aOptions)
|
||||
{
|
||||
DEBUG("disableNetworkInterfaceAlarms: %s", GET_CHAR(mIfname));
|
||||
RUN_CHAIN(aOptions, sNetworkInterfaceDisableAlarmChain, networkInterfaceAlarmFail);
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* handling main thread's reload Wifi firmware request
|
||||
*/
|
||||
int32_t NetworkUtils::setWifiOperationMode(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setWifiOperationMode(NetworkParams& aOptions)
|
||||
{
|
||||
DEBUG("setWifiOperationMode: %s %s", GET_CHAR(mIfname), GET_CHAR(mMode));
|
||||
RUN_CHAIN(aOptions, sWifiOperationModeChain, wifiOperationModeFail);
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
/**
|
||||
* handling main thread's enable/disable WiFi Tethering request
|
||||
*/
|
||||
int32_t NetworkUtils::setWifiTethering(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setWifiTethering(NetworkParams& aOptions)
|
||||
{
|
||||
bool enable = aOptions.mEnable;
|
||||
IFProperties interfaceProperties;
|
||||
@ -1518,10 +1655,10 @@ int32_t NetworkUtils::setWifiTethering(NetworkParams& aOptions)
|
||||
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
|
||||
RUN_CHAIN(aOptions, sWifiDisableChain, wifiTetheringFail)
|
||||
}
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
int32_t NetworkUtils::setUSBTethering(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::setUSBTethering(NetworkParams& aOptions)
|
||||
{
|
||||
bool enable = aOptions.mEnable;
|
||||
IFProperties interfaceProperties;
|
||||
@ -1550,7 +1687,7 @@ int32_t NetworkUtils::setUSBTethering(NetworkParams& aOptions)
|
||||
GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
|
||||
RUN_CHAIN(aOptions, sUSBDisableChain, usbTetheringFail)
|
||||
}
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
void NetworkUtils::escapeQuote(nsCString& aString)
|
||||
@ -1559,7 +1696,7 @@ void NetworkUtils::escapeQuote(nsCString& aString)
|
||||
aString.ReplaceSubstring("\"", "\\\"");
|
||||
}
|
||||
|
||||
void NetworkUtils::checkUsbRndisState(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::checkUsbRndisState(NetworkParams& aOptions)
|
||||
{
|
||||
static uint32_t retry = 0;
|
||||
|
||||
@ -1574,27 +1711,25 @@ void NetworkUtils::checkUsbRndisState(NetworkParams& aOptions)
|
||||
NetworkResultOptions result;
|
||||
result.mEnable = aOptions.mEnable;
|
||||
result.mResult = true;
|
||||
postMessage(aOptions, result);
|
||||
retry = 0;
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
if (retry < USB_FUNCTION_RETRY_TIMES) {
|
||||
retry++;
|
||||
usleep(USB_FUNCTION_RETRY_INTERVAL * 1000);
|
||||
checkUsbRndisState(aOptions);
|
||||
return;
|
||||
return checkUsbRndisState(aOptions);
|
||||
}
|
||||
|
||||
NetworkResultOptions result;
|
||||
result.mResult = false;
|
||||
postMessage(aOptions, result);
|
||||
retry = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify usb function's property to turn on USB RNDIS function
|
||||
*/
|
||||
int32_t NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
|
||||
{
|
||||
bool report = aOptions.mReport;
|
||||
|
||||
@ -1650,7 +1785,7 @@ int32_t NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
|
||||
// Trigger the timer to check usb state and report the result to NetworkManager.
|
||||
if (report) {
|
||||
usleep(USB_FUNCTION_RETRY_INTERVAL * 1000);
|
||||
checkUsbRndisState(aOptions);
|
||||
return checkUsbRndisState(aOptions);
|
||||
}
|
||||
return SUCCESS;
|
||||
}
|
||||
@ -1658,10 +1793,10 @@ int32_t NetworkUtils::enableUsbRndis(NetworkParams& aOptions)
|
||||
/**
|
||||
* handling upstream interface change event.
|
||||
*/
|
||||
int32_t NetworkUtils::updateUpStream(NetworkParams& aOptions)
|
||||
CommandResult NetworkUtils::updateUpStream(NetworkParams& aOptions)
|
||||
{
|
||||
RUN_CHAIN(aOptions, sUpdateUpStreamChain, updateUpStreamFail)
|
||||
return SUCCESS;
|
||||
return CommandResult::Pending();
|
||||
}
|
||||
|
||||
void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
|
||||
|
@ -28,48 +28,6 @@ public:
|
||||
NetworkParams() {
|
||||
}
|
||||
|
||||
NetworkParams(const NetworkParams& aOther) {
|
||||
mIp = aOther.mIp;
|
||||
mCmd = aOther.mCmd;
|
||||
mDomain = aOther.mDomain;
|
||||
mGateway = aOther.mGateway;
|
||||
mGateways = aOther.mGateways;
|
||||
mId = aOther.mId;
|
||||
mIfname = aOther.mIfname;
|
||||
mPrefixLength = aOther.mPrefixLength;
|
||||
mOldIfname = aOther.mOldIfname;
|
||||
mMode = aOther.mMode;
|
||||
mReport = aOther.mReport;
|
||||
mIsAsync = aOther.mIsAsync;
|
||||
mEnabled = aOther.mEnabled;
|
||||
mWifictrlinterfacename = aOther.mWifictrlinterfacename;
|
||||
mInternalIfname = aOther.mInternalIfname;
|
||||
mExternalIfname = aOther.mExternalIfname;
|
||||
mEnable = aOther.mEnable;
|
||||
mSsid = aOther.mSsid;
|
||||
mSecurity = aOther.mSecurity;
|
||||
mKey = aOther.mKey;
|
||||
mPrefix = aOther.mPrefix;
|
||||
mLink = aOther.mLink;
|
||||
mInterfaceList = aOther.mInterfaceList;
|
||||
mWifiStartIp = aOther.mWifiStartIp;
|
||||
mWifiEndIp = aOther.mWifiEndIp;
|
||||
mUsbStartIp = aOther.mUsbStartIp;
|
||||
mUsbEndIp = aOther.mUsbEndIp;
|
||||
mDns1 = aOther.mDns1;
|
||||
mDns2 = aOther.mDns2;
|
||||
mDnses = aOther.mDnses;
|
||||
mStartIp = aOther.mStartIp;
|
||||
mEndIp = aOther.mEndIp;
|
||||
mServerIp = aOther.mServerIp;
|
||||
mMaskLength = aOther.mMaskLength;
|
||||
mPreInternalIfname = aOther.mPreInternalIfname;
|
||||
mPreExternalIfname = aOther.mPreExternalIfname;
|
||||
mCurInternalIfname = aOther.mCurInternalIfname;
|
||||
mCurExternalIfname = aOther.mCurExternalIfname;
|
||||
mThreshold = aOther.mThreshold;
|
||||
}
|
||||
|
||||
NetworkParams(const mozilla::dom::NetworkCommandOptions& aOther) {
|
||||
|
||||
#define COPY_SEQUENCE_FIELD(prop, type) \
|
||||
@ -112,7 +70,6 @@ public:
|
||||
COPY_OPT_STRING_FIELD(mOldIfname, EmptyString())
|
||||
COPY_OPT_STRING_FIELD(mMode, EmptyString())
|
||||
COPY_OPT_FIELD(mReport, false)
|
||||
COPY_OPT_FIELD(mIsAsync, false)
|
||||
COPY_OPT_FIELD(mEnabled, false)
|
||||
COPY_OPT_STRING_FIELD(mWifictrlinterfacename, EmptyString())
|
||||
COPY_OPT_STRING_FIELD(mInternalIfname, EmptyString())
|
||||
@ -140,6 +97,11 @@ public:
|
||||
COPY_OPT_STRING_FIELD(mCurInternalIfname, EmptyString())
|
||||
COPY_OPT_STRING_FIELD(mCurExternalIfname, EmptyString())
|
||||
COPY_OPT_FIELD(mThreshold, -1)
|
||||
COPY_OPT_FIELD(mIpaddr, 0)
|
||||
COPY_OPT_FIELD(mMask, 0)
|
||||
COPY_OPT_FIELD(mGateway_long, 0)
|
||||
COPY_OPT_FIELD(mDns1_long, 0)
|
||||
COPY_OPT_FIELD(mDns2_long, 0)
|
||||
|
||||
#undef COPY_SEQUENCE_FIELD
|
||||
#undef COPY_OPT_STRING_FIELD
|
||||
@ -158,7 +120,6 @@ public:
|
||||
nsString mOldIfname;
|
||||
nsString mMode;
|
||||
bool mReport;
|
||||
bool mIsAsync;
|
||||
bool mEnabled;
|
||||
nsString mWifictrlinterfacename;
|
||||
nsString mInternalIfname;
|
||||
@ -186,6 +147,11 @@ public:
|
||||
nsString mCurInternalIfname;
|
||||
nsString mCurExternalIfname;
|
||||
long mThreshold;
|
||||
long mIpaddr;
|
||||
long mMask;
|
||||
long mGateway_long;
|
||||
long mDns1_long;
|
||||
long mDns2_long;
|
||||
};
|
||||
|
||||
// CommandChain store the necessary information to execute command one by one.
|
||||
@ -235,6 +201,25 @@ private:
|
||||
ErrorCallback mError;
|
||||
};
|
||||
|
||||
// A helper class to easily construct a resolved
|
||||
// or a pending result for command execution.
|
||||
class CommandResult
|
||||
{
|
||||
public:
|
||||
struct Pending {};
|
||||
|
||||
public:
|
||||
CommandResult(int32_t aResultCode);
|
||||
CommandResult(const mozilla::dom::NetworkResultOptions& aResult);
|
||||
CommandResult(const Pending&);
|
||||
bool isPending() const;
|
||||
|
||||
mozilla::dom::NetworkResultOptions mResult;
|
||||
|
||||
private:
|
||||
bool mIsPending;
|
||||
};
|
||||
|
||||
class NetworkUtils MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
@ -250,24 +235,29 @@ private:
|
||||
/**
|
||||
* Commands supported by NetworkUtils.
|
||||
*/
|
||||
int32_t setDNS(NetworkParams& aOptions);
|
||||
int32_t setDefaultRouteAndDNS(NetworkParams& aOptions);
|
||||
int32_t addHostRoute(NetworkParams& aOptions);
|
||||
int32_t removeDefaultRoute(NetworkParams& aOptions);
|
||||
int32_t removeHostRoute(NetworkParams& aOptions);
|
||||
int32_t removeHostRoutes(NetworkParams& aOptions);
|
||||
int32_t removeNetworkRoute(NetworkParams& aOptions);
|
||||
int32_t addSecondaryRoute(NetworkParams& aOptions);
|
||||
int32_t removeSecondaryRoute(NetworkParams& aOptions);
|
||||
int32_t setNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
int32_t enableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
int32_t disableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
int32_t setWifiOperationMode(NetworkParams& aOptions);
|
||||
int32_t setDhcpServer(NetworkParams& aOptions);
|
||||
int32_t setWifiTethering(NetworkParams& aOptions);
|
||||
int32_t setUSBTethering(NetworkParams& aOptions);
|
||||
int32_t enableUsbRndis(NetworkParams& aOptions);
|
||||
int32_t updateUpStream(NetworkParams& aOptions);
|
||||
CommandResult configureInterface(NetworkParams& aOptions);
|
||||
CommandResult dhcpRequest(NetworkParams& aOptions);
|
||||
CommandResult enableInterface(NetworkParams& aOptions);
|
||||
CommandResult disableInterface(NetworkParams& aOptions);
|
||||
CommandResult resetConnections(NetworkParams& aOptions);
|
||||
CommandResult setDefaultRoute(NetworkParams& aOptions);
|
||||
CommandResult addHostRoute(NetworkParams& aOptions);
|
||||
CommandResult removeDefaultRoute(NetworkParams& aOptions);
|
||||
CommandResult removeHostRoute(NetworkParams& aOptions);
|
||||
CommandResult removeHostRoutes(NetworkParams& aOptions);
|
||||
CommandResult removeNetworkRoute(NetworkParams& aOptions);
|
||||
CommandResult setDNS(NetworkParams& aOptions);
|
||||
CommandResult addSecondaryRoute(NetworkParams& aOptions);
|
||||
CommandResult removeSecondaryRoute(NetworkParams& aOptions);
|
||||
CommandResult setNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult enableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult disableNetworkInterfaceAlarm(NetworkParams& aOptions);
|
||||
CommandResult setWifiOperationMode(NetworkParams& aOptions);
|
||||
CommandResult setDhcpServer(NetworkParams& aOptions);
|
||||
CommandResult setWifiTethering(NetworkParams& aOptions);
|
||||
CommandResult setUSBTethering(NetworkParams& aOptions);
|
||||
CommandResult enableUsbRndis(NetworkParams& aOptions);
|
||||
CommandResult updateUpStream(NetworkParams& aOptions);
|
||||
|
||||
/**
|
||||
* function pointer array holds all netd commands should be executed
|
||||
@ -360,7 +350,7 @@ private:
|
||||
/**
|
||||
* Utility functions.
|
||||
*/
|
||||
void checkUsbRndisState(NetworkParams& aOptions);
|
||||
CommandResult checkUsbRndisState(NetworkParams& aOptions);
|
||||
void dumpParams(NetworkParams& aOptions, const char* aType);
|
||||
|
||||
static void escapeQuote(nsCString& aString);
|
||||
|
@ -33,24 +33,9 @@ class NetworkResultDispatcher : public nsRunnable
|
||||
{
|
||||
public:
|
||||
NetworkResultDispatcher(const NetworkResultOptions& aResult)
|
||||
: mResult(aResult)
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
#define COPY_FIELD(prop) mResult.prop = aResult.prop;
|
||||
COPY_FIELD(mId)
|
||||
COPY_FIELD(mRet)
|
||||
COPY_FIELD(mBroadcast)
|
||||
COPY_FIELD(mTopic)
|
||||
COPY_FIELD(mReason)
|
||||
COPY_FIELD(mResultCode)
|
||||
COPY_FIELD(mResultReason)
|
||||
COPY_FIELD(mError)
|
||||
COPY_FIELD(mEnable)
|
||||
COPY_FIELD(mResult)
|
||||
COPY_FIELD(mSuccess)
|
||||
COPY_FIELD(mCurExternalIfname)
|
||||
COPY_FIELD(mCurInternalIfname)
|
||||
#undef COPY_FIELD
|
||||
}
|
||||
|
||||
NS_IMETHOD Run()
|
||||
|
@ -101,10 +101,65 @@ interface nsIUpdateUpStreamCallback : nsISupports
|
||||
void updateUpStreamResult(in boolean success, in DOMString externalIfname);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(eedca6c0-1310-11e4-9191-0800200c9a66)]
|
||||
interface nsISetDnsCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to report the result of setting DNS server.
|
||||
*
|
||||
* @param error
|
||||
* An error message if the operation wasn't successful,
|
||||
* or `null` if it was.
|
||||
*/
|
||||
void setDnsResult(in jsval error);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(5d0e1a60-1187-11e4-9191-0800200c9a66)]
|
||||
interface nsINativeCommandCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to report the result of a network native command.
|
||||
*
|
||||
* @param success
|
||||
* Boolean to indicate the operation is successful or not.
|
||||
*/
|
||||
void nativeCommandResult(in boolean success);
|
||||
};
|
||||
|
||||
[scriptable, function, uuid(694abb80-1187-11e4-9191-0800200c9a66)]
|
||||
interface nsIDhcpRequestCallback : nsISupports
|
||||
{
|
||||
/**
|
||||
* Callback function used to report the result of DHCP client request.
|
||||
*
|
||||
* @param success
|
||||
* Boolean to indicate the operation is successful or not.
|
||||
*
|
||||
* @param dhcpInfo
|
||||
* An object to represent the successful DHCP request:
|
||||
*
|
||||
* - gateway_str: string
|
||||
* - dns1_str: string
|
||||
* - dns2_str: string
|
||||
* - mask_str: string
|
||||
* - server_str: string
|
||||
* - vendor_str: string
|
||||
* - lease: long
|
||||
* - mask: long
|
||||
* - ipaddr: long
|
||||
* - gateway: long
|
||||
* - dns1: long
|
||||
* - dns2: long
|
||||
* - server: long
|
||||
*/
|
||||
void dhcpRequestResult(in boolean success, in jsval dhcpInfo);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Provide network services.
|
||||
*/
|
||||
[scriptable, uuid(ddb38428-0cf2-4c6a-a3c9-5e2f00fc54db)]
|
||||
[scriptable, uuid(9f1d78e0-1314-11e4-9191-0800200c9a66)]
|
||||
interface nsINetworkService : nsISupports
|
||||
{
|
||||
/**
|
||||
@ -229,19 +284,26 @@ interface nsINetworkService : nsISupports
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface which contains the DNS we want to set.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of setting DNS server.
|
||||
*/
|
||||
void setDNS(in nsINetworkInterface networkInterface);
|
||||
void setDNS(in nsINetworkInterface networkInterface,
|
||||
in nsISetDnsCallback callback);
|
||||
|
||||
/**
|
||||
* Set default route and DNS.
|
||||
* Set default route.
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface we want to set to the default route and dns.
|
||||
* The network interface we want to set to the default route.
|
||||
* @param oldInterface
|
||||
* The previous network interface.
|
||||
* @param callback
|
||||
* Callback to notify the result of setting default route.
|
||||
*/
|
||||
void setDefaultRouteAndDNS(in nsINetworkInterface networkInterface,
|
||||
in nsINetworkInterface oldInterface);
|
||||
void setDefaultRoute(in nsINetworkInterface networkInterface,
|
||||
in nsINetworkInterface oldInterface,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Remove default route.
|
||||
@ -341,4 +403,71 @@ interface nsINetworkService : nsISupports
|
||||
void updateUpStream(in jsval previous,
|
||||
in jsval current,
|
||||
in nsIUpdateUpStreamCallback callback);
|
||||
|
||||
/**
|
||||
* Configure a network interface.
|
||||
*
|
||||
* @param config
|
||||
* An object containing the detail that we want to configure the interface:
|
||||
*
|
||||
* - ifname: string
|
||||
* - ipaddr: long
|
||||
* - mask: long
|
||||
* - gateway: long
|
||||
* - dns1: long
|
||||
* - dns2: long
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of configurating network interface.
|
||||
*/
|
||||
void configureInterface(in jsval config,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Issue a DHCP client request.
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface which we wnat to do the DHCP request on.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of the DHCP request.
|
||||
*/
|
||||
void dhcpRequest(in DOMString interfaceName,
|
||||
in nsIDhcpRequestCallback callback);
|
||||
|
||||
/**
|
||||
* Enable a network interface.
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to enable.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of disabling network interface.
|
||||
*/
|
||||
void enableInterface(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Disable a network interface.
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to disable.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of disabling network interface.
|
||||
*/
|
||||
void disableInterface(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
|
||||
/**
|
||||
* Reset all connections
|
||||
*
|
||||
* @param networkInterface
|
||||
* The network interface name which we want to reset.
|
||||
*
|
||||
* @param callback
|
||||
* Callback to notify the result of resetting connections.
|
||||
*/
|
||||
void resetConnections(in DOMString interfaceName,
|
||||
in nsINativeCommandCallback callback);
|
||||
};
|
||||
|
@ -6,6 +6,9 @@ interface FMRadio : EventTarget {
|
||||
/* Indicates if the FM radio is enabled. */
|
||||
readonly attribute boolean enabled;
|
||||
|
||||
/* Indicates if RDS reception is enabled */
|
||||
readonly attribute boolean rdsEnabled;
|
||||
|
||||
/* Indicates if the antenna is plugged and available. */
|
||||
readonly attribute boolean antennaAvailable;
|
||||
|
||||
@ -31,12 +34,58 @@ interface FMRadio : EventTarget {
|
||||
*/
|
||||
readonly attribute double channelWidth;
|
||||
|
||||
/**
|
||||
* This is a mask consisting of bits corresponding to
|
||||
* (1 << groupcode) that can be specified to receive
|
||||
* raw RDS groups of specific group types. Note that
|
||||
* groupcode corresponds to the upper 5 bits in block B.
|
||||
*/
|
||||
attribute unsigned long rdsGroupMask;
|
||||
|
||||
/**
|
||||
* The Program Identification (PI) code.
|
||||
* Available if RDS is enabled on both the station and on this device.
|
||||
* The value is null otherwise.
|
||||
*/
|
||||
readonly attribute unsigned short? pi;
|
||||
|
||||
/**
|
||||
* The Program Type (PTY) code.
|
||||
* Available if RDS is enabled on both the station and on this device.
|
||||
* The value is null otherwise.
|
||||
*/
|
||||
readonly attribute octet? pty;
|
||||
|
||||
/**
|
||||
* The Program Service (PS) name.
|
||||
* Available if RDS is enabled on the station and on this device
|
||||
*/
|
||||
readonly attribute DOMString? ps;
|
||||
|
||||
/**
|
||||
* The radiotext, as provided by group 2A/2B.
|
||||
* Available if RDS is enabled on the station and on this device
|
||||
*/
|
||||
readonly attribute DOMString? rt;
|
||||
|
||||
/**
|
||||
* The last RDS group received.
|
||||
* Available if RDS is enabled on the station and on this device
|
||||
*/
|
||||
readonly attribute Uint16Array? rdsgroup;
|
||||
|
||||
/* Fired when the FM radio is enabled. */
|
||||
attribute EventHandler onenabled;
|
||||
|
||||
/* Fired when the FM radio is disabled. */
|
||||
attribute EventHandler ondisabled;
|
||||
|
||||
/* Fired when the RDS is enabled. */
|
||||
attribute EventHandler onrdsenabled;
|
||||
|
||||
/* Fired when the RDS is disabled. */
|
||||
attribute EventHandler onrdsdisabled;
|
||||
|
||||
/**
|
||||
* Fired when the antenna becomes available or unavailable, i.e., fired when
|
||||
* the antennaAvailable attribute changes.
|
||||
@ -46,6 +95,21 @@ interface FMRadio : EventTarget {
|
||||
/* Fired when the FM radio's frequency is changed. */
|
||||
attribute EventHandler onfrequencychange;
|
||||
|
||||
/* Fired when the PI code changes */
|
||||
attribute EventHandler onpichange;
|
||||
|
||||
/* Fired when the PTY changes */
|
||||
attribute EventHandler onptychange;
|
||||
|
||||
/* Fired when the PS name changes */
|
||||
attribute EventHandler onpschange;
|
||||
|
||||
/* Fired when the radiotext changes */
|
||||
attribute EventHandler onrtchange;
|
||||
|
||||
/* Fired when we get a new RDS group */
|
||||
attribute EventHandler onnewrdsgroup;
|
||||
|
||||
/**
|
||||
* Power the FM radio off. The disabled event will be fired if this request
|
||||
* completes successfully.
|
||||
@ -94,5 +158,19 @@ interface FMRadio : EventTarget {
|
||||
* error will be fired.
|
||||
*/
|
||||
DOMRequest cancelSeek();
|
||||
|
||||
/**
|
||||
* Enable RDS reception.
|
||||
*
|
||||
* If the radio is off, RDS will be enabled when the radio is turned on.
|
||||
*/
|
||||
DOMRequest enableRDS();
|
||||
|
||||
/**
|
||||
* Disable RDS reception.
|
||||
*
|
||||
* If the radio is off, RDS will not be enabled when the radio is turned on.
|
||||
*/
|
||||
DOMRequest disableRDS();
|
||||
};
|
||||
|
||||
|
@ -22,7 +22,6 @@ dictionary NetworkCommandOptions
|
||||
sequence<DOMString> gateways; // for "setDefaultRouteAndDNS", "removeDefaultRoute".
|
||||
DOMString mode; // for "setWifiOperationMode".
|
||||
boolean report; // for "setWifiOperationMode".
|
||||
boolean isAsync; // for "setWifiOperationMode".
|
||||
boolean enabled; // for "setDhcpServer".
|
||||
DOMString wifictrlinterfacename; // for "setWifiTethering".
|
||||
DOMString internalIfname; // for "setWifiTethering".
|
||||
@ -50,6 +49,12 @@ dictionary NetworkCommandOptions
|
||||
DOMString preExternalIfname; // for "updateUpStream".
|
||||
DOMString curInternalIfname; // for "updateUpStream".
|
||||
DOMString curExternalIfname; // for "updateUpStream".
|
||||
|
||||
long ipaddr; // for "ifc_configure".
|
||||
long mask; // for "ifc_configure".
|
||||
long gateway_long; // for "ifc_configure".
|
||||
long dns1_long; // for "ifc_configure".
|
||||
long dns2_long; // for "ifc_configure".
|
||||
};
|
||||
|
||||
/**
|
||||
@ -73,4 +78,22 @@ dictionary NetworkResultOptions
|
||||
boolean success = false; // for "setDhcpServer".
|
||||
DOMString curExternalIfname = ""; // for "updateUpStream".
|
||||
DOMString curInternalIfname = ""; // for "updateUpStream".
|
||||
|
||||
DOMString reply = ""; // for "command".
|
||||
DOMString route = ""; // for "ifc_get_default_route".
|
||||
DOMString ipaddr_str = ""; // The following are for the result of
|
||||
// dhcp_do_request.
|
||||
DOMString gateway_str = "";
|
||||
DOMString dns1_str = "";
|
||||
DOMString dns2_str = "";
|
||||
DOMString mask_str = "";
|
||||
DOMString server_str = "";
|
||||
DOMString vendor_str = "";
|
||||
long lease = 0;
|
||||
long mask = 0;
|
||||
long ipaddr = 0;
|
||||
long gateway = 0;
|
||||
long dns1 = 0;
|
||||
long dns2 = 0;
|
||||
long server = 0;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user