bug 982842 - initial a11y ipc impl r=davidb, bent

This commit is contained in:
Trevor Saunders 2014-03-07 16:35:19 -05:00
parent 308481a1f4
commit 619e12383a
32 changed files with 821 additions and 21 deletions

View File

@ -12,6 +12,7 @@
#include "nsAccUtils.h" #include "nsAccUtils.h"
#include "nsIAccessibleRelation.h" #include "nsIAccessibleRelation.h"
#include "nsIAccessibleTable.h" #include "nsIAccessibleTable.h"
#include "ProxyAccessible.h"
#include "RootAccessible.h" #include "RootAccessible.h"
#include "nsIAccessibleValue.h" #include "nsIAccessibleValue.h"
#include "nsMai.h" #include "nsMai.h"
@ -20,6 +21,7 @@
#include "nsAutoPtr.h" #include "nsAutoPtr.h"
#include "prprf.h" #include "prprf.h"
#include "nsStateMap.h" #include "nsStateMap.h"
#include "mozilla/a11y/Platform.h"
#include "Relation.h" #include "Relation.h"
#include "RootAccessible.h" #include "RootAccessible.h"
#include "States.h" #include "States.h"
@ -133,9 +135,13 @@ struct MaiAtkObject
* The AccessibleWrap whose properties and features are exported * The AccessibleWrap whose properties and features are exported
* via this object instance. * via this object instance.
*/ */
AccessibleWrap* accWrap; uintptr_t 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 struct MaiAtkObjectClass
{ {
AtkObjectClass parent_class; AtkObjectClass parent_class;
@ -248,7 +254,7 @@ AccessibleWrap::ShutdownAtkObject()
{ {
if (mAtkObject) { if (mAtkObject) {
if (IS_MAI_OBJECT(mAtkObject)) { if (IS_MAI_OBJECT(mAtkObject)) {
MAI_ATK_OBJECT(mAtkObject)->accWrap = nullptr; MAI_ATK_OBJECT(mAtkObject)->accWrap = 0;
} }
SetMaiHyperlink(nullptr); SetMaiHyperlink(nullptr);
g_object_unref(mAtkObject); g_object_unref(mAtkObject);
@ -582,8 +588,7 @@ initializeCB(AtkObject *aAtkObj, gpointer aData)
ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData); ATK_OBJECT_CLASS(parent_class)->initialize(aAtkObj, aData);
/* initialize object */ /* initialize object */
MAI_ATK_OBJECT(aAtkObj)->accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap = reinterpret_cast<uintptr_t>(aData);
static_cast<AccessibleWrap*>(aData);
} }
void void
@ -591,7 +596,7 @@ finalizeCB(GObject *aObj)
{ {
if (!IS_MAI_OBJECT(aObj)) if (!IS_MAI_OBJECT(aObj))
return; return;
NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == nullptr, "AccWrap NOT null"); NS_ASSERTION(MAI_ATK_OBJECT(aObj)->accWrap == 0, "AccWrap NOT null");
// call parent finalize function // call parent finalize function
// finalize of GObjectClass will unref the accessible parent if has // finalize of GObjectClass will unref the accessible parent if has
@ -663,25 +668,33 @@ getDescriptionCB(AtkObject *aAtkObj)
AtkRole AtkRole
getRoleCB(AtkObject *aAtkObj) getRoleCB(AtkObject *aAtkObj)
{ {
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
if (!accWrap)
return ATK_ROLE_INVALID;
#ifdef DEBUG
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
"Does not support nsIAccessibleText when it should");
#endif
if (aAtkObj->role != ATK_ROLE_INVALID) if (aAtkObj->role != ATK_ROLE_INVALID)
return aAtkObj->role; return aAtkObj->role;
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
a11y::role role;
if (!accWrap) {
ProxyAccessible* proxy = GetProxy(aAtkObj);
if (!proxy)
return ATK_ROLE_INVALID;
role = proxy->Role();
} else {
#ifdef DEBUG
NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
"Does not support nsIAccessibleText when it should");
#endif
role = accWrap->Role();
}
#define ROLE(geckoRole, stringRole, atkRole, macRole, \ #define ROLE(geckoRole, stringRole, atkRole, macRole, \
msaaRole, ia2Role, nameRule) \ msaaRole, ia2Role, nameRule) \
case roles::geckoRole: \ case roles::geckoRole: \
aAtkObj->role = atkRole; \ aAtkObj->role = atkRole; \
break; break;
switch (accWrap->Role()) { switch (role) {
#include "RoleMap.h" #include "RoleMap.h"
default: default:
MOZ_CRASH("Unknown role."); MOZ_CRASH("Unknown role.");
@ -946,7 +959,13 @@ AccessibleWrap*
GetAccessibleWrap(AtkObject* aAtkObj) GetAccessibleWrap(AtkObject* aAtkObj)
{ {
NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr); NS_ENSURE_TRUE(IS_MAI_OBJECT(aAtkObj), nullptr);
AccessibleWrap* accWrap = MAI_ATK_OBJECT(aAtkObj)->accWrap;
// Make sure its native is an AccessibleWrap not a proxy.
if (MAI_ATK_OBJECT(aAtkObj)->accWrap & IS_PROXY)
return nullptr;
AccessibleWrap* accWrap =
reinterpret_cast<AccessibleWrap*>(MAI_ATK_OBJECT(aAtkObj)->accWrap);
// Check if the accessible was deconstructed. // Check if the accessible was deconstructed.
if (!accWrap) if (!accWrap)
@ -961,6 +980,49 @@ GetAccessibleWrap(AtkObject* aAtkObj)
return accWrap; return accWrap;
} }
ProxyAccessible*
GetProxy(AtkObject* aObj)
{
if (!aObj || !(MAI_ATK_OBJECT(aObj)->accWrap & IS_PROXY))
return nullptr;
return reinterpret_cast<ProxyAccessible*>(MAI_ATK_OBJECT(aObj)->accWrap
& ~IS_PROXY);
}
static uint16_t
GetInterfacesForProxy(ProxyAccessible* aProxy)
{
return MAI_INTERFACE_COMPONENT;
}
void
a11y::ProxyCreated(ProxyAccessible* aProxy)
{
GType type = GetMaiAtkType(GetInterfacesForProxy(aProxy));
NS_ASSERTION(type, "why don't we have a type!");
AtkObject* obj =
reinterpret_cast<AtkObject *>
(g_object_new(type, nullptr));
if (!obj)
return;
atk_object_initialize(obj, aProxy);
obj->role = ATK_ROLE_INVALID;
obj->layer = ATK_LAYER_INVALID;
aProxy->SetWrapper(reinterpret_cast<uintptr_t>(obj) | IS_PROXY);
}
void
a11y::ProxyDestroyed(ProxyAccessible* aProxy)
{
auto obj = reinterpret_cast<MaiAtkObject*>(aProxy->GetWrapper() & ~IS_PROXY);
obj->accWrap = 0;
g_object_unref(obj);
aProxy->SetWrapper(0);
}
nsresult nsresult
AccessibleWrap::HandleAccEvent(AccEvent* aEvent) AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
{ {

View File

@ -97,7 +97,7 @@ private:
static EAvailableAtkSignals gAvailableAtkSignals; static EAvailableAtkSignals gAvailableAtkSignals;
uint16_t CreateMaiInterfaces(void); uint16_t CreateMaiInterfaces();
}; };
} // namespace a11y } // namespace a11y

View File

@ -35,6 +35,7 @@ LOCAL_INCLUDES += [
'/accessible/base', '/accessible/base',
'/accessible/generic', '/accessible/generic',
'/accessible/html', '/accessible/html',
'/accessible/ipc',
'/accessible/xpcom', '/accessible/xpcom',
'/accessible/xul', '/accessible/xul',
'/other-licenses/atk-1.0', '/other-licenses/atk-1.0',

View File

@ -13,6 +13,12 @@
#include "AccessibleWrap.h" #include "AccessibleWrap.h"
namespace mozilla {
namespace a11y {
class ProxyAccessible;
}
}
#define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ()) #define MAI_TYPE_ATK_OBJECT (mai_atk_object_get_type ())
#define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ #define MAI_ATK_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
MAI_TYPE_ATK_OBJECT, MaiAtkObject)) MAI_TYPE_ATK_OBJECT, MaiAtkObject))
@ -29,6 +35,7 @@
GType mai_atk_object_get_type(void); GType mai_atk_object_get_type(void);
GType mai_util_get_type(); GType mai_util_get_type();
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj); mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
extern int atkMajorVersion, atkMinorVersion; extern int atkMajorVersion, atkMinorVersion;

View File

@ -232,6 +232,8 @@ public:
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; } bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; } bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
Accessible* Parent() const { return mParent; }
protected: protected:
nsCOMPtr<nsINode> mNode; nsCOMPtr<nsINode> mNode;
nsRefPtr<Accessible> mParent; nsRefPtr<Accessible> mParent;

View File

@ -8,6 +8,7 @@
#include "ApplicationAccessible.h" #include "ApplicationAccessible.h"
#include "ARIAMap.h" #include "ARIAMap.h"
#include "DocAccessible-inl.h" #include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "nsAccessibilityService.h" #include "nsAccessibilityService.h"
#include "RootAccessibleWrap.h" #include "RootAccessibleWrap.h"
@ -27,6 +28,8 @@
#include "nsServiceManagerUtils.h" #include "nsServiceManagerUtils.h"
#include "nsIWebProgress.h" #include "nsIWebProgress.h"
#include "nsCoreUtils.h" #include "nsCoreUtils.h"
#include "nsXULAppAPI.h"
#include "mozilla/dom/ContentChild.h"
using namespace mozilla; using namespace mozilla;
using namespace mozilla::a11y; using namespace mozilla::a11y;
@ -418,6 +421,12 @@ DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER, docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
ApplicationAcc()); 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 { } else {
parentDocAcc->BindChildDocument(docAcc); parentDocAcc->BindChildDocument(docAcc);
} }

View File

@ -17,6 +17,7 @@ namespace a11y {
class Accessible; class Accessible;
class DocAccessible; class DocAccessible;
class DocAccessibleParent;
/** /**
* Manage the document accessible life cycle. * Manage the document accessible life cycle.
@ -65,6 +66,25 @@ public:
RemoveListeners(aDocument); RemoveListeners(aDocument);
} }
/*
* Notification that a top level document in a content process has gone away.
*/
void RemoteDocShutdown(DocAccessibleParent* aDoc)
{
DebugOnly<bool> result = mRemoteDocuments.RemoveElement(aDoc);
MOZ_ASSERT(result, "Why didn't we find the document!");
}
/*
* Notify of a new top level document in a content process.
*/
void RemoteDocAdded(DocAccessibleParent* aDoc)
{
MOZ_ASSERT(!mRemoteDocuments.Contains(aDoc),
"How did we already have the doc!");
mRemoteDocuments.AppendElement(aDoc);
}
#ifdef DEBUG #ifdef DEBUG
bool IsProcessingRefreshDriverNotification() const; bool IsProcessingRefreshDriverNotification() const;
#endif #endif
@ -144,6 +164,11 @@ private:
#endif #endif
DocAccessibleHashtable mDocAccessibleCache; DocAccessibleHashtable mDocAccessibleCache;
/*
* The list of remote top level documents.
*/
nsTArray<DocAccessibleParent*> mRemoteDocuments;
}; };
/** /**

View File

@ -8,6 +8,7 @@
#include "Accessible-inl.h" #include "Accessible-inl.h"
#include "nsEventShell.h" #include "nsEventShell.h"
#include "DocAccessible.h" #include "DocAccessible.h"
#include "DocAccessibleChild.h"
#include "nsAccessibilityService.h" #include "nsAccessibilityService.h"
#include "nsTextEquivUtils.h" #include "nsTextEquivUtils.h"
#ifdef A11Y_LOG #ifdef A11Y_LOG
@ -555,5 +556,15 @@ EventQueue::ProcessEventQueue()
if (!mDocument) if (!mDocument)
return; return;
if (XRE_GetProcessType() != GeckoProcessType_Default) {
DocAccessibleChild* ipcDoc = mDocument->IPCDoc();
if (event->mEventType == nsIAccessibleEvent::EVENT_SHOW)
ipcDoc->ShowEvent(downcast_accEvent(event));
else if (event->mEventType == nsIAccessibleEvent::EVENT_HIDE)
ipcDoc->SendHideEvent(reinterpret_cast<uintptr_t>(event->GetAccessible()));
else
ipcDoc->SendEvent(event->GetEventType());
}
} }
} }

View File

@ -6,9 +6,11 @@
#include "NotificationController.h" #include "NotificationController.h"
#include "DocAccessible-inl.h" #include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "TextLeafAccessible.h" #include "TextLeafAccessible.h"
#include "TextUpdater.h" #include "TextUpdater.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
#include "mozilla/Telemetry.h" #include "mozilla/Telemetry.h"
@ -217,8 +219,19 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
if (ownerContent) { if (ownerContent) {
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent); Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) { if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
if (mDocument->AppendChildDocument(childDoc)) if (mDocument->AppendChildDocument(childDoc)) {
if (XRE_GetProcessType() != GeckoProcessType_Default) {
DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc);
childDoc->SetIPCDoc(ipcDoc);
auto contentChild = dom::ContentChild::GetSingleton();
DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
uint64_t id = reinterpret_cast<uintptr_t>(outerDocAcc->UniqueID());
contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc,
id);
}
continue; continue;
}
outerDocAcc->RemoveChild(childDoc); outerDocAcc->RemoveChild(childDoc);
} }

View File

@ -7,6 +7,8 @@
namespace mozilla { namespace mozilla {
namespace a11y { namespace a11y {
class ProxyAccessible;
enum EPlatformDisabledState { enum EPlatformDisabledState {
ePlatformIsForceEnabled = -1, ePlatformIsForceEnabled = -1,
ePlatformIsEnabled = 0, ePlatformIsEnabled = 0,
@ -47,6 +49,17 @@ void PlatformInit();
*/ */
void PlatformShutdown(); 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 a11y
} // namespace mozilla } // namespace mozilla

View File

@ -783,7 +783,9 @@ enum Role {
/** /**
* Represent a keyboard or keypad key (ARIA role "key"). * Represent a keyboard or keypad key (ARIA role "key").
*/ */
KEY = 129 KEY = 129,
LAST_ROLE = KEY
}; };
} // namespace role } // namespace role

View File

@ -60,6 +60,7 @@ if CONFIG['A11Y_LOG']:
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [
'/accessible/generic', '/accessible/generic',
'/accessible/html', '/accessible/html',
'/accessible/ipc',
'/accessible/xpcom', '/accessible/xpcom',
'/accessible/xul', '/accessible/xul',
'/dom/xbl', '/dom/xbl',
@ -93,3 +94,5 @@ FINAL_LIBRARY = 'xul'
if CONFIG['MOZ_ENABLE_GTK']: if CONFIG['MOZ_ENABLE_GTK']:
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS'] CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -6,6 +6,7 @@
#include "Accessible-inl.h" #include "Accessible-inl.h"
#include "AccIterator.h" #include "AccIterator.h"
#include "DocAccessible-inl.h" #include "DocAccessible-inl.h"
#include "DocAccessibleChild.h"
#include "HTMLImageMapAccessible.h" #include "HTMLImageMapAccessible.h"
#include "nsAccCache.h" #include "nsAccCache.h"
#include "nsAccessiblePivot.h" #include "nsAccessiblePivot.h"
@ -83,7 +84,7 @@ DocAccessible::
mScrollPositionChangedTicks(0), mScrollPositionChangedTicks(0),
mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0), mLoadState(eTreeConstructionPending), mDocFlags(0), mLoadEventType(0),
mVirtualCursor(nullptr), mVirtualCursor(nullptr),
mPresShell(aPresShell) mPresShell(aPresShell), mIPCDoc(nullptr)
{ {
mGenericTypes |= eDocument; mGenericTypes |= eDocument;
mStateFlags |= eNotNodeMapEntry; mStateFlags |= eNotNodeMapEntry;
@ -473,6 +474,12 @@ DocAccessible::Shutdown()
mChildDocuments.Clear(); mChildDocuments.Clear();
// XXX thinking about ordering?
if (XRE_GetProcessType() != GeckoProcessType_Default) {
DocAccessibleChild::Send__delete__(mIPCDoc);
MOZ_ASSERT(!mIPCDoc);
}
if (mVirtualCursor) { if (mVirtualCursor) {
mVirtualCursor->RemoveObserver(this); mVirtualCursor->RemoveObserver(this);
mVirtualCursor = nullptr; mVirtualCursor = nullptr;
@ -1436,6 +1443,13 @@ DocAccessible::DoInitialUpdate()
nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent()); nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(Parent());
ParentDocument()->FireDelayedEvent(reorderEvent); ParentDocument()->FireDelayedEvent(reorderEvent);
} }
uint32_t childCount = ChildCount();
for (uint32_t i = 0; i < childCount; i++) {
Accessible* child = GetChildAt(i);
nsRefPtr<AccShowEvent> event = new AccShowEvent(child, child->GetContent());
FireDelayedEvent(event);
}
} }
void void

View File

@ -33,6 +33,7 @@ namespace a11y {
class DocManager; class DocManager;
class NotificationController; class NotificationController;
class DocAccessibleChild;
class RelatedAccIterator; class RelatedAccIterator;
template<class Class, class Arg> template<class Class, class Arg>
class TNotification; class TNotification;
@ -519,6 +520,20 @@ protected:
*/ */
bool IsLoadEventTarget() const; 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. * Used to fire scrolling end event after page scroll.
* *
@ -642,6 +657,9 @@ protected:
private: private:
nsIPresShell* mPresShell; nsIPresShell* mPresShell;
// Exclusively owned by IPDL so don't manually delete it!
DocAccessibleChild* mIPCDoc;
}; };
inline DocAccessible* inline DocAccessible*

View File

@ -28,6 +28,7 @@ UNIFIED_SOURCES += [
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [
'/accessible/base', '/accessible/base',
'/accessible/html', '/accessible/html',
'/accessible/ipc',
'/accessible/xpcom', '/accessible/xpcom',
'/accessible/xul', '/accessible/xul',
'/content/base/src', '/content/base/src',
@ -54,3 +55,5 @@ else:
] ]
FINAL_LIBRARY = 'xul' FINAL_LIBRARY = 'xul'
include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocAccessibleChild.h"
#include "Accessible-inl.h"
namespace mozilla {
namespace a11y {
void
SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
{
uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
uint32_t role = aRoot->Role();
uint32_t childCount = aRoot->ChildCount();
nsString name;
aRoot->Name(name);
aTree.AppendElement(AccessibleData(id, role, childCount, name));
for (uint32_t i = 0; i < childCount; i++)
SerializeTree(aRoot->GetChildAt(i), aTree);
}
void
DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
{
Accessible* parent = aShowEvent->Parent();
uint64_t parentID = parent->IsDoc() ? 0 : reinterpret_cast<uint64_t>(parent->UniqueID());
uint32_t idxInParent = aShowEvent->GetAccessible()->IndexInParent();
nsTArray<AccessibleData> shownTree;
ShowEventData data(parentID, idxInParent, shownTree);
SerializeTree(aShowEvent->GetAccessible(), data.NewTree());
SendShowEvent(data);
}
}
}

View File

@ -0,0 +1,43 @@
/* -*- 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

View File

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DocAccessibleParent.h"
#include "nsAutoPtr.h"
#include "mozilla/a11y/Platform.h"
namespace mozilla {
namespace a11y {
bool
DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
{
if (aData.NewTree().IsEmpty()) {
NS_ERROR("no children being added");
return false;
}
ProxyAccessible* parent = nullptr;
if (aData.ID()) {
ProxyEntry* e = mAccessibles.GetEntry(aData.ID());
if (e)
parent = e->mProxy;
} else {
parent = this;
}
// XXX This should really never happen, but sometimes we fail to fire the
// required show events.
if (!parent) {
NS_ERROR("adding child to unknown accessible");
return false;
}
uint32_t newChildIdx = aData.Idx();
if (newChildIdx > parent->ChildrenCount()) {
NS_ERROR("invalid index to add child at");
return false;
}
uint32_t consumed = AddSubtree(parent, aData.NewTree(), 0, newChildIdx);
MOZ_ASSERT(consumed == aData.NewTree().Length());
for (uint32_t i = 0; i < consumed; i++) {
uint64_t id = aData.NewTree()[i].ID();
MOZ_ASSERT(mAccessibles.GetEntry(id));
}
return consumed;
}
uint32_t
DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
const nsTArray<a11y::AccessibleData>& aNewTree,
uint32_t aIdx, uint32_t aIdxInParent)
{
if (aNewTree.Length() <= aIdx) {
NS_ERROR("bad index in serialized tree!");
return 0;
}
const AccessibleData& newChild = aNewTree[aIdx];
if (newChild.Role() > roles::LAST_ROLE) {
NS_ERROR("invalid role");
return 0;
}
auto role = static_cast<a11y::role>(newChild.Role());
ProxyAccessible* newProxy =
new ProxyAccessible(newChild.ID(), aParent, this, role, newChild.Name());
aParent->AddChildAt(aIdxInParent, newProxy);
mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy;
ProxyCreated(newProxy);
uint32_t accessibles = 1;
uint32_t kids = newChild.ChildrenCount();
for (uint32_t i = 0; i < kids; i++) {
uint32_t consumed = AddSubtree(newProxy, aNewTree, aIdx + accessibles, i);
if (!consumed)
return 0;
accessibles += consumed;
}
MOZ_ASSERT(newProxy->ChildrenCount() == kids);
return accessibles;
}
bool
DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
{
ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
if (!rootEntry) {
NS_ERROR("invalid root being removed!");
return true;
}
ProxyAccessible* root = rootEntry->mProxy;
if (!root) {
NS_ERROR("invalid root being removed!");
return true;
}
root->Shutdown();
return true;
}
}
}

View File

@ -0,0 +1,141 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_DocAccessibleParent_h
#define mozilla_a11y_DocAccessibleParent_h
#include "nsAccessibilityService.h"
#include "ProxyAccessible.h"
#include "mozilla/a11y/PDocAccessibleParent.h"
#include "nsClassHashtable.h"
#include "nsHashKeys.h"
#include "nsISupportsImpl.h"
namespace mozilla {
namespace a11y {
/*
* These objects live in the main process and comunicate with and represent
* an accessible document in a content process.
*/
class DocAccessibleParent : public ProxyAccessible,
public PDocAccessibleParent
{
public:
DocAccessibleParent() :
mParentDoc(nullptr)
{ MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
~DocAccessibleParent()
{
MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
MOZ_ASSERT(mChildDocs.Length() == 0);
MOZ_ASSERT(!mParentDoc);
}
/*
* Called when a message from a document in a child process notifies the main
* process it is firing an event.
*/
virtual bool RecvEvent(const uint32_t& aType) MOZ_OVERRIDE
{
return true;
}
virtual bool RecvShowEvent(const ShowEventData& aData) MOZ_OVERRIDE;
virtual bool RecvHideEvent(const uint64_t& aRootID) MOZ_OVERRIDE;
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE
{
MOZ_ASSERT(mChildDocs.IsEmpty(),
"why wheren't the child docs destroyed already?");
mParentDoc ? mParentDoc->RemoveChildDoc(this)
: GetAccService()->RemoteDocShutdown(this);
}
/*
* Return the main processes representation of the parent document (if any)
* of the document this object represents.
*/
DocAccessibleParent* Parent() const { return mParentDoc; }
/*
* Called when a document in a content process notifies the main process of a
* new child document.
*/
bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID)
{
ProxyAccessible* outerDoc = mAccessibles.GetEntry(aParentID)->mProxy;
if (!outerDoc)
return false;
aChildDoc->mParent = outerDoc;
outerDoc->SetChildDoc(aChildDoc);
mChildDocs.AppendElement(aChildDoc);
aChildDoc->mParentDoc = this;
return true;
}
/*
* Called when the document in the content process this object represents
* notifies the main process a child document has been removed.
*/
void RemoveChildDoc(DocAccessibleParent* aChildDoc)
{
aChildDoc->mParent->SetChildDoc(nullptr);
mChildDocs.RemoveElement(aChildDoc);
aChildDoc->mParentDoc = nullptr;
MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
}
void RemoveAccessible(ProxyAccessible* aAccessible)
{
MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
mAccessibles.RemoveEntry(aAccessible->ID());
}
private:
class ProxyEntry : public PLDHashEntryHdr
{
public:
ProxyEntry(const void*) : mProxy(nullptr) {}
ProxyEntry(ProxyEntry&& aOther) :
mProxy(aOther.mProxy) { aOther.mProxy = nullptr; }
~ProxyEntry() { delete mProxy; }
typedef uint64_t KeyType;
typedef const void* KeyTypePointer;
bool KeyEquals(const void* aKey) const
{ return mProxy->ID() == (uint64_t)aKey; }
static const void* KeyToPointer(uint64_t aKey) { return (void*)aKey; }
static PLDHashNumber HashKey(const void* aKey) { return (uint64_t)aKey; }
enum { ALLOW_MEMMOVE = true };
ProxyAccessible* mProxy;
};
uint32_t AddSubtree(ProxyAccessible* aParent,
const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
uint32_t aIdxInParent);
nsTArray<DocAccessibleParent*> mChildDocs;
DocAccessibleParent* mParentDoc;
/*
* Conceptually this is a map from IDs to proxies, but we store the ID in the
* proxy object so we can't use a real map.
*/
nsTHashtable<ProxyEntry> mAccessibles;
};
}
}
#endif

View File

@ -0,0 +1,44 @@
/* -*- 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);
};
}
}

View File

@ -0,0 +1,42 @@
/* -*- 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;
}
}
}
}

View File

@ -0,0 +1,77 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_a11y_ProxyAccessible_h
#define mozilla_a11y_ProxyAccessible_h
#include "mozilla/a11y/Role.h"
#include "nsString.h"
#include "nsTArray.h"
namespace mozilla {
namespace a11y {
class DocAccessibleParent;
class ProxyAccessible
{
public:
ProxyAccessible(uint64_t aID, ProxyAccessible* aParent,
DocAccessibleParent* aDoc, role aRole,
const nsString& aName) :
mParent(aParent), mDoc(aDoc), mID(aID), mRole(aRole), mOuterDoc(false), mName(aName)
{
MOZ_COUNT_CTOR(ProxyAccessible);
}
~ProxyAccessible() { MOZ_COUNT_DTOR(ProxyAccessible); }
void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
{ mChildren.InsertElementAt(aIdx, aChild); }
uint32_t ChildrenCount() const { return mChildren.Length(); }
void Shutdown();
void SetChildDoc(DocAccessibleParent*);
/**
* Get the role of the accessible we're proxying.
*/
role Role() const { return mRole; }
/**
* Allow the platform to store a pointers worth of data on us.
*/
uintptr_t GetWrapper() const { return mWrapper; }
void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
/*
* Return the ID of the accessible being proxied.
*/
uint64_t ID() const { return mID; }
protected:
ProxyAccessible() :
mParent(nullptr), mDoc(nullptr) { MOZ_COUNT_CTOR(ProxyAccessible); }
protected:
ProxyAccessible* mParent;
private:
nsTArray<ProxyAccessible*> mChildren;
DocAccessibleParent* mDoc;
uintptr_t mWrapper;
uint64_t mID;
role mRole : 31;
bool mOuterDoc : 1;
nsString mName;
};
}
}
#endif

28
accessible/ipc/moz.build Normal file
View File

@ -0,0 +1,28 @@
# -*- 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')

View File

@ -33,6 +33,15 @@ PlatformShutdown()
{ {
} }
void
ProxyCreated(ProxyAccessible*)
{
}
void
ProxyDestroyed(ProxyAccessible*)
{
}
} }
} }

View File

@ -15,7 +15,7 @@ elif toolkit == 'cocoa':
else: else:
DIRS += ['other'] DIRS += ['other']
DIRS += ['base', 'generic', 'html', 'interfaces', 'jsat', 'xpcom'] DIRS += ['base', 'generic', 'html', 'interfaces', 'ipc', 'jsat', 'xpcom']
if CONFIG['MOZ_XUL']: if CONFIG['MOZ_XUL']:
DIRS += ['xul'] DIRS += ['xul']

View File

@ -18,3 +18,13 @@ void
a11y::PlatformShutdown() a11y::PlatformShutdown()
{ {
} }
void
a11y::ProxyCreated(ProxyAccessible*)
{
}
void
a11y::ProxyDestroyed(ProxyAccessible*)
{
}

View File

@ -34,3 +34,12 @@ a11y::PlatformShutdown()
nsWinUtils::ShutdownWindowEmulation(); nsWinUtils::ShutdownWindowEmulation();
} }
void
a11y::ProxyCreated(ProxyAccessible*)
{
}
void
a11y::ProxyDestroyed(ProxyAccessible*)
{
}

View File

@ -19,6 +19,7 @@
#include "TabChild.h" #include "TabChild.h"
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/a11y/DocAccessibleChild.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/ContentBridgeChild.h" #include "mozilla/dom/ContentBridgeChild.h"
#include "mozilla/dom/ContentBridgeParent.h" #include "mozilla/dom/ContentBridgeParent.h"
@ -705,6 +706,20 @@ ContentChild::InitXPCOM()
InitOnContentProcessCreated(); InitOnContentProcessCreated();
} }
a11y::PDocAccessibleChild*
ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
{
MOZ_ASSERT(false, "should never call this!");
return nullptr;
}
bool
ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
{
delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
return true;
}
PMemoryReportRequestChild* PMemoryReportRequestChild*
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration, ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
const bool &aAnonymize, const bool &aAnonymize,

View File

@ -376,6 +376,8 @@ public:
const uint64_t& aID, const uint64_t& aID,
const bool& aIsForApp, const bool& aIsForApp,
const bool& aIsForBrowser) MOZ_OVERRIDE; const bool& aIsForBrowser) MOZ_OVERRIDE;
virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) MOZ_OVERRIDE;
virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) MOZ_OVERRIDE;
private: private:
virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;

View File

@ -30,6 +30,8 @@
#include "CrashReporterParent.h" #include "CrashReporterParent.h"
#include "IHistory.h" #include "IHistory.h"
#include "mozIApplication.h" #include "mozIApplication.h"
#include "mozilla/a11y/DocAccessibleParent.h"
#include "nsAccessibilityService.h"
#include "mozilla/ClearOnShutdown.h" #include "mozilla/ClearOnShutdown.h"
#include "mozilla/dom/asmjscache/AsmJSCache.h" #include "mozilla/dom/asmjscache/AsmJSCache.h"
#include "mozilla/dom/Element.h" #include "mozilla/dom/Element.h"
@ -2695,6 +2697,34 @@ ContentParent::Observe(nsISupports* aSubject,
return NS_OK; return NS_OK;
} }
a11y::PDocAccessibleParent*
ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&)
{
return new a11y::DocAccessibleParent();
}
bool
ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
{
delete static_cast<a11y::DocAccessibleParent*>(aParent);
return true;
}
bool
ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID)
{
auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
if (aParentDoc) {
MOZ_ASSERT(aParentID);
auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
return parentDoc->AddChildDoc(doc, aParentID);
} else {
MOZ_ASSERT(!aParentID);
GetAccService()->RemoteDocAdded(doc);
}
return true;
}
PCompositorParent* PCompositorParent*
ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport, ContentParent::AllocPCompositorParent(mozilla::ipc::Transport* aTransport,
base::ProcessId aOtherProcess) base::ProcessId aOtherProcess)

View File

@ -662,6 +662,11 @@ private:
int32_t* aSliceRefCnt, int32_t* aSliceRefCnt,
bool* aResult) MOZ_OVERRIDE; 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 // If you add strong pointers to cycle collected objects here, be sure to
// release these objects in ShutDownProcess. See the comment there for more // release these objects in ShutDownProcess. See the comment there for more
// details. // details.

View File

@ -13,6 +13,7 @@ include protocol PCompositor;
include protocol PContentBridge; include protocol PContentBridge;
include protocol PCycleCollectWithLogs; include protocol PCycleCollectWithLogs;
include protocol PCrashReporter; include protocol PCrashReporter;
include protocol PDocAccessible;
include protocol PExternalHelperApp; include protocol PExternalHelperApp;
include protocol PDeviceStorageRequest; include protocol PDeviceStorageRequest;
include protocol PFileDescriptorSet; include protocol PFileDescriptorSet;
@ -325,6 +326,7 @@ intr protocol PContent
manages PBrowser; manages PBrowser;
manages PCrashReporter; manages PCrashReporter;
manages PCycleCollectWithLogs; manages PCycleCollectWithLogs;
manages PDocAccessible;
manages PDeviceStorageRequest; manages PDeviceStorageRequest;
manages PFileSystemRequest; manages PFileSystemRequest;
manages PExternalHelperApp; manages PExternalHelperApp;
@ -481,6 +483,14 @@ child:
OnAppThemeChanged(); OnAppThemeChanged();
parent: 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 * Tell the content process some attributes of itself. This is
* among the first information queried by content processes after * among the first information queried by content processes after