diff --git a/accessible/atk/AccessibleWrap.cpp b/accessible/atk/AccessibleWrap.cpp index cca059e9b34c..5f98faac2cc5 100644 --- a/accessible/atk/AccessibleWrap.cpp +++ b/accessible/atk/AccessibleWrap.cpp @@ -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(aData); + MAI_ATK_OBJECT(aAtkObj)->accWrap = + static_cast(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(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(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 - (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(obj) | IS_PROXY); -} - -void -a11y::ProxyDestroyed(ProxyAccessible* aProxy) -{ - auto obj = reinterpret_cast(aProxy->GetWrapper() & ~IS_PROXY); - obj->accWrap = 0; - g_object_unref(obj); - aProxy->SetWrapper(0); -} - nsresult AccessibleWrap::HandleAccEvent(AccEvent* aEvent) { diff --git a/accessible/atk/AccessibleWrap.h b/accessible/atk/AccessibleWrap.h index 95d03c5fc16c..5518a80bd5d8 100644 --- a/accessible/atk/AccessibleWrap.h +++ b/accessible/atk/AccessibleWrap.h @@ -97,7 +97,7 @@ private: static EAvailableAtkSignals gAvailableAtkSignals; - uint16_t CreateMaiInterfaces(); + uint16_t CreateMaiInterfaces(void); }; } // namespace a11y diff --git a/accessible/atk/moz.build b/accessible/atk/moz.build index 96fc8cee15d7..37ec9e8fded5 100644 --- a/accessible/atk/moz.build +++ b/accessible/atk/moz.build @@ -35,7 +35,6 @@ LOCAL_INCLUDES += [ '/accessible/base', '/accessible/generic', '/accessible/html', - '/accessible/ipc', '/accessible/xpcom', '/accessible/xul', '/other-licenses/atk-1.0', diff --git a/accessible/atk/nsMai.h b/accessible/atk/nsMai.h index 6338a3492bae..5de552ac67a5 100644 --- a/accessible/atk/nsMai.h +++ b/accessible/atk/nsMai.h @@ -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; diff --git a/accessible/base/AccEvent.h b/accessible/base/AccEvent.h index ee28efa511b6..f97b1fc4e01b 100644 --- a/accessible/base/AccEvent.h +++ b/accessible/base/AccEvent.h @@ -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 mNode; nsRefPtr mParent; diff --git a/accessible/base/DocManager.cpp b/accessible/base/DocManager.cpp index 985ba64f5853..eed51d5ca01a 100644 --- a/accessible/base/DocManager.cpp +++ b/accessible/base/DocManager.cpp @@ -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); } diff --git a/accessible/base/DocManager.h b/accessible/base/DocManager.h index 69193820eb67..13e80a602372 100644 --- a/accessible/base/DocManager.h +++ b/accessible/base/DocManager.h @@ -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 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 mRemoteDocuments; }; /** diff --git a/accessible/base/EventQueue.cpp b/accessible/base/EventQueue.cpp index 65102917efef..6338785e49c9 100644 --- a/accessible/base/EventQueue.cpp +++ b/accessible/base/EventQueue.cpp @@ -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(event->GetAccessible())); - else - ipcDoc->SendEvent(event->GetEventType()); - } } } diff --git a/accessible/base/NotificationController.cpp b/accessible/base/NotificationController.cpp index b45e23a2ab69..82a591ab72c1 100644 --- a/accessible/base/NotificationController.cpp +++ b/accessible/base/NotificationController.cpp @@ -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(outerDocAcc->UniqueID()); - contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, - id); - } - + if (mDocument->AppendChildDocument(childDoc)) continue; - } outerDocAcc->RemoveChild(childDoc); } diff --git a/accessible/base/Platform.h b/accessible/base/Platform.h index 41954be440d8..fa56963fdbed 100644 --- a/accessible/base/Platform.h +++ b/accessible/base/Platform.h @@ -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 diff --git a/accessible/base/Role.h b/accessible/base/Role.h index 0d5746f3dc13..2cd378d64482 100644 --- a/accessible/base/Role.h +++ b/accessible/base/Role.h @@ -783,9 +783,7 @@ enum Role { /** * Represent a keyboard or keypad key (ARIA role "key"). */ - KEY = 129, - - LAST_ROLE = KEY + KEY = 129 }; } // namespace role diff --git a/accessible/base/moz.build b/accessible/base/moz.build index 0dad363cc445..e31cf3e5c4c3 100644 --- a/accessible/base/moz.build +++ b/accessible/base/moz.build @@ -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') diff --git a/accessible/generic/DocAccessible.cpp b/accessible/generic/DocAccessible.cpp index 369700260b2a..8e1a02cfe52f 100644 --- a/accessible/generic/DocAccessible.cpp +++ b/accessible/generic/DocAccessible.cpp @@ -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 reorderEvent = new AccReorderEvent(Parent()); ParentDocument()->FireDelayedEvent(reorderEvent); } - - uint32_t childCount = ChildCount(); - for (uint32_t i = 0; i < childCount; i++) { - Accessible* child = GetChildAt(i); - nsRefPtr event = new AccShowEvent(child, child->GetContent()); - FireDelayedEvent(event); - } } void diff --git a/accessible/generic/DocAccessible.h b/accessible/generic/DocAccessible.h index 42b655e69a55..4d7858c1cfa0 100644 --- a/accessible/generic/DocAccessible.h +++ b/accessible/generic/DocAccessible.h @@ -33,7 +33,6 @@ namespace a11y { class DocManager; class NotificationController; -class DocAccessibleChild; class RelatedAccIterator; template 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* diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build index 3b393c650fb6..236715bda01d 100644 --- a/accessible/generic/moz.build +++ b/accessible/generic/moz.build @@ -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') diff --git a/accessible/ipc/DocAccessibleChild.cpp b/accessible/ipc/DocAccessibleChild.cpp deleted file mode 100644 index 5070b1fe9739..000000000000 --- a/accessible/ipc/DocAccessibleChild.cpp +++ /dev/null @@ -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& aTree) -{ - uint64_t id = reinterpret_cast(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(parent->UniqueID()); - uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent(); - nsTArray shownTree; - ShowEventData data(parentID, idxInParent, shownTree); - SerializeTree(aShowEvent->GetAccessible(), data.NewTree()); - SendShowEvent(data); -} -} -} diff --git a/accessible/ipc/DocAccessibleChild.h b/accessible/ipc/DocAccessibleChild.h deleted file mode 100644 index 50fdcc7cfd1f..000000000000 --- a/accessible/ipc/DocAccessibleChild.h +++ /dev/null @@ -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 diff --git a/accessible/ipc/DocAccessibleParent.cpp b/accessible/ipc/DocAccessibleParent.cpp deleted file mode 100644 index 0a60f6ddbee2..000000000000 --- a/accessible/ipc/DocAccessibleParent.cpp +++ /dev/null @@ -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& 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(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; -} -} -} diff --git a/accessible/ipc/DocAccessibleParent.h b/accessible/ipc/DocAccessibleParent.h deleted file mode 100644 index b92a5e0ac127..000000000000 --- a/accessible/ipc/DocAccessibleParent.h +++ /dev/null @@ -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& aNewTree, uint32_t aIdx, - uint32_t aIdxInParent); - - nsTArray 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 mAccessibles; -}; - -} -} - -#endif diff --git a/accessible/ipc/PDocAccessible.ipdl b/accessible/ipc/PDocAccessible.ipdl deleted file mode 100644 index b3328b31b680..000000000000 --- a/accessible/ipc/PDocAccessible.ipdl +++ /dev/null @@ -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); -}; - -} -} diff --git a/accessible/ipc/ProxyAccessible.cpp b/accessible/ipc/ProxyAccessible.cpp deleted file mode 100644 index 5af1aa41d8fb..000000000000 --- a/accessible/ipc/ProxyAccessible.cpp +++ /dev/null @@ -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; - } -} -} -} diff --git a/accessible/ipc/ProxyAccessible.h b/accessible/ipc/ProxyAccessible.h deleted file mode 100644 index 39fd379d1e24..000000000000 --- a/accessible/ipc/ProxyAccessible.h +++ /dev/null @@ -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 mChildren; - DocAccessibleParent* mDoc; - uintptr_t mWrapper; - uint64_t mID; - role mRole : 31; - bool mOuterDoc : 1; - nsString mName; -}; - -} -} - -#endif diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build deleted file mode 100644 index 64a45e9980eb..000000000000 --- a/accessible/ipc/moz.build +++ /dev/null @@ -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') diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm index 4d82f2c5e836..98bbac02a9b9 100644 --- a/accessible/mac/Platform.mm +++ b/accessible/mac/Platform.mm @@ -33,15 +33,6 @@ PlatformShutdown() { } -void -ProxyCreated(ProxyAccessible*) -{ -} - -void -ProxyDestroyed(ProxyAccessible*) -{ -} } } diff --git a/accessible/moz.build b/accessible/moz.build index 993d2202937f..44f345780018 100644 --- a/accessible/moz.build +++ b/accessible/moz.build @@ -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'] diff --git a/accessible/other/Platform.cpp b/accessible/other/Platform.cpp index 768f687c738c..87c9fc6071f8 100644 --- a/accessible/other/Platform.cpp +++ b/accessible/other/Platform.cpp @@ -18,13 +18,3 @@ void a11y::PlatformShutdown() { } - -void -a11y::ProxyCreated(ProxyAccessible*) -{ -} - -void -a11y::ProxyDestroyed(ProxyAccessible*) -{ -} diff --git a/accessible/windows/msaa/Platform.cpp b/accessible/windows/msaa/Platform.cpp index 0ed1f5ffc073..263eb1a4d130 100644 --- a/accessible/windows/msaa/Platform.cpp +++ b/accessible/windows/msaa/Platform.cpp @@ -34,12 +34,3 @@ a11y::PlatformShutdown() nsWinUtils::ShutdownWindowEmulation(); } -void -a11y::ProxyCreated(ProxyAccessible*) -{ -} - -void -a11y::ProxyDestroyed(ProxyAccessible*) -{ -} diff --git a/b2g/config/dolphin/config.json b/b2g/config/dolphin/config.json index 6e979dd4680b..83a3cd85464f 100644 --- a/b2g/config/dolphin/config.json +++ b/b2g/config/dolphin/config.json @@ -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", diff --git a/b2g/config/dolphin/sources.xml b/b2g/config/dolphin/sources.xml index a21af1cec99c..3835074fd978 100644 --- a/b2g/config/dolphin/sources.xml +++ b/b2g/config/dolphin/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index d70d7c6567ec..54f1bceda98f 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 0f99fa55915a..d0e5ded35b47 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index c693d206d18a..24084e6fa36c 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index d70d7c6567ec..54f1bceda98f 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,7 +19,7 @@ - + diff --git a/b2g/config/flame-kk/config.json b/b2g/config/flame-kk/config.json index 08829b317305..2c292090ab42 100644 --- a/b2g/config/flame-kk/config.json +++ b/b2g/config/flame-kk/config.json @@ -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", diff --git a/b2g/config/flame-kk/sources.xml b/b2g/config/flame-kk/sources.xml index f5f8958fdc5d..80491bd53abd 100644 --- a/b2g/config/flame-kk/sources.xml +++ b/b2g/config/flame-kk/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/flame/config.json b/b2g/config/flame/config.json index e387a749507b..fcbf5f53580e 100644 --- a/b2g/config/flame/config.json +++ b/b2g/config/flame/config.json @@ -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", diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 69938d06f06a..fc7dbc6d1c46 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 6a490a92a774..411f796ca22b 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "7f097f40e32ecba580890ce1d3df2a493641bdec", + "revision": "aa3ab2d389dce3ba351a897b4ae56f1fe9e1780d", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/config.json b/b2g/config/hamachi/config.json index eb26f67fdf65..b2f8f083a267 100644 --- a/b2g/config/hamachi/config.json +++ b/b2g/config/hamachi/config.json @@ -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", diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 3e302af1a5a4..ad51dbb97c23 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/helix/config.json b/b2g/config/helix/config.json index 9342c51c492e..c4a07090a676 100644 --- a/b2g/config/helix/config.json +++ b/b2g/config/helix/config.json @@ -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", diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index e924eac37818..c3cb50a93923 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/config.json b/b2g/config/nexus-4/config.json index 0f4c5851587c..d1a2ba0b9e30 100644 --- a/b2g/config/nexus-4/config.json +++ b/b2g/config/nexus-4/config.json @@ -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", diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index d27a3c72cb82..eb29396c195a 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/b2g/config/wasabi/config.json b/b2g/config/wasabi/config.json index 1336c7944349..8e51bf8008c6 100644 --- a/b2g/config/wasabi/config.json +++ b/b2g/config/wasabi/config.json @@ -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", diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 8a78e21f5d68..0dbc3a0be82e 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,7 +17,7 @@ - + diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index a395a0a60114..086ddac446c4 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -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); diff --git a/browser/base/content/newtab/updater.js b/browser/base/content/newtab/updater.js index 8c6f8d679f46..24fa82025e03 100644 --- a/browser/base/content/newtab/updater.js +++ b/browser/base/content/newtab/updater.js @@ -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); diff --git a/browser/components/loop/content/conversation.html b/browser/components/loop/content/conversation.html index d673be68c52f..95916cba8f2f 100644 --- a/browser/components/loop/content/conversation.html +++ b/browser/components/loop/content/conversation.html @@ -30,8 +30,14 @@ + + + + + + diff --git a/browser/components/loop/content/js/client.js b/browser/components/loop/content/js/client.js index c9d93660e1db..94f856e7211b 100644 --- a/browser/components/loop/content/js/client.js +++ b/browser/components/loop/content/js/client.js @@ -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. * diff --git a/browser/components/loop/content/js/conversation.js b/browser/components/loop/content/js/conversation.js index d94325c51c78..2180407b5399 100644 --- a/browser/components/loop/content/js/conversation.js +++ b/browser/components/loop/content/js/conversation.js @@ -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 diff --git a/browser/components/loop/content/js/conversation.jsx b/browser/components/loop/content/js/conversation.jsx index 9adbae363102..e0c22a82b53f 100644 --- a/browser/components/loop/content/js/conversation.jsx +++ b/browser/components/loop/content/js/conversation.jsx @@ -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 ( -
+

{mozL10n.get("incoming_call_title2")}

-
+
-
+
- @@ -146,11 +144,11 @@ loop.conversation = (function(mozL10n) {
-
+
-
+
@@ -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 (); + } + + return (); + } + }); + /** * 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(, document.querySelector('#main')); + + dispatcher.dispatch(new loop.shared.actions.GatherCallData({ + callId: callId, + calleeId: outgoingEmail + })); } return { + ConversationControllerView: ConversationControllerView, IncomingConversationView: IncomingConversationView, IncomingCallView: IncomingCallView, init: init diff --git a/browser/components/loop/content/js/conversationViews.js b/browser/components/loop/content/js/conversationViews.js new file mode 100644 index 000000000000..de65a250fb74 --- /dev/null +++ b/browser/components/loop/content/js/conversationViews.js @@ -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); diff --git a/browser/components/loop/content/js/conversationViews.jsx b/browser/components/loop/content/js/conversationViews.jsx new file mode 100644 index 000000000000..5d774e1ac576 --- /dev/null +++ b/browser/components/loop/content/js/conversationViews.jsx @@ -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 ( +
+

{this.props.calleeId}

+
{this.props.children}
+
+ ); + } + }); + + /** + * 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 ( + + +

{pendingStateString}

+ +
+
+ +
+
+ +
+ ); + } + }); + + /** + * Call failed view. Displayed when a call fails. + */ + var CallFailedView = React.createClass({ + render: function() { + return ( +
+

{mozL10n.get("generic_failure_title")}

+
+ ); + } + }); + + /** + * 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 (); + } + + return () + } + }); + + return { + PendingConversationView: PendingConversationView, + ConversationDetailView: ConversationDetailView, + CallFailedView: CallFailedView, + OutgoingConversationView: OutgoingConversationView + }; + +})(document.mozL10n || navigator.mozL10n); diff --git a/browser/components/loop/content/shared/css/conversation.css b/browser/components/loop/content/shared/css/conversation.css index 58710ea45472..ecdb34bd115b 100644 --- a/browser/components/loop/content/shared/css/conversation.css +++ b/browser/components/loop/content/shared/css/conversation.css @@ -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; } diff --git a/browser/components/loop/content/shared/js/actions.js b/browser/components/loop/content/shared/js/actions.js new file mode 100644 index 000000000000..e2693879c50c --- /dev/null +++ b/browser/components/loop/content/shared/js/actions.js @@ -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 + }) + }; +})(); diff --git a/browser/components/loop/content/shared/js/conversationStore.js b/browser/components/loop/content/shared/js/conversationStore.js new file mode 100644 index 000000000000..9fdc98c694ab --- /dev/null +++ b/browser/components/loop/content/shared/js/conversationStore.js @@ -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 + }; +})(); diff --git a/browser/components/loop/content/shared/js/dispatcher.js b/browser/components/loop/content/shared/js/dispatcher.js new file mode 100644 index 000000000000..408f0bf6de57 --- /dev/null +++ b/browser/components/loop/content/shared/js/dispatcher.js @@ -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; +})(); diff --git a/browser/components/loop/content/shared/js/utils.js b/browser/components/loop/content/shared/js/utils.js index 50e9ddfc4c33..09f004054b63 100644 --- a/browser/components/loop/content/shared/js/utils.js +++ b/browser/components/loop/content/shared/js/utils.js @@ -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 diff --git a/browser/components/loop/content/shared/js/validate.js b/browser/components/loop/content/shared/js/validate.js new file mode 100644 index 000000000000..596089f4a537 --- /dev/null +++ b/browser/components/loop/content/shared/js/validate.js @@ -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 + }; +})(); diff --git a/browser/components/loop/content/shared/js/websocket.js b/browser/components/loop/content/shared/js/websocket.js index 82d8b03b05ed..75859a435c12 100644 --- a/browser/components/loop/content/shared/js/websocket.js +++ b/browser/components/loop/content/shared/js/websocket.js @@ -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); diff --git a/browser/components/loop/jar.mn b/browser/components/loop/jar.mn index 3efd65675742..2a2381742567 100644 --- a/browser/components/loop/jar.mn +++ b/browser/components/loop/jar.mn @@ -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 diff --git a/browser/components/loop/test/desktop-local/client_test.js b/browser/components/loop/test/desktop-local/client_test.js index 85d156ffe043..56ed988878a7 100644 --- a/browser/components/loop/test/desktop-local/client_test.js +++ b/browser/components/loop/test/desktop-local/client_test.js @@ -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); + })); + }); + }); }); }); diff --git a/browser/components/loop/test/desktop-local/conversationViews_test.js b/browser/components/loop/test/desktop-local/conversationViews_test.js new file mode 100644 index 000000000000..83f8e70d9c94 --- /dev/null +++ b/browser/components/loop/test/desktop-local/conversationViews_test.js @@ -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); + }); + }); +}); diff --git a/browser/components/loop/test/desktop-local/conversation_test.js b/browser/components/loop/test/desktop-local/conversation_test.js index 218d3b1848ef..a0bcd39c32cf 100644 --- a/browser/components/loop/test/desktop-local/conversation_test.js +++ b/browser/components/loop/test/desktop-local/conversation_test.js @@ -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"); diff --git a/browser/components/loop/test/desktop-local/index.html b/browser/components/loop/test/desktop-local/index.html index 477a47a701f4..64e60b36f41b 100644 --- a/browser/components/loop/test/desktop-local/index.html +++ b/browser/components/loop/test/desktop-local/index.html @@ -34,11 +34,16 @@ + + + + + @@ -47,6 +52,7 @@ + + + + + @@ -47,6 +51,9 @@ + + + + + + diff --git a/browser/components/loop/ui/ui-showcase.css b/browser/components/loop/ui/ui-showcase.css index 7a6ed557cd82..fc77da4da2ef 100644 --- a/browser/components/loop/ui/ui-showcase.css +++ b/browser/components/loop/ui/ui-showcase.css @@ -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; } diff --git a/browser/components/loop/ui/ui-showcase.js b/browser/components/loop/ui/ui-showcase.js index 7b8bdc9d020b..99d967ae9384 100644 --- a/browser/components/loop/ui/ui-showcase.js +++ b/browser/components/loop/ui/ui-showcase.js @@ -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"; }); })(); diff --git a/browser/components/loop/ui/ui-showcase.jsx b/browser/components/loop/ui/ui-showcase.jsx index 04f109907a8c..306e0960961e 100644 --- a/browser/components/loop/ui/ui-showcase.jsx +++ b/browser/components/loop/ui/ui-showcase.jsx @@ -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 @@
- +
- + +
+
+
+ +
+ +
+
@@ -446,6 +462,9 @@ React.renderComponent(, body); _renderComponentsInIframes(); + + // Put the title back, in case views changed it. + document.title = "Loop UI Components Showcase"; }); })(); diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index 91be00fa25fd..3ee09b4b8c77 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -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") diff --git a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp index 6bf6edba9ff7..15380444666a 100644 --- a/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp +++ b/dom/bluetooth2/ipc/BluetoothServiceChildProcess.cpp @@ -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" diff --git a/dom/fmradio/FMRadio.cpp b/dom/fmradio/FMRadio.cpp index ada434208ba9..54b62a9ca69c 100644 --- a/dom/fmradio/FMRadio.cpp +++ b/dom/fmradio/FMRadio.cpp @@ -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 +FMRadio::GetPi() const +{ + return IFMRadioService::Singleton()->GetPi(); +} + +Nullable +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 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 FMRadio::Enable(double aFrequency) { @@ -350,6 +443,32 @@ FMRadio::CancelSeek() return r.forget(); } +already_AddRefed +FMRadio::EnableRDS() +{ + nsCOMPtr win = GetOwner(); + if (!win) { + return nullptr; + } + + nsRefPtr r = new FMRadioRequest(win, this); + IFMRadioService::Singleton()->EnableRDS(r); + return r.forget(); +} + +already_AddRefed +FMRadio::DisableRDS() +{ + nsCOMPtr win = GetOwner(); + if (!win) { + return nullptr; + } + + nsRefPtr r = new FMRadioRequest(win, this); + FMRadioService::Singleton()->DisableRDS(r); + return r.forget(); +} + NS_IMETHODIMP FMRadio::HandleEvent(nsIDOMEvent* aEvent) { diff --git a/dom/fmradio/FMRadio.h b/dom/fmradio/FMRadio.h index f6daf0c7cc80..140f05314400 100644 --- a/dom/fmradio/FMRadio.h +++ b/dom/fmradio/FMRadio.h @@ -55,6 +55,8 @@ public: static bool Enabled(); + bool RdsEnabled(); + bool AntennaAvailable() const; Nullable GetFrequency() const; @@ -65,6 +67,20 @@ public: double ChannelWidth() const; + uint32_t RdsGroupMask() const; + + void SetRdsGroupMask(uint32_t aRdsGroupMask); + + Nullable GetPi() const; + + Nullable GetPty() const; + + void GetPs(DOMString& aPsname) const; + + void GetRt(DOMString& aRadiotext) const; + + void GetRdsgroup(JSContext* cx, JS::MutableHandle retval); + already_AddRefed Enable(double aFrequency); already_AddRefed Disable(); @@ -77,10 +93,21 @@ public: already_AddRefed CancelSeek(); + already_AddRefed EnableRDS(); + + already_AddRefed 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; diff --git a/dom/fmradio/FMRadioCommon.h b/dom/fmradio/FMRadioCommon.h index 42fc690adb7d..b8d8325f7418 100644 --- a/dom/fmradio/FMRadioCommon.h +++ b/dom/fmradio/FMRadioCommon.h @@ -30,7 +30,13 @@ BEGIN_FMRADIO_NAMESPACE enum FMRadioEventType { FrequencyChanged, - EnabledChanged + EnabledChanged, + RDSEnabledChanged, + PIChanged, + PSChanged, + PTYChanged, + RadiotextChanged, + NewRDSGroup }; typedef mozilla::Observer FMRadioEventObserver; diff --git a/dom/fmradio/FMRadioService.cpp b/dom/fmradio/FMRadioService.cpp index e80a278ce951..3a198624df82 100644 --- a/dom/fmradio/FMRadioService.cpp +++ b/dom/fmradio/FMRadioService.cpp @@ -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 +FMRadioService::GetPi() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + if (!mPISet) { + return Nullable(); + } + return Nullable(mPI); +} + +Nullable +FMRadioService::GetPty() const +{ + MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!"); + if (!mPTYSet) { + return Nullable(); + } + return Nullable(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; } } diff --git a/dom/fmradio/FMRadioService.h b/dom/fmradio/FMRadioService.h index eb9666545d2e..836ef2601003 100644 --- a/dom/fmradio/FMRadioService.h +++ b/dom/fmradio/FMRadioService.h @@ -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 GetPi() const = 0; + virtual Nullable 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 GetPi() const MOZ_OVERRIDE; + virtual Nullable 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 sFMRadioService; + + uint32_t mRDSGroupMask; + + uint16_t mLastPI; + uint16_t mLastPTY; + Atomic mPI; + Atomic mPTY; + Atomic mPISet; + Atomic 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 diff --git a/dom/fmradio/ipc/FMRadioChild.cpp b/dom/fmradio/ipc/FMRadioChild.cpp index 0433bfce199a..f74fda4e1832 100644 --- a/dom/fmradio/ipc/FMRadioChild.cpp +++ b/dom/fmradio/ipc/FMRadioChild.cpp @@ -16,7 +16,13 @@ StaticAutoPtr 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 +FMRadioChild::GetPi() const +{ + return mPI; +} + +Nullable +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__() { diff --git a/dom/fmradio/ipc/FMRadioChild.h b/dom/fmradio/ipc/FMRadioChild.h index ff9f6162f04d..1f39f1d8c24c 100644 --- a/dom/fmradio/ipc/FMRadioChild.h +++ b/dom/fmradio/ipc/FMRadioChild.h @@ -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 GetPi() const MOZ_OVERRIDE; + virtual Nullable 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 mPI; + Nullable mPTY; + nsAutoString mPSName; + nsAutoString mRadiotext; + uint64_t mRDSGroup; + uint32_t mRDSGroupMask; FMRadioEventObserverList mObserverList; diff --git a/dom/fmradio/ipc/FMRadioParent.cpp b/dom/fmradio/ipc/FMRadioParent.cpp index b8af6680f030..50db2cd83b4a 100644 --- a/dom/fmradio/ipc/FMRadioParent.cpp +++ b/dom/fmradio/ipc/FMRadioParent.cpp @@ -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 pi = + IFMRadioService::Singleton()->GetPi(); + unused << SendNotifyPIChanged(!pi.IsNull(), + pi.IsNull() ? 0 : pi.Value()); + break; + } + case PTYChanged: { + Nullable 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 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 diff --git a/dom/fmradio/ipc/FMRadioParent.h b/dom/fmradio/ipc/FMRadioParent.h index 96cc5a484ee6..024b240d75ef 100644 --- a/dom/fmradio/ipc/FMRadioParent.h +++ b/dom/fmradio/ipc/FMRadioParent.h @@ -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 diff --git a/dom/fmradio/ipc/PFMRadio.ipdl b/dom/fmradio/ipc/PFMRadio.ipdl index be863e1a5e15..f4929cfe70b9 100644 --- a/dom/fmradio/ipc/PFMRadio.ipdl +++ b/dom/fmradio/ipc/PFMRadio.ipdl @@ -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 diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 6810cfe101ec..4480f01df41e 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -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(aChild); - return true; -} - PMemoryReportRequestChild* ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration, const bool &aAnonymize, diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 97738af734c5..ef8187543c75 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -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; diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 6bb6c7d80707..a11ea2c5a3f7 100755 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -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(aParent); - return true; -} - -bool -ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) -{ - auto doc = static_cast(aDoc); - if (aParentDoc) { - MOZ_ASSERT(aParentID); - auto parentDoc = static_cast(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) diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 1ab6467cd976..5d820600c24c 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -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. diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index deb3e87721dd..6f0d4118871e 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -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 diff --git a/dom/system/gonk/NetworkManager.js b/dom/system/gonk/NetworkManager.js index 80171c5410d9..fea3d8e9f6f8 100644 --- a/dom/system/gonk/NetworkManager.js +++ b/dom/system/gonk/NetworkManager.js @@ -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() { diff --git a/dom/system/gonk/NetworkService.js b/dom/system/gonk/NetworkService.js index 36acdd7d434d..2b327ac3fa34 100644 --- a/dom/system/gonk/NetworkService.js +++ b/dom/system/gonk/NetworkService.js @@ -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) { diff --git a/dom/system/gonk/NetworkUtils.cpp b/dom/system/gonk/NetworkUtils.cpp index df1e637169f1..d67cc9de233a 100644 --- a/dom/system/gonk/NetworkUtils.cpp +++ b/dom/system/gonk/NetworkUtils.cpp @@ -101,7 +101,14 @@ typedef Tuple3 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 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) diff --git a/dom/system/gonk/NetworkUtils.h b/dom/system/gonk/NetworkUtils.h index 630a912441c1..f352c9343ba8 100644 --- a/dom/system/gonk/NetworkUtils.h +++ b/dom/system/gonk/NetworkUtils.h @@ -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); diff --git a/dom/system/gonk/NetworkWorker.cpp b/dom/system/gonk/NetworkWorker.cpp index b848c406736f..ed67d3182c65 100644 --- a/dom/system/gonk/NetworkWorker.cpp +++ b/dom/system/gonk/NetworkWorker.cpp @@ -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() diff --git a/dom/system/gonk/nsINetworkService.idl b/dom/system/gonk/nsINetworkService.idl index 8e5b265899b7..da3c4ec2f93b 100644 --- a/dom/system/gonk/nsINetworkService.idl +++ b/dom/system/gonk/nsINetworkService.idl @@ -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); }; diff --git a/dom/webidl/FMRadio.webidl b/dom/webidl/FMRadio.webidl index cb1c3e8051ff..57db07691ae1 100644 --- a/dom/webidl/FMRadio.webidl +++ b/dom/webidl/FMRadio.webidl @@ -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(); }; diff --git a/dom/webidl/NetworkOptions.webidl b/dom/webidl/NetworkOptions.webidl index 09702ceae40c..c9df8a48278f 100644 --- a/dom/webidl/NetworkOptions.webidl +++ b/dom/webidl/NetworkOptions.webidl @@ -22,7 +22,6 @@ dictionary NetworkCommandOptions sequence 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; }; diff --git a/dom/webidl/WifiOptions.webidl b/dom/webidl/WifiOptions.webidl index 4f4f93c28b85..22105b1b0cb4 100644 --- a/dom/webidl/WifiOptions.webidl +++ b/dom/webidl/WifiOptions.webidl @@ -10,22 +10,6 @@ dictionary WifiCommandOptions long id = 0; // opaque id. DOMString cmd = ""; // the command name. DOMString request; // for "command" - DOMString ifname; // for "ifc_reset_connections", "ifc_enable", - // "ifc_disable", "ifc_remove_host_routes", - // "ifc_remove_default_route", "dhcp_stop", - // "dhcp_release_lease", "ifc_get_default_route", - // "ifc_add_host_route", "ifc_set_default_route", - // "ifc_configure", "dhcp_do_request", - // "dhcp_do_request_renew". - long route; // for "ifc_add_host_route", "ifc_set_default_route". - long ipaddr; // for "ifc_configure". - long mask; // for "ifc_configure". - long gateway; // for "ifc_configure". - long dns1; // for "ifc_configure". - long dns2; // for "ifc_configure". - DOMString key; // for "property_get", "property_set". - DOMString value; // for "property_set". - DOMString defaultValue; // for "property_get". }; /** @@ -37,24 +21,6 @@ dictionary WifiResultOptions long status = 0; // the return status of the command. // Used by most commands. DOMString reply = ""; // for "command". - DOMString route = ""; // for "ifc_get_default_route". - DOMString error = ""; // for "dhcp_get_errmsg". - DOMString value = ""; // for "property_get". - 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; }; diff --git a/dom/wifi/WifiNetUtil.jsm b/dom/wifi/WifiNetUtil.jsm index fb1aa7df1179..2d80de6b4374 100644 --- a/dom/wifi/WifiNetUtil.jsm +++ b/dom/wifi/WifiNetUtil.jsm @@ -30,23 +30,8 @@ this.WifiNetUtil = function(controlMessage) { var util = {}; - util.configureInterface = function(cfg, callback) { - let message = { cmd: "ifc_configure", - ifname: cfg.ifname, - ipaddr: cfg.ipaddr, - mask: cfg.mask, - gateway: cfg.gateway, - dns1: cfg.dns1, - dns2: cfg.dns2 }; - - controlMessage(message, function(data) { - callback(!data.status); - }); - }; - util.runDhcp = function (ifname, callback) { - controlMessage({ cmd: "dhcp_do_request", ifname: ifname }, function(data) { - var dhcpInfo = data.status ? null : data; + gNetworkService.dhcpRequest(ifname, function(success, dhcpInfo) { util.runIpConfig(ifname, dhcpInfo, callback); }); }; @@ -62,18 +47,6 @@ this.WifiNetUtil = function(controlMessage) { stopProcess(dhcpService, processName, callback); }; - util.enableInterface = function (ifname, callback) { - controlMessage({ cmd: "ifc_enable", ifname: ifname }, function (data) { - callback(!data.status); - }); - }; - - util.disableInterface = function (ifname, callback) { - controlMessage({ cmd: "ifc_disable", ifname: ifname }, function (data) { - callback(!data.status); - }); - }; - util.startDhcpServer = function (config, callback) { gNetworkService.setDhcpServer(true, config, function (error) { callback(!error); @@ -86,60 +59,6 @@ this.WifiNetUtil = function(controlMessage) { }); }; - util.addHostRoute = function (ifname, route, callback) { - controlMessage({ cmd: "ifc_add_host_route", ifname: ifname, route: route }, function(data) { - callback(!data.status); - }); - }; - - util.removeHostRoutes = function (ifname, callback) { - controlMessage({ cmd: "ifc_remove_host_routes", ifname: ifname }, function(data) { - callback(!data.status); - }); - }; - - util.setDefaultRoute = function (ifname, route, callback) { - controlMessage({ cmd: "ifc_set_default_route", ifname: ifname, route: route }, function(data) { - callback(!data.status); - }); - }; - - util.getDefaultRoute = function (ifname, callback) { - controlMessage({ cmd: "ifc_get_default_route", ifname: ifname }, function(data) { - callback(!data.route); - }); - }; - - util.removeDefaultRoute = function (ifname, callback) { - controlMessage({ cmd: "ifc_remove_default_route", ifname: ifname }, function(data) { - callback(!data.status); - }); - }; - - util.resetConnections = function (ifname, callback) { - controlMessage({ cmd: "ifc_reset_connections", ifname: ifname }, function(data) { - callback(!data.status); - }); - }; - - util.releaseDhcpLease = function (ifname, callback) { - controlMessage({ cmd: "dhcp_release_lease", ifname: ifname }, function(data) { - callback(!data.status); - }); - }; - - util.getDhcpError = function (callback) { - controlMessage({ cmd: "dhcp_get_errmsg" }, function(data) { - callback(data.error); - }); - }; - - util.runDhcpRenew = function (ifname, callback) { - controlMessage({ cmd: "dhcp_do_request", ifname: ifname }, function(data) { - callback(data.status ? null : data); - }); - }; - util.runIpConfig = function (name, data, callback) { if (!data) { debug("IP config failed to run"); diff --git a/dom/wifi/WifiP2pManager.jsm b/dom/wifi/WifiP2pManager.jsm index b10843303b95..61e299a6c18b 100644 --- a/dom/wifi/WifiP2pManager.jsm +++ b/dom/wifi/WifiP2pManager.jsm @@ -21,6 +21,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager", "@mozilla.org/network/manager;1", "nsINetworkManager"); +XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService", + "@mozilla.org/network/service;1", + "nsINetworkService"); + this.EXPORTED_SYMBOLS = ["WifiP2pManager"]; const EVENT_IGNORED = -1; @@ -647,7 +651,7 @@ function P2pStateMachine(aP2pCommand, aNetUtil) { // Step 4: Enable p2p0 net interface. wpa_supplicant may have // already done it for us. - aNetUtil.enableInterface(P2P_INTERFACE_NAME, function (success) { + gNetworkService.enableInterface(P2P_INTERFACE_NAME, function (success) { onSuccess(); }); }); @@ -1318,7 +1322,7 @@ function P2pStateMachine(aP2pCommand, aNetUtil) { debug('P2P function disabled'); aP2pCommand.closeSupplicantConnection(function (status) { debug('Supplicant connection closed'); - aNetUtil.disableInterface(P2P_INTERFACE_NAME, function (success){ + gNetworkService.disableInterface(P2P_INTERFACE_NAME, function (success){ debug('Disabled interface: ' + P2P_INTERFACE_NAME); _onDisabled(true); _sm.gotoState(stateDisabled); diff --git a/dom/wifi/WifiProxyService.cpp b/dom/wifi/WifiProxyService.cpp index 113c6fcb2b78..9c054b1b87f0 100644 --- a/dom/wifi/WifiProxyService.cpp +++ b/dom/wifi/WifiProxyService.cpp @@ -89,36 +89,10 @@ class WifiResultDispatcher : public nsRunnable { public: WifiResultDispatcher(WifiResultOptions& aResult, const nsACString& aInterface) - : mInterface(aInterface) + : mResult(aResult) + , mInterface(aInterface) { MOZ_ASSERT(!NS_IsMainThread()); - - // XXX: is there a better way to copy webidl dictionnaries? - // the copy constructor is private. -#define COPY_FIELD(prop) mResult.prop = aResult.prop; - - COPY_FIELD(mId) - COPY_FIELD(mStatus) - COPY_FIELD(mReply) - COPY_FIELD(mRoute) - COPY_FIELD(mError) - COPY_FIELD(mValue) - COPY_FIELD(mIpaddr_str) - COPY_FIELD(mGateway_str) - COPY_FIELD(mDns1_str) - COPY_FIELD(mDns2_str) - COPY_FIELD(mMask_str) - COPY_FIELD(mServer_str) - COPY_FIELD(mVendor_str) - COPY_FIELD(mLease) - COPY_FIELD(mMask) - COPY_FIELD(mIpaddr) - COPY_FIELD(mGateway) - COPY_FIELD(mDns1) - COPY_FIELD(mDns2) - COPY_FIELD(mServer) - -#undef COPY_FIELD } NS_IMETHOD Run() diff --git a/dom/wifi/WifiUtils.cpp b/dom/wifi/WifiUtils.cpp index e461bfa399c3..140e32f4054b 100644 --- a/dom/wifi/WifiUtils.cpp +++ b/dom/wifi/WifiUtils.cpp @@ -8,7 +8,6 @@ #include #include "prinit.h" #include "js/CharacterEncoding.h" -#include "mozilla/dom/network/NetUtils.h" using namespace mozilla::dom; @@ -380,14 +379,17 @@ public: // Concrete class to use to access the wpa supplicant. WpaSupplicant::WpaSupplicant() { - if (NetUtils::SdkVersion() < 16) { + char propVersion[PROPERTY_VALUE_MAX]; + property_get("ro.build.version.sdk", propVersion, "0"); + mSdkVersion = strtol(propVersion, nullptr, 10); + + if (mSdkVersion < 16) { mImpl = new ICSWpaSupplicantImpl(); - } else if (NetUtils::SdkVersion() < 19) { + } else if (mSdkVersion < 19) { mImpl = new JBWpaSupplicantImpl(); } else { mImpl = new KKWpaSupplicantImpl(); } - mNetUtils = new NetUtils(); mWifiHotspotUtils = new WifiHotspotUtils(); }; @@ -418,9 +420,6 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions, const nsCString& aInterface) { CHECK_HWLIB(false) - if (!mNetUtils->GetSharedLibrary()) { - return false; - } if (!mWifiHotspotUtils->GetSharedLibrary()) { return false; @@ -455,81 +454,6 @@ bool WpaSupplicant::ExecuteCommand(CommandOptions aOptions, aResult.mStatus = mImpl->do_wifi_stop_supplicant(0); } else if (aOptions.mCmd.EqualsLiteral("connect_to_supplicant")) { aResult.mStatus = mImpl->do_wifi_connect_to_supplicant(aInterface.get()); - } else if (aOptions.mCmd.EqualsLiteral("ifc_enable")) { - aResult.mStatus = mNetUtils->do_ifc_enable(GET_CHAR(mIfname)); - } else if (aOptions.mCmd.EqualsLiteral("ifc_disable")) { - aResult.mStatus = mNetUtils->do_ifc_disable(GET_CHAR(mIfname)); - } else if (aOptions.mCmd.EqualsLiteral("ifc_configure")) { - aResult.mStatus = mNetUtils->do_ifc_configure( - GET_CHAR(mIfname), aOptions.mIpaddr, aOptions.mMask, - aOptions.mGateway, aOptions.mDns1, aOptions.mDns2 - ); - } else if (aOptions.mCmd.EqualsLiteral("ifc_reset_connections")) { - aResult.mStatus = mNetUtils->do_ifc_reset_connections( - GET_CHAR(mIfname), RESET_ALL_ADDRESSES - ); - } else if (aOptions.mCmd.EqualsLiteral("dhcp_stop")) { - aResult.mStatus = mNetUtils->do_dhcp_stop(GET_CHAR(mIfname)); - } else if (aOptions.mCmd.EqualsLiteral("dhcp_do_request")) { - 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]; - aResult.mStatus = - mNetUtils->do_dhcp_do_request(GET_CHAR(mIfname), - ipaddr, - gateway, - &prefixLength, - dns1, - dns2, - server, - &lease, - vendorinfo); - - if (aResult.mStatus == -1) { - // Early return since we failed. - return true; - } - - aResult.mIpaddr_str = NS_ConvertUTF8toUTF16(ipaddr); - aResult.mGateway_str = NS_ConvertUTF8toUTF16(gateway); - aResult.mDns1_str = NS_ConvertUTF8toUTF16(dns1); - aResult.mDns2_str = NS_ConvertUTF8toUTF16(dns2); - aResult.mServer_str = NS_ConvertUTF8toUTF16(server); - aResult.mVendor_str = NS_ConvertUTF8toUTF16(vendorinfo); - aResult.mLease = lease; - aResult.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); \ - aResult.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); - - //aResult.mask_str = netHelpers.ipToString(obj.mask); - char inet_str[64]; - if (inet_ntop(AF_INET, &aResult.mMask, inet_str, sizeof(inet_str))) { - aResult.mMask_str = NS_ConvertUTF8toUTF16(inet_str); - } } else if (aOptions.mCmd.EqualsLiteral("hostapd_command")) { size_t len = BUFFER_SIZE - 1; char buffer[BUFFER_SIZE]; @@ -595,7 +519,7 @@ WpaSupplicant::CheckBuffer(char* buffer, int32_t length, return; } - if (NetUtils::SdkVersion() < 18) { + if (mSdkVersion < 18) { buffer[length] = 0; LossyConvertUTF8toUTF16(buffer, length, aEvent); return; diff --git a/dom/wifi/WifiUtils.h b/dom/wifi/WifiUtils.h index 7aacf6935eda..8764a02b1893 100644 --- a/dom/wifi/WifiUtils.h +++ b/dom/wifi/WifiUtils.h @@ -13,29 +13,12 @@ #include "nsString.h" #include "nsAutoPtr.h" #include "mozilla/dom/WifiOptionsBinding.h" -#include "mozilla/dom/network/NetUtils.h" #include "WifiHotspotUtils.h" // Needed to add a copy constructor to WifiCommandOptions. struct CommandOptions { public: - CommandOptions(const CommandOptions& aOther) { - mId = aOther.mId; - mCmd = aOther.mCmd; - mRequest = aOther.mRequest; - mIfname = aOther.mIfname; - mRoute = aOther.mRoute; - mIpaddr = aOther.mIpaddr; - mMask = aOther.mMask; - mGateway = aOther.mGateway; - mDns1 = aOther.mDns1; - mDns2 = aOther.mDns2; - mKey = aOther.mKey; - mValue = aOther.mValue; - mDefaultValue = aOther.mDefaultValue; - } - CommandOptions(const mozilla::dom::WifiCommandOptions& aOther) { #define COPY_OPT_FIELD(prop, defaultValue) \ @@ -49,16 +32,6 @@ public: COPY_FIELD(mId) COPY_FIELD(mCmd) COPY_OPT_FIELD(mRequest, EmptyString()) - COPY_OPT_FIELD(mIfname, EmptyString()) - COPY_OPT_FIELD(mIpaddr, 0) - COPY_OPT_FIELD(mRoute, 0) - COPY_OPT_FIELD(mMask, 0) - COPY_OPT_FIELD(mGateway, 0) - COPY_OPT_FIELD(mDns1, 0) - COPY_OPT_FIELD(mDns2, 0) - COPY_OPT_FIELD(mKey, EmptyString()) - COPY_OPT_FIELD(mValue, EmptyString()) - COPY_OPT_FIELD(mDefaultValue, EmptyString()) #undef COPY_OPT_FIELD #undef COPY_FIELD @@ -66,18 +39,8 @@ public: // All the fields, not Optional<> anymore to get copy constructors. nsString mCmd; - nsString mDefaultValue; - int32_t mDns1; - int32_t mDns2; - int32_t mGateway; int32_t mId; - nsString mIfname; - int32_t mIpaddr; - nsString mKey; - int32_t mMask; nsString mRequest; - int32_t mRoute; - nsString mValue; }; // Abstract class that exposes libhardware_legacy functions we need for @@ -130,9 +93,10 @@ public: private: nsAutoPtr mImpl; - nsAutoPtr mNetUtils; nsAutoPtr mWifiHotspotUtils; + uint32_t mSdkVersion; + protected: void CheckBuffer(char* buffer, int32_t length, nsAString& aEvent); uint32_t MakeMask(uint32_t len); diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 83c32d606402..a778050eb4e6 100644 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -415,8 +415,8 @@ var WifiManager = (function() { // and routing table is changed but still cannot connect to network // so the workaround here is disable interface the enable again to // trigger network reconnect with static ip. - netUtil.disableInterface(manager.ifname, function (ok) { - netUtil.enableInterface(manager.ifname, function (ok) { + gNetworkService.disableInterface(manager.ifname, function (ok) { + gNetworkService.enableInterface(manager.ifname, function (ok) { }); }); } @@ -442,12 +442,12 @@ var WifiManager = (function() { } // Set ip, mask length, gateway, dns to network interface - netUtil.configureInterface( { ifname: ifname, - ipaddr: staticIpInfo.ipaddr, - mask: staticIpInfo.maskLength, - gateway: staticIpInfo.gateway, - dns1: staticIpInfo.dns1, - dns2: staticIpInfo.dns2 }, function (data) { + gNetworkService.configureInterface( { ifname: ifname, + ipaddr: staticIpInfo.ipaddr, + mask: staticIpInfo.maskLength, + gateway: staticIpInfo.gateway, + dns1: staticIpInfo.dns1, + dns2: staticIpInfo.dns2 }, function (data) { netUtil.runIpConfig(ifname, staticIpInfo, function(data) { dhcpInfo = data.info; notify("networkconnected", data); @@ -587,17 +587,17 @@ var WifiManager = (function() { manager.connectionDropped = function(callback) { // Reset network interface when connection drop - netUtil.configureInterface( { ifname: manager.ifname, - ipaddr: 0, - mask: 0, - gateway: 0, - dns1: 0, - dns2: 0 }, function (data) { + gNetworkService.configureInterface( { ifname: manager.ifname, + ipaddr: 0, + mask: 0, + gateway: 0, + dns1: 0, + dns2: 0 }, function (data) { }); // If we got disconnected, kill the DHCP client in preparation for // reconnection. - netUtil.resetConnections(manager.ifname, function() { + gNetworkService.resetConnections(manager.ifname, function() { netUtil.stopDhcp(manager.ifname, function() { callback(); }); @@ -889,7 +889,7 @@ var WifiManager = (function() { } suppressEvents = true; wifiCommand.killSupplicant(function() { - netUtil.disableInterface(manager.ifname, function (ok) { + gNetworkService.disableInterface(manager.ifname, function (ok) { suppressEvents = false; callback(); }); @@ -996,7 +996,7 @@ var WifiManager = (function() { } manager.supplicantStarted = true; - netUtil.enableInterface(manager.ifname, function (ok) { + gNetworkService.enableInterface(manager.ifname, function (ok) { callback(ok ? 0 : -1); }); }); @@ -1040,7 +1040,7 @@ var WifiManager = (function() { wifiCommand.closeSupplicantConnection(function() { manager.connectToSupplicant = false; manager.state = "UNINITIALIZED"; - netUtil.disableInterface(manager.ifname, function (ok) { + gNetworkService.disableInterface(manager.ifname, function (ok) { unloadDriver(WIFI_FIRMWARE_STATION, callback); }); }); diff --git a/hal/Hal.cpp b/hal/Hal.cpp index f09d5f622de7..790af80847d9 100644 --- a/hal/Hal.cpp +++ b/hal/Hal.cpp @@ -1004,12 +1004,15 @@ ProcessPriorityToString(ProcessPriority aPriority, } static StaticAutoPtr > sFMRadioObservers; +static StaticAutoPtr > sFMRadioRDSObservers; static void InitializeFMRadioObserver() { if (!sFMRadioObservers) { sFMRadioObservers = new ObserverList; + sFMRadioRDSObservers = new ObserverList; + ClearOnShutdown(&sFMRadioRDSObservers); ClearOnShutdown(&sFMRadioObservers); } } @@ -1034,6 +1037,27 @@ NotifyFMRadioStatus(const FMRadioOperationInformation& aFMRadioState) { sFMRadioObservers->Broadcast(aFMRadioState); } +void +RegisterFMRadioRDSObserver(FMRadioRDSObserver* aFMRadioRDSObserver) { + AssertMainThread(); + InitializeFMRadioObserver(); + sFMRadioRDSObservers->AddObserver(aFMRadioRDSObserver); +} + +void +UnregisterFMRadioRDSObserver(FMRadioRDSObserver* aFMRadioRDSObserver) { + AssertMainThread(); + InitializeFMRadioObserver(); + sFMRadioRDSObservers->RemoveObserver(aFMRadioRDSObserver); +} + + +void +NotifyFMRadioRDSGroup(const FMRadioRDSGroup& aRDSGroup) { + InitializeFMRadioObserver(); + sFMRadioRDSObservers->Broadcast(aRDSGroup); +} + void EnableFMRadio(const FMRadioSettings& aInfo) { AssertMainThread(); @@ -1088,6 +1112,18 @@ CancelFMRadioSeek() { PROXY_IF_SANDBOXED(CancelFMRadioSeek()); } +void +EnableRDS(uint32_t aMask) { + AssertMainThread(); + PROXY_IF_SANDBOXED(EnableRDS(aMask)); +} + +void +DisableRDS() { + AssertMainThread(); + PROXY_IF_SANDBOXED(DisableRDS()); +} + FMRadioSettings GetFMBandSettings(FMRadioCountry aCountry) { FMRadioSettings settings; diff --git a/hal/Hal.h b/hal/Hal.h index b2aeec320198..523bd32ee01f 100644 --- a/hal/Hal.h +++ b/hal/Hal.h @@ -503,12 +503,28 @@ void RegisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver); */ void UnregisterFMRadioObserver(hal::FMRadioObserver* aRadioObserver); +/** + * Register an observer for the FM radio. + */ +void RegisterFMRadioRDSObserver(hal::FMRadioRDSObserver* aRDSObserver); + +/** + * Unregister the observer for the FM radio. + */ +void UnregisterFMRadioRDSObserver(hal::FMRadioRDSObserver* aRDSObserver); + /** * Notify observers that a call to EnableFMRadio, DisableFMRadio, or FMRadioSeek * has completed, and indicate what the call returned. */ void NotifyFMRadioStatus(const hal::FMRadioOperationInformation& aRadioState); +/** + * Notify observers of new RDS data + * This can be called on any thread. + */ +void NotifyFMRadioRDSGroup(const hal::FMRadioRDSGroup& aRDSGroup); + /** * Enable the FM radio and configure it according to the settings in aInfo. */ @@ -565,6 +581,16 @@ void CancelFMRadioSeek(); */ hal::FMRadioSettings GetFMBandSettings(hal::FMRadioCountry aCountry); +/** + * Enable RDS data reception + */ +void EnableRDS(uint32_t aMask); + +/** + * Disable RDS data reception + */ +void DisableRDS(); + /** * Start a watchdog to compulsively shutdown the system if it hangs. * @param aMode Specify how to shutdown the system. diff --git a/hal/HalTypes.h b/hal/HalTypes.h index 12cfcad516c4..a33d1a2cbac1 100644 --- a/hal/HalTypes.h +++ b/hal/HalTypes.h @@ -225,7 +225,9 @@ enum FMRadioCountry { NUM_FM_RADIO_COUNTRY }; +class FMRadioRDSGroup; typedef Observer FMRadioObserver; +typedef Observer FMRadioRDSObserver; } // namespace hal } // namespace mozilla diff --git a/hal/fallback/FallbackFMRadio.cpp b/hal/fallback/FallbackFMRadio.cpp index 664928b9a777..ac9de9684b42 100644 --- a/hal/fallback/FallbackFMRadio.cpp +++ b/hal/fallback/FallbackFMRadio.cpp @@ -57,5 +57,13 @@ void CancelFMRadioSeek() {} +void +EnableRDS(uint32_t aMask) +{} + +void +DisableRDS() +{} + } // hal_impl } // namespace mozilla diff --git a/hal/gonk/GonkFMRadio.cpp b/hal/gonk/GonkFMRadio.cpp index 5f0406836769..270c3b084fd8 100644 --- a/hal/gonk/GonkFMRadio.cpp +++ b/hal/gonk/GonkFMRadio.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,23 @@ #define V4L2_CID_RDS_RECEPTION (V4L2_CID_FM_RX_CLASS_BASE + 2) #endif +#ifndef V4L2_RDS_BLOCK_MSK +struct v4l2_rds_data { + uint8_t lsb; + uint8_t msb; + uint8_t block; +} __attribute__ ((packed)); +#define V4L2_RDS_BLOCK_MSK 0x7 +#define V4L2_RDS_BLOCK_A 0 +#define V4L2_RDS_BLOCK_B 1 +#define V4L2_RDS_BLOCK_C 2 +#define V4L2_RDS_BLOCK_D 3 +#define V4L2_RDS_BLOCK_C_ALT 4 +#define V4L2_RDS_BLOCK_INVALID 7 +#define V4L2_RDS_BLOCK_CORRECTED 0x40 +#define V4L2_RDS_BLOCK_ERROR 0x80 +#endif + namespace mozilla { namespace hal_impl { @@ -49,10 +67,13 @@ uint32_t GetFMRadioFrequency(); static int sRadioFD; static bool sRadioEnabled; +static bool sRDSEnabled; static pthread_t sRadioThread; +static pthread_t sRDSThread; static hal::FMRadioSettings sRadioSettings; static int sMsmFMVersion; static bool sMsmFMMode; +static bool sRDSSupported; static int setControl(uint32_t id, int32_t value) @@ -311,6 +332,8 @@ EnableFMRadio(const hal::FMRadioSettings& aInfo) hal::NotifyFMRadioStatus(info); return; } + + sRDSSupported = cap.capabilities & V4L2_CAP_RDS_CAPTURE; sRadioSettings = aInfo; if (sMsmFMMode) { @@ -366,6 +389,9 @@ DisableFMRadio() if (!sRadioEnabled) return; + if (sRDSEnabled) + hal::DisableRDS(); + sRadioEnabled = false; if (sMsmFMMode) { @@ -494,5 +520,186 @@ void CancelFMRadioSeek() {} +/* Runs on the rds thread */ +static void* +readRDSDataThread(void* data) +{ + v4l2_rds_data rdsblocks[16]; + uint16_t blocks[4]; + + ScopedClose pipefd((int)data); + + ScopedClose epollfd(epoll_create(2)); + if (epollfd < 0) { + HAL_LOG("Could not create epoll FD for RDS thread (%d)", errno); + return nullptr; + } + + epoll_event event = { + EPOLLIN, + { 0 } + }; + + event.data.fd = pipefd; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd, &event) < 0) { + HAL_LOG("Could not set up epoll FD for RDS thread (%d)", errno); + return nullptr; + } + + event.data.fd = sRadioFD; + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sRadioFD, &event) < 0) { + HAL_LOG("Could not set up epoll FD for RDS thread (%d)", errno); + return nullptr; + } + + epoll_event events[2] = {{ 0 }}; + int event_count; + uint32_t block_bitmap = 0; + while ((event_count = epoll_wait(epollfd, events, 2, -1)) > 0 || + errno == EINTR) { + bool RDSDataAvailable = false; + for (int i = 0; i < event_count; i++) { + if (events[i].data.fd == pipefd) { + if (!sRDSEnabled) + return nullptr; + char tmp[32]; + TEMP_FAILURE_RETRY(read(pipefd, tmp, sizeof(tmp))); + } else if (events[i].data.fd == sRadioFD) { + RDSDataAvailable = true; + } + } + + if (!RDSDataAvailable) + continue; + + ssize_t len = + TEMP_FAILURE_RETRY(read(sRadioFD, rdsblocks, sizeof(rdsblocks))); + if (len < 0) { + HAL_LOG("Unexpected error while reading RDS data %d", errno); + return nullptr; + } + + int blockcount = len / sizeof(rdsblocks[0]); + int lastblock = -1; + for (int i = 0; i < blockcount; i++) { + if ((rdsblocks[i].block & V4L2_RDS_BLOCK_MSK) == V4L2_RDS_BLOCK_INVALID || + rdsblocks[i].block & V4L2_RDS_BLOCK_ERROR) { + block_bitmap |= 1 << V4L2_RDS_BLOCK_INVALID; + continue; + } + + int blocknum = rdsblocks[i].block & V4L2_RDS_BLOCK_MSK; + // In some cases, the full set of bits in an RDS group isn't + // needed, in which case version B RDS groups can be sent. + // Version B groups replace block C with block C' (V4L2_RDS_BLOCK_C_ALT). + // Block C' always stores the PI code, so receivers can find the PI + // code more quickly/reliably. + // However, we only process whole RDS groups, so it doesn't matter here. + if (blocknum == V4L2_RDS_BLOCK_C_ALT) + blocknum = V4L2_RDS_BLOCK_C; + if (blocknum > V4L2_RDS_BLOCK_D) { + HAL_LOG("Unexpected RDS block number %d. This is a driver bug.", + blocknum); + continue; + } + + if (blocknum == V4L2_RDS_BLOCK_A) + block_bitmap = 0; + + // Skip the group if we skipped a block. + // This stops us from processing blocks sent out of order. + if (block_bitmap != ((1 << blocknum) - 1)) { + block_bitmap |= 1 << V4L2_RDS_BLOCK_INVALID; + continue; + } + + block_bitmap |= 1 << blocknum; + + lastblock = blocknum; + blocks[blocknum] = (rdsblocks[i].msb << 8) | rdsblocks[i].lsb; + + // Make sure we have all 4 blocks and that they're valid + if (block_bitmap != 0x0F) + continue; + + hal::FMRadioRDSGroup group; + group.blockA() = blocks[V4L2_RDS_BLOCK_A]; + group.blockB() = blocks[V4L2_RDS_BLOCK_B]; + group.blockC() = blocks[V4L2_RDS_BLOCK_C]; + group.blockD() = blocks[V4L2_RDS_BLOCK_D]; + NotifyFMRadioRDSGroup(group); + } + } + + return nullptr; +} + +static int sRDSPipeFD; + +void +EnableRDS(uint32_t aMask) +{ + if (!sRadioEnabled || !sRDSSupported) + return; + + if (sMsmFMMode) + setControl(V4L2_CID_PRIVATE_TAVARUA_RDSGROUP_MASK, aMask); + + if (sRDSEnabled) + return; + + int pipefd[2]; + int rc = pipe2(pipefd, O_NONBLOCK); + if (rc < 0) { + HAL_LOG("Could not create RDS thread signaling pipes (%d)", rc); + return; + } + + ScopedClose writefd(pipefd[1]); + ScopedClose readfd(pipefd[0]); + + rc = setControl(V4L2_CID_RDS_RECEPTION, true); + if (rc < 0) { + HAL_LOG("Could not enable RDS reception (%d)", rc); + return; + } + + sRDSPipeFD = writefd; + + sRDSEnabled = true; + + rc = pthread_create(&sRDSThread, nullptr, + readRDSDataThread, (void*)pipefd[0]); + if (rc) { + HAL_LOG("Could not start RDS reception thread (%d)", rc); + setControl(V4L2_CID_RDS_RECEPTION, false); + sRDSEnabled = false; + return; + } + + readfd.forget(); + writefd.forget(); +} + +void +DisableRDS() +{ + if (!sRadioEnabled || !sRDSEnabled) + return; + + int rc = setControl(V4L2_CID_RDS_RECEPTION, false); + if (rc < 0) { + HAL_LOG("Could not disable RDS reception (%d)", rc); + } + + sRDSEnabled = false; + + write(sRDSPipeFD, "x", 1); + + pthread_join(sRDSThread, nullptr); + + close(sRDSPipeFD); +} + } // hal_impl } // namespace mozilla diff --git a/hal/sandbox/PHal.ipdl b/hal/sandbox/PHal.ipdl index a52d7e6ef779..f45d232ed249 100644 --- a/hal/sandbox/PHal.ipdl +++ b/hal/sandbox/PHal.ipdl @@ -69,6 +69,13 @@ struct FMRadioOperationInformation { uint32_t frequency; }; +struct FMRadioRDSGroup { + uint16_t blockA; + uint16_t blockB; + uint16_t blockC; + uint16_t blockD; +}; + struct FMRadioSettings { FMRadioCountry country; uint32_t upperLimit; diff --git a/hal/sandbox/SandboxHal.cpp b/hal/sandbox/SandboxHal.cpp index f2faba6e4e36..1f662dacfb20 100644 --- a/hal/sandbox/SandboxHal.cpp +++ b/hal/sandbox/SandboxHal.cpp @@ -426,6 +426,18 @@ CancelFMRadioSeek() NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts."); } +void +EnableRDS(uint32_t aMask) +{ + NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts."); +} + +void +DisableRDS() +{ + NS_RUNTIMEABORT("FM radio cannot be called from sandboxed contexts."); +} + void FactoryReset(FactoryResetReason& aReason) { diff --git a/mobile/android/base/menu/MenuItemActionBar.java b/mobile/android/base/menu/MenuItemActionBar.java index 0317960b609f..8bbe9e6b2561 100644 --- a/mobile/android/base/menu/MenuItemActionBar.java +++ b/mobile/android/base/menu/MenuItemActionBar.java @@ -4,6 +4,7 @@ package org.mozilla.gecko.menu; +import org.mozilla.gecko.NewTabletUI; import org.mozilla.gecko.R; import android.content.Context; @@ -20,7 +21,9 @@ public class MenuItemActionBar extends ImageButton } public MenuItemActionBar(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.menuItemActionBarStyle); + // TODO: Remove this branch (and associated attr) when old tablet is removed. + this(context, attrs, (NewTabletUI.isEnabled(context)) ? + R.attr.menuItemActionBarStyleNewTablet : R.attr.menuItemActionBarStyle); } public MenuItemActionBar(Context context, AttributeSet attrs, int defStyle) { diff --git a/mobile/android/base/resources/layout-large-v11/new_tablet_browser_toolbar.xml b/mobile/android/base/resources/layout-large-v11/new_tablet_browser_toolbar.xml index f109edafa1c2..d9e0938fb99f 100644 --- a/mobile/android/base/resources/layout-large-v11/new_tablet_browser_toolbar.xml +++ b/mobile/android/base/resources/layout-large-v11/new_tablet_browser_toolbar.xml @@ -51,12 +51,10 @@ android:paddingLeft="6dip" android:paddingRight="4dip"/> - diff --git a/mobile/android/base/resources/values-large-v11/dimens.xml b/mobile/android/base/resources/values-large-v11/dimens.xml index c52e78b580b7..466d546804dc 100644 --- a/mobile/android/base/resources/values-large-v11/dimens.xml +++ b/mobile/android/base/resources/values-large-v11/dimens.xml @@ -15,6 +15,8 @@ -13dip -6dp + 19dp + 26sp 200dp diff --git a/mobile/android/base/resources/values-large-v11/styles.xml b/mobile/android/base/resources/values-large-v11/styles.xml index afa8cd646a63..9b198b9b17a9 100644 --- a/mobile/android/base/resources/values-large-v11/styles.xml +++ b/mobile/android/base/resources/values-large-v11/styles.xml @@ -70,6 +70,13 @@ fitCenter + + diff --git a/mobile/android/base/resources/values/attrs.xml b/mobile/android/base/resources/values/attrs.xml index 9783bcd7af74..a61dcb516ecd 100644 --- a/mobile/android/base/resources/values/attrs.xml +++ b/mobile/android/base/resources/values/attrs.xml @@ -14,6 +14,9 @@ + + + diff --git a/mobile/android/base/resources/values/themes.xml b/mobile/android/base/resources/values/themes.xml index 3e5f0ecf6bdf..63ed3e8987ab 100644 --- a/mobile/android/base/resources/values/themes.xml +++ b/mobile/android/base/resources/values/themes.xml @@ -93,6 +93,7 @@ @style/Widget.GeckoMenuListView @style/Widget.HomeListView @style/Widget.MenuItemActionBar + @style/Widget.MenuItemActionBar.NewTablet @style/GeckoActionBar.Button @style/Widget.MenuItemSecondaryActionBar @style/Widget.PanelGridView diff --git a/mobile/android/base/tests/BaseRobocopTest.java b/mobile/android/base/tests/BaseRobocopTest.java index 2aaa851da6df..03994afd96f7 100644 --- a/mobile/android/base/tests/BaseRobocopTest.java +++ b/mobile/android/base/tests/BaseRobocopTest.java @@ -15,6 +15,8 @@ import org.mozilla.gecko.FennecTalosAssert; import org.mozilla.gecko.AppConstants; import android.app.Activity; +import android.content.Context; +import android.os.PowerManager; import android.test.ActivityInstrumentationTestCase2; import android.util.Log; @@ -107,4 +109,13 @@ public abstract class BaseRobocopTest extends ActivityInstrumentationTestCase2 skiplist; + rv = ensure_copy_recursive(newDir, destDir, skiplist); + } +#endif if (rv) { LOG(("Moving newDir to destDir failed, err: %d", rv)); LOG(("Now, try to move tmpDir back to destDir")); @@ -2177,8 +2185,7 @@ UpdateThreadFunc(void *param) // staged directory as it won't be useful any more. ensure_remove_recursive(gWorkingDirPath); WriteStatusFile(sUsingService ? "pending-service" : "pending"); - char processUpdates[] = "MOZ_PROCESS_UPDATES="; - putenv(processUpdates); // We need to use -process-updates again in the tests + putenv(const_cast("MOZ_PROCESS_UPDATES=")); // We need to use -process-updates again in the tests reportRealResults = false; // pretend success } diff --git a/toolkit/themes/osx/mozapps/update/updates.css b/toolkit/themes/osx/mozapps/update/updates.css index 332cacc9fd04..d7e18ad10307 100644 --- a/toolkit/themes/osx/mozapps/update/updates.css +++ b/toolkit/themes/osx/mozapps/update/updates.css @@ -40,6 +40,7 @@ wizardpage { .wizard-buttons button { -moz-appearance: toolbarbutton; + color: ButtonText; min-height: 22px; margin: 0 6px; padding: 0;