mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 12:27:29 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
17
accessible/.eslintrc.js
Normal file
17
accessible/.eslintrc.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../.eslintrc.js"
|
||||
],
|
||||
"globals": {
|
||||
"Cc": true,
|
||||
"Ci": true,
|
||||
"Components": true,
|
||||
"console": true,
|
||||
"Cu": true,
|
||||
"dump": true,
|
||||
"Services": true,
|
||||
"XPCOMUtils": true
|
||||
}
|
||||
};
|
||||
68
accessible/aom/AccessibleNode.cpp
Normal file
68
accessible/aom/AccessibleNode.cpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "AccessibleNode.h"
|
||||
#include "mozilla/dom/AccessibleNodeBinding.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "DocAccessible.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(AccessibleNode)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AccessibleNode)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(AccessibleNode)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(AccessibleNode)
|
||||
|
||||
AccessibleNode::AccessibleNode(nsINode* aNode) : mDOMNode(aNode)
|
||||
{
|
||||
DocAccessible* doc =
|
||||
GetOrCreateAccService()->GetDocAccessible(mDOMNode->OwnerDoc());
|
||||
if (doc) {
|
||||
mIntl = doc->GetAccessible(mDOMNode);
|
||||
}
|
||||
}
|
||||
|
||||
AccessibleNode::~AccessibleNode()
|
||||
{
|
||||
}
|
||||
|
||||
/* virtual */ JSObject*
|
||||
AccessibleNode::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return AccessibleNodeBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
/* virtual */ ParentObject
|
||||
AccessibleNode::GetParentObject() const
|
||||
{
|
||||
return mDOMNode->GetParentObject();
|
||||
}
|
||||
|
||||
void
|
||||
AccessibleNode::GetRole(nsAString& aRole)
|
||||
{
|
||||
if (mIntl) {
|
||||
GetOrCreateAccService()->GetStringRole(mIntl->Role(), aRole);
|
||||
return;
|
||||
}
|
||||
|
||||
aRole.AssignLiteral("unknown");
|
||||
}
|
||||
|
||||
nsINode*
|
||||
AccessibleNode::GetDOMNode()
|
||||
{
|
||||
return mDOMNode;
|
||||
}
|
||||
53
accessible/aom/AccessibleNode.h
Normal file
53
accessible/aom/AccessibleNode.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=40: */
|
||||
/* 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 A11Y_AOM_ACCESSIBLENODE_H
|
||||
#define A11Y_AOM_ACCESSIBLENODE_H
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
|
||||
class nsINode;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace a11y {
|
||||
class Accessible;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
|
||||
struct ParentObject;
|
||||
|
||||
class AccessibleNode : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
explicit AccessibleNode(nsINode* aNode);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AccessibleNode);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
|
||||
virtual dom::ParentObject GetParentObject() const final;
|
||||
|
||||
void GetRole(nsAString& aRole);
|
||||
nsINode* GetDOMNode();
|
||||
|
||||
protected:
|
||||
AccessibleNode(const AccessibleNode& aCopy) = delete;
|
||||
AccessibleNode& operator=(const AccessibleNode& aCopy) = delete;
|
||||
virtual ~AccessibleNode();
|
||||
|
||||
RefPtr<a11y::Accessible> mIntl;
|
||||
RefPtr<nsINode> mDOMNode;
|
||||
};
|
||||
|
||||
} // dom
|
||||
} // mozilla
|
||||
|
||||
|
||||
#endif // A11Y_JSAPI_ACCESSIBLENODE
|
||||
40
accessible/aom/moz.build
Normal file
40
accessible/aom/moz.build
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# -*- Mode: python; 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/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'AccessibleNode.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AccessibleNode.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
]
|
||||
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/atk',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
21
accessible/atk/ARIAGridAccessibleWrap.h
Normal file
21
accessible/atk/ARIAGridAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- 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_ARIAGRIDACCESSIBLEWRAP_H
|
||||
#define MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H
|
||||
|
||||
#include "ARIAGridAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class ARIAGridAccessible ARIAGridAccessibleWrap;
|
||||
typedef class ARIAGridCellAccessible ARIAGridCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
1759
accessible/atk/AccessibleWrap.cpp
Normal file
1759
accessible/atk/AccessibleWrap.cpp
Normal file
File diff suppressed because it is too large
Load diff
92
accessible/atk/AccessibleWrap.h
Normal file
92
accessible/atk/AccessibleWrap.h
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* -*- 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 __NS_ACCESSIBLE_WRAP_H__
|
||||
#define __NS_ACCESSIBLE_WRAP_H__
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "Accessible.h"
|
||||
|
||||
struct _AtkObject;
|
||||
typedef struct _AtkObject AtkObject;
|
||||
|
||||
enum AtkProperty {
|
||||
PROP_0, // gobject convention
|
||||
PROP_NAME,
|
||||
PROP_DESCRIPTION,
|
||||
PROP_PARENT, // ancestry has changed
|
||||
PROP_ROLE,
|
||||
PROP_LAYER,
|
||||
PROP_MDI_ZORDER,
|
||||
PROP_TABLE_CAPTION,
|
||||
PROP_TABLE_COLUMN_DESCRIPTION,
|
||||
PROP_TABLE_COLUMN_HEADER,
|
||||
PROP_TABLE_ROW_DESCRIPTION,
|
||||
PROP_TABLE_ROW_HEADER,
|
||||
PROP_TABLE_SUMMARY,
|
||||
PROP_LAST // gobject convention
|
||||
};
|
||||
|
||||
struct AtkPropertyChange {
|
||||
int32_t type; // property type as listed above
|
||||
void *oldvalue;
|
||||
void *newvalue;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class MaiHyperlink;
|
||||
|
||||
/**
|
||||
* AccessibleWrap, and its descendents in atk directory provide the
|
||||
* implementation of AtkObject.
|
||||
*/
|
||||
class AccessibleWrap : public Accessible
|
||||
{
|
||||
public:
|
||||
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
|
||||
virtual ~AccessibleWrap();
|
||||
void ShutdownAtkObject();
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
// return the atk object for this AccessibleWrap
|
||||
virtual void GetNativeInterface(void** aOutAccessible) override;
|
||||
virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
|
||||
|
||||
AtkObject * GetAtkObject(void);
|
||||
static AtkObject* GetAtkObject(Accessible* aAccessible);
|
||||
|
||||
bool IsValidObject();
|
||||
|
||||
static const char * ReturnString(nsAString &aString) {
|
||||
static nsCString returnedString;
|
||||
returnedString = NS_ConvertUTF16toUTF8(aString);
|
||||
return returnedString.get();
|
||||
}
|
||||
|
||||
static void GetKeyBinding(Accessible* aAccessible, nsAString& aResult);
|
||||
|
||||
static Accessible* GetColumnHeader(TableAccessible* aAccessible,
|
||||
int32_t aColIdx);
|
||||
static Accessible* GetRowHeader(TableAccessible* aAccessible,
|
||||
int32_t aRowIdx);
|
||||
protected:
|
||||
|
||||
nsresult FireAtkStateChangeEvent(AccEvent* aEvent, AtkObject *aObject);
|
||||
nsresult FireAtkTextChangedEvent(AccEvent* aEvent, AtkObject *aObject);
|
||||
|
||||
AtkObject *mAtkObject;
|
||||
|
||||
private:
|
||||
uint16_t CreateMaiInterfaces();
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __NS_ACCESSIBLE_WRAP_H__ */
|
||||
167
accessible/atk/ApplicationAccessibleWrap.cpp
Normal file
167
accessible/atk/ApplicationAccessibleWrap.cpp
Normal file
|
|
@ -0,0 +1,167 @@
|
|||
/* -*- 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 "ApplicationAccessibleWrap.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsMai.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <atk/atk.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
|
||||
// ApplicationAccessibleWrap
|
||||
|
||||
ApplicationAccessibleWrap::ApplicationAccessibleWrap():
|
||||
ApplicationAccessible()
|
||||
{
|
||||
}
|
||||
|
||||
ApplicationAccessibleWrap::~ApplicationAccessibleWrap()
|
||||
{
|
||||
AccessibleWrap::ShutdownAtkObject();
|
||||
}
|
||||
|
||||
gboolean
|
||||
toplevel_event_watcher(GSignalInvocationHint* ihint,
|
||||
guint n_param_values,
|
||||
const GValue* param_values,
|
||||
gpointer data)
|
||||
{
|
||||
static GQuark sQuark_gecko_acc_obj = 0;
|
||||
|
||||
if (!sQuark_gecko_acc_obj)
|
||||
sQuark_gecko_acc_obj = g_quark_from_static_string("GeckoAccObj");
|
||||
|
||||
if (nsAccessibilityService::IsShutdown())
|
||||
return TRUE;
|
||||
|
||||
GObject* object = reinterpret_cast<GObject*>(g_value_get_object(param_values));
|
||||
if (!GTK_IS_WINDOW(object))
|
||||
return TRUE;
|
||||
|
||||
AtkObject* child = gtk_widget_get_accessible(GTK_WIDGET(object));
|
||||
|
||||
// GTK native dialog
|
||||
if (!IS_MAI_OBJECT(child) &&
|
||||
(atk_object_get_role(child) == ATK_ROLE_DIALOG)) {
|
||||
|
||||
if (data == reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW)) {
|
||||
|
||||
// Attach the dialog accessible to app accessible tree
|
||||
Accessible* windowAcc = GetAccService()->AddNativeRootAccessible(child);
|
||||
g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj,
|
||||
reinterpret_cast<gpointer>(windowAcc));
|
||||
|
||||
} else {
|
||||
|
||||
// Deattach the dialog accessible
|
||||
Accessible* windowAcc =
|
||||
reinterpret_cast<Accessible*>
|
||||
(g_object_get_qdata(G_OBJECT(child), sQuark_gecko_acc_obj));
|
||||
if (windowAcc) {
|
||||
GetAccService()->RemoveNativeRootAccessible(windowAcc);
|
||||
g_object_set_qdata(G_OBJECT(child), sQuark_gecko_acc_obj, nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ENameValueFlag
|
||||
ApplicationAccessibleWrap::Name(nsString& aName)
|
||||
{
|
||||
// ATK doesn't provide a way to obtain an application name (for example,
|
||||
// Firefox or Thunderbird) like IA2 does. Thus let's return an application
|
||||
// name as accessible name that was used to get a branding name (for example,
|
||||
// Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
|
||||
AppName(aName);
|
||||
return eNameOK;
|
||||
}
|
||||
|
||||
void
|
||||
ApplicationAccessibleWrap::GetNativeInterface(void** aOutAccessible)
|
||||
{
|
||||
*aOutAccessible = nullptr;
|
||||
|
||||
if (!mAtkObject) {
|
||||
mAtkObject =
|
||||
reinterpret_cast<AtkObject*>(g_object_new(MAI_TYPE_ATK_OBJECT, nullptr));
|
||||
if (!mAtkObject)
|
||||
return;
|
||||
|
||||
atk_object_initialize(mAtkObject, this);
|
||||
mAtkObject->role = ATK_ROLE_INVALID;
|
||||
mAtkObject->layer = ATK_LAYER_INVALID;
|
||||
}
|
||||
|
||||
*aOutAccessible = mAtkObject;
|
||||
}
|
||||
|
||||
struct AtkRootAccessibleAddedEvent {
|
||||
AtkObject *app_accessible;
|
||||
AtkObject *root_accessible;
|
||||
uint32_t index;
|
||||
};
|
||||
|
||||
gboolean fireRootAccessibleAddedCB(gpointer data)
|
||||
{
|
||||
AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)data;
|
||||
g_signal_emit_by_name(eventData->app_accessible, "children_changed::add",
|
||||
eventData->index, eventData->root_accessible, nullptr);
|
||||
g_object_unref(eventData->app_accessible);
|
||||
g_object_unref(eventData->root_accessible);
|
||||
free(data);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool
|
||||
ApplicationAccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aChild)
|
||||
{
|
||||
if (!ApplicationAccessible::InsertChildAt(aIdx, aChild))
|
||||
return false;
|
||||
|
||||
AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
|
||||
atk_object_set_parent(atkAccessible, mAtkObject);
|
||||
|
||||
uint32_t count = mChildren.Length();
|
||||
|
||||
// Emit children_changed::add in a timeout
|
||||
// to make sure aRootAccWrap is fully initialized.
|
||||
AtkRootAccessibleAddedEvent* eventData = (AtkRootAccessibleAddedEvent*)
|
||||
malloc(sizeof(AtkRootAccessibleAddedEvent));
|
||||
if (eventData) {
|
||||
eventData->app_accessible = mAtkObject;
|
||||
eventData->root_accessible = atkAccessible;
|
||||
eventData->index = count -1;
|
||||
g_object_ref(mAtkObject);
|
||||
g_object_ref(atkAccessible);
|
||||
g_timeout_add(0, fireRootAccessibleAddedCB, eventData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ApplicationAccessibleWrap::RemoveChild(Accessible* aChild)
|
||||
{
|
||||
int32_t index = aChild->IndexInParent();
|
||||
|
||||
AtkObject* atkAccessible = AccessibleWrap::GetAtkObject(aChild);
|
||||
atk_object_set_parent(atkAccessible, nullptr);
|
||||
g_signal_emit_by_name(mAtkObject, "children_changed::remove", index,
|
||||
atkAccessible, nullptr);
|
||||
|
||||
return ApplicationAccessible::RemoveChild(aChild);
|
||||
}
|
||||
|
||||
35
accessible/atk/ApplicationAccessibleWrap.h
Normal file
35
accessible/atk/ApplicationAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- 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_ApplicationAccessibleWrap_h__
|
||||
#define mozilla_a11y_ApplicationAccessibleWrap_h__
|
||||
|
||||
#include "ApplicationAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class ApplicationAccessibleWrap: public ApplicationAccessible
|
||||
{
|
||||
public:
|
||||
ApplicationAccessibleWrap();
|
||||
virtual ~ApplicationAccessibleWrap();
|
||||
|
||||
// Accessible
|
||||
virtual mozilla::a11y::ENameValueFlag Name(nsString& aName) override;
|
||||
virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
|
||||
virtual bool RemoveChild(Accessible* aChild) override;
|
||||
|
||||
/**
|
||||
* Return the atk object for app root accessible.
|
||||
*/
|
||||
virtual void GetNativeInterface(void** aOutAccessible) override;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __NS_APP_ROOT_ACCESSIBLE_H__ */
|
||||
126
accessible/atk/AtkSocketAccessible.cpp
Normal file
126
accessible/atk/AtkSocketAccessible.cpp
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/* -*- 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 <atk/atk.h>
|
||||
#include "AtkSocketAccessible.h"
|
||||
|
||||
#include "InterfaceInitFuncs.h"
|
||||
#include "nsMai.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
AtkSocketEmbedType AtkSocketAccessible::g_atk_socket_embed = nullptr;
|
||||
GType AtkSocketAccessible::g_atk_socket_type = G_TYPE_INVALID;
|
||||
const char* AtkSocketAccessible::sATKSocketEmbedSymbol = "atk_socket_embed";
|
||||
const char* AtkSocketAccessible::sATKSocketGetTypeSymbol = "atk_socket_get_type";
|
||||
|
||||
bool AtkSocketAccessible::gCanEmbed = FALSE;
|
||||
|
||||
extern "C" void mai_atk_component_iface_init(AtkComponentIface* aIface);
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED(MaiAtkSocket, mai_atk_socket,
|
||||
AtkSocketAccessible::g_atk_socket_type, 0,
|
||||
G_IMPLEMENT_INTERFACE(ATK_TYPE_COMPONENT,
|
||||
mai_atk_component_iface_init))
|
||||
|
||||
void
|
||||
mai_atk_socket_class_init(MaiAtkSocketClass* aAcc)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
mai_atk_socket_init(MaiAtkSocket* aAcc)
|
||||
{
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
mai_atk_socket_new(AccessibleWrap* aAccWrap)
|
||||
{
|
||||
NS_ENSURE_TRUE(aAccWrap, nullptr);
|
||||
|
||||
MaiAtkSocket* acc = nullptr;
|
||||
acc = static_cast<MaiAtkSocket*>(g_object_new(MAI_TYPE_ATK_SOCKET, nullptr));
|
||||
NS_ENSURE_TRUE(acc, nullptr);
|
||||
|
||||
acc->accWrap = aAccWrap;
|
||||
return ATK_OBJECT(acc);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
static AtkObject*
|
||||
RefAccessibleAtPoint(AtkComponent* aComponent, gint aX, gint aY,
|
||||
AtkCoordType aCoordType)
|
||||
{
|
||||
NS_ENSURE_TRUE(MAI_IS_ATK_SOCKET(aComponent), nullptr);
|
||||
|
||||
return refAccessibleAtPointHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)),
|
||||
aX, aY, aCoordType);
|
||||
}
|
||||
|
||||
static void
|
||||
GetExtents(AtkComponent* aComponent, gint* aX, gint* aY, gint* aWidth,
|
||||
gint* aHeight, AtkCoordType aCoordType)
|
||||
{
|
||||
*aX = *aY = *aWidth = *aHeight = 0;
|
||||
|
||||
if (!MAI_IS_ATK_SOCKET(aComponent))
|
||||
return;
|
||||
|
||||
getExtentsHelper(ATK_OBJECT(MAI_ATK_SOCKET(aComponent)),
|
||||
aX, aY, aWidth, aHeight, aCoordType);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
mai_atk_component_iface_init(AtkComponentIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid Interface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->ref_accessible_at_point = RefAccessibleAtPoint;
|
||||
aIface->get_extents = GetExtents;
|
||||
}
|
||||
|
||||
AtkSocketAccessible::AtkSocketAccessible(nsIContent* aContent,
|
||||
DocAccessible* aDoc,
|
||||
const nsCString& aPlugId) :
|
||||
AccessibleWrap(aContent, aDoc)
|
||||
{
|
||||
mAtkObject = mai_atk_socket_new(this);
|
||||
if (!mAtkObject)
|
||||
return;
|
||||
|
||||
// Embeds the children of an AtkPlug, specified by plugId, as the children of
|
||||
// this socket.
|
||||
// Using G_TYPE macros instead of ATK_SOCKET macros to avoid undefined
|
||||
// symbols.
|
||||
if (gCanEmbed && G_TYPE_CHECK_INSTANCE_TYPE(mAtkObject, g_atk_socket_type) &&
|
||||
!aPlugId.IsVoid()) {
|
||||
AtkSocket* accSocket =
|
||||
G_TYPE_CHECK_INSTANCE_CAST(mAtkObject, g_atk_socket_type, AtkSocket);
|
||||
g_atk_socket_embed(accSocket, (gchar*)aPlugId.get());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AtkSocketAccessible::GetNativeInterface(void** aOutAccessible)
|
||||
{
|
||||
*aOutAccessible = mAtkObject;
|
||||
}
|
||||
|
||||
void
|
||||
AtkSocketAccessible::Shutdown()
|
||||
{
|
||||
if (mAtkObject) {
|
||||
if (MAI_IS_ATK_SOCKET(mAtkObject))
|
||||
MAI_ATK_SOCKET(mAtkObject)->accWrap = nullptr;
|
||||
g_object_unref(mAtkObject);
|
||||
mAtkObject = nullptr;
|
||||
}
|
||||
AccessibleWrap::Shutdown();
|
||||
}
|
||||
56
accessible/atk/AtkSocketAccessible.h
Normal file
56
accessible/atk/AtkSocketAccessible.h
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
/* -*- 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 _AtkSocketAccessible_H_
|
||||
#define _AtkSocketAccessible_H_
|
||||
|
||||
#include "AccessibleWrap.h"
|
||||
|
||||
// This file gets included by nsAccessibilityService.cpp, which can't include
|
||||
// atk.h (or glib.h), so we can't rely on it being included.
|
||||
#ifdef __ATK_H__
|
||||
extern "C" typedef void (*AtkSocketEmbedType) (AtkSocket*, gchar*);
|
||||
#else
|
||||
extern "C" typedef void (*AtkSocketEmbedType) (void*, void*);
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Provides a AccessibleWrap wrapper around AtkSocket for out-of-process
|
||||
* accessibles.
|
||||
*/
|
||||
class AtkSocketAccessible : public AccessibleWrap
|
||||
{
|
||||
public:
|
||||
|
||||
// Soft references to AtkSocket
|
||||
static AtkSocketEmbedType g_atk_socket_embed;
|
||||
#ifdef __ATK_H__
|
||||
static GType g_atk_socket_type;
|
||||
#endif
|
||||
static const char* sATKSocketEmbedSymbol;
|
||||
static const char* sATKSocketGetTypeSymbol;
|
||||
|
||||
/*
|
||||
* True if the current Atk version supports AtkSocket and it was correctly
|
||||
* loaded.
|
||||
*/
|
||||
static bool gCanEmbed;
|
||||
|
||||
AtkSocketAccessible(nsIContent* aContent, DocAccessible* aDoc,
|
||||
const nsCString& aPlugId);
|
||||
|
||||
virtual void Shutdown() override;
|
||||
|
||||
virtual void GetNativeInterface(void** aOutAccessible) override;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
25
accessible/atk/DocAccessibleWrap.cpp
Normal file
25
accessible/atk/DocAccessibleWrap.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- 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 "nsMai.h"
|
||||
#include "DocAccessibleWrap.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocAccessibleWrap
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DocAccessibleWrap::
|
||||
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
|
||||
DocAccessible(aDocument, aPresShell), mActivated(false)
|
||||
{
|
||||
}
|
||||
|
||||
DocAccessibleWrap::~DocAccessibleWrap()
|
||||
{
|
||||
}
|
||||
|
||||
31
accessible/atk/DocAccessibleWrap.h
Normal file
31
accessible/atk/DocAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/* For documentation of the accessibility architecture,
|
||||
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
|
||||
*/
|
||||
|
||||
#ifndef mozilla_a11y_DocAccessibleWrap_h__
|
||||
#define mozilla_a11y_DocAccessibleWrap_h__
|
||||
|
||||
#include "DocAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessibleWrap : public DocAccessible
|
||||
{
|
||||
public:
|
||||
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
virtual ~DocAccessibleWrap();
|
||||
|
||||
bool mActivated;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
23
accessible/atk/HTMLTableAccessibleWrap.h
Normal file
23
accessible/atk/HTMLTableAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- 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_HTMLTableAccessibleWrap_h__
|
||||
#define mozilla_a11y_HTMLTableAccessibleWrap_h__
|
||||
|
||||
#include "HTMLTableAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class HTMLTableAccessible HTMLTableAccessibleWrap;
|
||||
typedef class HTMLTableCellAccessible HTMLTableCellAccessibleWrap;
|
||||
typedef class HTMLTableHeaderCellAccessible HTMLTableHeaderCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
21
accessible/atk/HyperTextAccessibleWrap.h
Normal file
21
accessible/atk/HyperTextAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- 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_HyperTextAccessibleWrap_h__
|
||||
#define mozilla_a11y_HyperTextAccessibleWrap_h__
|
||||
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class HyperTextAccessible HyperTextAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
21
accessible/atk/ImageAccessibleWrap.h
Normal file
21
accessible/atk/ImageAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- 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_ImageAccessibleWrap_h__
|
||||
#define mozilla_a11y_ImageAccessibleWrap_h__
|
||||
|
||||
#include "ImageAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class ImageAccessible ImageAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
44
accessible/atk/InterfaceInitFuncs.h
Normal file
44
accessible/atk/InterfaceInitFuncs.h
Normal 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/. */
|
||||
|
||||
#ifndef ATK_INTERFACE_INIT_FUNCS_H_
|
||||
#define ATK_INTERFACE_INIT_FUNCS_H_
|
||||
|
||||
#include <atk/atk.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class AccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
extern "C" {
|
||||
void actionInterfaceInitCB(AtkActionIface* aIface);
|
||||
void componentInterfaceInitCB(AtkComponentIface* aIface);
|
||||
void documentInterfaceInitCB(AtkDocumentIface *aIface);
|
||||
void editableTextInterfaceInitCB(AtkEditableTextIface* aIface);
|
||||
void hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface *aIface);
|
||||
void hypertextInterfaceInitCB(AtkHypertextIface* aIface);
|
||||
void imageInterfaceInitCB(AtkImageIface* aIface);
|
||||
void selectionInterfaceInitCB(AtkSelectionIface* aIface);
|
||||
void tableInterfaceInitCB(AtkTableIface *aIface);
|
||||
void tableCellInterfaceInitCB(AtkTableCellIface *aIface);
|
||||
void textInterfaceInitCB(AtkTextIface* aIface);
|
||||
void valueInterfaceInitCB(AtkValueIface *aIface);
|
||||
}
|
||||
|
||||
/**
|
||||
* XXX these should live in a file of utils for atk.
|
||||
*/
|
||||
AtkObject* refAccessibleAtPointHelper(AtkObject* aAtkObj,
|
||||
gint aX, gint aY, AtkCoordType aCoordType);
|
||||
void getExtentsHelper(AtkObject* aAtkObj,
|
||||
gint* aX, gint* aY, gint* aWidth, gint* aHeight,
|
||||
AtkCoordType aCoordType);
|
||||
|
||||
#endif // ATK_INTERFACE_INIT_FUNCS_H_
|
||||
377
accessible/atk/Platform.cpp
Normal file
377
accessible/atk/Platform.cpp
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/* -*- 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 "Platform.h"
|
||||
|
||||
#include "nsIAccessibleEvent.h"
|
||||
#include "nsIGConfService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsMai.h"
|
||||
#include "AtkSocketAccessible.h"
|
||||
#include "prenv.h"
|
||||
#include "prlink.h"
|
||||
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
#include <dbus/dbus.h>
|
||||
#endif
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
extern "C" __attribute__((weak,visibility("default"))) int atk_bridge_adaptor_init(int*, char **[]);
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
int atkMajorVersion = 1, atkMinorVersion = 12;
|
||||
|
||||
GType (*gAtkTableCellGetTypeFunc)();
|
||||
|
||||
extern "C" {
|
||||
typedef GType (* AtkGetTypeType) (void);
|
||||
typedef void (*GnomeAccessibilityInit) (void);
|
||||
typedef void (*GnomeAccessibilityShutdown) (void);
|
||||
}
|
||||
|
||||
static PRLibrary* sATKLib = nullptr;
|
||||
static const char sATKLibName[] = "libatk-1.0.so.0";
|
||||
static const char sATKHyperlinkImplGetTypeSymbol[] =
|
||||
"atk_hyperlink_impl_get_type";
|
||||
|
||||
gboolean toplevel_event_watcher(GSignalInvocationHint*, guint, const GValue*,
|
||||
gpointer);
|
||||
static bool sToplevel_event_hook_added = false;
|
||||
static gulong sToplevel_show_hook = 0;
|
||||
static gulong sToplevel_hide_hook = 0;
|
||||
|
||||
GType g_atk_hyperlink_impl_type = G_TYPE_INVALID;
|
||||
|
||||
struct GnomeAccessibilityModule
|
||||
{
|
||||
const char *libName;
|
||||
PRLibrary *lib;
|
||||
const char *initName;
|
||||
GnomeAccessibilityInit init;
|
||||
const char *shutdownName;
|
||||
GnomeAccessibilityShutdown shutdown;
|
||||
};
|
||||
|
||||
static GnomeAccessibilityModule sAtkBridge = {
|
||||
#ifdef AIX
|
||||
"libatk-bridge.a(libatk-bridge.so.0)", nullptr,
|
||||
#else
|
||||
"libatk-bridge.so", nullptr,
|
||||
#endif
|
||||
"gnome_accessibility_module_init", nullptr,
|
||||
"gnome_accessibility_module_shutdown", nullptr
|
||||
};
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
static GnomeAccessibilityModule sGail = {
|
||||
"libgail.so", nullptr,
|
||||
"gnome_accessibility_module_init", nullptr,
|
||||
"gnome_accessibility_module_shutdown", nullptr
|
||||
};
|
||||
#endif
|
||||
|
||||
static nsresult
|
||||
LoadGtkModule(GnomeAccessibilityModule& aModule)
|
||||
{
|
||||
NS_ENSURE_ARG(aModule.libName);
|
||||
|
||||
if (!(aModule.lib = PR_LoadLibrary(aModule.libName))) {
|
||||
//try to load the module with "gtk-2.0/modules" appended
|
||||
char *curLibPath = PR_GetLibraryPath();
|
||||
nsAutoCString libPath(curLibPath);
|
||||
#if defined(LINUX) && defined(__x86_64__)
|
||||
libPath.AppendLiteral(":/usr/lib64:/usr/lib");
|
||||
#else
|
||||
libPath.AppendLiteral(":/usr/lib");
|
||||
#endif
|
||||
PR_FreeLibraryName(curLibPath);
|
||||
|
||||
int16_t loc1 = 0, loc2 = 0;
|
||||
int16_t subLen = 0;
|
||||
while (loc2 >= 0) {
|
||||
loc2 = libPath.FindChar(':', loc1);
|
||||
if (loc2 < 0)
|
||||
subLen = libPath.Length() - loc1;
|
||||
else
|
||||
subLen = loc2 - loc1;
|
||||
nsAutoCString sub(Substring(libPath, loc1, subLen));
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
sub.AppendLiteral("/gtk-2.0/modules/");
|
||||
#else
|
||||
sub.AppendLiteral("/gtk-3.0/modules/");
|
||||
#endif
|
||||
sub.Append(aModule.libName);
|
||||
aModule.lib = PR_LoadLibrary(sub.get());
|
||||
if (aModule.lib)
|
||||
break;
|
||||
|
||||
loc1 = loc2+1;
|
||||
}
|
||||
if (!aModule.lib)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
//we have loaded the library, try to get the function ptrs
|
||||
if (!(aModule.init = PR_FindFunctionSymbol(aModule.lib,
|
||||
aModule.initName)) ||
|
||||
!(aModule.shutdown = PR_FindFunctionSymbol(aModule.lib,
|
||||
aModule.shutdownName))) {
|
||||
|
||||
//fail, :(
|
||||
PR_UnloadLibrary(aModule.lib);
|
||||
aModule.lib = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
a11y::PlatformInit()
|
||||
{
|
||||
if (!ShouldA11yBeEnabled())
|
||||
return;
|
||||
|
||||
sATKLib = PR_LoadLibrary(sATKLibName);
|
||||
if (!sATKLib)
|
||||
return;
|
||||
|
||||
AtkGetTypeType pfn_atk_hyperlink_impl_get_type =
|
||||
(AtkGetTypeType) PR_FindFunctionSymbol(sATKLib, sATKHyperlinkImplGetTypeSymbol);
|
||||
if (pfn_atk_hyperlink_impl_get_type)
|
||||
g_atk_hyperlink_impl_type = pfn_atk_hyperlink_impl_get_type();
|
||||
|
||||
AtkGetTypeType pfn_atk_socket_get_type = (AtkGetTypeType)
|
||||
PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible::sATKSocketGetTypeSymbol);
|
||||
if (pfn_atk_socket_get_type) {
|
||||
AtkSocketAccessible::g_atk_socket_type = pfn_atk_socket_get_type();
|
||||
AtkSocketAccessible::g_atk_socket_embed = (AtkSocketEmbedType)
|
||||
PR_FindFunctionSymbol(sATKLib, AtkSocketAccessible ::sATKSocketEmbedSymbol);
|
||||
AtkSocketAccessible::gCanEmbed =
|
||||
AtkSocketAccessible::g_atk_socket_type != G_TYPE_INVALID &&
|
||||
AtkSocketAccessible::g_atk_socket_embed;
|
||||
}
|
||||
|
||||
gAtkTableCellGetTypeFunc = (GType (*)())
|
||||
PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
|
||||
|
||||
const char* (*atkGetVersion)() =
|
||||
(const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
|
||||
if (atkGetVersion) {
|
||||
const char* version = atkGetVersion();
|
||||
if (version) {
|
||||
char* endPtr = nullptr;
|
||||
atkMajorVersion = strtol(version, &endPtr, 10);
|
||||
if (*endPtr == '.')
|
||||
atkMinorVersion = strtol(endPtr + 1, &endPtr, 10);
|
||||
}
|
||||
}
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
// Load and initialize gail library.
|
||||
nsresult rv = LoadGtkModule(sGail);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
(*sGail.init)();
|
||||
#endif
|
||||
|
||||
// Initialize the MAI Utility class, it will overwrite gail_util.
|
||||
g_type_class_unref(g_type_class_ref(mai_util_get_type()));
|
||||
|
||||
// Init atk-bridge now
|
||||
PR_SetEnv("NO_AT_BRIDGE=0");
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
if (atk_bridge_adaptor_init) {
|
||||
atk_bridge_adaptor_init(nullptr, nullptr);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
nsresult rv = LoadGtkModule(sAtkBridge);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
(*sAtkBridge.init)();
|
||||
}
|
||||
}
|
||||
|
||||
if (!sToplevel_event_hook_added) {
|
||||
sToplevel_event_hook_added = true;
|
||||
sToplevel_show_hook =
|
||||
g_signal_add_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
|
||||
0, toplevel_event_watcher,
|
||||
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_SHOW),
|
||||
nullptr);
|
||||
sToplevel_hide_hook =
|
||||
g_signal_add_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW), 0,
|
||||
toplevel_event_watcher,
|
||||
reinterpret_cast<gpointer>(nsIAccessibleEvent::EVENT_HIDE),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
a11y::PlatformShutdown()
|
||||
{
|
||||
if (sToplevel_event_hook_added) {
|
||||
sToplevel_event_hook_added = false;
|
||||
g_signal_remove_emission_hook(g_signal_lookup("show", GTK_TYPE_WINDOW),
|
||||
sToplevel_show_hook);
|
||||
g_signal_remove_emission_hook(g_signal_lookup("hide", GTK_TYPE_WINDOW),
|
||||
sToplevel_hide_hook);
|
||||
}
|
||||
|
||||
if (sAtkBridge.lib) {
|
||||
// Do not shutdown/unload atk-bridge,
|
||||
// an exit function registered will take care of it
|
||||
// if (sAtkBridge.shutdown)
|
||||
// (*sAtkBridge.shutdown)();
|
||||
// PR_UnloadLibrary(sAtkBridge.lib);
|
||||
sAtkBridge.lib = nullptr;
|
||||
sAtkBridge.init = nullptr;
|
||||
sAtkBridge.shutdown = nullptr;
|
||||
}
|
||||
#if (MOZ_WIDGET_GTK == 2)
|
||||
if (sGail.lib) {
|
||||
// Do not shutdown gail because
|
||||
// 1) Maybe it's not init-ed by us. e.g. GtkEmbed
|
||||
// 2) We need it to avoid assert in spi_atk_tidy_windows
|
||||
// if (sGail.shutdown)
|
||||
// (*sGail.shutdown)();
|
||||
// PR_UnloadLibrary(sGail.lib);
|
||||
sGail.lib = nullptr;
|
||||
sGail.init = nullptr;
|
||||
sGail.shutdown = nullptr;
|
||||
}
|
||||
#endif
|
||||
// if (sATKLib) {
|
||||
// PR_UnloadLibrary(sATKLib);
|
||||
// sATKLib = nullptr;
|
||||
// }
|
||||
}
|
||||
|
||||
static const char sAccEnv [] = "GNOME_ACCESSIBILITY";
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
static DBusPendingCall *sPendingCall = nullptr;
|
||||
#endif
|
||||
|
||||
void
|
||||
a11y::PreInit()
|
||||
{
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
static bool sChecked = FALSE;
|
||||
if (sChecked)
|
||||
return;
|
||||
|
||||
sChecked = TRUE;
|
||||
|
||||
// dbus is only checked if GNOME_ACCESSIBILITY is unset
|
||||
// also make sure that a session bus address is available to prevent dbus from
|
||||
// starting a new one. Dbus confuses the test harness when it creates a new
|
||||
// process (see bug 693343)
|
||||
if (PR_GetEnv(sAccEnv) || !PR_GetEnv("DBUS_SESSION_BUS_ADDRESS"))
|
||||
return;
|
||||
|
||||
DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nullptr);
|
||||
if (!bus)
|
||||
return;
|
||||
|
||||
dbus_connection_set_exit_on_disconnect(bus, FALSE);
|
||||
|
||||
static const char* iface = "org.a11y.Status";
|
||||
static const char* member = "IsEnabled";
|
||||
DBusMessage *message;
|
||||
message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get");
|
||||
if (!message)
|
||||
goto dbus_done;
|
||||
|
||||
dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
|
||||
DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
|
||||
dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
|
||||
dbus_message_unref(message);
|
||||
|
||||
dbus_done:
|
||||
dbus_connection_unref(bus);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
a11y::ShouldA11yBeEnabled()
|
||||
{
|
||||
static bool sChecked = false, sShouldEnable = false;
|
||||
if (sChecked)
|
||||
return sShouldEnable;
|
||||
|
||||
sChecked = true;
|
||||
|
||||
EPlatformDisabledState disabledState = PlatformDisabledState();
|
||||
if (disabledState == ePlatformIsDisabled)
|
||||
return sShouldEnable = false;
|
||||
|
||||
// check if accessibility enabled/disabled by environment variable
|
||||
const char* envValue = PR_GetEnv(sAccEnv);
|
||||
if (envValue)
|
||||
return sShouldEnable = !!atoi(envValue);
|
||||
|
||||
#ifdef MOZ_ENABLE_DBUS
|
||||
PreInit();
|
||||
bool dbusSuccess = false;
|
||||
DBusMessage *reply = nullptr;
|
||||
if (!sPendingCall)
|
||||
goto dbus_done;
|
||||
|
||||
dbus_pending_call_block(sPendingCall);
|
||||
reply = dbus_pending_call_steal_reply(sPendingCall);
|
||||
dbus_pending_call_unref(sPendingCall);
|
||||
sPendingCall = nullptr;
|
||||
if (!reply ||
|
||||
dbus_message_get_type(reply) != DBUS_MESSAGE_TYPE_METHOD_RETURN ||
|
||||
strcmp(dbus_message_get_signature (reply), DBUS_TYPE_VARIANT_AS_STRING))
|
||||
goto dbus_done;
|
||||
|
||||
DBusMessageIter iter, iter_variant, iter_struct;
|
||||
dbus_bool_t dResult;
|
||||
dbus_message_iter_init(reply, &iter);
|
||||
dbus_message_iter_recurse (&iter, &iter_variant);
|
||||
switch (dbus_message_iter_get_arg_type(&iter_variant)) {
|
||||
case DBUS_TYPE_STRUCT:
|
||||
// at-spi2-core 2.2.0-2.2.1 had a bug where it returned a struct
|
||||
dbus_message_iter_recurse(&iter_variant, &iter_struct);
|
||||
if (dbus_message_iter_get_arg_type(&iter_struct) == DBUS_TYPE_BOOLEAN) {
|
||||
dbus_message_iter_get_basic(&iter_struct, &dResult);
|
||||
sShouldEnable = dResult;
|
||||
dbusSuccess = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case DBUS_TYPE_BOOLEAN:
|
||||
dbus_message_iter_get_basic(&iter_variant, &dResult);
|
||||
sShouldEnable = dResult;
|
||||
dbusSuccess = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
dbus_done:
|
||||
if (reply)
|
||||
dbus_message_unref(reply);
|
||||
|
||||
if (dbusSuccess)
|
||||
return sShouldEnable;
|
||||
#endif
|
||||
|
||||
//check gconf-2 setting
|
||||
#define GCONF_A11Y_KEY "/desktop/gnome/interface/accessibility"
|
||||
nsresult rv = NS_OK;
|
||||
nsCOMPtr<nsIGConfService> gconf =
|
||||
do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv) && gconf)
|
||||
gconf->GetBool(NS_LITERAL_CSTRING(GCONF_A11Y_KEY), &sShouldEnable);
|
||||
|
||||
return sShouldEnable;
|
||||
}
|
||||
24
accessible/atk/RootAccessibleWrap.cpp
Normal file
24
accessible/atk/RootAccessibleWrap.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/* -*- 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 "RootAccessibleWrap.h"
|
||||
|
||||
#include "nsMai.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
GtkWindowAccessible::GtkWindowAccessible(AtkObject* aAccessible) :
|
||||
DummyAccessible()
|
||||
{
|
||||
g_object_ref(aAccessible);
|
||||
mAtkObject = aAccessible;
|
||||
}
|
||||
|
||||
GtkWindowAccessible::~GtkWindowAccessible()
|
||||
{
|
||||
g_object_unref(mAtkObject);
|
||||
mAtkObject = nullptr;
|
||||
}
|
||||
34
accessible/atk/RootAccessibleWrap.h
Normal file
34
accessible/atk/RootAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- 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_RootAccessibleWrap_h__
|
||||
#define mozilla_a11y_RootAccessibleWrap_h__
|
||||
|
||||
#include "BaseAccessibles.h"
|
||||
#include "RootAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef RootAccessible RootAccessibleWrap;
|
||||
|
||||
/* GtkWindowAccessible is the accessible class for gtk+ native window.
|
||||
* The instance of GtkWindowAccessible is a child of MaiAppRoot instance.
|
||||
* It is added into root when the toplevel window is created, and removed
|
||||
* from root when the toplevel window is destroyed.
|
||||
*/
|
||||
class GtkWindowAccessible final : public DummyAccessible
|
||||
{
|
||||
public:
|
||||
explicit GtkWindowAccessible(AtkObject* aAccessible);
|
||||
virtual ~GtkWindowAccessible();
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_a11y_Root_Accessible_Wrap_h__ */
|
||||
|
||||
20
accessible/atk/TextLeafAccessibleWrap.h
Normal file
20
accessible/atk/TextLeafAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- 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_TextLeafAccessibleWrap_h__
|
||||
#define mozilla_a11y_TextLeafAccessibleWrap_h__
|
||||
|
||||
#include "TextLeafAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class TextLeafAccessible TextLeafAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
411
accessible/atk/UtilInterface.cpp
Normal file
411
accessible/atk/UtilInterface.cpp
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/* -*- 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 "ApplicationAccessibleWrap.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsMai.h"
|
||||
|
||||
#include <atk/atk.h>
|
||||
#include <gtk/gtk.h>
|
||||
#include <string.h>
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
typedef AtkUtil MaiUtil;
|
||||
typedef AtkUtilClass MaiUtilClass;
|
||||
|
||||
#define MAI_VERSION MOZILLA_VERSION
|
||||
#define MAI_NAME "Gecko"
|
||||
|
||||
extern "C" {
|
||||
static guint (*gail_add_global_event_listener)(GSignalEmissionHook listener,
|
||||
const gchar* event_type);
|
||||
static void (*gail_remove_global_event_listener) (guint remove_listener);
|
||||
static void (*gail_remove_key_event_listener) (guint remove_listener);
|
||||
static AtkObject* (*gail_get_root)();
|
||||
}
|
||||
|
||||
struct MaiUtilListenerInfo
|
||||
{
|
||||
gint key;
|
||||
guint signal_id;
|
||||
gulong hook_id;
|
||||
// For window create/destory/minimize/maximize/restore/activate/deactivate
|
||||
// events, we'll chain gail_util's add/remove_global_event_listener.
|
||||
// So we store the listenerid returned by gail's add_global_event_listener
|
||||
// in this structure to call gail's remove_global_event_listener later.
|
||||
guint gail_listenerid;
|
||||
};
|
||||
|
||||
static GHashTable* sListener_list = nullptr;
|
||||
static gint sListener_idx = 1;
|
||||
|
||||
extern "C" {
|
||||
static guint
|
||||
add_listener (GSignalEmissionHook listener,
|
||||
const gchar *object_type,
|
||||
const gchar *signal,
|
||||
const gchar *hook_data,
|
||||
guint gail_listenerid = 0)
|
||||
{
|
||||
GType type;
|
||||
guint signal_id;
|
||||
gint rc = 0;
|
||||
|
||||
type = g_type_from_name(object_type);
|
||||
if (type) {
|
||||
signal_id = g_signal_lookup(signal, type);
|
||||
if (signal_id > 0) {
|
||||
MaiUtilListenerInfo *listener_info;
|
||||
|
||||
rc = sListener_idx;
|
||||
|
||||
listener_info = (MaiUtilListenerInfo *)
|
||||
g_malloc(sizeof(MaiUtilListenerInfo));
|
||||
listener_info->key = sListener_idx;
|
||||
listener_info->hook_id =
|
||||
g_signal_add_emission_hook(signal_id, 0, listener,
|
||||
g_strdup(hook_data),
|
||||
(GDestroyNotify)g_free);
|
||||
listener_info->signal_id = signal_id;
|
||||
listener_info->gail_listenerid = gail_listenerid;
|
||||
|
||||
g_hash_table_insert(sListener_list, &(listener_info->key),
|
||||
listener_info);
|
||||
sListener_idx++;
|
||||
}
|
||||
else {
|
||||
g_warning("Invalid signal type %s\n", signal);
|
||||
}
|
||||
}
|
||||
else {
|
||||
g_warning("Invalid object type %s\n", object_type);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static guint
|
||||
mai_util_add_global_event_listener(GSignalEmissionHook listener,
|
||||
const gchar *event_type)
|
||||
{
|
||||
guint rc = 0;
|
||||
gchar **split_string;
|
||||
|
||||
split_string = g_strsplit (event_type, ":", 3);
|
||||
|
||||
if (split_string) {
|
||||
if (!strcmp ("window", split_string[0])) {
|
||||
guint gail_listenerid = 0;
|
||||
if (gail_add_global_event_listener) {
|
||||
// call gail's function to track gtk native window events
|
||||
gail_listenerid =
|
||||
gail_add_global_event_listener(listener, event_type);
|
||||
}
|
||||
|
||||
rc = add_listener (listener, "MaiAtkObject", split_string[1],
|
||||
event_type, gail_listenerid);
|
||||
}
|
||||
else {
|
||||
rc = add_listener (listener, split_string[1], split_string[2],
|
||||
event_type);
|
||||
}
|
||||
g_strfreev(split_string);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void
|
||||
mai_util_remove_global_event_listener(guint remove_listener)
|
||||
{
|
||||
if (remove_listener > 0) {
|
||||
MaiUtilListenerInfo *listener_info;
|
||||
gint tmp_idx = remove_listener;
|
||||
|
||||
listener_info = (MaiUtilListenerInfo *)
|
||||
g_hash_table_lookup(sListener_list, &tmp_idx);
|
||||
|
||||
if (listener_info != nullptr) {
|
||||
if (gail_remove_global_event_listener &&
|
||||
listener_info->gail_listenerid) {
|
||||
gail_remove_global_event_listener(listener_info->gail_listenerid);
|
||||
}
|
||||
|
||||
/* Hook id of 0 and signal id of 0 are invalid */
|
||||
if (listener_info->hook_id != 0 && listener_info->signal_id != 0) {
|
||||
/* Remove the emission hook */
|
||||
g_signal_remove_emission_hook(listener_info->signal_id,
|
||||
listener_info->hook_id);
|
||||
|
||||
/* Remove the element from the hash */
|
||||
g_hash_table_remove(sListener_list, &tmp_idx);
|
||||
}
|
||||
else {
|
||||
g_warning("Invalid listener hook_id %ld or signal_id %d\n",
|
||||
listener_info->hook_id, listener_info->signal_id);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// atk-bridge is initialized with gail (e.g. yelp)
|
||||
// try gail_remove_global_event_listener
|
||||
if (gail_remove_global_event_listener) {
|
||||
return gail_remove_global_event_listener(remove_listener);
|
||||
}
|
||||
|
||||
g_warning("No listener with the specified listener id %d",
|
||||
remove_listener);
|
||||
}
|
||||
}
|
||||
else {
|
||||
g_warning("Invalid listener_id %d", remove_listener);
|
||||
}
|
||||
}
|
||||
|
||||
static AtkKeyEventStruct *
|
||||
atk_key_event_from_gdk_event_key (GdkEventKey *key)
|
||||
{
|
||||
AtkKeyEventStruct *event = g_new0(AtkKeyEventStruct, 1);
|
||||
switch (key->type) {
|
||||
case GDK_KEY_PRESS:
|
||||
event->type = ATK_KEY_EVENT_PRESS;
|
||||
break;
|
||||
case GDK_KEY_RELEASE:
|
||||
event->type = ATK_KEY_EVENT_RELEASE;
|
||||
break;
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
return nullptr;
|
||||
}
|
||||
event->state = key->state;
|
||||
event->keyval = key->keyval;
|
||||
event->length = key->length;
|
||||
if (key->string && key->string [0] &&
|
||||
(key->state & GDK_CONTROL_MASK ||
|
||||
g_unichar_isgraph (g_utf8_get_char (key->string)))) {
|
||||
event->string = key->string;
|
||||
}
|
||||
else if (key->type == GDK_KEY_PRESS ||
|
||||
key->type == GDK_KEY_RELEASE) {
|
||||
event->string = gdk_keyval_name (key->keyval);
|
||||
}
|
||||
event->keycode = key->hardware_keycode;
|
||||
event->timestamp = key->time;
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
struct MaiKeyEventInfo
|
||||
{
|
||||
AtkKeyEventStruct *key_event;
|
||||
gpointer func_data;
|
||||
};
|
||||
|
||||
union AtkKeySnoopFuncPointer
|
||||
{
|
||||
AtkKeySnoopFunc func_ptr;
|
||||
gpointer data;
|
||||
};
|
||||
|
||||
static gboolean
|
||||
notify_hf(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
MaiKeyEventInfo *info = (MaiKeyEventInfo *)data;
|
||||
AtkKeySnoopFuncPointer atkKeySnoop;
|
||||
atkKeySnoop.data = value;
|
||||
return (atkKeySnoop.func_ptr)(info->key_event, info->func_data) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_hf(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
GHashTable *new_table = (GHashTable *) data;
|
||||
g_hash_table_insert (new_table, key, value);
|
||||
}
|
||||
|
||||
static GHashTable* sKey_listener_list = nullptr;
|
||||
|
||||
static gint
|
||||
mai_key_snooper(GtkWidget *the_widget, GdkEventKey *event, gpointer func_data)
|
||||
{
|
||||
/* notify each AtkKeySnoopFunc in turn... */
|
||||
|
||||
MaiKeyEventInfo *info = g_new0(MaiKeyEventInfo, 1);
|
||||
gint consumed = 0;
|
||||
if (sKey_listener_list) {
|
||||
GHashTable *new_hash = g_hash_table_new(nullptr, nullptr);
|
||||
g_hash_table_foreach (sKey_listener_list, insert_hf, new_hash);
|
||||
info->key_event = atk_key_event_from_gdk_event_key (event);
|
||||
info->func_data = func_data;
|
||||
consumed = g_hash_table_foreach_steal (new_hash, notify_hf, info);
|
||||
g_hash_table_destroy (new_hash);
|
||||
g_free(info->key_event);
|
||||
}
|
||||
g_free(info);
|
||||
return (consumed ? 1 : 0);
|
||||
}
|
||||
|
||||
static guint sKey_snooper_id = 0;
|
||||
|
||||
static guint
|
||||
mai_util_add_key_event_listener(AtkKeySnoopFunc listener, gpointer data)
|
||||
{
|
||||
if (MOZ_UNLIKELY(!listener)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static guint key = 0;
|
||||
|
||||
if (!sKey_listener_list) {
|
||||
sKey_listener_list = g_hash_table_new(nullptr, nullptr);
|
||||
}
|
||||
|
||||
// If we have no registered event listeners then we need to (re)install the
|
||||
// key event snooper.
|
||||
if (g_hash_table_size(sKey_listener_list) == 0) {
|
||||
sKey_snooper_id = gtk_key_snooper_install(mai_key_snooper, data);
|
||||
}
|
||||
|
||||
AtkKeySnoopFuncPointer atkKeySnoop;
|
||||
atkKeySnoop.func_ptr = listener;
|
||||
key++;
|
||||
g_hash_table_insert(sKey_listener_list, GUINT_TO_POINTER(key),
|
||||
atkKeySnoop.data);
|
||||
return key;
|
||||
}
|
||||
|
||||
static void
|
||||
mai_util_remove_key_event_listener (guint remove_listener)
|
||||
{
|
||||
if (!sKey_listener_list) {
|
||||
// atk-bridge is initialized with gail (e.g. yelp)
|
||||
// try gail_remove_key_event_listener
|
||||
return gail_remove_key_event_listener(remove_listener);
|
||||
}
|
||||
|
||||
g_hash_table_remove(sKey_listener_list, GUINT_TO_POINTER (remove_listener));
|
||||
if (g_hash_table_size(sKey_listener_list) == 0) {
|
||||
gtk_key_snooper_remove(sKey_snooper_id);
|
||||
}
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
mai_util_get_root()
|
||||
{
|
||||
ApplicationAccessible* app = ApplicationAcc();
|
||||
if (app)
|
||||
return app->GetAtkObject();
|
||||
|
||||
// We've shutdown, try to use gail instead
|
||||
// (to avoid assert in spi_atk_tidy_windows())
|
||||
// XXX tbsaunde then why didn't we replace the gail atk_util impl?
|
||||
if (gail_get_root)
|
||||
return gail_get_root();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
mai_util_get_toolkit_name()
|
||||
{
|
||||
return MAI_NAME;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
mai_util_get_toolkit_version()
|
||||
{
|
||||
return MAI_VERSION;
|
||||
}
|
||||
|
||||
static void
|
||||
_listener_info_destroy(gpointer data)
|
||||
{
|
||||
g_free(data);
|
||||
}
|
||||
|
||||
static void
|
||||
window_added (AtkObject *atk_obj,
|
||||
guint index,
|
||||
AtkObject *child)
|
||||
{
|
||||
if (!IS_MAI_OBJECT(child))
|
||||
return;
|
||||
|
||||
static guint id = g_signal_lookup ("create", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit (child, id, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
window_removed (AtkObject *atk_obj,
|
||||
guint index,
|
||||
AtkObject *child)
|
||||
{
|
||||
if (!IS_MAI_OBJECT(child))
|
||||
return;
|
||||
|
||||
static guint id = g_signal_lookup ("destroy", MAI_TYPE_ATK_OBJECT);
|
||||
g_signal_emit (child, id, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
UtilInterfaceInit(MaiUtilClass* klass)
|
||||
{
|
||||
AtkUtilClass *atk_class;
|
||||
gpointer data;
|
||||
|
||||
data = g_type_class_peek(ATK_TYPE_UTIL);
|
||||
atk_class = ATK_UTIL_CLASS(data);
|
||||
|
||||
// save gail function pointer
|
||||
gail_add_global_event_listener = atk_class->add_global_event_listener;
|
||||
gail_remove_global_event_listener = atk_class->remove_global_event_listener;
|
||||
gail_remove_key_event_listener = atk_class->remove_key_event_listener;
|
||||
gail_get_root = atk_class->get_root;
|
||||
|
||||
atk_class->add_global_event_listener =
|
||||
mai_util_add_global_event_listener;
|
||||
atk_class->remove_global_event_listener =
|
||||
mai_util_remove_global_event_listener;
|
||||
atk_class->add_key_event_listener = mai_util_add_key_event_listener;
|
||||
atk_class->remove_key_event_listener = mai_util_remove_key_event_listener;
|
||||
atk_class->get_root = mai_util_get_root;
|
||||
atk_class->get_toolkit_name = mai_util_get_toolkit_name;
|
||||
atk_class->get_toolkit_version = mai_util_get_toolkit_version;
|
||||
|
||||
sListener_list = g_hash_table_new_full(g_int_hash, g_int_equal, nullptr,
|
||||
_listener_info_destroy);
|
||||
// Keep track of added/removed windows.
|
||||
AtkObject *root = atk_get_root ();
|
||||
g_signal_connect (root, "children-changed::add", (GCallback) window_added, nullptr);
|
||||
g_signal_connect (root, "children-changed::remove", (GCallback) window_removed, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
GType
|
||||
mai_util_get_type()
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (!type) {
|
||||
static const GTypeInfo tinfo = {
|
||||
sizeof(MaiUtilClass),
|
||||
(GBaseInitFunc) nullptr, /* base init */
|
||||
(GBaseFinalizeFunc) nullptr, /* base finalize */
|
||||
(GClassInitFunc) UtilInterfaceInit, /* class init */
|
||||
(GClassFinalizeFunc) nullptr, /* class finalize */
|
||||
nullptr, /* class data */
|
||||
sizeof(MaiUtil), /* instance size */
|
||||
0, /* nb preallocs */
|
||||
(GInstanceInitFunc) nullptr, /* instance init */
|
||||
nullptr /* value table */
|
||||
};
|
||||
|
||||
type = g_type_register_static(ATK_TYPE_UTIL,
|
||||
"MaiUtil", &tinfo, GTypeFlags(0));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
21
accessible/atk/XULListboxAccessibleWrap.h
Normal file
21
accessible/atk/XULListboxAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/* -*- 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_XULListboxAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULListboxAccessibleWrap_h__
|
||||
|
||||
#include "XULListboxAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULListboxAccessible XULListboxAccessibleWrap;
|
||||
typedef class XULListCellAccessible XULListCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
20
accessible/atk/XULMenuAccessibleWrap.h
Normal file
20
accessible/atk/XULMenuAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* -*- 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_XULMenuAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULMenuAccessibleWrap_h__
|
||||
|
||||
#include "XULMenuAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULMenuitemAccessible XULMenuitemAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
22
accessible/atk/XULTreeGridAccessibleWrap.h
Normal file
22
accessible/atk/XULTreeGridAccessibleWrap.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- 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_XULTreeGridAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULTreeGridAccessibleWrap_h__
|
||||
|
||||
#include "XULTreeGridAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULTreeGridAccessible XULTreeGridAccessibleWrap;
|
||||
typedef class XULTreeGridCellAccessible XULTreeGridCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
63
accessible/atk/moz.build
Normal file
63
accessible/atk/moz.build
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# -*- Mode: python; 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/.
|
||||
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'AccessibleWrap.h',
|
||||
'HyperTextAccessibleWrap.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'AccessibleWrap.cpp',
|
||||
'ApplicationAccessibleWrap.cpp',
|
||||
'AtkSocketAccessible.cpp',
|
||||
'DocAccessibleWrap.cpp',
|
||||
'nsMaiHyperlink.cpp',
|
||||
'nsMaiInterfaceAction.cpp',
|
||||
'nsMaiInterfaceComponent.cpp',
|
||||
'nsMaiInterfaceDocument.cpp',
|
||||
'nsMaiInterfaceEditableText.cpp',
|
||||
'nsMaiInterfaceHyperlinkImpl.cpp',
|
||||
'nsMaiInterfaceHypertext.cpp',
|
||||
'nsMaiInterfaceImage.cpp',
|
||||
'nsMaiInterfaceSelection.cpp',
|
||||
'nsMaiInterfaceTable.cpp',
|
||||
'nsMaiInterfaceTableCell.cpp',
|
||||
'nsMaiInterfaceText.cpp',
|
||||
'nsMaiInterfaceValue.cpp',
|
||||
'Platform.cpp',
|
||||
'RootAccessibleWrap.cpp',
|
||||
'UtilInterface.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/ipc/other',
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/other-licenses/atk-1.0',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
CFLAGS += CONFIG['TK_CFLAGS']
|
||||
CXXFLAGS += CONFIG['TK_CFLAGS']
|
||||
|
||||
if CONFIG['MOZ_ENABLE_DBUS']:
|
||||
CXXFLAGS += CONFIG['MOZ_DBUS_CFLAGS']
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['CLANG_CXX'] or CONFIG['GNU_CXX']:
|
||||
# Used in G_DEFINE_TYPE_EXTENDED macro, probably fixed in newer glib /
|
||||
# gobject headers. See bug 1243331 comment 3.
|
||||
CXXFLAGS += [
|
||||
'-Wno-error=shadow',
|
||||
'-Wno-unused-local-typedefs',
|
||||
]
|
||||
149
accessible/atk/nsMai.h
Normal file
149
accessible/atk/nsMai.h
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/* -*- 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 __NS_MAI_H__
|
||||
#define __NS_MAI_H__
|
||||
|
||||
#include <atk/atk.h>
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
#include "AccessibleOrProxy.h"
|
||||
#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))
|
||||
#define MAI_ATK_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
MAI_TYPE_ATK_OBJECT, \
|
||||
MaiAtkObjectClass))
|
||||
#define IS_MAI_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
MAI_TYPE_ATK_OBJECT))
|
||||
#define IS_MAI_OBJECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
MAI_TYPE_ATK_OBJECT))
|
||||
#define MAI_ATK_OBJECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
MAI_TYPE_ATK_OBJECT, \
|
||||
MaiAtkObjectClass))
|
||||
GType mai_atk_object_get_type(void);
|
||||
GType mai_util_get_type();
|
||||
extern "C" GType mai_atk_socket_get_type(void);
|
||||
|
||||
/* MaiAtkSocket */
|
||||
|
||||
#define MAI_TYPE_ATK_SOCKET (mai_atk_socket_get_type ())
|
||||
#define MAI_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\
|
||||
MAI_TYPE_ATK_SOCKET, MaiAtkSocket))
|
||||
#define MAI_IS_ATK_SOCKET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
|
||||
MAI_TYPE_ATK_SOCKET))
|
||||
#define MAI_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\
|
||||
MAI_TYPE_ATK_SOCKET,\
|
||||
MaiAtkSocketClass))
|
||||
#define MAI_IS_ATK_SOCKET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\
|
||||
MAI_TYPE_ATK_SOCKET))
|
||||
#define MAI_ATK_SOCKET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
|
||||
MAI_TYPE_ATK_SOCKET,\
|
||||
MaiAtkSocketClass))
|
||||
|
||||
typedef struct _MaiAtkSocket
|
||||
{
|
||||
AtkSocket parent;
|
||||
|
||||
mozilla::a11y::AccessibleWrap* accWrap;
|
||||
} MaiAtkSocket;
|
||||
|
||||
typedef struct _MaiAtkSocketClass
|
||||
{
|
||||
AtkSocketClass parent_class;
|
||||
} MaiAtkSocketClass;
|
||||
|
||||
// This is a pointer to the atk_table_cell_get_type function if we are using
|
||||
// a version of atk that defines that.
|
||||
extern "C" GType (*gAtkTableCellGetTypeFunc)();
|
||||
|
||||
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
|
||||
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
|
||||
mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);
|
||||
AtkObject* GetWrapperFor(mozilla::a11y::ProxyAccessible* aProxy);
|
||||
AtkObject* GetWrapperFor(mozilla::a11y::AccessibleOrProxy aObj);
|
||||
|
||||
extern int atkMajorVersion, atkMinorVersion;
|
||||
|
||||
/**
|
||||
* Return true if the loaded version of libatk-1.0.so is at least
|
||||
* aMajor.aMinor.0.
|
||||
*/
|
||||
static inline bool
|
||||
IsAtkVersionAtLeast(int aMajor, int aMinor)
|
||||
{
|
||||
return aMajor < atkMajorVersion ||
|
||||
(aMajor == atkMajorVersion && aMinor <= atkMinorVersion);
|
||||
}
|
||||
|
||||
// This is or'd with the pointer in MaiAtkObject::accWrap if the wrap-ee is a
|
||||
// proxy.
|
||||
static const uintptr_t IS_PROXY = 1;
|
||||
|
||||
/**
|
||||
* This MaiAtkObject is a thin wrapper, in the MAI namespace, for AtkObject
|
||||
*/
|
||||
struct MaiAtkObject
|
||||
{
|
||||
AtkObject parent;
|
||||
/*
|
||||
* The AccessibleWrap whose properties and features are exported
|
||||
* via this object instance.
|
||||
*/
|
||||
mozilla::a11y::AccessibleOrProxy accWrap;
|
||||
|
||||
/*
|
||||
* Get the AtkHyperlink for this atk object.
|
||||
*/
|
||||
AtkHyperlink* GetAtkHyperlink();
|
||||
|
||||
/*
|
||||
* Shutdown this AtkObject.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/*
|
||||
* Notify atk of a state change on this AtkObject.
|
||||
*/
|
||||
void FireStateChangeEvent(uint64_t aState, bool aEnabled);
|
||||
|
||||
/*
|
||||
* Notify ATK of a text change within this ATK object.
|
||||
*/
|
||||
void FireTextChangeEvent(const nsString& aStr, int32_t aStart, uint32_t aLen,
|
||||
bool aIsInsert, bool aIsFromUser);
|
||||
|
||||
/**
|
||||
* Notify ATK of a shown or hidden subtree rooted at aObject whose parent is
|
||||
* aParent
|
||||
*/
|
||||
void FireAtkShowHideEvent(AtkObject* aParent, bool aIsAdded, bool aFromUser);
|
||||
|
||||
private:
|
||||
/*
|
||||
* do we have text-remove and text-insert signals if not we need to use
|
||||
* text-changed see AccessibleWrap::FireAtkTextChangedEvent() and
|
||||
* bug 619002
|
||||
*/
|
||||
enum EAvailableAtkSignals {
|
||||
eUnknown,
|
||||
eHaveNewAtkTextSignals,
|
||||
eNoNewAtkSignals
|
||||
};
|
||||
|
||||
static EAvailableAtkSignals gAvailableAtkSignals;
|
||||
};
|
||||
|
||||
#endif /* __NS_MAI_H__ */
|
||||
262
accessible/atk/nsMaiHyperlink.cpp
Normal file
262
accessible/atk/nsMaiHyperlink.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
/* -*- 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 "nsIURI.h"
|
||||
#include "nsMaiHyperlink.h"
|
||||
#include "mozilla/a11y/ProxyAccessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
/* MaiAtkHyperlink */
|
||||
|
||||
#define MAI_TYPE_ATK_HYPERLINK (mai_atk_hyperlink_get_type ())
|
||||
#define MAI_ATK_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),\
|
||||
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlink))
|
||||
#define MAI_ATK_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),\
|
||||
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlinkClass))
|
||||
#define MAI_IS_ATK_HYPERLINK(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),\
|
||||
MAI_TYPE_ATK_HYPERLINK))
|
||||
#define MAI_IS_ATK_HYPERLINK_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),\
|
||||
MAI_TYPE_ATK_HYPERLINK))
|
||||
#define MAI_ATK_HYPERLINK_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
|
||||
MAI_TYPE_ATK_HYPERLINK, MaiAtkHyperlinkClass))
|
||||
|
||||
/**
|
||||
* This MaiAtkHyperlink is a thin wrapper, in the MAI namespace,
|
||||
* for AtkHyperlink
|
||||
*/
|
||||
|
||||
struct MaiAtkHyperlink
|
||||
{
|
||||
AtkHyperlink parent;
|
||||
|
||||
/*
|
||||
* The MaiHyperlink whose properties and features are exported via this
|
||||
* hyperlink instance.
|
||||
*/
|
||||
MaiHyperlink *maiHyperlink;
|
||||
};
|
||||
|
||||
struct MaiAtkHyperlinkClass
|
||||
{
|
||||
AtkHyperlinkClass parent_class;
|
||||
};
|
||||
|
||||
GType mai_atk_hyperlink_get_type(void);
|
||||
|
||||
G_BEGIN_DECLS
|
||||
/* callbacks for AtkHyperlink */
|
||||
static void classInitCB(AtkHyperlinkClass *aClass);
|
||||
static void finalizeCB(GObject *aObj);
|
||||
|
||||
/* callbacks for AtkHyperlink virtual functions */
|
||||
static gchar *getUriCB(AtkHyperlink *aLink, gint aLinkIndex);
|
||||
static AtkObject *getObjectCB(AtkHyperlink *aLink, gint aLinkIndex);
|
||||
static gint getEndIndexCB(AtkHyperlink *aLink);
|
||||
static gint getStartIndexCB(AtkHyperlink *aLink);
|
||||
static gboolean isValidCB(AtkHyperlink *aLink);
|
||||
static gint getAnchorCountCB(AtkHyperlink *aLink);
|
||||
G_END_DECLS
|
||||
|
||||
static gpointer parent_class = nullptr;
|
||||
|
||||
static MaiHyperlink*
|
||||
GetMaiHyperlink(AtkHyperlink *aHyperlink)
|
||||
{
|
||||
NS_ENSURE_TRUE(MAI_IS_ATK_HYPERLINK(aHyperlink), nullptr);
|
||||
MaiHyperlink * maiHyperlink =
|
||||
MAI_ATK_HYPERLINK(aHyperlink)->maiHyperlink;
|
||||
NS_ENSURE_TRUE(maiHyperlink != nullptr, nullptr);
|
||||
NS_ENSURE_TRUE(maiHyperlink->GetAtkHyperlink() == aHyperlink, nullptr);
|
||||
return maiHyperlink;
|
||||
}
|
||||
|
||||
GType
|
||||
mai_atk_hyperlink_get_type(void)
|
||||
{
|
||||
static GType type = 0;
|
||||
|
||||
if (!type) {
|
||||
static const GTypeInfo tinfo = {
|
||||
sizeof(MaiAtkHyperlinkClass),
|
||||
(GBaseInitFunc)nullptr,
|
||||
(GBaseFinalizeFunc)nullptr,
|
||||
(GClassInitFunc)classInitCB,
|
||||
(GClassFinalizeFunc)nullptr,
|
||||
nullptr, /* class data */
|
||||
sizeof(MaiAtkHyperlink), /* instance size */
|
||||
0, /* nb preallocs */
|
||||
(GInstanceInitFunc)nullptr,
|
||||
nullptr /* value table */
|
||||
};
|
||||
|
||||
type = g_type_register_static(ATK_TYPE_HYPERLINK,
|
||||
"MaiAtkHyperlink",
|
||||
&tinfo, GTypeFlags(0));
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
MaiHyperlink::MaiHyperlink(AccessibleOrProxy aHyperLink) :
|
||||
mHyperlink(aHyperLink),
|
||||
mMaiAtkHyperlink(nullptr)
|
||||
{
|
||||
mMaiAtkHyperlink =
|
||||
reinterpret_cast<AtkHyperlink *>
|
||||
(g_object_new(mai_atk_hyperlink_get_type(), nullptr));
|
||||
NS_ASSERTION(mMaiAtkHyperlink, "OUT OF MEMORY");
|
||||
if (!mMaiAtkHyperlink)
|
||||
return;
|
||||
|
||||
MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = this;
|
||||
}
|
||||
|
||||
MaiHyperlink::~MaiHyperlink()
|
||||
{
|
||||
if (mMaiAtkHyperlink) {
|
||||
MAI_ATK_HYPERLINK(mMaiAtkHyperlink)->maiHyperlink = nullptr;
|
||||
g_object_unref(mMaiAtkHyperlink);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* static functions for ATK callbacks */
|
||||
|
||||
void
|
||||
classInitCB(AtkHyperlinkClass *aClass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(aClass);
|
||||
|
||||
parent_class = g_type_class_peek_parent(aClass);
|
||||
|
||||
aClass->get_uri = getUriCB;
|
||||
aClass->get_object = getObjectCB;
|
||||
aClass->get_end_index = getEndIndexCB;
|
||||
aClass->get_start_index = getStartIndexCB;
|
||||
aClass->is_valid = isValidCB;
|
||||
aClass->get_n_anchors = getAnchorCountCB;
|
||||
|
||||
gobject_class->finalize = finalizeCB;
|
||||
}
|
||||
|
||||
void
|
||||
finalizeCB(GObject *aObj)
|
||||
{
|
||||
NS_ASSERTION(MAI_IS_ATK_HYPERLINK(aObj), "Invalid MaiAtkHyperlink");
|
||||
if (!MAI_IS_ATK_HYPERLINK(aObj))
|
||||
return;
|
||||
|
||||
MaiAtkHyperlink *maiAtkHyperlink = MAI_ATK_HYPERLINK(aObj);
|
||||
maiAtkHyperlink->maiHyperlink = nullptr;
|
||||
|
||||
/* call parent finalize function */
|
||||
if (G_OBJECT_CLASS (parent_class)->finalize)
|
||||
G_OBJECT_CLASS (parent_class)->finalize(aObj);
|
||||
}
|
||||
|
||||
gchar *
|
||||
getUriCB(AtkHyperlink *aLink, gint aLinkIndex)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink)
|
||||
return nullptr;
|
||||
|
||||
nsAutoCString cautoStr;
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink()) {
|
||||
nsCOMPtr<nsIURI> uri = hyperlink->AnchorURIAt(aLinkIndex);
|
||||
if (!uri)
|
||||
return nullptr;
|
||||
|
||||
nsresult rv = uri->GetSpec(cautoStr);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
return g_strdup(cautoStr.get());
|
||||
}
|
||||
|
||||
bool valid;
|
||||
maiLink->Proxy()->AnchorURIAt(aLinkIndex, cautoStr, &valid);
|
||||
if (!valid)
|
||||
return nullptr;
|
||||
|
||||
return g_strdup(cautoStr.get());
|
||||
}
|
||||
|
||||
AtkObject *
|
||||
getObjectCB(AtkHyperlink *aLink, gint aLinkIndex)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink()) {
|
||||
Accessible* anchor = hyperlink->AnchorAt(aLinkIndex);
|
||||
NS_ENSURE_TRUE(anchor, nullptr);
|
||||
|
||||
return AccessibleWrap::GetAtkObject(anchor);
|
||||
}
|
||||
|
||||
ProxyAccessible* anchor = maiLink->Proxy()->AnchorAt(aLinkIndex);
|
||||
return anchor ? GetWrapperFor(anchor) : nullptr;
|
||||
}
|
||||
|
||||
gint
|
||||
getEndIndexCB(AtkHyperlink *aLink)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink)
|
||||
return false;
|
||||
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
|
||||
return static_cast<gint>(hyperlink->EndOffset());
|
||||
|
||||
bool valid = false;
|
||||
uint32_t endIdx = maiLink->Proxy()->EndOffset(&valid);
|
||||
return valid ? static_cast<gint>(endIdx) : -1;
|
||||
}
|
||||
|
||||
gint
|
||||
getStartIndexCB(AtkHyperlink *aLink)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink)
|
||||
return -1;
|
||||
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
|
||||
return static_cast<gint>(hyperlink->StartOffset());
|
||||
|
||||
bool valid = false;
|
||||
uint32_t startIdx = maiLink->Proxy()->StartOffset(&valid);
|
||||
return valid ? static_cast<gint>(startIdx) : -1;
|
||||
}
|
||||
|
||||
gboolean
|
||||
isValidCB(AtkHyperlink *aLink)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink)
|
||||
return false;
|
||||
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
|
||||
return static_cast<gboolean>(hyperlink->IsLinkValid());
|
||||
|
||||
return static_cast<gboolean>(maiLink->Proxy()->IsLinkValid());
|
||||
}
|
||||
|
||||
gint
|
||||
getAnchorCountCB(AtkHyperlink *aLink)
|
||||
{
|
||||
MaiHyperlink* maiLink = GetMaiHyperlink(aLink);
|
||||
if (!maiLink)
|
||||
return -1;
|
||||
|
||||
if (Accessible* hyperlink = maiLink->GetAccHyperlink())
|
||||
return static_cast<gint>(hyperlink->AnchorCount());
|
||||
|
||||
bool valid = false;
|
||||
uint32_t anchorCount = maiLink->Proxy()->AnchorCount(&valid);
|
||||
return valid ? static_cast<gint>(anchorCount) : -1;
|
||||
}
|
||||
55
accessible/atk/nsMaiHyperlink.h
Normal file
55
accessible/atk/nsMaiHyperlink.h
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- 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 __MAI_HYPERLINK_H__
|
||||
#define __MAI_HYPERLINK_H__
|
||||
|
||||
#include "nsMai.h"
|
||||
#include "Accessible.h"
|
||||
|
||||
struct _AtkHyperlink;
|
||||
typedef struct _AtkHyperlink AtkHyperlink;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/*
|
||||
* MaiHyperlink is a auxiliary class for MaiInterfaceHyperText.
|
||||
*/
|
||||
|
||||
class MaiHyperlink
|
||||
{
|
||||
public:
|
||||
explicit MaiHyperlink(AccessibleOrProxy aHyperLink);
|
||||
~MaiHyperlink();
|
||||
|
||||
public:
|
||||
AtkHyperlink* GetAtkHyperlink() const { return mMaiAtkHyperlink; }
|
||||
Accessible* GetAccHyperlink()
|
||||
{
|
||||
if (!mHyperlink.IsAccessible())
|
||||
return nullptr;
|
||||
|
||||
Accessible* link = mHyperlink.AsAccessible();
|
||||
if (!link) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NS_ASSERTION(link->IsLink(), "Why isn't it a link!");
|
||||
return link;
|
||||
}
|
||||
|
||||
ProxyAccessible* Proxy() const { return mHyperlink.AsProxy(); }
|
||||
|
||||
protected:
|
||||
AccessibleOrProxy mHyperlink;
|
||||
AtkHyperlink* mMaiAtkHyperlink;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* __MAI_HYPERLINK_H__ */
|
||||
105
accessible/atk/nsMaiInterfaceAction.cpp
Normal file
105
accessible/atk/nsMaiInterfaceAction.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsMai.h"
|
||||
#include "Role.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "nsString.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
|
||||
static gboolean
|
||||
doActionCB(AtkAction *aAction, gint aActionIndex)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
|
||||
if (accWrap) {
|
||||
return accWrap->DoAction(aActionIndex);
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction));
|
||||
return proxy && proxy->DoAction(aActionIndex);
|
||||
}
|
||||
|
||||
static gint
|
||||
getActionCountCB(AtkAction *aAction)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
|
||||
if (accWrap) {
|
||||
return accWrap->ActionCount();
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction));
|
||||
return proxy ? proxy->ActionCount() : 0;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getActionDescriptionCB(AtkAction *aAction, gint aActionIndex)
|
||||
{
|
||||
nsAutoString description;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
|
||||
if (accWrap) {
|
||||
accWrap->ActionDescriptionAt(aActionIndex, description);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
|
||||
proxy->ActionDescriptionAt(aActionIndex, description);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AccessibleWrap::ReturnString(description);
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getActionNameCB(AtkAction *aAction, gint aActionIndex)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
|
||||
if (accWrap) {
|
||||
accWrap->ActionNameAt(aActionIndex, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
|
||||
proxy->ActionNameAt(aActionIndex, autoStr);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AccessibleWrap::ReturnString(autoStr);
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getKeyBindingCB(AtkAction *aAction, gint aActionIndex)
|
||||
{
|
||||
nsAutoString keyBindingsStr;
|
||||
AccessibleWrap* acc = GetAccessibleWrap(ATK_OBJECT(aAction));
|
||||
if (acc) {
|
||||
AccessibleWrap::GetKeyBinding(acc, keyBindingsStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aAction))) {
|
||||
proxy->AtkKeyBinding(keyBindingsStr);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AccessibleWrap::ReturnString(keyBindingsStr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
actionInterfaceInitCB(AtkActionIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid aIface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->do_action = doActionCB;
|
||||
aIface->get_n_actions = getActionCountCB;
|
||||
aIface->get_description = getActionDescriptionCB;
|
||||
aIface->get_keybinding = getKeyBindingCB;
|
||||
aIface->get_name = getActionNameCB;
|
||||
}
|
||||
151
accessible/atk/nsMaiInterfaceComponent.cpp
Normal file
151
accessible/atk/nsMaiInterfaceComponent.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "AccessibleWrap.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsMai.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "mozilla/a11y/ProxyAccessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
|
||||
static AtkObject*
|
||||
refAccessibleAtPointCB(AtkComponent* aComponent, gint aAccX, gint aAccY,
|
||||
AtkCoordType aCoordType)
|
||||
{
|
||||
return refAccessibleAtPointHelper(ATK_OBJECT(aComponent),
|
||||
aAccX, aAccY, aCoordType);
|
||||
}
|
||||
|
||||
static void
|
||||
getExtentsCB(AtkComponent* aComponent, gint* aX, gint* aY,
|
||||
gint* aWidth, gint* aHeight, AtkCoordType aCoordType)
|
||||
{
|
||||
getExtentsHelper(ATK_OBJECT(aComponent),
|
||||
aX, aY, aWidth, aHeight, aCoordType);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
grabFocusCB(AtkComponent* aComponent)
|
||||
{
|
||||
AtkObject* atkObject = ATK_OBJECT(aComponent);
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(atkObject);
|
||||
if (accWrap) {
|
||||
accWrap->TakeFocus();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = GetProxy(atkObject);
|
||||
if (proxy) {
|
||||
proxy->TakeFocus();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
AtkObject*
|
||||
refAccessibleAtPointHelper(AtkObject* aAtkObj, gint aX, gint aY,
|
||||
AtkCoordType aCoordType)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
if (accWrap) {
|
||||
if (accWrap->IsDefunct() || nsAccUtils::MustPrune(accWrap)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Accessible::ChildAtPoint(x,y) is in screen pixels.
|
||||
if (aCoordType == ATK_XY_WINDOW) {
|
||||
nsIntPoint winCoords =
|
||||
nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode());
|
||||
aX += winCoords.x;
|
||||
aY += winCoords.y;
|
||||
}
|
||||
|
||||
Accessible* accAtPoint = accWrap->ChildAtPoint(aX, aY,
|
||||
Accessible::eDirectChild);
|
||||
if (!accAtPoint) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AtkObject* atkObj = AccessibleWrap::GetAtkObject(accAtPoint);
|
||||
if (atkObj) {
|
||||
g_object_ref(atkObj);
|
||||
}
|
||||
|
||||
return atkObj;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
|
||||
ProxyAccessible* result =
|
||||
proxy->AccessibleAtPoint(aX, aY, aCoordType == ATK_XY_WINDOW);
|
||||
AtkObject* atkObj = result ? GetWrapperFor(result) : nullptr;
|
||||
if (atkObj) {
|
||||
g_object_ref(atkObj);
|
||||
}
|
||||
return atkObj;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
getExtentsHelper(AtkObject* aAtkObj,
|
||||
gint* aX, gint* aY, gint* aWidth, gint* aHeight,
|
||||
AtkCoordType aCoordType)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj);
|
||||
*aX = *aY = *aWidth = *aHeight = 0;
|
||||
|
||||
if (accWrap) {
|
||||
if (accWrap->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRect screenRect = accWrap->Bounds();
|
||||
if (screenRect.IsEmpty())
|
||||
return;
|
||||
|
||||
if (aCoordType == ATK_XY_WINDOW) {
|
||||
nsIntPoint winCoords =
|
||||
nsCoreUtils::GetScreenCoordsForWindow(accWrap->GetNode());
|
||||
screenRect.x -= winCoords.x;
|
||||
screenRect.y -= winCoords.y;
|
||||
}
|
||||
|
||||
*aX = screenRect.x;
|
||||
*aY = screenRect.y;
|
||||
*aWidth = screenRect.width;
|
||||
*aHeight = screenRect.height;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(aAtkObj)) {
|
||||
proxy->Extents(aCoordType == ATK_XY_WINDOW, aX, aY, aWidth, aHeight);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
componentInterfaceInitCB(AtkComponentIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid Interface");
|
||||
if(MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Use default implementation in atk for contains, get_position,
|
||||
* and get_size
|
||||
*/
|
||||
aIface->ref_accessible_at_point = refAccessibleAtPointCB;
|
||||
aIface->get_extents = getExtentsCB;
|
||||
aIface->grab_focus = grabFocusCB;
|
||||
}
|
||||
151
accessible/atk/nsMaiInterfaceDocument.cpp
Normal file
151
accessible/atk/nsMaiInterfaceDocument.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccessibleWrap.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
static const char* const kDocTypeName = "W3C-doctype";
|
||||
static const char* const kDocUrlName = "DocURL";
|
||||
static const char* const kMimeTypeName = "MimeType";
|
||||
|
||||
// below functions are vfuncs on an ATK interface so they need to be C call
|
||||
extern "C" {
|
||||
|
||||
static const gchar* getDocumentLocaleCB(AtkDocument* aDocument);
|
||||
static AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument);
|
||||
static const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument,
|
||||
const gchar* aAttrName);
|
||||
|
||||
void
|
||||
documentInterfaceInitCB(AtkDocumentIface *aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid Interface");
|
||||
if(MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
/*
|
||||
* We don't support get_document or set_attribute right now.
|
||||
* get_document_type is deprecated, we return DocType in
|
||||
* get_document_attribute_value and get_document_attributes instead.
|
||||
*/
|
||||
aIface->get_document_attributes = getDocumentAttributesCB;
|
||||
aIface->get_document_attribute_value = getDocumentAttributeValueCB;
|
||||
aIface->get_document_locale = getDocumentLocaleCB;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
getDocumentLocaleCB(AtkDocument *aDocument)
|
||||
{
|
||||
nsAutoString locale;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
|
||||
if (accWrap) {
|
||||
accWrap->Language(locale);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
|
||||
proxy->Language(locale);
|
||||
}
|
||||
|
||||
return locale.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(locale);
|
||||
}
|
||||
|
||||
static inline GSList *
|
||||
prependToList(GSList *aList, const char *const aName, const nsAutoString &aValue)
|
||||
{
|
||||
if (aValue.IsEmpty()) {
|
||||
return aList;
|
||||
}
|
||||
|
||||
// libspi will free these
|
||||
AtkAttribute *atkAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
|
||||
atkAttr->name = g_strdup(aName);
|
||||
atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
|
||||
return g_slist_prepend(aList, atkAttr);
|
||||
}
|
||||
|
||||
AtkAttributeSet *
|
||||
getDocumentAttributesCB(AtkDocument *aDocument)
|
||||
{
|
||||
nsAutoString url;
|
||||
nsAutoString w3cDocType;
|
||||
nsAutoString mimeType;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
|
||||
if (accWrap) {
|
||||
if (!accWrap->IsDoc()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DocAccessible* document = accWrap->AsDoc();
|
||||
document->URL(url);
|
||||
document->DocType(w3cDocType);
|
||||
document->MimeType(mimeType);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aDocument))) {
|
||||
proxy->URLDocTypeMimeType(url, w3cDocType, mimeType);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// according to atkobject.h, AtkAttributeSet is a GSList
|
||||
GSList* attributes = nullptr;
|
||||
attributes = prependToList(attributes, kDocUrlName, url);
|
||||
attributes = prependToList(attributes, kDocTypeName, w3cDocType);
|
||||
attributes = prependToList(attributes, kMimeTypeName, mimeType);
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
const gchar *
|
||||
getDocumentAttributeValueCB(AtkDocument *aDocument,
|
||||
const gchar *aAttrName)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
DocAccessible* document = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
|
||||
if (accWrap) {
|
||||
if (!accWrap->IsDoc()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
document = accWrap->AsDoc();
|
||||
} else {
|
||||
proxy = GetProxy(ATK_OBJECT(aDocument));
|
||||
if (!proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoString attrValue;
|
||||
if (!strcasecmp(aAttrName, kDocTypeName)) {
|
||||
if (document) {
|
||||
document->DocType(attrValue);
|
||||
} else {
|
||||
proxy->DocType(attrValue);
|
||||
}
|
||||
} else if (!strcasecmp(aAttrName, kDocUrlName)) {
|
||||
if (document) {
|
||||
document->URL(attrValue);
|
||||
} else {
|
||||
proxy->URL(attrValue);
|
||||
}
|
||||
} else if (!strcasecmp(aAttrName, kMimeTypeName)) {
|
||||
if (document) {
|
||||
document->MimeType(attrValue);
|
||||
} else {
|
||||
proxy->MimeType(attrValue);
|
||||
}
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return attrValue.IsEmpty() ? nullptr : AccessibleWrap::ReturnString(attrValue);
|
||||
}
|
||||
}
|
||||
134
accessible/atk/nsMaiInterfaceEditableText.cpp
Normal file
134
accessible/atk/nsMaiInterfaceEditableText.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "HyperTextAccessible-inl.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "nsString.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
static void
|
||||
setTextContentsCB(AtkEditableText *aText, const gchar *aString)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 strContent(aString);
|
||||
text->ReplaceText(strContent);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
NS_ConvertUTF8toUTF16 strContent(aString);
|
||||
proxy->ReplaceText(strContent);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
insertTextCB(AtkEditableText *aText,
|
||||
const gchar *aString, gint aLength, gint *aPosition)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
NS_ConvertUTF8toUTF16 strContent(aString);
|
||||
text->InsertText(strContent, *aPosition);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
NS_ConvertUTF8toUTF16 strContent(aString);
|
||||
proxy->InsertText(strContent, *aPosition);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
copyTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
text->CopyText(aStartPos, aEndPos);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->CopyText(aStartPos, aEndPos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
cutTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
text->CutText(aStartPos, aEndPos);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->CutText(aStartPos, aEndPos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
deleteTextCB(AtkEditableText *aText, gint aStartPos, gint aEndPos)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
text->DeleteText(aStartPos, aEndPos);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->DeleteText(aStartPos, aEndPos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
pasteTextCB(AtkEditableText *aText, gint aPosition)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
text->PasteText(aPosition);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->PasteText(aPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
editableTextInterfaceInitCB(AtkEditableTextIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid aIface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->set_text_contents = setTextContentsCB;
|
||||
aIface->insert_text = insertTextCB;
|
||||
aIface->copy_text = copyTextCB;
|
||||
aIface->cut_text = cutTextCB;
|
||||
aIface->delete_text = deleteTextCB;
|
||||
aIface->paste_text = pasteTextCB;
|
||||
}
|
||||
37
accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp
Normal file
37
accessible/atk/nsMaiInterfaceHyperlinkImpl.cpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "nsMaiHyperlink.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
static AtkHyperlink*
|
||||
getHyperlinkCB(AtkHyperlinkImpl* aImpl)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImpl));
|
||||
if (!accWrap && !GetProxy(ATK_OBJECT(aImpl)))
|
||||
return nullptr;
|
||||
|
||||
if (accWrap)
|
||||
NS_ASSERTION(accWrap->IsLink(), "why isn't it a link!");
|
||||
|
||||
return MAI_ATK_OBJECT(aImpl)->GetAtkHyperlink();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hyperlinkImplInterfaceInitCB(AtkHyperlinkImplIface *aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "no interface!");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_hyperlink = getHyperlinkCB;
|
||||
}
|
||||
94
accessible/atk/nsMaiInterfaceHypertext.cpp
Normal file
94
accessible/atk/nsMaiInterfaceHypertext.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "nsMai.h"
|
||||
#include "nsMaiHyperlink.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
|
||||
static AtkHyperlink*
|
||||
getLinkCB(AtkHypertext *aText, gint aLinkIndex)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
AtkObject* atkHyperLink = nullptr;
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* hyperText = accWrap->AsHyperText();
|
||||
NS_ENSURE_TRUE(hyperText, nullptr);
|
||||
|
||||
Accessible* hyperLink = hyperText->LinkAt(aLinkIndex);
|
||||
if (!hyperLink || !hyperLink->IsLink()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
atkHyperLink = AccessibleWrap::GetAtkObject(hyperLink);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
ProxyAccessible* proxyLink = proxy->LinkAt(aLinkIndex);
|
||||
if (!proxyLink)
|
||||
return nullptr;
|
||||
|
||||
atkHyperLink = GetWrapperFor(proxyLink);
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(IS_MAI_OBJECT(atkHyperLink), nullptr);
|
||||
return MAI_ATK_OBJECT(atkHyperLink)->GetAtkHyperlink();
|
||||
}
|
||||
|
||||
static gint
|
||||
getLinkCountCB(AtkHypertext *aText)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* hyperText = accWrap->AsHyperText();
|
||||
NS_ENSURE_TRUE(hyperText, -1);
|
||||
return hyperText->LinkCount();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->LinkCount();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getLinkIndexCB(AtkHypertext *aText, gint aCharIndex)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* hyperText = accWrap->AsHyperText();
|
||||
NS_ENSURE_TRUE(hyperText, -1);
|
||||
|
||||
return hyperText->LinkIndexAtOffset(aCharIndex);
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->LinkIndexAtOffset(aCharIndex);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
hypertextInterfaceInitCB(AtkHypertextIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "no interface!");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_link = getLinkCB;
|
||||
aIface->get_n_links = getLinkCountCB;
|
||||
aIface->get_link_index = getLinkIndexCB;
|
||||
}
|
||||
77
accessible/atk/nsMaiInterfaceImage.cpp
Normal file
77
accessible/atk/nsMaiInterfaceImage.cpp
Normal 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/. */
|
||||
|
||||
#include "InterfaceInitFuncs.h"
|
||||
|
||||
#include "AccessibleWrap.h"
|
||||
#include "ImageAccessible.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "nsMai.h"
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "nsIURI.h"
|
||||
#include "ProxyAccessible.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
const gchar* getDescriptionCB(AtkObject* aAtkObj);
|
||||
|
||||
static void
|
||||
getImagePositionCB(AtkImage* aImage, gint* aAccX, gint* aAccY,
|
||||
AtkCoordType aCoordType)
|
||||
{
|
||||
nsIntPoint pos;
|
||||
uint32_t geckoCoordType = (aCoordType == ATK_XY_WINDOW) ?
|
||||
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE :
|
||||
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
|
||||
if (accWrap && accWrap->IsImage()) {
|
||||
ImageAccessible* image = accWrap->AsImage();
|
||||
pos = image->Position(geckoCoordType);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
|
||||
pos = proxy->ImagePosition(geckoCoordType);
|
||||
}
|
||||
|
||||
*aAccX = pos.x;
|
||||
*aAccY = pos.y;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getImageDescriptionCB(AtkImage* aImage)
|
||||
{
|
||||
return getDescriptionCB(ATK_OBJECT(aImage));
|
||||
}
|
||||
|
||||
static void
|
||||
getImageSizeCB(AtkImage* aImage, gint* aAccWidth, gint* aAccHeight)
|
||||
{
|
||||
nsIntSize size;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aImage));
|
||||
if (accWrap && accWrap->IsImage()) {
|
||||
size = accWrap->AsImage()->Size();
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aImage))) {
|
||||
size = proxy->ImageSize();
|
||||
}
|
||||
|
||||
*aAccWidth = size.width;
|
||||
*aAccHeight = size.height;
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
void
|
||||
imageInterfaceInitCB(AtkImageIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "no interface!");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_image_position = getImagePositionCB;
|
||||
aIface->get_image_description = getImageDescriptionCB;
|
||||
aIface->get_image_size = getImageSizeCB;
|
||||
}
|
||||
152
accessible/atk/nsMaiInterfaceSelection.cpp
Normal file
152
accessible/atk/nsMaiInterfaceSelection.cpp
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccessibleWrap.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
#include <atk/atk.h>
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
|
||||
static gboolean
|
||||
addSelectionCB(AtkSelection *aSelection, gint i)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->AddItemToSelection(i);
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->AddItemToSelection(i);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
clearSelectionCB(AtkSelection *aSelection)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->UnselectAll();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->UnselectAll();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
refSelectionCB(AtkSelection *aSelection, gint i)
|
||||
{
|
||||
AtkObject* atkObj = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
Accessible* selectedItem = accWrap->GetSelectedItem(i);
|
||||
if (!selectedItem) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
atkObj = AccessibleWrap::GetAtkObject(selectedItem);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
ProxyAccessible* selectedItem = proxy->GetSelectedItem(i);
|
||||
if (selectedItem) {
|
||||
atkObj = GetWrapperFor(selectedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (atkObj) {
|
||||
g_object_ref(atkObj);
|
||||
}
|
||||
|
||||
return atkObj;
|
||||
}
|
||||
|
||||
static gint
|
||||
getSelectionCountCB(AtkSelection *aSelection)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->SelectedItemCount();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->SelectedItemCount();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
isChildSelectedCB(AtkSelection *aSelection, gint i)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->IsItemSelected(i);
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->IsItemSelected(i);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
removeSelectionCB(AtkSelection *aSelection, gint i)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->RemoveItemFromSelection(i);
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->RemoveItemFromSelection(i);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
selectAllSelectionCB(AtkSelection *aSelection)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aSelection));
|
||||
if (accWrap && accWrap->IsSelect()) {
|
||||
return accWrap->SelectAll();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aSelection))) {
|
||||
return proxy->SelectAll();
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
selectionInterfaceInitCB(AtkSelectionIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid aIface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->add_selection = addSelectionCB;
|
||||
aIface->clear_selection = clearSelectionCB;
|
||||
aIface->ref_selection = refSelectionCB;
|
||||
aIface->get_selection_count = getSelectionCountCB;
|
||||
aIface->is_child_selected = isChildSelectedCB;
|
||||
aIface->remove_selection = removeSelectionCB;
|
||||
aIface->select_all_selection = selectAllSelectionCB;
|
||||
}
|
||||
391
accessible/atk/nsMaiInterfaceTable.cpp
Normal file
391
accessible/atk/nsMaiInterfaceTable.cpp
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccessibleWrap.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "TableAccessible.h"
|
||||
#include "TableCellAccessible.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "nsArrayUtils.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
static AtkObject*
|
||||
refAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx)
|
||||
{
|
||||
if (aRowIdx < 0 || aColIdx < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AtkObject* cellAtkObj = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
Accessible* cell = accWrap->AsTable()->CellAt(aRowIdx, aColIdx);
|
||||
if (!cell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cellAtkObj = AccessibleWrap::GetAtkObject(cell);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
ProxyAccessible* cell = proxy->TableCellAt(aRowIdx, aColIdx);
|
||||
if (!cell) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
cellAtkObj = GetWrapperFor(cell);
|
||||
}
|
||||
|
||||
if (cellAtkObj) {
|
||||
g_object_ref(cellAtkObj);
|
||||
}
|
||||
|
||||
return cellAtkObj;
|
||||
}
|
||||
|
||||
static gint
|
||||
getIndexAtCB(AtkTable* aTable, gint aRowIdx, gint aColIdx)
|
||||
{
|
||||
if (aRowIdx < 0 || aColIdx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->CellIndexAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableCellIndexAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getColumnAtIndexCB(AtkTable *aTable, gint aIdx)
|
||||
{
|
||||
if (aIdx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->ColIndexAt(aIdx));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableColumnIndexAt(aIdx));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getRowAtIndexCB(AtkTable *aTable, gint aIdx)
|
||||
{
|
||||
if (aIdx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->RowIndexAt(aIdx));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableRowIndexAt(aIdx));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getColumnCountCB(AtkTable *aTable)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->ColCount());
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableColumnCount());
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getRowCountCB(AtkTable *aTable)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->RowCount());
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableRowCount());
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getColumnExtentAtCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
|
||||
{
|
||||
if (aRowIdx < 0 || aColIdx < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->ColExtentAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableColumnExtentAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getRowExtentAtCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gint>(accWrap->AsTable()->RowExtentAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gint>(proxy->TableRowExtentAt(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
getCaptionCB(AtkTable* aTable)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
Accessible* caption = accWrap->AsTable()->Caption();
|
||||
return caption ? AccessibleWrap::GetAtkObject(caption) : nullptr;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
ProxyAccessible* caption = proxy->TableCaption();
|
||||
return caption ? GetWrapperFor(caption) : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getColumnDescriptionCB(AtkTable *aTable, gint aColumn)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->ColDescription(aColumn, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
proxy->TableColumnDescription(aColumn, autoStr);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AccessibleWrap::ReturnString(autoStr);
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
getColumnHeaderCB(AtkTable *aTable, gint aColIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
Accessible* header =
|
||||
AccessibleWrap::GetColumnHeader(accWrap->AsTable(), aColIdx);
|
||||
return header ? AccessibleWrap::GetAtkObject(header) : nullptr;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
ProxyAccessible* header = proxy->AtkTableColumnHeader(aColIdx);
|
||||
return header ? GetWrapperFor(header) : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const gchar*
|
||||
getRowDescriptionCB(AtkTable *aTable, gint aRow)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->RowDescription(aRow, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
proxy->TableRowDescription(aRow, autoStr);
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AccessibleWrap::ReturnString(autoStr);
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
getRowHeaderCB(AtkTable *aTable, gint aRowIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
Accessible* header =
|
||||
AccessibleWrap::GetRowHeader(accWrap->AsTable(), aRowIdx);
|
||||
return header ? AccessibleWrap::GetAtkObject(header) : nullptr;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
ProxyAccessible* header = proxy->AtkTableRowHeader(aRowIdx);
|
||||
return header ? GetWrapperFor(header) : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
getSummaryCB(AtkTable *aTable)
|
||||
{
|
||||
// Neither html:table nor xul:tree nor ARIA grid/tree have an ability to
|
||||
// link an accessible object to specify a summary. There is closes method
|
||||
// in TableAccessible::summary to get a summary as a string which is not
|
||||
// mapped directly to ATK.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static gint
|
||||
getSelectedColumnsCB(AtkTable *aTable, gint** aSelected)
|
||||
{
|
||||
*aSelected = nullptr;
|
||||
|
||||
AutoTArray<uint32_t, 10> cols;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->SelectedColIndices(&cols);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
proxy->TableSelectedColumnIndices(&cols);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cols.IsEmpty())
|
||||
return 0;
|
||||
|
||||
gint* atkColumns = g_new(gint, cols.Length());
|
||||
if (!atkColumns) {
|
||||
NS_WARNING("OUT OF MEMORY");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(atkColumns, cols.Elements(), cols.Length() * sizeof(uint32_t));
|
||||
*aSelected = atkColumns;
|
||||
return cols.Length();
|
||||
}
|
||||
|
||||
static gint
|
||||
getSelectedRowsCB(AtkTable *aTable, gint **aSelected)
|
||||
{
|
||||
AutoTArray<uint32_t, 10> rows;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
accWrap->AsTable()->SelectedRowIndices(&rows);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
proxy->TableSelectedRowIndices(&rows);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
gint* atkRows = g_new(gint, rows.Length());
|
||||
if (!atkRows) {
|
||||
NS_WARNING("OUT OF MEMORY");
|
||||
return 0;
|
||||
}
|
||||
|
||||
memcpy(atkRows, rows.Elements(), rows.Length() * sizeof(uint32_t));
|
||||
*aSelected = atkRows;
|
||||
return rows.Length();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
isColumnSelectedCB(AtkTable *aTable, gint aColIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gboolean>(accWrap->AsTable()->IsColSelected(aColIdx));
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gboolean>(proxy->TableColumnSelected(aColIdx));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
isRowSelectedCB(AtkTable *aTable, gint aRowIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gboolean>(accWrap->AsTable()->IsRowSelected(aRowIdx));
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gboolean>(proxy->TableRowSelected(aRowIdx));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
isCellSelectedCB(AtkTable *aTable, gint aRowIdx, gint aColIdx)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTable));
|
||||
if (accWrap) {
|
||||
return static_cast<gboolean>(accWrap->AsTable()->
|
||||
IsCellSelected(aRowIdx, aColIdx));
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTable))) {
|
||||
return static_cast<gboolean>(proxy->TableCellSelected(aRowIdx, aColIdx));
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tableInterfaceInitCB(AtkTableIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "no interface!");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->ref_at = refAtCB;
|
||||
aIface->get_index_at = getIndexAtCB;
|
||||
aIface->get_column_at_index = getColumnAtIndexCB;
|
||||
aIface->get_row_at_index = getRowAtIndexCB;
|
||||
aIface->get_n_columns = getColumnCountCB;
|
||||
aIface->get_n_rows = getRowCountCB;
|
||||
aIface->get_column_extent_at = getColumnExtentAtCB;
|
||||
aIface->get_row_extent_at = getRowExtentAtCB;
|
||||
aIface->get_caption = getCaptionCB;
|
||||
aIface->get_column_description = getColumnDescriptionCB;
|
||||
aIface->get_column_header = getColumnHeaderCB;
|
||||
aIface->get_row_description = getRowDescriptionCB;
|
||||
aIface->get_row_header = getRowHeaderCB;
|
||||
aIface->get_summary = getSummaryCB;
|
||||
aIface->get_selected_columns = getSelectedColumnsCB;
|
||||
aIface->get_selected_rows = getSelectedRowsCB;
|
||||
aIface->is_column_selected = isColumnSelectedCB;
|
||||
aIface->is_row_selected = isRowSelectedCB;
|
||||
aIface->is_selected = isCellSelectedCB;
|
||||
}
|
||||
216
accessible/atk/nsMaiInterfaceTableCell.cpp
Normal file
216
accessible/atk/nsMaiInterfaceTableCell.cpp
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccessibleWrap.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "TableAccessible.h"
|
||||
#include "TableCellAccessible.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "nsArrayUtils.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
static gint
|
||||
GetColumnSpanCB(AtkTableCell* aCell)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
|
||||
if (accWrap) {
|
||||
return accWrap->AsTableCell()->ColExtent();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
return proxy->ColExtent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
GetRowSpanCB(AtkTableCell* aCell)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
|
||||
if (accWrap) {
|
||||
return accWrap->AsTableCell()->RowExtent();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
return proxy->RowExtent();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol)
|
||||
{
|
||||
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
|
||||
TableCellAccessible* cell = accWrap->AsTableCell();
|
||||
*aRow = cell->RowIdx();
|
||||
*aCol = cell->ColIdx();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
uint32_t rowIdx = 0, colIdx = 0;
|
||||
proxy->GetPosition(&rowIdx, &colIdx);
|
||||
*aCol = colIdx;
|
||||
*aRow = rowIdx;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
GetColumnRowSpanCB(AtkTableCell* aCell, gint* aCol, gint* aRow,
|
||||
gint* aColExtent, gint* aRowExtent) {
|
||||
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
|
||||
TableCellAccessible* cellAcc = accWrap->AsTableCell();
|
||||
*aCol = cellAcc->ColIdx();
|
||||
*aRow = cellAcc->RowIdx();
|
||||
*aColExtent = cellAcc->ColExtent();
|
||||
*aRowExtent = cellAcc->ColExtent();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
uint32_t colIdx = 0, rowIdx = 0, colExtent = 0, rowExtent = 0;
|
||||
proxy->GetColRowExtents(&colIdx, &rowIdx, &colExtent, &rowExtent);
|
||||
*aCol = colIdx;
|
||||
*aRow = rowIdx;
|
||||
*aColExtent = colExtent;
|
||||
*aRowExtent = rowExtent;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static AtkObject*
|
||||
GetTableCB(AtkTableCell* aTableCell)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTableCell));
|
||||
if (accWrap) {
|
||||
TableAccessible* table = accWrap->AsTableCell()->Table();
|
||||
if (!table) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Accessible* tableAcc = table->AsAccessible();
|
||||
return tableAcc ? AccessibleWrap::GetAtkObject(tableAcc) : nullptr;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTableCell))) {
|
||||
ProxyAccessible* table = proxy->TableOfACell();
|
||||
return table ? GetWrapperFor(table) : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static GPtrArray*
|
||||
GetColumnHeaderCellsCB(AtkTableCell* aCell)
|
||||
{
|
||||
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
|
||||
AutoTArray<Accessible*, 10> headers;
|
||||
accWrap->AsTableCell()->ColHeaderCells(&headers);
|
||||
if (headers.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
|
||||
for (Accessible* header: headers) {
|
||||
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
|
||||
g_object_ref(atkHeader);
|
||||
g_ptr_array_add(atkHeaders, atkHeader);
|
||||
}
|
||||
|
||||
return atkHeaders;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
AutoTArray<ProxyAccessible*, 10> headers;
|
||||
proxy->ColHeaderCells(&headers);
|
||||
if (headers.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
|
||||
for (ProxyAccessible* header: headers) {
|
||||
AtkObject* atkHeader = GetWrapperFor(header);
|
||||
g_object_ref(atkHeader);
|
||||
g_ptr_array_add(atkHeaders, atkHeader);
|
||||
}
|
||||
|
||||
return atkHeaders;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static GPtrArray*
|
||||
GetRowHeaderCellsCB(AtkTableCell* aCell)
|
||||
{
|
||||
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
|
||||
AutoTArray<Accessible*, 10> headers;
|
||||
accWrap->AsTableCell()->RowHeaderCells(&headers);
|
||||
if (headers.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
|
||||
for (Accessible* header: headers) {
|
||||
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
|
||||
g_object_ref(atkHeader);
|
||||
g_ptr_array_add(atkHeaders, atkHeader);
|
||||
}
|
||||
|
||||
return atkHeaders;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
|
||||
AutoTArray<ProxyAccessible*, 10> headers;
|
||||
proxy->RowHeaderCells(&headers);
|
||||
if (headers.IsEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
|
||||
for (ProxyAccessible* header: headers) {
|
||||
AtkObject* atkHeader = GetWrapperFor(header);
|
||||
g_object_ref(atkHeader);
|
||||
g_ptr_array_add(atkHeaders, atkHeader);
|
||||
}
|
||||
|
||||
return atkHeaders;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tableCellInterfaceInitCB(AtkTableCellIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "no interface!");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_column_span = GetColumnSpanCB;
|
||||
aIface->get_column_header_cells = GetColumnHeaderCellsCB;
|
||||
aIface->get_position = GetPositionCB;
|
||||
aIface->get_row_span = GetRowSpanCB;
|
||||
aIface->get_row_header_cells = GetRowHeaderCellsCB;
|
||||
aIface->get_row_column_span = GetColumnRowSpanCB;
|
||||
aIface->get_table = GetTableCB;
|
||||
}
|
||||
629
accessible/atk/nsMaiInterfaceText.cpp
Normal file
629
accessible/atk/nsMaiInterfaceText.cpp
Normal file
|
|
@ -0,0 +1,629 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
#include "mozilla/a11y/PDocAccessible.h"
|
||||
#include "Accessible-inl.h"
|
||||
#include "HyperTextAccessible-inl.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "nsIPersistentProperties2.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
static const char* sAtkTextAttrNames[ATK_TEXT_ATTR_LAST_DEFINED];
|
||||
|
||||
void
|
||||
ConvertTextAttributeToAtkAttribute(const nsACString& aName,
|
||||
const nsAString& aValue,
|
||||
AtkAttributeSet** aAttributeSet)
|
||||
{
|
||||
// Handle attributes where atk has its own name.
|
||||
const char* atkName = nullptr;
|
||||
nsAutoString atkValue;
|
||||
if (aName.EqualsLiteral("color")) {
|
||||
// The format of the atk attribute is r,g,b and the gecko one is
|
||||
// rgb(r, g, b).
|
||||
atkValue = Substring(aValue, 4, aValue.Length() - 5);
|
||||
atkValue.StripWhitespace();
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FG_COLOR];
|
||||
} else if (aName.EqualsLiteral("background-color")) {
|
||||
// The format of the atk attribute is r,g,b and the gecko one is
|
||||
// rgb(r, g, b).
|
||||
atkValue = Substring(aValue, 4, aValue.Length() - 5);
|
||||
atkValue.StripWhitespace();
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_BG_COLOR];
|
||||
} else if (aName.EqualsLiteral("font-family")) {
|
||||
atkValue = aValue;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_FAMILY_NAME];
|
||||
} else if (aName.EqualsLiteral("font-size")) {
|
||||
// ATK wants the number of pixels without px at the end.
|
||||
atkValue = StringHead(aValue, aValue.Length() - 2);
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_SIZE];
|
||||
} else if (aName.EqualsLiteral("font-weight")) {
|
||||
atkValue = aValue;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_WEIGHT];
|
||||
} else if (aName.EqualsLiteral("invalid")) {
|
||||
atkValue = aValue;
|
||||
atkName = sAtkTextAttrNames[ATK_TEXT_ATTR_INVALID];
|
||||
}
|
||||
|
||||
if (atkName) {
|
||||
AtkAttribute* objAttr =
|
||||
static_cast<AtkAttribute*>(g_malloc(sizeof(AtkAttribute)));
|
||||
objAttr->name = g_strdup(atkName);
|
||||
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(atkValue).get());
|
||||
*aAttributeSet = g_slist_prepend(*aAttributeSet, objAttr);
|
||||
}
|
||||
}
|
||||
|
||||
static AtkAttributeSet*
|
||||
ConvertToAtkTextAttributeSet(nsTArray<Attribute>& aAttributes)
|
||||
{
|
||||
AtkAttributeSet* objAttributeSet = nullptr;
|
||||
for (size_t i = 0; i < aAttributes.Length(); ++i) {
|
||||
AtkAttribute* objAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
|
||||
objAttr->name = g_strdup(aAttributes[i].Name().get());
|
||||
objAttr->value =
|
||||
g_strdup(NS_ConvertUTF16toUTF8(aAttributes[i].Value()).get());
|
||||
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
|
||||
ConvertTextAttributeToAtkAttribute(aAttributes[i].Name(),
|
||||
aAttributes[i].Value(),
|
||||
&objAttributeSet);
|
||||
}
|
||||
return objAttributeSet;
|
||||
}
|
||||
|
||||
static AtkAttributeSet*
|
||||
ConvertToAtkTextAttributeSet(nsIPersistentProperties* aAttributes)
|
||||
{
|
||||
if (!aAttributes)
|
||||
return nullptr;
|
||||
|
||||
AtkAttributeSet* objAttributeSet = nullptr;
|
||||
nsCOMPtr<nsISimpleEnumerator> propEnum;
|
||||
nsresult rv = aAttributes->Enumerate(getter_AddRefs(propEnum));
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
|
||||
bool hasMore = false;
|
||||
while (NS_SUCCEEDED(propEnum->HasMoreElements(&hasMore)) && hasMore) {
|
||||
nsCOMPtr<nsISupports> sup;
|
||||
rv = propEnum->GetNext(getter_AddRefs(sup));
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
nsCOMPtr<nsIPropertyElement> propElem(do_QueryInterface(sup));
|
||||
NS_ENSURE_TRUE(propElem, objAttributeSet);
|
||||
|
||||
nsAutoCString name;
|
||||
rv = propElem->GetKey(name);
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
nsAutoString value;
|
||||
rv = propElem->GetValue(value);
|
||||
NS_ENSURE_SUCCESS(rv, objAttributeSet);
|
||||
|
||||
AtkAttribute* objAttr = (AtkAttribute*)g_malloc(sizeof(AtkAttribute));
|
||||
objAttr->name = g_strdup(name.get());
|
||||
objAttr->value = g_strdup(NS_ConvertUTF16toUTF8(value).get());
|
||||
objAttributeSet = g_slist_prepend(objAttributeSet, objAttr);
|
||||
|
||||
ConvertTextAttributeToAtkAttribute(name, value, &objAttributeSet);
|
||||
}
|
||||
|
||||
// libatk-adaptor will free it
|
||||
return objAttributeSet;
|
||||
}
|
||||
|
||||
static void
|
||||
ConvertTexttoAsterisks(AccessibleWrap* accWrap, nsAString& aString)
|
||||
{
|
||||
// convert each char to "*" when it's "password text"
|
||||
if (accWrap->NativeRole() == roles::PASSWORD_TEXT) {
|
||||
for (uint32_t i = 0; i < aString.Length(); i++)
|
||||
aString.Replace(i, 1, NS_LITERAL_STRING("*"));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
static gchar*
|
||||
getTextCB(AtkText *aText, gint aStartOffset, gint aEndOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
nsAutoString autoStr;
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole())
|
||||
return nullptr;
|
||||
|
||||
text->TextSubstring(aStartOffset, aEndOffset, autoStr);
|
||||
|
||||
ConvertTexttoAsterisks(accWrap, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->TextSubstring(aStartOffset, aEndOffset, autoStr);
|
||||
}
|
||||
|
||||
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
|
||||
|
||||
//copy and return, libspi will free it.
|
||||
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
|
||||
}
|
||||
|
||||
static gchar*
|
||||
getTextAfterOffsetCB(AtkText *aText, gint aOffset,
|
||||
AtkTextBoundary aBoundaryType,
|
||||
gint *aStartOffset, gint *aEndOffset)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole())
|
||||
return nullptr;
|
||||
|
||||
text->TextAfterOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
|
||||
ConvertTexttoAsterisks(accWrap, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->GetTextAfterOffset(aOffset, aBoundaryType, autoStr, &startOffset,
|
||||
&endOffset);
|
||||
}
|
||||
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
|
||||
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
|
||||
}
|
||||
|
||||
static gchar*
|
||||
getTextAtOffsetCB(AtkText *aText, gint aOffset,
|
||||
AtkTextBoundary aBoundaryType,
|
||||
gint *aStartOffset, gint *aEndOffset)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole())
|
||||
return nullptr;
|
||||
|
||||
text->TextAtOffset(aOffset, aBoundaryType, &startOffset, &endOffset, autoStr);
|
||||
ConvertTexttoAsterisks(accWrap, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->GetTextAtOffset(aOffset, aBoundaryType, autoStr, &startOffset,
|
||||
&endOffset);
|
||||
}
|
||||
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
|
||||
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
|
||||
}
|
||||
|
||||
static gunichar
|
||||
getCharacterAtOffsetCB(AtkText* aText, gint aOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// char16_t is unsigned short in Mozilla, gnuichar is guint32 in glib.
|
||||
return static_cast<gunichar>(text->CharAt(aOffset));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return static_cast<gunichar>(proxy->CharAt(aOffset));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gchar*
|
||||
getTextBeforeOffsetCB(AtkText *aText, gint aOffset,
|
||||
AtkTextBoundary aBoundaryType,
|
||||
gint *aStartOffset, gint *aEndOffset)
|
||||
{
|
||||
nsAutoString autoStr;
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole())
|
||||
return nullptr;
|
||||
|
||||
text->TextBeforeOffset(aOffset, aBoundaryType,
|
||||
&startOffset, &endOffset, autoStr);
|
||||
ConvertTexttoAsterisks(accWrap, autoStr);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->GetTextBeforeOffset(aOffset, aBoundaryType, autoStr, &startOffset,
|
||||
&endOffset);
|
||||
}
|
||||
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
NS_ConvertUTF16toUTF8 cautoStr(autoStr);
|
||||
return (cautoStr.get()) ? g_strdup(cautoStr.get()) : nullptr;
|
||||
}
|
||||
|
||||
static gint
|
||||
getCaretOffsetCB(AtkText *aText)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return static_cast<gint>(text->CaretOffset());
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return static_cast<gint>(proxy->CaretOffset());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AtkAttributeSet*
|
||||
getRunAttributesCB(AtkText *aText, gint aOffset,
|
||||
gint *aStartOffset,
|
||||
gint *aEndOffset)
|
||||
{
|
||||
*aStartOffset = -1;
|
||||
*aEndOffset = -1;
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPersistentProperties> attributes =
|
||||
text->TextAttributes(false, aOffset, &startOffset, &endOffset);
|
||||
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
return ConvertToAtkTextAttributeSet(attributes);
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText));
|
||||
if (!proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->TextAttributes(false, aOffset, &attrs, &startOffset, &endOffset);
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
return ConvertToAtkTextAttributeSet(attrs);
|
||||
}
|
||||
|
||||
static AtkAttributeSet*
|
||||
getDefaultAttributesCB(AtkText *aText)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPersistentProperties> attributes = text->DefaultTextAttributes();
|
||||
return ConvertToAtkTextAttributeSet(attributes);
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText));
|
||||
if (!proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoTArray<Attribute, 10> attrs;
|
||||
proxy->DefaultTextAttributes(&attrs);
|
||||
return ConvertToAtkTextAttributeSet(attrs);
|
||||
}
|
||||
|
||||
static void
|
||||
getCharacterExtentsCB(AtkText *aText, gint aOffset,
|
||||
gint *aX, gint *aY,
|
||||
gint *aWidth, gint *aHeight,
|
||||
AtkCoordType aCoords)
|
||||
{
|
||||
if(!aX || !aY || !aWidth || !aHeight) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRect rect;
|
||||
uint32_t geckoCoordType;
|
||||
if (aCoords == ATK_XY_SCREEN) {
|
||||
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
|
||||
} else {
|
||||
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rect = text->CharBounds(aOffset, geckoCoordType);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
rect = proxy->CharBounds(aOffset, geckoCoordType);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
*aX = rect.x;
|
||||
*aY = rect.y;
|
||||
*aWidth = rect.width;
|
||||
*aHeight = rect.height;
|
||||
}
|
||||
|
||||
static void
|
||||
getRangeExtentsCB(AtkText *aText, gint aStartOffset, gint aEndOffset,
|
||||
AtkCoordType aCoords, AtkTextRectangle *aRect)
|
||||
{
|
||||
if (!aRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsIntRect rect;
|
||||
uint32_t geckoCoordType;
|
||||
if (aCoords == ATK_XY_SCREEN) {
|
||||
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE;
|
||||
} else {
|
||||
geckoCoordType = nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE;
|
||||
}
|
||||
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if(accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rect = text->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
rect = proxy->TextBounds(aStartOffset, aEndOffset, geckoCoordType);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
aRect->x = rect.x;
|
||||
aRect->y = rect.y;
|
||||
aRect->width = rect.width;
|
||||
aRect->height = rect.height;
|
||||
}
|
||||
|
||||
static gint
|
||||
getCharacterCountCB(AtkText *aText)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* textAcc = accWrap->AsHyperText();
|
||||
return
|
||||
textAcc->IsDefunct() ? 0 : static_cast<gint>(textAcc->CharacterCount());
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->CharacterCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint
|
||||
getOffsetAtPointCB(AtkText *aText,
|
||||
gint aX, gint aY,
|
||||
AtkCoordType aCoords)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return static_cast<gint>(
|
||||
text->OffsetAtPoint(aX, aY,
|
||||
(aCoords == ATK_XY_SCREEN ?
|
||||
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
|
||||
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return static_cast<gint>(
|
||||
proxy->OffsetAtPoint(aX, aY,
|
||||
(aCoords == ATK_XY_SCREEN ?
|
||||
nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE :
|
||||
nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE)));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gint
|
||||
getTextSelectionCountCB(AtkText *aText)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return text->SelectionCount();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->SelectionCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gchar*
|
||||
getTextSelectionCB(AtkText *aText, gint aSelectionNum,
|
||||
gint *aStartOffset, gint *aEndOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
int32_t startOffset = 0, endOffset = 0;
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
text->SelectionBoundsAt(aSelectionNum, &startOffset, &endOffset);
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
return getTextCB(aText, *aStartOffset, *aEndOffset);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
nsString data;
|
||||
proxy->SelectionBoundsAt(aSelectionNum, data, &startOffset, &endOffset);
|
||||
*aStartOffset = startOffset;
|
||||
*aEndOffset = endOffset;
|
||||
|
||||
NS_ConvertUTF16toUTF8 dataAsUTF8(data);
|
||||
return (dataAsUTF8.get()) ? g_strdup(dataAsUTF8.get()) : nullptr;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// set methods
|
||||
static gboolean
|
||||
addTextSelectionCB(AtkText *aText,
|
||||
gint aStartOffset,
|
||||
gint aEndOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return text->AddToSelection(aStartOffset, aEndOffset);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->AddToSelection(aStartOffset, aEndOffset);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
removeTextSelectionCB(AtkText *aText,
|
||||
gint aSelectionNum)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return text->RemoveFromSelection(aSelectionNum);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->RemoveFromSelection(aSelectionNum);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setTextSelectionCB(AtkText *aText, gint aSelectionNum,
|
||||
gint aStartOffset, gint aEndOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return text->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
|
||||
} else if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
return proxy->SetSelectionBoundsAt(aSelectionNum, aStartOffset, aEndOffset);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setCaretOffsetCB(AtkText *aText, gint aOffset)
|
||||
{
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aText));
|
||||
if (accWrap) {
|
||||
HyperTextAccessible* text = accWrap->AsHyperText();
|
||||
if (!text || !text->IsTextRole() || !text->IsValidOffset(aOffset)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
text->SetCaretOffset(aOffset);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aText))) {
|
||||
proxy->SetCaretOffset(aOffset);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
textInterfaceInitCB(AtkTextIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid aIface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_text = getTextCB;
|
||||
aIface->get_text_after_offset = getTextAfterOffsetCB;
|
||||
aIface->get_text_at_offset = getTextAtOffsetCB;
|
||||
aIface->get_character_at_offset = getCharacterAtOffsetCB;
|
||||
aIface->get_text_before_offset = getTextBeforeOffsetCB;
|
||||
aIface->get_caret_offset = getCaretOffsetCB;
|
||||
aIface->get_run_attributes = getRunAttributesCB;
|
||||
aIface->get_default_attributes = getDefaultAttributesCB;
|
||||
aIface->get_character_extents = getCharacterExtentsCB;
|
||||
aIface->get_range_extents = getRangeExtentsCB;
|
||||
aIface->get_character_count = getCharacterCountCB;
|
||||
aIface->get_offset_at_point = getOffsetAtPointCB;
|
||||
aIface->get_n_selections = getTextSelectionCountCB;
|
||||
aIface->get_selection = getTextSelectionCB;
|
||||
|
||||
// set methods
|
||||
aIface->add_selection = addTextSelectionCB;
|
||||
aIface->remove_selection = removeTextSelectionCB;
|
||||
aIface->set_selection = setTextSelectionCB;
|
||||
aIface->set_caret_offset = setCaretOffsetCB;
|
||||
|
||||
// Cache the string values of the atk text attribute names.
|
||||
for (uint32_t i = 0; i < ArrayLength(sAtkTextAttrNames); i++)
|
||||
sAtkTextAttrNames[i] =
|
||||
atk_text_attribute_get_name(static_cast<AtkTextAttribute>(i));
|
||||
}
|
||||
133
accessible/atk/nsMaiInterfaceValue.cpp
Normal file
133
accessible/atk/nsMaiInterfaceValue.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- 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 "InterfaceInitFuncs.h"
|
||||
|
||||
#include "AccessibleWrap.h"
|
||||
#include "nsMai.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "mozilla/FloatingPoint.h"
|
||||
#include "mozilla/Likely.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
extern "C" {
|
||||
|
||||
static void
|
||||
getCurrentValueCB(AtkValue *obj, GValue *value)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
|
||||
if (!accWrap) {
|
||||
proxy = GetProxy(ATK_OBJECT(obj));
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset (value, 0, sizeof (GValue));
|
||||
double accValue = accWrap ? accWrap->CurValue() : proxy->CurValue();
|
||||
if (IsNaN(accValue))
|
||||
return;
|
||||
|
||||
g_value_init (value, G_TYPE_DOUBLE);
|
||||
g_value_set_double (value, accValue);
|
||||
}
|
||||
|
||||
static void
|
||||
getMaximumValueCB(AtkValue *obj, GValue *value)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
|
||||
if (!accWrap) {
|
||||
proxy = GetProxy(ATK_OBJECT(obj));
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(value, 0, sizeof (GValue));
|
||||
double accValue = accWrap ? accWrap->MaxValue() : proxy->MaxValue();
|
||||
if (IsNaN(accValue))
|
||||
return;
|
||||
|
||||
g_value_init(value, G_TYPE_DOUBLE);
|
||||
g_value_set_double(value, accValue);
|
||||
}
|
||||
|
||||
static void
|
||||
getMinimumValueCB(AtkValue *obj, GValue *value)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
|
||||
if (!accWrap) {
|
||||
proxy = GetProxy(ATK_OBJECT(obj));
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(value, 0, sizeof (GValue));
|
||||
double accValue = accWrap ? accWrap->MinValue() : proxy->MinValue();
|
||||
if (IsNaN(accValue))
|
||||
return;
|
||||
|
||||
g_value_init(value, G_TYPE_DOUBLE);
|
||||
g_value_set_double(value, accValue);
|
||||
}
|
||||
|
||||
static void
|
||||
getMinimumIncrementCB(AtkValue *obj, GValue *minimumIncrement)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
|
||||
if (!accWrap) {
|
||||
proxy = GetProxy(ATK_OBJECT(obj));
|
||||
if (!proxy) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
memset(minimumIncrement, 0, sizeof (GValue));
|
||||
double accValue = accWrap ? accWrap->Step() : proxy->Step();
|
||||
if (IsNaN(accValue))
|
||||
accValue = 0; // zero if the minimum increment is undefined
|
||||
|
||||
g_value_init(minimumIncrement, G_TYPE_DOUBLE);
|
||||
g_value_set_double(minimumIncrement, accValue);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
setCurrentValueCB(AtkValue *obj, const GValue *value)
|
||||
{
|
||||
ProxyAccessible* proxy = nullptr;
|
||||
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(obj));
|
||||
if (!accWrap) {
|
||||
proxy = GetProxy(ATK_OBJECT(obj));
|
||||
if (!proxy) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
double accValue =g_value_get_double(value);
|
||||
return accWrap ? accWrap->SetCurValue(accValue) : proxy->SetCurValue(accValue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
valueInterfaceInitCB(AtkValueIface* aIface)
|
||||
{
|
||||
NS_ASSERTION(aIface, "Invalid aIface");
|
||||
if (MOZ_UNLIKELY(!aIface))
|
||||
return;
|
||||
|
||||
aIface->get_current_value = getCurrentValueCB;
|
||||
aIface->get_maximum_value = getMaximumValueCB;
|
||||
aIface->get_minimum_value = getMinimumValueCB;
|
||||
aIface->get_minimum_increment = getMinimumIncrementCB;
|
||||
aIface->set_current_value = setCurrentValueCB;
|
||||
}
|
||||
117
accessible/atk/nsStateMap.h
Normal file
117
accessible/atk/nsStateMap.h
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/* -*- 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 <atk/atk.h>
|
||||
#include "AccessibleWrap.h"
|
||||
|
||||
/******************************************************************************
|
||||
The following accessible states aren't translated, just ignored:
|
||||
STATE_READONLY: Supported indirectly via EXT_STATE_EDITABLE
|
||||
STATE_HOTTRACKED: No ATK equivalent. No known use case.
|
||||
The nsIAccessible state is not currently supported.
|
||||
STATE_FLOATING: No ATK equivalent. No known use case.
|
||||
The nsIAccessible state is not currently supported.
|
||||
STATE_MOVEABLE: No ATK equivalent. No known use case.
|
||||
The nsIAccessible state is not currently supported.
|
||||
STATE_SELFVOICING: No ATK equivalent -- the object has self-TTS.
|
||||
The nsIAccessible state is not currently supported.
|
||||
STATE_LINKED: The object is formatted as a hyperlink. Supported via ATK_ROLE_LINK.
|
||||
STATE_EXTSELECTABLE: Indicates that an object extends its selection.
|
||||
This is supported via STATE_MULTISELECTABLE.
|
||||
STATE_PROTECTED: The object is a password-protected edit control.
|
||||
Supported via ATK_ROLE_PASSWORD_TEXT
|
||||
STATE_HASPOPUP: Object displays a pop-up menu or window when invoked.
|
||||
No ATK equivalent. The accessible state is not
|
||||
currently supported.
|
||||
STATE_PINNED: The object is pinned, usually indicating it is fixed in
|
||||
place and has permanence. No ATK equivalent. The
|
||||
accessible state is not currently supported.
|
||||
|
||||
The following ATK states are not supported:
|
||||
ATK_STATE_ARMED: No clear use case, used briefly when button is activated
|
||||
ATK_STATE_HAS_TOOLTIP: No clear use case, no IA2 equivalent
|
||||
ATK_STATE_ICONIFIED: Mozilla does not have elements which are collapsable into icons
|
||||
ATK_STATE_TRUNCATED: No clear use case. Indicates that an object's onscreen content is truncated,
|
||||
e.g. a text value in a spreadsheet cell. No IA2 state.
|
||||
******************************************************************************/
|
||||
|
||||
enum EStateMapEntryType {
|
||||
kMapDirectly,
|
||||
kMapOpposite, // For example, UNAVAILABLE is the opposite of ENABLED
|
||||
kNoStateChange, // Don't fire state change event
|
||||
kNoSuchState
|
||||
};
|
||||
|
||||
const AtkStateType kNone = ATK_STATE_INVALID;
|
||||
|
||||
struct AtkStateMap {
|
||||
AtkStateType atkState;
|
||||
EStateMapEntryType stateMapEntryType;
|
||||
|
||||
static int32_t GetStateIndexFor(uint64_t aState)
|
||||
{
|
||||
int32_t stateIndex = -1;
|
||||
while (aState > 0) {
|
||||
++ stateIndex;
|
||||
aState >>= 1;
|
||||
}
|
||||
return stateIndex; // Returns -1 if not mapped
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Map array from cross platform states to ATK states
|
||||
static const AtkStateMap gAtkStateMap[] = { // Cross Platform States
|
||||
{ kNone, kMapOpposite }, // states::UNAVAILABLE = 1 << 0
|
||||
{ ATK_STATE_SELECTED, kMapDirectly }, // states::SELECTED = 1 << 1
|
||||
{ ATK_STATE_FOCUSED, kMapDirectly }, // states::FOCUSED = 1 << 2
|
||||
{ ATK_STATE_PRESSED, kMapDirectly }, // states::PRESSED = 1 << 3
|
||||
{ ATK_STATE_CHECKED, kMapDirectly }, // states::CHECKED = 1 << 4
|
||||
{ ATK_STATE_INDETERMINATE, kMapDirectly }, // states::MIXED = 1 << 5
|
||||
{ kNone, kMapDirectly }, // states::READONLY = 1 << 6
|
||||
{ kNone, kMapDirectly }, // states::HOTTRACKED = 1 << 7
|
||||
{ ATK_STATE_DEFAULT, kMapDirectly }, // states::DEFAULT = 1 << 8
|
||||
{ ATK_STATE_EXPANDED, kMapDirectly }, // states::EXPANDED = 1 << 9
|
||||
{ kNone, kNoStateChange }, // states::COLLAPSED = 1 << 10
|
||||
{ ATK_STATE_BUSY, kMapDirectly }, // states::BUSY = 1 << 11
|
||||
{ kNone, kMapDirectly }, // states::FLOATING = 1 << 12
|
||||
{ ATK_STATE_CHECKABLE, kMapDirectly }, // states::CHECKABLE = 1 << 13
|
||||
{ ATK_STATE_ANIMATED, kMapDirectly }, // states::ANIMATED = 1 << 14
|
||||
{ ATK_STATE_VISIBLE, kMapOpposite }, // states::INVISIBLE = 1 << 15
|
||||
{ ATK_STATE_SHOWING, kMapOpposite }, // states::OFFSCREEN = 1 << 16
|
||||
{ ATK_STATE_RESIZABLE, kMapDirectly }, // states::SIZEABLE = 1 << 17
|
||||
{ kNone, kMapDirectly }, // states::MOVEABLE = 1 << 18
|
||||
{ kNone, kMapDirectly }, // states::SELFVOICING = 1 << 19
|
||||
{ ATK_STATE_FOCUSABLE, kMapDirectly }, // states::FOCUSABLE = 1 << 20
|
||||
{ ATK_STATE_SELECTABLE, kMapDirectly }, // states::SELECTABLE = 1 << 21
|
||||
{ kNone, kMapDirectly }, // states::LINKED = 1 << 22
|
||||
{ ATK_STATE_VISITED, kMapDirectly }, // states::TRAVERSED = 1 << 23
|
||||
{ ATK_STATE_MULTISELECTABLE, kMapDirectly }, // states::MULTISELECTABLE = 1 << 24
|
||||
{ kNone, kMapDirectly }, // states::EXTSELECTABLE = 1 << 25
|
||||
{ ATK_STATE_REQUIRED, kMapDirectly }, // states::STATE_REQUIRED = 1 << 26
|
||||
{ kNone, kMapDirectly }, // states::ALERT_MEDIUM = 1 << 27
|
||||
{ ATK_STATE_INVALID_ENTRY, kMapDirectly }, // states::INVALID = 1 << 28
|
||||
{ kNone, kMapDirectly }, // states::PROTECTED = 1 << 29
|
||||
{ kNone, kMapDirectly }, // states::HASPOPUP = 1 << 30
|
||||
{ ATK_STATE_SUPPORTS_AUTOCOMPLETION, kMapDirectly }, // states::SUPPORTS_AUTOCOMPLETION = 1 << 31
|
||||
{ ATK_STATE_DEFUNCT, kMapDirectly }, // states::DEFUNCT = 1 << 32
|
||||
{ ATK_STATE_SELECTABLE_TEXT, kMapDirectly }, // states::SELECTABLE_TEXT = 1 << 33
|
||||
{ ATK_STATE_EDITABLE, kMapDirectly }, // states::EDITABLE = 1 << 34
|
||||
{ ATK_STATE_ACTIVE, kMapDirectly }, // states::ACTIVE = 1 << 35
|
||||
{ ATK_STATE_MODAL, kMapDirectly }, // states::MODAL = 1 << 36
|
||||
{ ATK_STATE_MULTI_LINE, kMapDirectly }, // states::MULTI_LINE = 1 << 37
|
||||
{ ATK_STATE_HORIZONTAL, kMapDirectly }, // states::HORIZONTAL = 1 << 38
|
||||
{ ATK_STATE_OPAQUE, kMapDirectly }, // states::OPAQUE = 1 << 39
|
||||
{ ATK_STATE_SINGLE_LINE, kMapDirectly }, // states::SINGLE_LINE = 1 << 40
|
||||
{ ATK_STATE_TRANSIENT, kMapDirectly }, // states::TRANSIENT = 1 << 41
|
||||
{ ATK_STATE_VERTICAL, kMapDirectly }, // states::VERTICAL = 1 << 42
|
||||
{ ATK_STATE_STALE, kMapDirectly }, // states::STALE = 1 << 43
|
||||
{ ATK_STATE_ENABLED, kMapDirectly }, // states::ENABLED = 1 << 44
|
||||
{ ATK_STATE_SENSITIVE, kMapDirectly }, // states::SENSITIVE = 1 << 45
|
||||
{ ATK_STATE_EXPANDABLE, kMapDirectly }, // states::EXPANDABLE = 1 << 46
|
||||
{ kNone, kMapDirectly }, // states::PINNED = 1 << 47
|
||||
{ kNone, kNoSuchState }, // = 1 << 48
|
||||
};
|
||||
1008
accessible/base/ARIAMap.cpp
Normal file
1008
accessible/base/ARIAMap.cpp
Normal file
File diff suppressed because it is too large
Load diff
305
accessible/base/ARIAMap.h
Normal file
305
accessible/base/ARIAMap.h
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* 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_aria_ARIAMap_h_
|
||||
#define mozilla_a11y_aria_ARIAMap_h_
|
||||
|
||||
#include "ARIAStateMap.h"
|
||||
#include "mozilla/a11y/AccTypes.h"
|
||||
#include "mozilla/a11y/Role.h"
|
||||
|
||||
#include "nsIAtom.h"
|
||||
#include "nsIContent.h"
|
||||
|
||||
class nsINode;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Value constants
|
||||
|
||||
/**
|
||||
* Used to define if role requires to expose Value interface.
|
||||
*/
|
||||
enum EValueRule
|
||||
{
|
||||
/**
|
||||
* Value interface isn't exposed.
|
||||
*/
|
||||
eNoValue,
|
||||
|
||||
/**
|
||||
* Value interface is implemented, supports value, min and max from
|
||||
* aria-valuenow, aria-valuemin and aria-valuemax.
|
||||
*/
|
||||
eHasValueMinMax
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Action constants
|
||||
|
||||
/**
|
||||
* Used to define if the role requires to expose action.
|
||||
*/
|
||||
enum EActionRule
|
||||
{
|
||||
eNoAction,
|
||||
eActivateAction,
|
||||
eClickAction,
|
||||
ePressAction,
|
||||
eCheckUncheckAction,
|
||||
eExpandAction,
|
||||
eJumpAction,
|
||||
eOpenCloseAction,
|
||||
eSelectAction,
|
||||
eSortAction,
|
||||
eSwitchAction
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Live region constants
|
||||
|
||||
/**
|
||||
* Used to define if role exposes default value of aria-live attribute.
|
||||
*/
|
||||
enum ELiveAttrRule
|
||||
{
|
||||
eNoLiveAttr,
|
||||
eOffLiveAttr,
|
||||
ePoliteLiveAttr
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Role constants
|
||||
|
||||
/**
|
||||
* ARIA role overrides role from native markup.
|
||||
*/
|
||||
const bool kUseMapRole = true;
|
||||
|
||||
/**
|
||||
* ARIA role doesn't override the role from native markup.
|
||||
*/
|
||||
const bool kUseNativeRole = false;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ARIA attribute characteristic masks
|
||||
|
||||
/**
|
||||
* This mask indicates the attribute should not be exposed as an object
|
||||
* attribute via the catch-all logic in Accessible::Attributes().
|
||||
* This means it either isn't mean't to be exposed as an object attribute, or
|
||||
* that it should, but is already handled in other code.
|
||||
*/
|
||||
const uint8_t ATTR_BYPASSOBJ = 0x1 << 0;
|
||||
const uint8_t ATTR_BYPASSOBJ_IF_FALSE = 0x1 << 1;
|
||||
|
||||
/**
|
||||
* This mask indicates the attribute is expected to have an NMTOKEN or bool value.
|
||||
* (See for example usage in Accessible::Attributes())
|
||||
*/
|
||||
const uint8_t ATTR_VALTOKEN = 0x1 << 2;
|
||||
|
||||
/**
|
||||
* Indicate the attribute is global state or property (refer to
|
||||
* http://www.w3.org/TR/wai-aria/states_and_properties#global_states).
|
||||
*/
|
||||
const uint8_t ATTR_GLOBAL = 0x1 << 3;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// State map entry
|
||||
|
||||
/**
|
||||
* Used in nsRoleMapEntry.state if no nsIAccessibleStates are automatic for
|
||||
* a given role.
|
||||
*/
|
||||
#define kNoReqStates 0
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Role map entry
|
||||
|
||||
/**
|
||||
* For each ARIA role, this maps the nsIAccessible information.
|
||||
*/
|
||||
struct nsRoleMapEntry
|
||||
{
|
||||
/**
|
||||
* Return true if matches to the given ARIA role.
|
||||
*/
|
||||
bool Is(nsIAtom* aARIARole) const
|
||||
{ return *roleAtom == aARIARole; }
|
||||
|
||||
/**
|
||||
* Return true if ARIA role has the given accessible type.
|
||||
*/
|
||||
bool IsOfType(mozilla::a11y::AccGenericType aType) const
|
||||
{ return accTypes & aType; }
|
||||
|
||||
/**
|
||||
* Return ARIA role.
|
||||
*/
|
||||
const nsDependentAtomString ARIARoleString() const
|
||||
{ return nsDependentAtomString(*roleAtom); }
|
||||
|
||||
// ARIA role: string representation such as "button"
|
||||
nsIAtom** roleAtom;
|
||||
|
||||
// Role mapping rule: maps to enum Role
|
||||
mozilla::a11y::role role;
|
||||
|
||||
// Role rule: whether to use mapped role or native semantics
|
||||
bool roleRule;
|
||||
|
||||
// Value mapping rule: how to compute accessible value
|
||||
EValueRule valueRule;
|
||||
|
||||
// Action mapping rule, how to expose accessible action
|
||||
EActionRule actionRule;
|
||||
|
||||
// 'live' and 'container-live' object attributes mapping rule: how to expose
|
||||
// these object attributes if ARIA 'live' attribute is missed.
|
||||
ELiveAttrRule liveAttRule;
|
||||
|
||||
// Accessible types this role belongs to.
|
||||
uint32_t accTypes;
|
||||
|
||||
// Automatic state mapping rule: always include in states
|
||||
uint64_t state; // or kNoReqStates if no default state for this role
|
||||
|
||||
// ARIA properties supported for this role (in other words, the aria-foo
|
||||
// attribute to accessible states mapping rules).
|
||||
// Currently you cannot have unlimited mappings, because
|
||||
// a variable sized array would not allow the use of
|
||||
// C++'s struct initialization feature.
|
||||
mozilla::a11y::aria::EStateRule attributeMap1;
|
||||
mozilla::a11y::aria::EStateRule attributeMap2;
|
||||
mozilla::a11y::aria::EStateRule attributeMap3;
|
||||
mozilla::a11y::aria::EStateRule attributeMap4;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ARIA map
|
||||
|
||||
/**
|
||||
* These provide the mappings for WAI-ARIA roles, states and properties using
|
||||
* the structs defined in this file and ARIAStateMap files.
|
||||
*/
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace aria {
|
||||
|
||||
/**
|
||||
* Empty role map entry. Used by accessibility service to create an accessible
|
||||
* if the accessible can't use role of used accessible class. For example,
|
||||
* it is used for table cells that aren't contained by table.
|
||||
*/
|
||||
extern nsRoleMapEntry gEmptyRoleMap;
|
||||
|
||||
/**
|
||||
* Constants for the role map entry index to indicate that the role map entry
|
||||
* isn't in sWAIRoleMaps, but rather is a special entry: nullptr,
|
||||
* gEmptyRoleMap, and sLandmarkRoleMap
|
||||
*/
|
||||
const uint8_t NO_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 2;
|
||||
const uint8_t EMPTY_ROLE_MAP_ENTRY_INDEX = UINT8_MAX - 1;
|
||||
const uint8_t LANDMARK_ROLE_MAP_ENTRY_INDEX = UINT8_MAX;
|
||||
|
||||
/**
|
||||
* Get the role map entry for a given DOM node. This will use the first
|
||||
* ARIA role if the role attribute provides a space delimited list of roles.
|
||||
*
|
||||
* @param aEl [in] the DOM node to get the role map entry for
|
||||
* @return a pointer to the role map entry for the ARIA role, or nullptr
|
||||
* if none
|
||||
*/
|
||||
const nsRoleMapEntry* GetRoleMap(dom::Element* aEl);
|
||||
|
||||
/**
|
||||
* Get the role map entry pointer's index for a given DOM node. This will use
|
||||
* the first ARIA role if the role attribute provides a space delimited list of
|
||||
* roles.
|
||||
*
|
||||
* @param aEl [in] the DOM node to get the role map entry for
|
||||
* @return the index of the pointer to the role map entry for the ARIA
|
||||
* role, or NO_ROLE_MAP_ENTRY_INDEX if none
|
||||
*/
|
||||
uint8_t GetRoleMapIndex(dom::Element* aEl);
|
||||
|
||||
/**
|
||||
* Get the role map entry pointer for a given role map entry index.
|
||||
*
|
||||
* @param aRoleMapIndex [in] the role map index to get the role map entry
|
||||
* pointer for
|
||||
* @return a pointer to the role map entry for the ARIA role,
|
||||
* or nullptr, if none
|
||||
*/
|
||||
const nsRoleMapEntry* GetRoleMapFromIndex(uint8_t aRoleMapIndex);
|
||||
|
||||
/**
|
||||
* Get the role map entry index for a given role map entry pointer. If the role
|
||||
* map entry is within sWAIRoleMaps, return the index within that array,
|
||||
* otherwise return one of the special index constants listed above.
|
||||
*
|
||||
* @param aRoleMap [in] the role map entry pointer to get the index for
|
||||
* @return the index of the pointer to the role map entry, or
|
||||
* NO_ROLE_MAP_ENTRY_INDEX if none
|
||||
*/
|
||||
uint8_t GetIndexFromRoleMap(const nsRoleMapEntry* aRoleMap);
|
||||
|
||||
/**
|
||||
* Return accessible state from ARIA universal states applied to the given
|
||||
* element.
|
||||
*/
|
||||
uint64_t UniversalStatesFor(mozilla::dom::Element* aElement);
|
||||
|
||||
/**
|
||||
* Get the ARIA attribute characteristics for a given ARIA attribute.
|
||||
*
|
||||
* @param aAtom ARIA attribute
|
||||
* @return A bitflag representing the attribute characteristics
|
||||
* (see above for possible bit masks, prefixed "ATTR_")
|
||||
*/
|
||||
uint8_t AttrCharacteristicsFor(nsIAtom* aAtom);
|
||||
|
||||
/**
|
||||
* Return true if the element has defined aria-hidden.
|
||||
*/
|
||||
bool HasDefinedARIAHidden(nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Represents a simple enumerator for iterating through ARIA attributes
|
||||
* exposed as object attributes on a given accessible.
|
||||
*/
|
||||
class AttrIterator
|
||||
{
|
||||
public:
|
||||
explicit AttrIterator(nsIContent* aContent) :
|
||||
mContent(aContent), mAttrIdx(0)
|
||||
{
|
||||
mAttrCount = mContent->GetAttrCount();
|
||||
}
|
||||
|
||||
bool Next(nsAString& aAttrName, nsAString& aAttrValue);
|
||||
|
||||
private:
|
||||
AttrIterator() = delete;
|
||||
AttrIterator(const AttrIterator&) = delete;
|
||||
AttrIterator& operator= (const AttrIterator&) = delete;
|
||||
|
||||
nsIContent* mContent;
|
||||
uint32_t mAttrIdx;
|
||||
uint32_t mAttrCount;
|
||||
};
|
||||
|
||||
} // namespace aria
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
376
accessible/base/ARIAStateMap.cpp
Normal file
376
accessible/base/ARIAStateMap.cpp
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/* -*- 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 "ARIAMap.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::a11y::aria;
|
||||
|
||||
/**
|
||||
* Used to store state map rule data for ARIA attribute of enum type.
|
||||
*/
|
||||
struct EnumTypeData
|
||||
{
|
||||
// ARIA attribute name.
|
||||
nsIAtom* const mAttrName;
|
||||
|
||||
// States if the attribute value is matched to the enum value. Used as
|
||||
// nsIContent::AttrValuesArray, last item must be nullptr.
|
||||
nsIAtom* const* const mValues[4];
|
||||
|
||||
// States applied if corresponding enum values are matched.
|
||||
const uint64_t mStates[3];
|
||||
|
||||
// States to clear in case of match.
|
||||
const uint64_t mClearState;
|
||||
};
|
||||
|
||||
enum ETokenType
|
||||
{
|
||||
eBoolType = 0,
|
||||
eMixedType = 1, // can take 'mixed' value
|
||||
eDefinedIfAbsent = 2 // permanent and false state are applied if absent
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to store state map rule data for ARIA attribute of token type (including
|
||||
* mixed value).
|
||||
*/
|
||||
struct TokenTypeData
|
||||
{
|
||||
TokenTypeData(nsIAtom* aAttrName, uint32_t aType,
|
||||
uint64_t aPermanentState,
|
||||
uint64_t aTrueState,
|
||||
uint64_t aFalseState = 0) :
|
||||
mAttrName(aAttrName), mType(aType), mPermanentState(aPermanentState),
|
||||
mTrueState(aTrueState), mFalseState(aFalseState)
|
||||
{ }
|
||||
|
||||
// ARIA attribute name.
|
||||
nsIAtom* const mAttrName;
|
||||
|
||||
// Type.
|
||||
const uint32_t mType;
|
||||
|
||||
// State applied if the attribute is defined or mType doesn't have
|
||||
// eDefinedIfAbsent flag set.
|
||||
const uint64_t mPermanentState;
|
||||
|
||||
// States applied if the attribute value is true/false.
|
||||
const uint64_t mTrueState;
|
||||
const uint64_t mFalseState;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map enum type attribute value to accessible state.
|
||||
*/
|
||||
static void MapEnumType(dom::Element* aElement, uint64_t* aState,
|
||||
const EnumTypeData& aData);
|
||||
|
||||
/**
|
||||
* Map token type attribute value to states.
|
||||
*/
|
||||
static void MapTokenType(dom::Element* aContent, uint64_t* aState,
|
||||
const TokenTypeData& aData);
|
||||
|
||||
bool
|
||||
aria::MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState)
|
||||
{
|
||||
switch (aRule) {
|
||||
case eARIAAutoComplete:
|
||||
{
|
||||
static const EnumTypeData data = {
|
||||
nsGkAtoms::aria_autocomplete,
|
||||
{ &nsGkAtoms::inlinevalue,
|
||||
&nsGkAtoms::list,
|
||||
&nsGkAtoms::both, nullptr },
|
||||
{ states::SUPPORTS_AUTOCOMPLETION,
|
||||
states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION,
|
||||
states::HASPOPUP | states::SUPPORTS_AUTOCOMPLETION }, 0
|
||||
};
|
||||
|
||||
MapEnumType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIABusy:
|
||||
{
|
||||
static const EnumTypeData data = {
|
||||
nsGkAtoms::aria_busy,
|
||||
{ &nsGkAtoms::_true,
|
||||
&nsGkAtoms::error, nullptr },
|
||||
{ states::BUSY,
|
||||
states::INVALID }, 0
|
||||
};
|
||||
|
||||
MapEnumType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIACheckableBool:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_checked, eBoolType | eDefinedIfAbsent,
|
||||
states::CHECKABLE, states::CHECKED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIACheckableMixed:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_checked, eMixedType | eDefinedIfAbsent,
|
||||
states::CHECKABLE, states::CHECKED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIACheckedMixed:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_checked, eMixedType,
|
||||
states::CHECKABLE, states::CHECKED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIADisabled:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_disabled, eBoolType,
|
||||
0, states::UNAVAILABLE);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAExpanded:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_expanded, eBoolType,
|
||||
0, states::EXPANDED, states::COLLAPSED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAHasPopup:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_haspopup, eBoolType,
|
||||
0, states::HASPOPUP);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAInvalid:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_invalid, eBoolType,
|
||||
0, states::INVALID);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAModal:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_modal, eBoolType,
|
||||
0, states::MODAL);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAMultiline:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_multiline, eBoolType | eDefinedIfAbsent,
|
||||
0, states::MULTI_LINE, states::SINGLE_LINE);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAMultiSelectable:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_multiselectable, eBoolType,
|
||||
0, states::MULTISELECTABLE | states::EXTSELECTABLE);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAOrientation:
|
||||
{
|
||||
static const EnumTypeData data = {
|
||||
nsGkAtoms::aria_orientation,
|
||||
{ &nsGkAtoms::horizontal,
|
||||
&nsGkAtoms::vertical, nullptr },
|
||||
{ states::HORIZONTAL,
|
||||
states::VERTICAL },
|
||||
states::HORIZONTAL | states::VERTICAL
|
||||
};
|
||||
|
||||
MapEnumType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAPressed:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_pressed, eMixedType,
|
||||
0, states::PRESSED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAReadonly:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_readonly, eBoolType,
|
||||
0, states::READONLY);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAReadonlyOrEditable:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_readonly, eBoolType | eDefinedIfAbsent,
|
||||
0, states::READONLY, states::EDITABLE);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAReadonlyOrEditableIfDefined:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_readonly, eBoolType,
|
||||
0, states::READONLY, states::EDITABLE);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIARequired:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_required, eBoolType,
|
||||
0, states::REQUIRED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIASelectable:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_selected, eBoolType | eDefinedIfAbsent,
|
||||
states::SELECTABLE, states::SELECTED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIASelectableIfDefined:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_selected, eBoolType,
|
||||
states::SELECTABLE, states::SELECTED);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eReadonlyUntilEditable:
|
||||
{
|
||||
if (!(*aState & states::EDITABLE))
|
||||
*aState |= states::READONLY;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case eIndeterminateIfNoValue:
|
||||
{
|
||||
if (!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuenow) &&
|
||||
!aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::aria_valuetext))
|
||||
*aState |= states::MIXED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case eFocusableUntilDisabled:
|
||||
{
|
||||
if (!nsAccUtils::HasDefinedARIAToken(aElement, nsGkAtoms::aria_disabled) ||
|
||||
aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_disabled,
|
||||
nsGkAtoms::_false, eCaseMatters))
|
||||
*aState |= states::FOCUSABLE;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MapEnumType(dom::Element* aElement, uint64_t* aState, const EnumTypeData& aData)
|
||||
{
|
||||
switch (aElement->FindAttrValueIn(kNameSpaceID_None, aData.mAttrName,
|
||||
aData.mValues, eCaseMatters)) {
|
||||
case 0:
|
||||
*aState = (*aState & ~aData.mClearState) | aData.mStates[0];
|
||||
return;
|
||||
case 1:
|
||||
*aState = (*aState & ~aData.mClearState) | aData.mStates[1];
|
||||
return;
|
||||
case 2:
|
||||
*aState = (*aState & ~aData.mClearState) | aData.mStates[2];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
MapTokenType(dom::Element* aElement, uint64_t* aState,
|
||||
const TokenTypeData& aData)
|
||||
{
|
||||
if (nsAccUtils::HasDefinedARIAToken(aElement, aData.mAttrName)) {
|
||||
if ((aData.mType & eMixedType) &&
|
||||
aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
|
||||
nsGkAtoms::mixed, eCaseMatters)) {
|
||||
*aState |= aData.mPermanentState | states::MIXED;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aElement->AttrValueIs(kNameSpaceID_None, aData.mAttrName,
|
||||
nsGkAtoms::_false, eCaseMatters)) {
|
||||
*aState |= aData.mPermanentState | aData.mFalseState;
|
||||
return;
|
||||
}
|
||||
|
||||
*aState |= aData.mPermanentState | aData.mTrueState;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aData.mType & eDefinedIfAbsent)
|
||||
*aState |= aData.mPermanentState | aData.mFalseState;
|
||||
}
|
||||
67
accessible/base/ARIAStateMap.h
Normal file
67
accessible/base/ARIAStateMap.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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_aria_ARIAStateMap_h_
|
||||
#define _mozilla_a11y_aria_ARIAStateMap_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Element;
|
||||
}
|
||||
|
||||
namespace a11y {
|
||||
namespace aria {
|
||||
|
||||
/**
|
||||
* List of the ARIA state mapping rules.
|
||||
*/
|
||||
enum EStateRule
|
||||
{
|
||||
eARIANone,
|
||||
eARIAAutoComplete,
|
||||
eARIABusy,
|
||||
eARIACheckableBool,
|
||||
eARIACheckableMixed,
|
||||
eARIACheckedMixed,
|
||||
eARIADisabled,
|
||||
eARIAExpanded,
|
||||
eARIAHasPopup,
|
||||
eARIAInvalid,
|
||||
eARIAModal,
|
||||
eARIAMultiline,
|
||||
eARIAMultiSelectable,
|
||||
eARIAOrientation,
|
||||
eARIAPressed,
|
||||
eARIAReadonly,
|
||||
eARIAReadonlyOrEditable,
|
||||
eARIAReadonlyOrEditableIfDefined,
|
||||
eARIARequired,
|
||||
eARIASelectable,
|
||||
eARIASelectableIfDefined,
|
||||
eReadonlyUntilEditable,
|
||||
eIndeterminateIfNoValue,
|
||||
eFocusableUntilDisabled
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose the accessible states for the given element accordingly to state
|
||||
* mapping rule.
|
||||
*
|
||||
* @param aRule [in] state mapping rule ID
|
||||
* @param aElement [in] node of the accessible
|
||||
* @param aState [in/out] accessible states
|
||||
* @return true if state map rule ID is valid
|
||||
*/
|
||||
bool MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState);
|
||||
|
||||
} // namespace aria
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
271
accessible/base/AccEvent.cpp
Normal file
271
accessible/base/AccEvent.cpp
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/* -*- 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 "AccEvent.h"
|
||||
|
||||
#include "nsAccUtils.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "xpcAccEvents.h"
|
||||
#include "States.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
static_assert(static_cast<bool>(eNoUserInput) == false &&
|
||||
static_cast<bool>(eFromUserInput) == true,
|
||||
"EIsFromUserInput cannot be casted to bool");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccEvent constructors
|
||||
|
||||
AccEvent::AccEvent(uint32_t aEventType, Accessible* aAccessible,
|
||||
EIsFromUserInput aIsFromUserInput, EEventRule aEventRule) :
|
||||
mEventType(aEventType), mEventRule(aEventRule), mAccessible(aAccessible)
|
||||
{
|
||||
if (aIsFromUserInput == eAutoDetect)
|
||||
mIsFromUserInput = EventStateManager::IsHandlingUserInput();
|
||||
else
|
||||
mIsFromUserInput = aIsFromUserInput == eFromUserInput ? true : false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccEvent cycle collection
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(AccEvent)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AccEvent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAccessible)
|
||||
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
|
||||
tmEvent->SetNextEvent(nullptr);
|
||||
tmEvent->SetPrevEvent(nullptr);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AccEvent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAccessible)
|
||||
if (AccTreeMutationEvent* tmEvent = downcast_accEvent(tmp)) {
|
||||
CycleCollectionNoteChild(cb, tmEvent->NextEvent(), "mNext");
|
||||
CycleCollectionNoteChild(cb, tmEvent->PrevEvent(), "mPrevEvent");
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AccEvent, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AccEvent, Release)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccTextChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Note: we pass in eAllowDupes to the base class because we don't support text
|
||||
// events coalescence. We fire delayed text change events in DocAccessible but
|
||||
// we continue to base the event off the accessible object rather than just the
|
||||
// node. This means we won't try to create an accessible based on the node when
|
||||
// we are ready to fire the event and so we will no longer assert at that point
|
||||
// if the node was removed from the document. Either way, the AT won't work with
|
||||
// a defunct accessible so the behaviour should be equivalent.
|
||||
AccTextChangeEvent::
|
||||
AccTextChangeEvent(Accessible* aAccessible, int32_t aStart,
|
||||
const nsAString& aModifiedText, bool aIsInserted,
|
||||
EIsFromUserInput aIsFromUserInput)
|
||||
: AccEvent(aIsInserted ?
|
||||
static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_INSERTED) :
|
||||
static_cast<uint32_t>(nsIAccessibleEvent::EVENT_TEXT_REMOVED),
|
||||
aAccessible, aIsFromUserInput, eAllowDupes)
|
||||
, mStart(aStart)
|
||||
, mIsInserted(aIsInserted)
|
||||
, mModifiedText(aModifiedText)
|
||||
{
|
||||
// XXX We should use IsFromUserInput here, but that isn't always correct
|
||||
// when the text change isn't related to content insertion or removal.
|
||||
mIsFromUserInput = mAccessible->State() &
|
||||
(states::FOCUSED | states::EDITABLE);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccHideEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccHideEvent::
|
||||
AccHideEvent(Accessible* aTarget, bool aNeedsShutdown) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_HIDE, aTarget),
|
||||
mNeedsShutdown(aNeedsShutdown)
|
||||
{
|
||||
mNextSibling = mAccessible->NextSibling();
|
||||
mPrevSibling = mAccessible->PrevSibling();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccShowEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccShowEvent::
|
||||
AccShowEvent(Accessible* aTarget) :
|
||||
AccMutationEvent(::nsIAccessibleEvent::EVENT_SHOW, aTarget)
|
||||
{
|
||||
int32_t idx = aTarget->IndexInParent();
|
||||
MOZ_ASSERT(idx >= 0);
|
||||
mInsertionIndex = idx;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccTextSelChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccTextSelChangeEvent::AccTextSelChangeEvent(HyperTextAccessible* aTarget,
|
||||
dom::Selection* aSelection,
|
||||
int32_t aReason) :
|
||||
AccEvent(nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED, aTarget,
|
||||
eAutoDetect, eCoalesceTextSelChange),
|
||||
mSel(aSelection), mReason(aReason) {}
|
||||
|
||||
AccTextSelChangeEvent::~AccTextSelChangeEvent() { }
|
||||
|
||||
bool
|
||||
AccTextSelChangeEvent::IsCaretMoveOnly() const
|
||||
{
|
||||
return mSel->RangeCount() == 1 && mSel->IsCollapsed() &&
|
||||
((mReason & (nsISelectionListener::COLLAPSETOSTART_REASON |
|
||||
nsISelectionListener::COLLAPSETOEND_REASON)) == 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccSelChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccSelChangeEvent::
|
||||
AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
|
||||
SelChangeType aSelChangeType) :
|
||||
AccEvent(0, aItem, eAutoDetect, eCoalesceSelectionChange),
|
||||
mWidget(aWidget), mItem(aItem), mSelChangeType(aSelChangeType),
|
||||
mPreceedingCount(0), mPackedEvent(nullptr)
|
||||
{
|
||||
if (aSelChangeType == eSelectionAdd) {
|
||||
if (mWidget->GetSelectedItem(1))
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
else
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
} else {
|
||||
mEventType = nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccTableChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccTableChangeEvent::
|
||||
AccTableChangeEvent(Accessible* aAccessible, uint32_t aEventType,
|
||||
int32_t aRowOrColIndex, int32_t aNumRowsOrCols) :
|
||||
AccEvent(aEventType, aAccessible),
|
||||
mRowOrColIndex(aRowOrColIndex), mNumRowsOrCols(aNumRowsOrCols)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccVCChangeEvent
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccVCChangeEvent::
|
||||
AccVCChangeEvent(Accessible* aAccessible,
|
||||
Accessible* aOldAccessible,
|
||||
int32_t aOldStart, int32_t aOldEnd,
|
||||
int16_t aReason, EIsFromUserInput aIsFromUserInput) :
|
||||
AccEvent(::nsIAccessibleEvent::EVENT_VIRTUALCURSOR_CHANGED, aAccessible,
|
||||
aIsFromUserInput),
|
||||
mOldAccessible(aOldAccessible), mOldStart(aOldStart), mOldEnd(aOldEnd),
|
||||
mReason(aReason)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<nsIAccessibleEvent>
|
||||
a11y::MakeXPCEvent(AccEvent* aEvent)
|
||||
{
|
||||
DocAccessible* doc = aEvent->Document();
|
||||
Accessible* acc = aEvent->GetAccessible();
|
||||
nsINode* node = acc->GetNode();
|
||||
nsIDOMNode* domNode = node ? node->AsDOMNode() : nullptr;
|
||||
bool fromUser = aEvent->IsFromUserInput();
|
||||
uint32_t type = aEvent->GetEventType();
|
||||
uint32_t eventGroup = aEvent->GetEventGroups();
|
||||
nsCOMPtr<nsIAccessibleEvent> xpEvent;
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eStateChangeEvent)) {
|
||||
AccStateChangeEvent* sc = downcast_accEvent(aEvent);
|
||||
bool extra = false;
|
||||
uint32_t state = nsAccUtils::To32States(sc->GetState(), &extra);
|
||||
xpEvent = new xpcAccStateChangeEvent(type, ToXPC(acc), ToXPCDocument(doc),
|
||||
domNode, fromUser,
|
||||
state, extra, sc->IsStateEnabled());
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eTextChangeEvent)) {
|
||||
AccTextChangeEvent* tc = downcast_accEvent(aEvent);
|
||||
nsString text;
|
||||
tc->GetModifiedText(text);
|
||||
xpEvent = new xpcAccTextChangeEvent(type, ToXPC(acc), ToXPCDocument(doc),
|
||||
domNode, fromUser,
|
||||
tc->GetStartOffset(), tc->GetLength(),
|
||||
tc->IsTextInserted(), text);
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eHideEvent)) {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
|
||||
xpEvent = new xpcAccHideEvent(type, ToXPC(acc), ToXPCDocument(doc),
|
||||
domNode, fromUser,
|
||||
ToXPC(hideEvent->TargetParent()),
|
||||
ToXPC(hideEvent->TargetNextSibling()),
|
||||
ToXPC(hideEvent->TargetPrevSibling()));
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eCaretMoveEvent)) {
|
||||
AccCaretMoveEvent* cm = downcast_accEvent(aEvent);
|
||||
xpEvent = new xpcAccCaretMoveEvent(type, ToXPC(acc), ToXPCDocument(doc),
|
||||
domNode, fromUser,
|
||||
cm->GetCaretOffset());
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eVirtualCursorChangeEvent)) {
|
||||
AccVCChangeEvent* vcc = downcast_accEvent(aEvent);
|
||||
xpEvent = new xpcAccVirtualCursorChangeEvent(type,
|
||||
ToXPC(acc), ToXPCDocument(doc),
|
||||
domNode, fromUser,
|
||||
ToXPC(vcc->OldAccessible()),
|
||||
vcc->OldStartOffset(),
|
||||
vcc->OldEndOffset(),
|
||||
vcc->Reason());
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
if (eventGroup & (1 << AccEvent::eObjectAttrChangedEvent)) {
|
||||
AccObjectAttrChangedEvent* oac = downcast_accEvent(aEvent);
|
||||
xpEvent = new xpcAccObjectAttributeChangedEvent(type,
|
||||
ToXPC(acc),
|
||||
ToXPCDocument(doc), domNode,
|
||||
fromUser,
|
||||
oac->GetAttribute());
|
||||
return xpEvent.forget();
|
||||
}
|
||||
|
||||
xpEvent = new xpcAccEvent(type, ToXPC(acc), ToXPCDocument(doc), domNode, fromUser);
|
||||
return xpEvent.forget();
|
||||
}
|
||||
570
accessible/base/AccEvent.h
Normal file
570
accessible/base/AccEvent.h
Normal file
|
|
@ -0,0 +1,570 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 _AccEvent_H_
|
||||
#define _AccEvent_H_
|
||||
|
||||
#include "nsIAccessibleEvent.h"
|
||||
|
||||
#include "mozilla/a11y/Accessible.h"
|
||||
|
||||
class nsEventShell;
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Selection;
|
||||
}
|
||||
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessible;
|
||||
|
||||
// Constants used to point whether the event is from user input.
|
||||
enum EIsFromUserInput
|
||||
{
|
||||
// eNoUserInput: event is not from user input
|
||||
eNoUserInput = 0,
|
||||
// eFromUserInput: event is from user input
|
||||
eFromUserInput = 1,
|
||||
// eAutoDetect: the value should be obtained from event state manager
|
||||
eAutoDetect = -1
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic accessible event.
|
||||
*/
|
||||
class AccEvent
|
||||
{
|
||||
public:
|
||||
|
||||
// Rule for accessible events.
|
||||
// The rule will be applied when flushing pending events.
|
||||
enum EEventRule {
|
||||
// eAllowDupes : More than one event of the same type is allowed.
|
||||
// This event will always be emitted. This flag is used for events that
|
||||
// don't support coalescence.
|
||||
eAllowDupes,
|
||||
|
||||
// eCoalesceReorder : For reorder events from the same subtree or the same
|
||||
// node, only the umbrella event on the ancestor will be emitted.
|
||||
eCoalesceReorder,
|
||||
|
||||
// eCoalesceOfSameType : For events of the same type, only the newest event
|
||||
// will be processed.
|
||||
eCoalesceOfSameType,
|
||||
|
||||
// eCoalesceSelectionChange: coalescence of selection change events.
|
||||
eCoalesceSelectionChange,
|
||||
|
||||
// eCoalesceStateChange: coalesce state change events.
|
||||
eCoalesceStateChange,
|
||||
|
||||
// eCoalesceTextSelChange: coalescence of text selection change events.
|
||||
eCoalesceTextSelChange,
|
||||
|
||||
// eRemoveDupes : For repeat events, only the newest event in queue
|
||||
// will be emitted.
|
||||
eRemoveDupes,
|
||||
|
||||
// eDoNotEmit : This event is confirmed as a duplicate, do not emit it.
|
||||
eDoNotEmit
|
||||
};
|
||||
|
||||
// Initialize with an accessible.
|
||||
AccEvent(uint32_t aEventType, Accessible* aAccessible,
|
||||
EIsFromUserInput aIsFromUserInput = eAutoDetect,
|
||||
EEventRule aEventRule = eRemoveDupes);
|
||||
|
||||
// AccEvent
|
||||
uint32_t GetEventType() const { return mEventType; }
|
||||
EEventRule GetEventRule() const { return mEventRule; }
|
||||
bool IsFromUserInput() const { return mIsFromUserInput; }
|
||||
EIsFromUserInput FromUserInput() const
|
||||
{ return static_cast<EIsFromUserInput>(mIsFromUserInput); }
|
||||
|
||||
Accessible* GetAccessible() const { return mAccessible; }
|
||||
DocAccessible* Document() const { return mAccessible->Document(); }
|
||||
|
||||
/**
|
||||
* Down casting.
|
||||
*/
|
||||
enum EventGroup {
|
||||
eGenericEvent,
|
||||
eStateChangeEvent,
|
||||
eTextChangeEvent,
|
||||
eTreeMutationEvent,
|
||||
eMutationEvent,
|
||||
eReorderEvent,
|
||||
eHideEvent,
|
||||
eShowEvent,
|
||||
eCaretMoveEvent,
|
||||
eTextSelChangeEvent,
|
||||
eSelectionChangeEvent,
|
||||
eTableChangeEvent,
|
||||
eVirtualCursorChangeEvent,
|
||||
eObjectAttrChangedEvent
|
||||
};
|
||||
|
||||
static const EventGroup kEventGroup = eGenericEvent;
|
||||
virtual unsigned int GetEventGroups() const
|
||||
{
|
||||
return 1U << eGenericEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference counting and cycle collection.
|
||||
*/
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AccEvent)
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(AccEvent)
|
||||
|
||||
protected:
|
||||
virtual ~AccEvent() {}
|
||||
|
||||
bool mIsFromUserInput;
|
||||
uint32_t mEventType;
|
||||
EEventRule mEventRule;
|
||||
RefPtr<Accessible> mAccessible;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class EventTree;
|
||||
friend class ::nsEventShell;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible state change event.
|
||||
*/
|
||||
class AccStateChangeEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState,
|
||||
bool aIsEnabled,
|
||||
EIsFromUserInput aIsFromUserInput = eAutoDetect) :
|
||||
AccEvent(nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
|
||||
aIsFromUserInput, eCoalesceStateChange),
|
||||
mState(aState), mIsEnabled(aIsEnabled) { }
|
||||
|
||||
AccStateChangeEvent(Accessible* aAccessible, uint64_t aState) :
|
||||
AccEvent(::nsIAccessibleEvent::EVENT_STATE_CHANGE, aAccessible,
|
||||
eAutoDetect, eCoalesceStateChange), mState(aState)
|
||||
{ mIsEnabled = (mAccessible->State() & mState) != 0; }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eStateChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eStateChangeEvent);
|
||||
}
|
||||
|
||||
// AccStateChangeEvent
|
||||
uint64_t GetState() const { return mState; }
|
||||
bool IsStateEnabled() const { return mIsEnabled; }
|
||||
|
||||
private:
|
||||
uint64_t mState;
|
||||
bool mIsEnabled;
|
||||
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible text change event.
|
||||
*/
|
||||
class AccTextChangeEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccTextChangeEvent(Accessible* aAccessible, int32_t aStart,
|
||||
const nsAString& aModifiedText, bool aIsInserted,
|
||||
EIsFromUserInput aIsFromUserInput = eAutoDetect);
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eTextChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eTextChangeEvent);
|
||||
}
|
||||
|
||||
// AccTextChangeEvent
|
||||
int32_t GetStartOffset() const { return mStart; }
|
||||
uint32_t GetLength() const { return mModifiedText.Length(); }
|
||||
bool IsTextInserted() const { return mIsInserted; }
|
||||
void GetModifiedText(nsAString& aModifiedText)
|
||||
{ aModifiedText = mModifiedText; }
|
||||
const nsString& ModifiedText() const { return mModifiedText; }
|
||||
|
||||
private:
|
||||
int32_t mStart;
|
||||
bool mIsInserted;
|
||||
nsString mModifiedText;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
/**
|
||||
* A base class for events related to tree mutation, either an AccMutation
|
||||
* event, or an AccReorderEvent.
|
||||
*/
|
||||
class AccTreeMutationEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
AccTreeMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccEvent(aEventType, aTarget, eAutoDetect, eCoalesceReorder), mGeneration(0) {}
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eTreeMutationEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eTreeMutationEvent);
|
||||
}
|
||||
|
||||
void SetNextEvent(AccTreeMutationEvent* aNext) { mNextEvent = aNext; }
|
||||
void SetPrevEvent(AccTreeMutationEvent* aPrev) { mPrevEvent = aPrev; }
|
||||
AccTreeMutationEvent* NextEvent() const { return mNextEvent; }
|
||||
AccTreeMutationEvent* PrevEvent() const { return mPrevEvent; }
|
||||
|
||||
/**
|
||||
* A sequence number to know when this event was fired.
|
||||
*/
|
||||
uint32_t EventGeneration() const { return mGeneration; }
|
||||
void SetEventGeneration(uint32_t aGeneration) { mGeneration = aGeneration; }
|
||||
|
||||
private:
|
||||
RefPtr<AccTreeMutationEvent> mNextEvent;
|
||||
RefPtr<AccTreeMutationEvent> mPrevEvent;
|
||||
uint32_t mGeneration;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for show and hide accessible events.
|
||||
*/
|
||||
class AccMutationEvent: public AccTreeMutationEvent
|
||||
{
|
||||
public:
|
||||
AccMutationEvent(uint32_t aEventType, Accessible* aTarget) :
|
||||
AccTreeMutationEvent(aEventType, aTarget)
|
||||
{
|
||||
// Don't coalesce these since they are coalesced by reorder event. Coalesce
|
||||
// contained text change events.
|
||||
mParent = mAccessible->Parent();
|
||||
}
|
||||
virtual ~AccMutationEvent() { }
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eMutationEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccTreeMutationEvent::GetEventGroups() | (1U << eMutationEvent);
|
||||
}
|
||||
|
||||
// MutationEvent
|
||||
bool IsShow() const { return mEventType == nsIAccessibleEvent::EVENT_SHOW; }
|
||||
bool IsHide() const { return mEventType == nsIAccessibleEvent::EVENT_HIDE; }
|
||||
|
||||
Accessible* Parent() const { return mParent; }
|
||||
|
||||
protected:
|
||||
nsCOMPtr<nsINode> mNode;
|
||||
RefPtr<Accessible> mParent;
|
||||
RefPtr<AccTextChangeEvent> mTextChangeEvent;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible hide event.
|
||||
*/
|
||||
class AccHideEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
explicit AccHideEvent(Accessible* aTarget, bool aNeedsShutdown = true);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eHideEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccMutationEvent::GetEventGroups() | (1U << eHideEvent);
|
||||
}
|
||||
|
||||
// AccHideEvent
|
||||
Accessible* TargetParent() const { return mParent; }
|
||||
Accessible* TargetNextSibling() const { return mNextSibling; }
|
||||
Accessible* TargetPrevSibling() const { return mPrevSibling; }
|
||||
bool NeedsShutdown() const { return mNeedsShutdown; }
|
||||
|
||||
protected:
|
||||
bool mNeedsShutdown;
|
||||
RefPtr<Accessible> mNextSibling;
|
||||
RefPtr<Accessible> mPrevSibling;
|
||||
|
||||
friend class EventTree;
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible show event.
|
||||
*/
|
||||
class AccShowEvent: public AccMutationEvent
|
||||
{
|
||||
public:
|
||||
explicit AccShowEvent(Accessible* aTarget);
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eShowEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccMutationEvent::GetEventGroups() | (1U << eShowEvent);
|
||||
}
|
||||
|
||||
uint32_t InsertionIndex() const { return mInsertionIndex; }
|
||||
|
||||
private:
|
||||
nsTArray<RefPtr<AccHideEvent>> mPrecedingEvents;
|
||||
uint32_t mInsertionIndex;
|
||||
|
||||
friend class EventTree;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class for reorder accessible event. Takes care about
|
||||
*/
|
||||
class AccReorderEvent : public AccTreeMutationEvent
|
||||
{
|
||||
public:
|
||||
explicit AccReorderEvent(Accessible* aTarget) :
|
||||
AccTreeMutationEvent(::nsIAccessibleEvent::EVENT_REORDER, aTarget) { }
|
||||
virtual ~AccReorderEvent() { }
|
||||
|
||||
// Event
|
||||
static const EventGroup kEventGroup = eReorderEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccTreeMutationEvent::GetEventGroups() | (1U << eReorderEvent);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible caret move event.
|
||||
*/
|
||||
class AccCaretMoveEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccCaretMoveEvent(Accessible* aAccessible, int32_t aCaretOffset,
|
||||
EIsFromUserInput aIsFromUserInput = eAutoDetect) :
|
||||
AccEvent(::nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED, aAccessible,
|
||||
aIsFromUserInput),
|
||||
mCaretOffset(aCaretOffset) { }
|
||||
virtual ~AccCaretMoveEvent() { }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eCaretMoveEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eCaretMoveEvent);
|
||||
}
|
||||
|
||||
// AccCaretMoveEvent
|
||||
int32_t GetCaretOffset() const { return mCaretOffset; }
|
||||
|
||||
private:
|
||||
int32_t mCaretOffset;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible text selection change event.
|
||||
*/
|
||||
class AccTextSelChangeEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
AccTextSelChangeEvent(HyperTextAccessible* aTarget,
|
||||
dom::Selection* aSelection,
|
||||
int32_t aReason);
|
||||
virtual ~AccTextSelChangeEvent();
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eTextSelChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eTextSelChangeEvent);
|
||||
}
|
||||
|
||||
// AccTextSelChangeEvent
|
||||
|
||||
/**
|
||||
* Return true if the text selection change wasn't caused by pure caret move.
|
||||
*/
|
||||
bool IsCaretMoveOnly() const;
|
||||
|
||||
private:
|
||||
RefPtr<dom::Selection> mSel;
|
||||
int32_t mReason;
|
||||
|
||||
friend class EventQueue;
|
||||
friend class SelectionManager;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible widget selection change event.
|
||||
*/
|
||||
class AccSelChangeEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
enum SelChangeType {
|
||||
eSelectionAdd,
|
||||
eSelectionRemove
|
||||
};
|
||||
|
||||
AccSelChangeEvent(Accessible* aWidget, Accessible* aItem,
|
||||
SelChangeType aSelChangeType);
|
||||
|
||||
virtual ~AccSelChangeEvent() { }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eSelectionChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eSelectionChangeEvent);
|
||||
}
|
||||
|
||||
// AccSelChangeEvent
|
||||
Accessible* Widget() const { return mWidget; }
|
||||
|
||||
private:
|
||||
RefPtr<Accessible> mWidget;
|
||||
RefPtr<Accessible> mItem;
|
||||
SelChangeType mSelChangeType;
|
||||
uint32_t mPreceedingCount;
|
||||
AccSelChangeEvent* mPackedEvent;
|
||||
|
||||
friend class EventQueue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Accessible table change event.
|
||||
*/
|
||||
class AccTableChangeEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
AccTableChangeEvent(Accessible* aAccessible, uint32_t aEventType,
|
||||
int32_t aRowOrColIndex, int32_t aNumRowsOrCols);
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eTableChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eTableChangeEvent);
|
||||
}
|
||||
|
||||
// AccTableChangeEvent
|
||||
uint32_t GetIndex() const { return mRowOrColIndex; }
|
||||
uint32_t GetCount() const { return mNumRowsOrCols; }
|
||||
|
||||
private:
|
||||
uint32_t mRowOrColIndex; // the start row/column after which the rows are inserted/deleted.
|
||||
uint32_t mNumRowsOrCols; // the number of inserted/deleted rows/columns
|
||||
};
|
||||
|
||||
/**
|
||||
* Accessible virtual cursor change event.
|
||||
*/
|
||||
class AccVCChangeEvent : public AccEvent
|
||||
{
|
||||
public:
|
||||
AccVCChangeEvent(Accessible* aAccessible,
|
||||
Accessible* aOldAccessible,
|
||||
int32_t aOldStart, int32_t aOldEnd,
|
||||
int16_t aReason,
|
||||
EIsFromUserInput aIsFromUserInput = eFromUserInput);
|
||||
|
||||
virtual ~AccVCChangeEvent() { }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eVirtualCursorChangeEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eVirtualCursorChangeEvent);
|
||||
}
|
||||
|
||||
// AccTableChangeEvent
|
||||
Accessible* OldAccessible() const { return mOldAccessible; }
|
||||
int32_t OldStartOffset() const { return mOldStart; }
|
||||
int32_t OldEndOffset() const { return mOldEnd; }
|
||||
int32_t Reason() const { return mReason; }
|
||||
|
||||
private:
|
||||
RefPtr<Accessible> mOldAccessible;
|
||||
int32_t mOldStart;
|
||||
int32_t mOldEnd;
|
||||
int16_t mReason;
|
||||
};
|
||||
|
||||
/**
|
||||
* Accessible object attribute changed event.
|
||||
*/
|
||||
class AccObjectAttrChangedEvent: public AccEvent
|
||||
{
|
||||
public:
|
||||
AccObjectAttrChangedEvent(Accessible* aAccessible, nsIAtom* aAttribute) :
|
||||
AccEvent(::nsIAccessibleEvent::EVENT_OBJECT_ATTRIBUTE_CHANGED, aAccessible),
|
||||
mAttribute(aAttribute) { }
|
||||
|
||||
// AccEvent
|
||||
static const EventGroup kEventGroup = eObjectAttrChangedEvent;
|
||||
virtual unsigned int GetEventGroups() const override
|
||||
{
|
||||
return AccEvent::GetEventGroups() | (1U << eObjectAttrChangedEvent);
|
||||
}
|
||||
|
||||
// AccObjectAttrChangedEvent
|
||||
nsIAtom* GetAttribute() const { return mAttribute; }
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIAtom> mAttribute;
|
||||
|
||||
virtual ~AccObjectAttrChangedEvent() { }
|
||||
};
|
||||
|
||||
/**
|
||||
* Downcast the generic accessible event object to derived type.
|
||||
*/
|
||||
class downcast_accEvent
|
||||
{
|
||||
public:
|
||||
explicit downcast_accEvent(AccEvent* e) : mRawPtr(e) { }
|
||||
|
||||
template<class Destination>
|
||||
operator Destination*() {
|
||||
if (!mRawPtr)
|
||||
return nullptr;
|
||||
|
||||
return mRawPtr->GetEventGroups() & (1U << Destination::kEventGroup) ?
|
||||
static_cast<Destination*>(mRawPtr) : nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
AccEvent* mRawPtr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a new xpcom accessible event for the given internal one.
|
||||
*/
|
||||
already_AddRefed<nsIAccessibleEvent>
|
||||
MakeXPCEvent(AccEvent* aEvent);
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
227
accessible/base/AccGroupInfo.cpp
Normal file
227
accessible/base/AccGroupInfo.cpp
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
/* 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 "AccGroupInfo.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
AccGroupInfo::AccGroupInfo(Accessible* aItem, role aRole) :
|
||||
mPosInSet(0), mSetSize(0), mParent(nullptr), mItem(aItem), mRole(aRole)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AccGroupInfo);
|
||||
Update();
|
||||
}
|
||||
|
||||
void
|
||||
AccGroupInfo::Update()
|
||||
{
|
||||
Accessible* parent = mItem->Parent();
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
int32_t indexInParent = mItem->IndexInParent();
|
||||
uint32_t siblingCount = parent->ChildCount();
|
||||
if (indexInParent == -1 ||
|
||||
indexInParent >= static_cast<int32_t>(siblingCount)) {
|
||||
NS_ERROR("Wrong index in parent! Tree invalidation problem.");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t level = nsAccUtils::GetARIAOrDefaultLevel(mItem);
|
||||
|
||||
// Compute position in set.
|
||||
mPosInSet = 1;
|
||||
for (int32_t idx = indexInParent - 1; idx >= 0 ; idx--) {
|
||||
Accessible* sibling = parent->GetChildAt(idx);
|
||||
roles::Role siblingRole = sibling->Role();
|
||||
|
||||
// If the sibling is separator then the group is ended.
|
||||
if (siblingRole == roles::SEPARATOR)
|
||||
break;
|
||||
|
||||
// If sibling is not visible and hasn't the same base role.
|
||||
if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
|
||||
continue;
|
||||
|
||||
// Check if it's hierarchical flatten structure, i.e. if the sibling
|
||||
// level is lesser than this one then group is ended, if the sibling level
|
||||
// is greater than this one then the group is split by some child elements
|
||||
// (group will be continued).
|
||||
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
|
||||
if (siblingLevel < level) {
|
||||
mParent = sibling;
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip subset.
|
||||
if (siblingLevel > level)
|
||||
continue;
|
||||
|
||||
// If the previous item in the group has calculated group information then
|
||||
// build group information for this item based on found one.
|
||||
if (sibling->mBits.groupInfo) {
|
||||
mPosInSet += sibling->mBits.groupInfo->mPosInSet;
|
||||
mParent = sibling->mBits.groupInfo->mParent;
|
||||
mSetSize = sibling->mBits.groupInfo->mSetSize;
|
||||
return;
|
||||
}
|
||||
|
||||
mPosInSet++;
|
||||
}
|
||||
|
||||
// Compute set size.
|
||||
mSetSize = mPosInSet;
|
||||
|
||||
for (uint32_t idx = indexInParent + 1; idx < siblingCount; idx++) {
|
||||
Accessible* sibling = parent->GetChildAt(idx);
|
||||
|
||||
roles::Role siblingRole = sibling->Role();
|
||||
|
||||
// If the sibling is separator then the group is ended.
|
||||
if (siblingRole == roles::SEPARATOR)
|
||||
break;
|
||||
|
||||
// If sibling is visible and has the same base role
|
||||
if (BaseRole(siblingRole) != mRole || sibling->State() & states::INVISIBLE)
|
||||
continue;
|
||||
|
||||
// and check if it's hierarchical flatten structure.
|
||||
int32_t siblingLevel = nsAccUtils::GetARIAOrDefaultLevel(sibling);
|
||||
if (siblingLevel < level)
|
||||
break;
|
||||
|
||||
// Skip subset.
|
||||
if (siblingLevel > level)
|
||||
continue;
|
||||
|
||||
// If the next item in the group has calculated group information then
|
||||
// build group information for this item based on found one.
|
||||
if (sibling->mBits.groupInfo) {
|
||||
mParent = sibling->mBits.groupInfo->mParent;
|
||||
mSetSize = sibling->mBits.groupInfo->mSetSize;
|
||||
return;
|
||||
}
|
||||
|
||||
mSetSize++;
|
||||
}
|
||||
|
||||
if (mParent)
|
||||
return;
|
||||
|
||||
roles::Role parentRole = parent->Role();
|
||||
if (ShouldReportRelations(mRole, parentRole))
|
||||
mParent = parent;
|
||||
|
||||
// ARIA tree and list can be arranged by using ARIA groups to organize levels.
|
||||
if (parentRole != roles::GROUPING)
|
||||
return;
|
||||
|
||||
// Way #1 for ARIA tree (not ARIA treegrid): previous sibling of a group is a
|
||||
// parent. In other words the parent of the tree item will be a group and
|
||||
// the previous tree item of the group is a conceptual parent of the tree
|
||||
// item.
|
||||
if (mRole == roles::OUTLINEITEM) {
|
||||
Accessible* parentPrevSibling = parent->PrevSibling();
|
||||
if (parentPrevSibling && parentPrevSibling->Role() == mRole) {
|
||||
mParent = parentPrevSibling;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Way #2 for ARIA list and tree: group is a child of an item. In other words
|
||||
// the parent of the item will be a group and containing item of the group is
|
||||
// a conceptual parent of the item.
|
||||
if (mRole == roles::LISTITEM || mRole == roles::OUTLINEITEM) {
|
||||
Accessible* grandParent = parent->Parent();
|
||||
if (grandParent && grandParent->Role() == mRole)
|
||||
mParent = grandParent;
|
||||
}
|
||||
}
|
||||
|
||||
Accessible*
|
||||
AccGroupInfo::FirstItemOf(Accessible* aContainer)
|
||||
{
|
||||
// ARIA tree can be arranged by ARIA groups case #1 (previous sibling of a
|
||||
// group is a parent) or by aria-level.
|
||||
a11y::role containerRole = aContainer->Role();
|
||||
Accessible* item = aContainer->NextSibling();
|
||||
if (item) {
|
||||
if (containerRole == roles::OUTLINEITEM && item->Role() == roles::GROUPING)
|
||||
item = item->FirstChild();
|
||||
|
||||
if (item) {
|
||||
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
|
||||
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// ARIA list and tree can be arranged by ARIA groups case #2 (group is
|
||||
// a child of an item).
|
||||
item = aContainer->LastChild();
|
||||
if (!item)
|
||||
return nullptr;
|
||||
|
||||
if (item->Role() == roles::GROUPING &&
|
||||
(containerRole == roles::LISTITEM || containerRole == roles::OUTLINEITEM)) {
|
||||
item = item->FirstChild();
|
||||
if (item) {
|
||||
AccGroupInfo* itemGroupInfo = item->GetGroupInfo();
|
||||
if (itemGroupInfo && itemGroupInfo->ConceptualParent() == aContainer)
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, it can be a direct child if the container is a list or tree.
|
||||
item = aContainer->FirstChild();
|
||||
if (ShouldReportRelations(item->Role(), containerRole))
|
||||
return item;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
AccGroupInfo::NextItemTo(Accessible* aItem)
|
||||
{
|
||||
AccGroupInfo* groupInfo = aItem->GetGroupInfo();
|
||||
if (!groupInfo)
|
||||
return nullptr;
|
||||
|
||||
// If the item in middle of the group then search next item in siblings.
|
||||
if (groupInfo->PosInSet() >= groupInfo->SetSize())
|
||||
return nullptr;
|
||||
|
||||
Accessible* parent = aItem->Parent();
|
||||
uint32_t childCount = parent->ChildCount();
|
||||
for (uint32_t idx = aItem->IndexInParent() + 1; idx < childCount; idx++) {
|
||||
Accessible* nextItem = parent->GetChildAt(idx);
|
||||
AccGroupInfo* nextGroupInfo = nextItem->GetGroupInfo();
|
||||
if (nextGroupInfo &&
|
||||
nextGroupInfo->ConceptualParent() == groupInfo->ConceptualParent()) {
|
||||
return nextItem;
|
||||
}
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Item in the middle of the group but there's no next item!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
AccGroupInfo::ShouldReportRelations(role aRole, role aParentRole)
|
||||
{
|
||||
// We only want to report hierarchy-based node relations for items in tree or
|
||||
// list form. ARIA level/owns relations are always reported.
|
||||
if (aParentRole == roles::OUTLINE && aRole == roles::OUTLINEITEM)
|
||||
return true;
|
||||
if (aParentRole == roles::TREE_TABLE && aRole == roles::ROW)
|
||||
return true;
|
||||
if (aParentRole == roles::LIST && aRole == roles::LISTITEM)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
114
accessible/base/AccGroupInfo.h
Normal file
114
accessible/base/AccGroupInfo.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* 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 AccGroupInfo_h_
|
||||
#define AccGroupInfo_h_
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Calculate and store group information.
|
||||
*/
|
||||
class AccGroupInfo
|
||||
{
|
||||
public:
|
||||
~AccGroupInfo() { MOZ_COUNT_DTOR(AccGroupInfo); }
|
||||
|
||||
/**
|
||||
* Return 1-based position in the group.
|
||||
*/
|
||||
uint32_t PosInSet() const { return mPosInSet; }
|
||||
|
||||
/**
|
||||
* Return a number of items in the group.
|
||||
*/
|
||||
uint32_t SetSize() const { return mSetSize; }
|
||||
|
||||
/**
|
||||
* Return a direct or logical parent of the accessible that this group info is
|
||||
* created for.
|
||||
*/
|
||||
Accessible* ConceptualParent() const { return mParent; }
|
||||
|
||||
/**
|
||||
* Update group information.
|
||||
*/
|
||||
void Update();
|
||||
|
||||
/**
|
||||
* Create group info.
|
||||
*/
|
||||
static AccGroupInfo* CreateGroupInfo(Accessible* aAccessible)
|
||||
{
|
||||
mozilla::a11y::role role = aAccessible->Role();
|
||||
if (role != mozilla::a11y::roles::ROW &&
|
||||
role != mozilla::a11y::roles::OUTLINEITEM &&
|
||||
role != mozilla::a11y::roles::OPTION &&
|
||||
role != mozilla::a11y::roles::LISTITEM &&
|
||||
role != mozilla::a11y::roles::MENUITEM &&
|
||||
role != mozilla::a11y::roles::COMBOBOX_OPTION &&
|
||||
role != mozilla::a11y::roles::RICH_OPTION &&
|
||||
role != mozilla::a11y::roles::CHECK_RICH_OPTION &&
|
||||
role != mozilla::a11y::roles::PARENT_MENUITEM &&
|
||||
role != mozilla::a11y::roles::CHECK_MENU_ITEM &&
|
||||
role != mozilla::a11y::roles::RADIO_MENU_ITEM &&
|
||||
role != mozilla::a11y::roles::RADIOBUTTON &&
|
||||
role != mozilla::a11y::roles::PAGETAB)
|
||||
return nullptr;
|
||||
|
||||
AccGroupInfo* info = new AccGroupInfo(aAccessible, BaseRole(role));
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a first item for the given container.
|
||||
*/
|
||||
static Accessible* FirstItemOf(Accessible* aContainer);
|
||||
|
||||
/**
|
||||
* Return next item of the same group to the given item.
|
||||
*/
|
||||
static Accessible* NextItemTo(Accessible* aItem);
|
||||
|
||||
protected:
|
||||
AccGroupInfo(Accessible* aItem, a11y::role aRole);
|
||||
|
||||
private:
|
||||
AccGroupInfo() = delete;
|
||||
AccGroupInfo(const AccGroupInfo&) = delete;
|
||||
AccGroupInfo& operator =(const AccGroupInfo&) = delete;
|
||||
|
||||
static mozilla::a11y::role BaseRole(mozilla::a11y::role aRole)
|
||||
{
|
||||
if (aRole == mozilla::a11y::roles::CHECK_MENU_ITEM ||
|
||||
aRole == mozilla::a11y::roles::PARENT_MENUITEM ||
|
||||
aRole == mozilla::a11y::roles::RADIO_MENU_ITEM)
|
||||
return mozilla::a11y::roles::MENUITEM;
|
||||
|
||||
if (aRole == mozilla::a11y::roles::CHECK_RICH_OPTION)
|
||||
return mozilla::a11y::roles::RICH_OPTION;
|
||||
|
||||
return aRole;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given parent and child roles should have their node
|
||||
* relations reported.
|
||||
*/
|
||||
static bool ShouldReportRelations(a11y::role aRole, a11y::role aParentRole);
|
||||
|
||||
uint32_t mPosInSet;
|
||||
uint32_t mSetSize;
|
||||
Accessible* mParent;
|
||||
Accessible* mItem;
|
||||
a11y::role mRole;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
} // namespace a11y
|
||||
|
||||
#endif
|
||||
414
accessible/base/AccIterator.cpp
Normal file
414
accessible/base/AccIterator.cpp
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/* 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 "AccIterator.h"
|
||||
|
||||
#include "AccGroupInfo.h"
|
||||
#ifdef MOZ_XUL
|
||||
#include "XULTreeAccessible.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/HTMLLabelElement.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AccIterator::AccIterator(Accessible* aAccessible,
|
||||
filters::FilterFuncPtr aFilterFunc) :
|
||||
mFilterFunc(aFilterFunc)
|
||||
{
|
||||
mState = new IteratorState(aAccessible);
|
||||
}
|
||||
|
||||
AccIterator::~AccIterator()
|
||||
{
|
||||
while (mState) {
|
||||
IteratorState *tmp = mState;
|
||||
mState = tmp->mParentState;
|
||||
delete tmp;
|
||||
}
|
||||
}
|
||||
|
||||
Accessible*
|
||||
AccIterator::Next()
|
||||
{
|
||||
while (mState) {
|
||||
Accessible* child = mState->mParent->GetChildAt(mState->mIndex++);
|
||||
if (!child) {
|
||||
IteratorState* tmp = mState;
|
||||
mState = mState->mParentState;
|
||||
delete tmp;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t result = mFilterFunc(child);
|
||||
if (result & filters::eMatch)
|
||||
return child;
|
||||
|
||||
if (!(result & filters::eSkipSubtree)) {
|
||||
IteratorState* childState = new IteratorState(child, mState);
|
||||
mState = childState;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccIterator::IteratorState
|
||||
|
||||
AccIterator::IteratorState::IteratorState(Accessible* aParent,
|
||||
IteratorState *mParentState) :
|
||||
mParent(aParent), mIndex(0), mParentState(mParentState)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// RelatedAccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
RelatedAccIterator::
|
||||
RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent,
|
||||
nsIAtom* aRelAttr) :
|
||||
mDocument(aDocument), mRelAttr(aRelAttr), mProviders(nullptr),
|
||||
mBindingParent(nullptr), mIndex(0)
|
||||
{
|
||||
mBindingParent = aDependentContent->GetBindingParent();
|
||||
nsIAtom* IDAttr = mBindingParent ?
|
||||
nsGkAtoms::anonid : nsGkAtoms::id;
|
||||
|
||||
nsAutoString id;
|
||||
if (aDependentContent->GetAttr(kNameSpaceID_None, IDAttr, id))
|
||||
mProviders = mDocument->mDependentIDsHash.Get(id);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
RelatedAccIterator::Next()
|
||||
{
|
||||
if (!mProviders)
|
||||
return nullptr;
|
||||
|
||||
while (mIndex < mProviders->Length()) {
|
||||
DocAccessible::AttrRelProvider* provider = (*mProviders)[mIndex++];
|
||||
|
||||
// Return related accessible for the given attribute and if the provider
|
||||
// content is in the same binding in the case of XBL usage.
|
||||
if (provider->mRelAttr == mRelAttr) {
|
||||
nsIContent* bindingParent = provider->mContent->GetBindingParent();
|
||||
bool inScope = mBindingParent == bindingParent ||
|
||||
mBindingParent == provider->mContent;
|
||||
|
||||
if (inScope) {
|
||||
Accessible* related = mDocument->GetAccessible(provider->mContent);
|
||||
if (related)
|
||||
return related;
|
||||
|
||||
// If the document content is pointed by relation then return the document
|
||||
// itself.
|
||||
if (provider->mContent == mDocument->GetContent())
|
||||
return mDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLLabelIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HTMLLabelIterator::
|
||||
HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible,
|
||||
LabelFilter aFilter) :
|
||||
mRelIter(aDocument, aAccessible->GetContent(), nsGkAtoms::_for),
|
||||
mAcc(aAccessible), mLabelFilter(aFilter)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLLabelIterator::IsLabel(Accessible* aLabel)
|
||||
{
|
||||
dom::HTMLLabelElement* labelEl =
|
||||
dom::HTMLLabelElement::FromContent(aLabel->GetContent());
|
||||
return labelEl && labelEl->GetControl() == mAcc->GetContent();
|
||||
}
|
||||
|
||||
Accessible*
|
||||
HTMLLabelIterator::Next()
|
||||
{
|
||||
// Get either <label for="[id]"> element which explicitly points to given
|
||||
// element, or <label> ancestor which implicitly point to it.
|
||||
Accessible* label = nullptr;
|
||||
while ((label = mRelIter.Next())) {
|
||||
if (IsLabel(label)) {
|
||||
return label;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore ancestor label on not widget accessible.
|
||||
if (mLabelFilter == eSkipAncestorLabel || !mAcc->IsWidget())
|
||||
return nullptr;
|
||||
|
||||
// Go up tree to get a name of ancestor label if there is one (an ancestor
|
||||
// <label> implicitly points to us). Don't go up farther than form or
|
||||
// document.
|
||||
Accessible* walkUp = mAcc->Parent();
|
||||
while (walkUp && !walkUp->IsDoc()) {
|
||||
nsIContent* walkUpEl = walkUp->GetContent();
|
||||
if (IsLabel(walkUp) &&
|
||||
!walkUpEl->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
|
||||
mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
|
||||
return walkUp;
|
||||
}
|
||||
|
||||
if (walkUpEl->IsHTMLElement(nsGkAtoms::form))
|
||||
break;
|
||||
|
||||
walkUp = walkUp->Parent();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTMLOutputIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
HTMLOutputIterator::
|
||||
HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement) :
|
||||
mRelIter(aDocument, aElement, nsGkAtoms::_for)
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
HTMLOutputIterator::Next()
|
||||
{
|
||||
Accessible* output = nullptr;
|
||||
while ((output = mRelIter.Next())) {
|
||||
if (output->GetContent()->IsHTMLElement(nsGkAtoms::output))
|
||||
return output;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULLabelIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
XULLabelIterator::
|
||||
XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement) :
|
||||
mRelIter(aDocument, aElement, nsGkAtoms::control)
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
XULLabelIterator::Next()
|
||||
{
|
||||
Accessible* label = nullptr;
|
||||
while ((label = mRelIter.Next())) {
|
||||
if (label->GetContent()->IsXULElement(nsGkAtoms::label))
|
||||
return label;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULDescriptionIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
XULDescriptionIterator::
|
||||
XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement) :
|
||||
mRelIter(aDocument, aElement, nsGkAtoms::control)
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
XULDescriptionIterator::Next()
|
||||
{
|
||||
Accessible* descr = nullptr;
|
||||
while ((descr = mRelIter.Next())) {
|
||||
if (descr->GetContent()->IsXULElement(nsGkAtoms::description))
|
||||
return descr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// IDRefsIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IDRefsIterator::
|
||||
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
|
||||
nsIAtom* aIDRefsAttr) :
|
||||
mContent(aContent), mDoc(aDoc), mCurrIdx(0)
|
||||
{
|
||||
if (mContent->IsInUncomposedDoc())
|
||||
mContent->GetAttr(kNameSpaceID_None, aIDRefsAttr, mIDs);
|
||||
}
|
||||
|
||||
const nsDependentSubstring
|
||||
IDRefsIterator::NextID()
|
||||
{
|
||||
for (; mCurrIdx < mIDs.Length(); mCurrIdx++) {
|
||||
if (!NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
|
||||
break;
|
||||
}
|
||||
|
||||
if (mCurrIdx >= mIDs.Length())
|
||||
return nsDependentSubstring();
|
||||
|
||||
nsAString::index_type idStartIdx = mCurrIdx;
|
||||
while (++mCurrIdx < mIDs.Length()) {
|
||||
if (NS_IsAsciiWhitespace(mIDs[mCurrIdx]))
|
||||
break;
|
||||
}
|
||||
|
||||
return Substring(mIDs, idStartIdx, mCurrIdx++ - idStartIdx);
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
IDRefsIterator::NextElem()
|
||||
{
|
||||
while (true) {
|
||||
const nsDependentSubstring id = NextID();
|
||||
if (id.IsEmpty())
|
||||
break;
|
||||
|
||||
nsIContent* refContent = GetElem(id);
|
||||
if (refContent)
|
||||
return refContent;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
IDRefsIterator::GetElem(const nsDependentSubstring& aID)
|
||||
{
|
||||
// Get elements in DOM tree by ID attribute if this is an explicit content.
|
||||
// In case of bound element check its anonymous subtree.
|
||||
if (!mContent->IsInAnonymousSubtree()) {
|
||||
dom::Element* refElm = mContent->OwnerDoc()->GetElementById(aID);
|
||||
if (refElm || !mContent->GetXBLBinding())
|
||||
return refElm;
|
||||
}
|
||||
|
||||
// If content is in anonymous subtree or an element having anonymous subtree
|
||||
// then use "anonid" attribute to get elements in anonymous subtree.
|
||||
|
||||
// Check inside the binding the element is contained in.
|
||||
nsIContent* bindingParent = mContent->GetBindingParent();
|
||||
if (bindingParent) {
|
||||
nsIContent* refElm = bindingParent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(bindingParent, nsGkAtoms::anonid, aID);
|
||||
|
||||
if (refElm)
|
||||
return refElm;
|
||||
}
|
||||
|
||||
// Check inside the binding of the element.
|
||||
if (mContent->GetXBLBinding()) {
|
||||
return mContent->OwnerDoc()->
|
||||
GetAnonymousElementByAttribute(mContent, nsGkAtoms::anonid, aID);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
IDRefsIterator::Next()
|
||||
{
|
||||
nsIContent* nextEl = nullptr;
|
||||
while ((nextEl = NextElem())) {
|
||||
Accessible* acc = mDoc->GetAccessible(nextEl);
|
||||
if (acc) {
|
||||
return acc;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SingleAccIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Accessible*
|
||||
SingleAccIterator::Next()
|
||||
{
|
||||
RefPtr<Accessible> nextAcc;
|
||||
mAcc.swap(nextAcc);
|
||||
if (!nextAcc || nextAcc->IsDefunct()) {
|
||||
return nullptr;
|
||||
}
|
||||
return nextAcc;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ItemIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Accessible*
|
||||
ItemIterator::Next()
|
||||
{
|
||||
if (mContainer) {
|
||||
mAnchor = AccGroupInfo::FirstItemOf(mContainer);
|
||||
mContainer = nullptr;
|
||||
return mAnchor;
|
||||
}
|
||||
|
||||
return mAnchor ? (mAnchor = AccGroupInfo::NextItemTo(mAnchor)) : nullptr;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// XULTreeItemIterator
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
XULTreeItemIterator::XULTreeItemIterator(XULTreeAccessible* aXULTree,
|
||||
nsITreeView* aTreeView,
|
||||
int32_t aRowIdx) :
|
||||
mXULTree(aXULTree), mTreeView(aTreeView), mRowCount(-1),
|
||||
mContainerLevel(-1), mCurrRowIdx(aRowIdx + 1)
|
||||
{
|
||||
mTreeView->GetRowCount(&mRowCount);
|
||||
if (aRowIdx != -1)
|
||||
mTreeView->GetLevel(aRowIdx, &mContainerLevel);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
XULTreeItemIterator::Next()
|
||||
{
|
||||
while (mCurrRowIdx < mRowCount) {
|
||||
int32_t level = 0;
|
||||
mTreeView->GetLevel(mCurrRowIdx, &level);
|
||||
|
||||
if (level == mContainerLevel + 1)
|
||||
return mXULTree->GetTreeItemAccessible(mCurrRowIdx++);
|
||||
|
||||
if (level <= mContainerLevel) { // got level up
|
||||
mCurrRowIdx = mRowCount;
|
||||
break;
|
||||
}
|
||||
|
||||
mCurrRowIdx++;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
325
accessible/base/AccIterator.h
Normal file
325
accessible/base/AccIterator.h
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- 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_AccIterator_h__
|
||||
#define mozilla_a11y_AccIterator_h__
|
||||
|
||||
#include "DocAccessible.h"
|
||||
#include "Filters.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class nsITreeView;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* AccIterable is a basic interface for iterators over accessibles.
|
||||
*/
|
||||
class AccIterable
|
||||
{
|
||||
public:
|
||||
virtual ~AccIterable() { }
|
||||
virtual Accessible* Next() = 0;
|
||||
|
||||
private:
|
||||
friend class Relation;
|
||||
std::unique_ptr<AccIterable> mNextIter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to iterate through accessible children or subtree complying with
|
||||
* filter function.
|
||||
*/
|
||||
class AccIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
AccIterator(Accessible* aRoot, filters::FilterFuncPtr aFilterFunc);
|
||||
virtual ~AccIterator();
|
||||
|
||||
/**
|
||||
* Return next accessible complying with filter function. Return the first
|
||||
* accessible for the first time.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
AccIterator();
|
||||
AccIterator(const AccIterator&);
|
||||
AccIterator& operator =(const AccIterator&);
|
||||
|
||||
struct IteratorState
|
||||
{
|
||||
explicit IteratorState(Accessible* aParent, IteratorState* mParentState = nullptr);
|
||||
|
||||
Accessible* mParent;
|
||||
int32_t mIndex;
|
||||
IteratorState* mParentState;
|
||||
};
|
||||
|
||||
filters::FilterFuncPtr mFilterFunc;
|
||||
IteratorState* mState;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Allows to traverse through related accessibles that are pointing to the given
|
||||
* dependent accessible by relation attribute.
|
||||
*/
|
||||
class RelatedAccIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param aDocument [in] the document accessible the related
|
||||
* & accessibles belong to.
|
||||
* @param aDependentContent [in] the content of dependent accessible that
|
||||
* relations were requested for
|
||||
* @param aRelAttr [in] relation attribute that relations are
|
||||
* pointed by
|
||||
*/
|
||||
RelatedAccIterator(DocAccessible* aDocument, nsIContent* aDependentContent,
|
||||
nsIAtom* aRelAttr);
|
||||
|
||||
virtual ~RelatedAccIterator() { }
|
||||
|
||||
/**
|
||||
* Return next related accessible for the given dependent accessible.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
RelatedAccIterator();
|
||||
RelatedAccIterator(const RelatedAccIterator&);
|
||||
RelatedAccIterator& operator = (const RelatedAccIterator&);
|
||||
|
||||
DocAccessible* mDocument;
|
||||
nsIAtom* mRelAttr;
|
||||
DocAccessible::AttrRelProviderArray* mProviders;
|
||||
nsIContent* mBindingParent;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate through HTML labels associated with the given accessible.
|
||||
*/
|
||||
class HTMLLabelIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
enum LabelFilter {
|
||||
eAllLabels,
|
||||
eSkipAncestorLabel
|
||||
};
|
||||
|
||||
HTMLLabelIterator(DocAccessible* aDocument, const Accessible* aAccessible,
|
||||
LabelFilter aFilter = eAllLabels);
|
||||
|
||||
virtual ~HTMLLabelIterator() { }
|
||||
|
||||
/**
|
||||
* Return next label accessible associated with the given element.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
HTMLLabelIterator();
|
||||
HTMLLabelIterator(const HTMLLabelIterator&);
|
||||
HTMLLabelIterator& operator = (const HTMLLabelIterator&);
|
||||
|
||||
bool IsLabel(Accessible* aLabel);
|
||||
|
||||
RelatedAccIterator mRelIter;
|
||||
// XXX: replace it on weak reference (bug 678429), it's safe to use raw
|
||||
// pointer now because iterators life cycle is short.
|
||||
const Accessible* mAcc;
|
||||
LabelFilter mLabelFilter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate through HTML outputs associated with the given element.
|
||||
*/
|
||||
class HTMLOutputIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
HTMLOutputIterator(DocAccessible* aDocument, nsIContent* aElement);
|
||||
virtual ~HTMLOutputIterator() { }
|
||||
|
||||
/**
|
||||
* Return next output accessible associated with the given element.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
HTMLOutputIterator();
|
||||
HTMLOutputIterator(const HTMLOutputIterator&);
|
||||
HTMLOutputIterator& operator = (const HTMLOutputIterator&);
|
||||
|
||||
RelatedAccIterator mRelIter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate through XUL labels associated with the given element.
|
||||
*/
|
||||
class XULLabelIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
XULLabelIterator(DocAccessible* aDocument, nsIContent* aElement);
|
||||
virtual ~XULLabelIterator() { }
|
||||
|
||||
/**
|
||||
* Return next label accessible associated with the given element.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
XULLabelIterator();
|
||||
XULLabelIterator(const XULLabelIterator&);
|
||||
XULLabelIterator& operator = (const XULLabelIterator&);
|
||||
|
||||
RelatedAccIterator mRelIter;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate through XUL descriptions associated with the given element.
|
||||
*/
|
||||
class XULDescriptionIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
XULDescriptionIterator(DocAccessible* aDocument, nsIContent* aElement);
|
||||
virtual ~XULDescriptionIterator() { }
|
||||
|
||||
/**
|
||||
* Return next description accessible associated with the given element.
|
||||
*/
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
XULDescriptionIterator();
|
||||
XULDescriptionIterator(const XULDescriptionIterator&);
|
||||
XULDescriptionIterator& operator = (const XULDescriptionIterator&);
|
||||
|
||||
RelatedAccIterator mRelIter;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to iterate through IDs, elements or accessibles pointed by IDRefs
|
||||
* attribute. Note, any method used to iterate through IDs, elements, or
|
||||
* accessibles moves iterator to next position.
|
||||
*/
|
||||
class IDRefsIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
IDRefsIterator(DocAccessible* aDoc, nsIContent* aContent,
|
||||
nsIAtom* aIDRefsAttr);
|
||||
virtual ~IDRefsIterator() { }
|
||||
|
||||
/**
|
||||
* Return next ID.
|
||||
*/
|
||||
const nsDependentSubstring NextID();
|
||||
|
||||
/**
|
||||
* Return next element.
|
||||
*/
|
||||
nsIContent* NextElem();
|
||||
|
||||
/**
|
||||
* Return the element with the given ID.
|
||||
*/
|
||||
nsIContent* GetElem(const nsDependentSubstring& aID);
|
||||
|
||||
// AccIterable
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
IDRefsIterator();
|
||||
IDRefsIterator(const IDRefsIterator&);
|
||||
IDRefsIterator operator = (const IDRefsIterator&);
|
||||
|
||||
nsString mIDs;
|
||||
nsIContent* mContent;
|
||||
DocAccessible* mDoc;
|
||||
nsAString::index_type mCurrIdx;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Iterator that points to a single accessible returning it on the first call
|
||||
* to Next().
|
||||
*/
|
||||
class SingleAccIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
explicit SingleAccIterator(Accessible* aTarget): mAcc(aTarget) { }
|
||||
virtual ~SingleAccIterator() { }
|
||||
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
SingleAccIterator();
|
||||
SingleAccIterator(const SingleAccIterator&);
|
||||
SingleAccIterator& operator = (const SingleAccIterator&);
|
||||
|
||||
RefPtr<Accessible> mAcc;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate items of the given item container.
|
||||
*/
|
||||
class ItemIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
explicit ItemIterator(Accessible* aItemContainer) :
|
||||
mContainer(aItemContainer), mAnchor(nullptr) { }
|
||||
virtual ~ItemIterator() { }
|
||||
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
ItemIterator() = delete;
|
||||
ItemIterator(const ItemIterator&) = delete;
|
||||
ItemIterator& operator = (const ItemIterator&) = delete;
|
||||
|
||||
Accessible* mContainer;
|
||||
Accessible* mAnchor;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Used to iterate through XUL tree items of the same level.
|
||||
*/
|
||||
class XULTreeItemIterator : public AccIterable
|
||||
{
|
||||
public:
|
||||
XULTreeItemIterator(XULTreeAccessible* aXULTree, nsITreeView* aTreeView,
|
||||
int32_t aRowIdx);
|
||||
virtual ~XULTreeItemIterator() { }
|
||||
|
||||
virtual Accessible* Next() override;
|
||||
|
||||
private:
|
||||
XULTreeItemIterator() = delete;
|
||||
XULTreeItemIterator(const XULTreeItemIterator&) = delete;
|
||||
XULTreeItemIterator& operator = (const XULTreeItemIterator&) = delete;
|
||||
|
||||
XULTreeAccessible* mXULTree;
|
||||
nsITreeView* mTreeView;
|
||||
int32_t mRowCount;
|
||||
int32_t mContainerLevel;
|
||||
int32_t mCurrRowIdx;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
94
accessible/base/AccTypes.h
Normal file
94
accessible/base/AccTypes.h
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/* -*- 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_AccTypes_h
|
||||
#define mozilla_a11y_AccTypes_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Accessible object types. Each accessible class can have own type.
|
||||
*/
|
||||
enum AccType {
|
||||
/**
|
||||
* This set of types is used for accessible creation, keep them together in
|
||||
* alphabetical order since they are used in switch statement.
|
||||
*/
|
||||
eNoType,
|
||||
eHTMLBRType,
|
||||
eHTMLButtonType,
|
||||
eHTMLCanvasType,
|
||||
eHTMLCaptionType,
|
||||
eHTMLCheckboxType,
|
||||
eHTMLComboboxType,
|
||||
eHTMLFileInputType,
|
||||
eHTMLGroupboxType,
|
||||
eHTMLHRType,
|
||||
eHTMLImageMapType,
|
||||
eHTMLLiType,
|
||||
eHTMLSelectListType,
|
||||
eHTMLMediaType,
|
||||
eHTMLRadioButtonType,
|
||||
eHTMLRangeType,
|
||||
eHTMLSpinnerType,
|
||||
eHTMLTableType,
|
||||
eHTMLTableCellType,
|
||||
eHTMLTableRowType,
|
||||
eHTMLTextFieldType,
|
||||
eHyperTextType,
|
||||
eImageType,
|
||||
eOuterDocType,
|
||||
ePluginType,
|
||||
eTextLeafType,
|
||||
|
||||
/**
|
||||
* Other accessible types.
|
||||
*/
|
||||
eApplicationType,
|
||||
eHTMLOptGroupType,
|
||||
eImageMapType,
|
||||
eMenuPopupType,
|
||||
eProxyType,
|
||||
eProgressType,
|
||||
eRootType,
|
||||
eXULLabelType,
|
||||
eXULListItemType,
|
||||
eXULTabpanelsType,
|
||||
eXULTreeType,
|
||||
|
||||
eLastAccType = eXULTreeType
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic accessible type, different accessible classes can share the same
|
||||
* type, the same accessible class can have several types.
|
||||
*/
|
||||
enum AccGenericType {
|
||||
eAlert = 1 << 0,
|
||||
eAutoComplete = 1 << 1,
|
||||
eAutoCompletePopup = 1 << 2,
|
||||
eButton = 1 << 3,
|
||||
eCombobox = 1 << 4,
|
||||
eDocument = 1 << 5,
|
||||
eHyperText = 1 << 6,
|
||||
eLandmark = 1 << 7,
|
||||
eList = 1 << 8,
|
||||
eListControl = 1 << 9,
|
||||
eMenuButton = 1 << 10,
|
||||
eSelect = 1 << 11,
|
||||
eTable = 1 << 12,
|
||||
eTableCell = 1 << 13,
|
||||
eTableRow = 1 << 14,
|
||||
eText = 1 << 15,
|
||||
|
||||
eLastAccGenericType = eText
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_AccTypes_h
|
||||
27
accessible/base/AccessibleOrProxy.cpp
Normal file
27
accessible/base/AccessibleOrProxy.cpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- 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 "AccessibleOrProxy.h"
|
||||
|
||||
AccessibleOrProxy
|
||||
AccessibleOrProxy::Parent() const
|
||||
{
|
||||
if (IsAccessible()) {
|
||||
return AsAccessible()->Parent();
|
||||
}
|
||||
|
||||
ProxyAccessible* proxy = AsProxy();
|
||||
if (!proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* parent = proxy->Parent()) {
|
||||
return parent;
|
||||
}
|
||||
|
||||
// Otherwise this should be the proxy for the tab's top level document.
|
||||
return proxy->OuterDocOfRemoteBrowser();
|
||||
}
|
||||
123
accessible/base/AccessibleOrProxy.h
Normal file
123
accessible/base/AccessibleOrProxy.h
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/* -*- 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_AccessibleOrProxy_h
|
||||
#define mozilla_a11y_AccessibleOrProxy_h
|
||||
|
||||
#include "mozilla/a11y/Accessible.h"
|
||||
#include "mozilla/a11y/ProxyAccessible.h"
|
||||
#include "mozilla/a11y/Role.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* This class stores an Accessible* or a ProxyAccessible* in a safe manner
|
||||
* with size sizeof(void*).
|
||||
*/
|
||||
class AccessibleOrProxy
|
||||
{
|
||||
public:
|
||||
MOZ_IMPLICIT AccessibleOrProxy(Accessible* aAcc) :
|
||||
mBits(reinterpret_cast<uintptr_t>(aAcc)) {}
|
||||
MOZ_IMPLICIT AccessibleOrProxy(ProxyAccessible* aProxy) :
|
||||
mBits(aProxy ? (reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY) : 0) {}
|
||||
MOZ_IMPLICIT AccessibleOrProxy(decltype(nullptr)) : mBits(0) {}
|
||||
|
||||
bool IsProxy() const { return mBits & IS_PROXY; }
|
||||
ProxyAccessible* AsProxy() const
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return reinterpret_cast<ProxyAccessible*>(mBits & ~IS_PROXY);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsAccessible() const { return !IsProxy(); }
|
||||
Accessible* AsAccessible() const
|
||||
{
|
||||
if (IsAccessible()) {
|
||||
return reinterpret_cast<Accessible*>(mBits);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool IsNull() const { return mBits == 0; }
|
||||
|
||||
uint32_t ChildCount() const
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return AsProxy()->ChildrenCount();
|
||||
}
|
||||
|
||||
return AsAccessible()->ChildCount();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the child object either an accessible or a proxied accessible at
|
||||
* the given index.
|
||||
*/
|
||||
AccessibleOrProxy ChildAt(uint32_t aIdx)
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return AsProxy()->ChildAt(aIdx);
|
||||
}
|
||||
|
||||
return AsAccessible()->GetChildAt(aIdx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first child object.
|
||||
*/
|
||||
AccessibleOrProxy FirstChild()
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return AsProxy()->FirstChild();
|
||||
}
|
||||
|
||||
return AsAccessible()->FirstChild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first child object.
|
||||
*/
|
||||
AccessibleOrProxy LastChild()
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return AsProxy()->LastChild();
|
||||
}
|
||||
|
||||
return AsAccessible()->LastChild();
|
||||
}
|
||||
|
||||
role Role() const
|
||||
{
|
||||
if (IsProxy()) {
|
||||
return AsProxy()->Role();
|
||||
}
|
||||
|
||||
return AsAccessible()->Role();
|
||||
}
|
||||
|
||||
AccessibleOrProxy Parent() const;
|
||||
|
||||
// XXX these are implementation details that ideally would not be exposed.
|
||||
uintptr_t Bits() const { return mBits; }
|
||||
void SetBits(uintptr_t aBits) { mBits = aBits; }
|
||||
|
||||
private:
|
||||
uintptr_t mBits;
|
||||
static const uintptr_t IS_PROXY = 0x1;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
26
accessible/base/Asserts.cpp
Normal file
26
accessible/base/Asserts.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* -*- 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 "nsIAccessibleRelation.h"
|
||||
#include "nsIAccessibleRole.h"
|
||||
#include "RelationType.h"
|
||||
#include "Role.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
|
||||
static_assert(static_cast<uint32_t>(roles::geckoRole) \
|
||||
== static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \
|
||||
"internal and xpcom roles differ!");
|
||||
#include "RoleMap.h"
|
||||
#undef ROLE
|
||||
|
||||
#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \
|
||||
static_assert(static_cast<uint32_t>(RelationType::geckoType) \
|
||||
== static_cast<uint32_t>(nsIAccessibleRelation::RELATION_ ## geckoType), \
|
||||
"internal and xpcom relations differ!");
|
||||
#include "RelationTypeMap.h"
|
||||
#undef RELATIONTYPE
|
||||
594
accessible/base/DocManager.cpp
Normal file
594
accessible/base/DocManager.cpp
Normal file
|
|
@ -0,0 +1,594 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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 "DocManager.h"
|
||||
|
||||
#include "ApplicationAccessible.h"
|
||||
#include "ARIAMap.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "DocAccessibleParent.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "Platform.h"
|
||||
#include "RootAccessibleWrap.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
|
||||
#include "nsCURILoader.h"
|
||||
#include "nsDocShellLoadTypes.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments;
|
||||
nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
|
||||
DocManager::sRemoteXPCDocumentCache = nullptr;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DocManager::DocManager()
|
||||
: mDocAccessibleCache(2), mXPCDocumentCache(0)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager public
|
||||
|
||||
DocAccessible*
|
||||
DocManager::GetDocAccessible(nsIDocument* aDocument)
|
||||
{
|
||||
if (!aDocument)
|
||||
return nullptr;
|
||||
|
||||
DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
|
||||
if (docAcc)
|
||||
return docAcc;
|
||||
|
||||
return CreateDocOrRootAccessible(aDocument);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
DocManager::FindAccessibleInCache(nsINode* aNode) const
|
||||
{
|
||||
for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
DocAccessible* docAccessible = iter.UserData();
|
||||
NS_ASSERTION(docAccessible,
|
||||
"No doc accessible for the object in doc accessible cache!");
|
||||
|
||||
if (docAccessible) {
|
||||
Accessible* accessible = docAccessible->GetAccessible(aNode);
|
||||
if (accessible) {
|
||||
return accessible;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::NotifyOfDocumentShutdown(DocAccessible* aDocument,
|
||||
nsIDocument* aDOMDocument)
|
||||
{
|
||||
// We need to remove listeners in both cases, when document is being shutdown
|
||||
// or when accessibility service is being shut down as well.
|
||||
RemoveListeners(aDOMDocument);
|
||||
|
||||
// Document will already be removed when accessibility service is shutting
|
||||
// down so we do not need to remove it twice.
|
||||
if (nsAccessibilityService::IsShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
|
||||
if (xpcDoc) {
|
||||
xpcDoc->Shutdown();
|
||||
mXPCDocumentCache.Remove(aDocument);
|
||||
}
|
||||
|
||||
mDocAccessibleCache.Remove(aDOMDocument);
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::NotifyOfRemoteDocShutdown(DocAccessibleParent* aDoc)
|
||||
{
|
||||
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
|
||||
if (doc) {
|
||||
doc->Shutdown();
|
||||
sRemoteXPCDocumentCache->Remove(aDoc);
|
||||
}
|
||||
}
|
||||
|
||||
xpcAccessibleDocument*
|
||||
DocManager::GetXPCDocument(DocAccessible* aDocument)
|
||||
{
|
||||
if (!aDocument)
|
||||
return nullptr;
|
||||
|
||||
xpcAccessibleDocument* xpcDoc = mXPCDocumentCache.GetWeak(aDocument);
|
||||
if (!xpcDoc) {
|
||||
xpcDoc = new xpcAccessibleDocument(aDocument);
|
||||
mXPCDocumentCache.Put(aDocument, xpcDoc);
|
||||
}
|
||||
return xpcDoc;
|
||||
}
|
||||
|
||||
xpcAccessibleDocument*
|
||||
DocManager::GetXPCDocument(DocAccessibleParent* aDoc)
|
||||
{
|
||||
xpcAccessibleDocument* doc = GetCachedXPCDocument(aDoc);
|
||||
if (doc) {
|
||||
return doc;
|
||||
}
|
||||
|
||||
if (!sRemoteXPCDocumentCache) {
|
||||
sRemoteXPCDocumentCache =
|
||||
new nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>;
|
||||
}
|
||||
|
||||
doc =
|
||||
new xpcAccessibleDocument(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
|
||||
sRemoteXPCDocumentCache->Put(aDoc, doc);
|
||||
|
||||
return doc;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool
|
||||
DocManager::IsProcessingRefreshDriverNotification() const
|
||||
{
|
||||
for (auto iter = mDocAccessibleCache.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
DocAccessible* docAccessible = iter.UserData();
|
||||
NS_ASSERTION(docAccessible,
|
||||
"No doc accessible for the object in doc accessible cache!");
|
||||
|
||||
if (docAccessible && docAccessible->mNotificationController &&
|
||||
docAccessible->mNotificationController->IsUpdating()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager protected
|
||||
|
||||
bool
|
||||
DocManager::Init()
|
||||
{
|
||||
nsCOMPtr<nsIWebProgress> progress =
|
||||
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
||||
|
||||
if (!progress)
|
||||
return false;
|
||||
|
||||
progress->AddProgressListener(static_cast<nsIWebProgressListener*>(this),
|
||||
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::Shutdown()
|
||||
{
|
||||
nsCOMPtr<nsIWebProgress> progress =
|
||||
do_GetService(NS_DOCUMENTLOADER_SERVICE_CONTRACTID);
|
||||
|
||||
if (progress)
|
||||
progress->RemoveProgressListener(static_cast<nsIWebProgressListener*>(this));
|
||||
|
||||
ClearDocCache();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsISupports
|
||||
|
||||
NS_IMPL_ISUPPORTS(DocManager,
|
||||
nsIWebProgressListener,
|
||||
nsIDOMEventListener,
|
||||
nsISupportsWeakReference)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIWebProgressListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::OnStateChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest, uint32_t aStateFlags,
|
||||
nsresult aStatus)
|
||||
{
|
||||
NS_ASSERTION(aStateFlags & STATE_IS_DOCUMENT, "Other notifications excluded");
|
||||
|
||||
if (nsAccessibilityService::IsShutdown() || !aWebProgress ||
|
||||
(aStateFlags & (STATE_START | STATE_STOP)) == 0)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<mozIDOMWindowProxy> DOMWindow;
|
||||
aWebProgress->GetDOMWindow(getter_AddRefs(DOMWindow));
|
||||
NS_ENSURE_STATE(DOMWindow);
|
||||
|
||||
nsPIDOMWindowOuter* piWindow = nsPIDOMWindowOuter::From(DOMWindow);
|
||||
MOZ_ASSERT(piWindow);
|
||||
|
||||
nsCOMPtr<nsIDocument> document = piWindow->GetDoc();
|
||||
NS_ENSURE_STATE(document);
|
||||
|
||||
// Document was loaded.
|
||||
if (aStateFlags & STATE_STOP) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocLoad))
|
||||
logging::DocLoad("document loaded", aWebProgress, aRequest, aStateFlags);
|
||||
#endif
|
||||
|
||||
// Figure out an event type to notify the document has been loaded.
|
||||
uint32_t eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_STOPPED;
|
||||
|
||||
// Some XUL documents get start state and then stop state with failure
|
||||
// status when everything is ok. Fire document load complete event in this
|
||||
// case.
|
||||
if (NS_SUCCEEDED(aStatus) || !nsCoreUtils::IsContentDocument(document))
|
||||
eventType = nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE;
|
||||
|
||||
// If end consumer has been retargeted for loaded content then do not fire
|
||||
// any event because it means no new document has been loaded, for example,
|
||||
// it happens when user clicks on file link.
|
||||
if (aRequest) {
|
||||
uint32_t loadFlags = 0;
|
||||
aRequest->GetLoadFlags(&loadFlags);
|
||||
if (loadFlags & nsIChannel::LOAD_RETARGETED_DOCUMENT_URI)
|
||||
eventType = 0;
|
||||
}
|
||||
|
||||
HandleDOMDocumentLoad(document, eventType);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Document loading was started.
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocLoad))
|
||||
logging::DocLoad("start document loading", aWebProgress, aRequest, aStateFlags);
|
||||
#endif
|
||||
|
||||
DocAccessible* docAcc = GetExistingDocAccessible(document);
|
||||
if (!docAcc)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIWebNavigation> webNav(do_GetInterface(DOMWindow));
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(webNav));
|
||||
NS_ENSURE_STATE(docShell);
|
||||
|
||||
bool isReloading = false;
|
||||
uint32_t loadType;
|
||||
docShell->GetLoadType(&loadType);
|
||||
if (loadType == LOAD_RELOAD_NORMAL ||
|
||||
loadType == LOAD_RELOAD_BYPASS_CACHE ||
|
||||
loadType == LOAD_RELOAD_BYPASS_PROXY ||
|
||||
loadType == LOAD_RELOAD_BYPASS_PROXY_AND_CACHE ||
|
||||
loadType == LOAD_RELOAD_ALLOW_MIXED_CONTENT) {
|
||||
isReloading = true;
|
||||
}
|
||||
|
||||
docAcc->NotifyOfLoading(isReloading);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::OnProgressChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
int32_t aCurSelfProgress,
|
||||
int32_t aMaxSelfProgress,
|
||||
int32_t aCurTotalProgress,
|
||||
int32_t aMaxTotalProgress)
|
||||
{
|
||||
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::OnLocationChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest, nsIURI* aLocation,
|
||||
uint32_t aFlags)
|
||||
{
|
||||
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::OnStatusChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest, nsresult aStatus,
|
||||
const char16_t* aMessage)
|
||||
{
|
||||
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::OnSecurityChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
uint32_t aState)
|
||||
{
|
||||
NS_NOTREACHED("notification excluded in AddProgressListener(...)");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsIDOMEventListener
|
||||
|
||||
NS_IMETHODIMP
|
||||
DocManager::HandleEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
nsAutoString type;
|
||||
aEvent->GetType(type);
|
||||
|
||||
nsCOMPtr<nsIDocument> document =
|
||||
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
|
||||
NS_ASSERTION(document, "pagehide or DOMContentLoaded for non document!");
|
||||
if (!document)
|
||||
return NS_OK;
|
||||
|
||||
if (type.EqualsLiteral("pagehide")) {
|
||||
// 'pagehide' event is registered on every DOM document we create an
|
||||
// accessible for, process the event for the target. This document
|
||||
// accessible and all its sub document accessible are shutdown as result of
|
||||
// processing.
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocDestroy))
|
||||
logging::DocDestroy("received 'pagehide' event", document);
|
||||
#endif
|
||||
|
||||
// Shutdown this one and sub document accessibles.
|
||||
|
||||
// We're allowed to not remove listeners when accessible document is
|
||||
// shutdown since we don't keep strong reference on chrome event target and
|
||||
// listeners are removed automatically when chrome event target goes away.
|
||||
DocAccessible* docAccessible = GetExistingDocAccessible(document);
|
||||
if (docAccessible)
|
||||
docAccessible->Shutdown();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// XXX: handle error pages loading separately since they get neither
|
||||
// webprogress notifications nor 'pageshow' event.
|
||||
if (type.EqualsLiteral("DOMContentLoaded") &&
|
||||
nsCoreUtils::IsErrorPage(document)) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocLoad))
|
||||
logging::DocLoad("handled 'DOMContentLoaded' event", document);
|
||||
#endif
|
||||
|
||||
HandleDOMDocumentLoad(document,
|
||||
nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager private
|
||||
|
||||
void
|
||||
DocManager::HandleDOMDocumentLoad(nsIDocument* aDocument,
|
||||
uint32_t aLoadEventType)
|
||||
{
|
||||
// Document accessible can be created before we were notified the DOM document
|
||||
// was loaded completely. However if it's not created yet then create it.
|
||||
DocAccessible* docAcc = GetExistingDocAccessible(aDocument);
|
||||
if (!docAcc) {
|
||||
docAcc = CreateDocOrRootAccessible(aDocument);
|
||||
if (!docAcc)
|
||||
return;
|
||||
}
|
||||
|
||||
docAcc->NotifyOfLoad(aLoadEventType);
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::AddListeners(nsIDocument* aDocument,
|
||||
bool aAddDOMContentLoadedListener)
|
||||
{
|
||||
nsPIDOMWindowOuter* window = aDocument->GetWindow();
|
||||
EventTarget* target = window->GetChromeEventHandler();
|
||||
EventListenerManager* elm = target->GetOrCreateListenerManager();
|
||||
elm->AddEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
|
||||
TrustedEventsAtCapture());
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocCreate))
|
||||
logging::Text("added 'pagehide' listener");
|
||||
#endif
|
||||
|
||||
if (aAddDOMContentLoadedListener) {
|
||||
elm->AddEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
|
||||
TrustedEventsAtCapture());
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocCreate))
|
||||
logging::Text("added 'DOMContentLoaded' listener");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::RemoveListeners(nsIDocument* aDocument)
|
||||
{
|
||||
nsPIDOMWindowOuter* window = aDocument->GetWindow();
|
||||
if (!window)
|
||||
return;
|
||||
|
||||
EventTarget* target = window->GetChromeEventHandler();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
EventListenerManager* elm = target->GetOrCreateListenerManager();
|
||||
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("pagehide"),
|
||||
TrustedEventsAtCapture());
|
||||
|
||||
elm->RemoveEventListenerByType(this, NS_LITERAL_STRING("DOMContentLoaded"),
|
||||
TrustedEventsAtCapture());
|
||||
}
|
||||
|
||||
DocAccessible*
|
||||
DocManager::CreateDocOrRootAccessible(nsIDocument* aDocument)
|
||||
{
|
||||
// Ignore hiding, resource documents and documents without docshell.
|
||||
if (!aDocument->IsVisibleConsideringAncestors() ||
|
||||
aDocument->IsResourceDoc() || !aDocument->IsActive())
|
||||
return nullptr;
|
||||
|
||||
// Ignore documents without presshell and not having root frame.
|
||||
nsIPresShell* presShell = aDocument->GetShell();
|
||||
if (!presShell || presShell->IsDestroying())
|
||||
return nullptr;
|
||||
|
||||
bool isRootDoc = nsCoreUtils::IsRootDocument(aDocument);
|
||||
|
||||
DocAccessible* parentDocAcc = nullptr;
|
||||
if (!isRootDoc) {
|
||||
// XXXaaronl: ideally we would traverse the presshell chain. Since there's
|
||||
// no easy way to do that, we cheat and use the document hierarchy.
|
||||
parentDocAcc = GetDocAccessible(aDocument->GetParentDocument());
|
||||
NS_ASSERTION(parentDocAcc,
|
||||
"Can't create an accessible for the document!");
|
||||
if (!parentDocAcc)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We only create root accessibles for the true root, otherwise create a
|
||||
// doc accessible.
|
||||
RefPtr<DocAccessible> docAcc = isRootDoc ?
|
||||
new RootAccessibleWrap(aDocument, presShell) :
|
||||
new DocAccessibleWrap(aDocument, presShell);
|
||||
|
||||
// Cache the document accessible into document cache.
|
||||
mDocAccessibleCache.Put(aDocument, docAcc);
|
||||
|
||||
// Initialize the document accessible.
|
||||
docAcc->Init();
|
||||
|
||||
// Bind the document to the tree.
|
||||
if (isRootDoc) {
|
||||
if (!ApplicationAcc()->AppendChild(docAcc)) {
|
||||
docAcc->Shutdown();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Fire reorder event to notify new accessible document has been attached to
|
||||
// the tree. The reorder event is delivered after the document tree is
|
||||
// constructed because event processing and tree construction are done by
|
||||
// the same document.
|
||||
// Note: don't use AccReorderEvent to avoid coalsecense and special reorder
|
||||
// events processing.
|
||||
docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
|
||||
ApplicationAcc());
|
||||
|
||||
if (IPCAccessibilityActive()) {
|
||||
nsIDocShell* docShell = aDocument->GetDocShell();
|
||||
if (docShell) {
|
||||
nsCOMPtr<nsITabChild> tabChild = docShell->GetTabChild();
|
||||
|
||||
// XXX We may need to handle the case that we don't have a tab child
|
||||
// differently. It may be that this will cause us to fail to notify
|
||||
// the parent process about important accessible documents.
|
||||
if (tabChild) {
|
||||
DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
|
||||
docAcc->SetIPCDoc(ipcDoc);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
IAccessibleHolder holder(CreateHolderFromAccessible(docAcc));
|
||||
#endif
|
||||
|
||||
static_cast<TabChild*>(tabChild.get())->
|
||||
SendPDocAccessibleConstructor(ipcDoc, nullptr, 0,
|
||||
#if defined(XP_WIN)
|
||||
AccessibleWrap::GetChildIDFor(docAcc),
|
||||
holder
|
||||
#else
|
||||
0, 0
|
||||
#endif
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parentDocAcc->BindChildDocument(docAcc);
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eDocCreate)) {
|
||||
logging::DocCreate("document creation finished", aDocument);
|
||||
logging::Stack();
|
||||
}
|
||||
#endif
|
||||
|
||||
AddListeners(aDocument, isRootDoc);
|
||||
return docAcc;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// DocManager static
|
||||
|
||||
void
|
||||
DocManager::ClearDocCache()
|
||||
{
|
||||
while (mDocAccessibleCache.Count() > 0) {
|
||||
auto iter = mDocAccessibleCache.Iter();
|
||||
MOZ_ASSERT(!iter.Done());
|
||||
DocAccessible* docAcc = iter.UserData();
|
||||
NS_ASSERTION(docAcc,
|
||||
"No doc accessible for the object in doc accessible cache!");
|
||||
if (docAcc) {
|
||||
docAcc->Shutdown();
|
||||
}
|
||||
|
||||
iter.Remove();
|
||||
}
|
||||
|
||||
// Ensure that all xpcom accessible documents are shut down as well.
|
||||
while (mXPCDocumentCache.Count() > 0) {
|
||||
auto iter = mXPCDocumentCache.Iter();
|
||||
MOZ_ASSERT(!iter.Done());
|
||||
xpcAccessibleDocument* xpcDoc = iter.UserData();
|
||||
NS_ASSERTION(xpcDoc, "No xpc doc for the object in xpc doc cache!");
|
||||
|
||||
if (xpcDoc) {
|
||||
xpcDoc->Shutdown();
|
||||
}
|
||||
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DocManager::RemoteDocAdded(DocAccessibleParent* aDoc)
|
||||
{
|
||||
if (!sRemoteDocuments) {
|
||||
sRemoteDocuments = new nsTArray<DocAccessibleParent*>;
|
||||
ClearOnShutdown(&sRemoteDocuments);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!sRemoteDocuments->Contains(aDoc),
|
||||
"How did we already have the doc!");
|
||||
sRemoteDocuments->AppendElement(aDoc);
|
||||
ProxyCreated(aDoc, Interfaces::DOCUMENT | Interfaces::HYPERTEXT);
|
||||
}
|
||||
189
accessible/base/DocManager.h
Normal file
189
accessible/base/DocManager.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/* 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_a11_DocManager_h_
|
||||
#define mozilla_a11_DocManager_h_
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsRefPtrHashtable.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class DocAccessible;
|
||||
class xpcAccessibleDocument;
|
||||
class DocAccessibleParent;
|
||||
|
||||
/**
|
||||
* Manage the document accessible life cycle.
|
||||
*/
|
||||
class DocManager : public nsIWebProgressListener,
|
||||
public nsIDOMEventListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIWEBPROGRESSLISTENER
|
||||
NS_DECL_NSIDOMEVENTLISTENER
|
||||
|
||||
/**
|
||||
* Return document accessible for the given DOM node.
|
||||
*/
|
||||
DocAccessible* GetDocAccessible(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Return document accessible for the given presshell.
|
||||
*/
|
||||
DocAccessible* GetDocAccessible(const nsIPresShell* aPresShell)
|
||||
{
|
||||
if (!aPresShell)
|
||||
return nullptr;
|
||||
|
||||
DocAccessible* doc = aPresShell->GetDocAccessible();
|
||||
if (doc)
|
||||
return doc;
|
||||
|
||||
return GetDocAccessible(aPresShell->GetDocument());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search through all document accessibles for an accessible with the given
|
||||
* unique id.
|
||||
*/
|
||||
Accessible* FindAccessibleInCache(nsINode* aNode) const;
|
||||
|
||||
/**
|
||||
* Called by document accessible when it gets shutdown.
|
||||
*/
|
||||
void NotifyOfDocumentShutdown(DocAccessible* aDocument,
|
||||
nsIDocument* aDOMDocument);
|
||||
|
||||
/**
|
||||
* Return XPCOM accessible document.
|
||||
*/
|
||||
xpcAccessibleDocument* GetXPCDocument(DocAccessible* aDocument);
|
||||
xpcAccessibleDocument* GetCachedXPCDocument(DocAccessible* aDocument) const
|
||||
{ return mXPCDocumentCache.GetWeak(aDocument); }
|
||||
|
||||
/*
|
||||
* Notification that a top level document in a content process has gone away.
|
||||
*/
|
||||
static void RemoteDocShutdown(DocAccessibleParent* aDoc)
|
||||
{
|
||||
DebugOnly<bool> result = sRemoteDocuments->RemoveElement(aDoc);
|
||||
MOZ_ASSERT(result, "Why didn't we find the document!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Notify of a new top level document in a content process.
|
||||
*/
|
||||
static void RemoteDocAdded(DocAccessibleParent* aDoc);
|
||||
|
||||
static const nsTArray<DocAccessibleParent*>* TopLevelRemoteDocs()
|
||||
{ return sRemoteDocuments; }
|
||||
|
||||
/**
|
||||
* Remove the xpc document for a remote document if there is one.
|
||||
*/
|
||||
static void NotifyOfRemoteDocShutdown(DocAccessibleParent* adoc);
|
||||
|
||||
/**
|
||||
* Get a XPC document for a remote document.
|
||||
*/
|
||||
static xpcAccessibleDocument* GetXPCDocument(DocAccessibleParent* aDoc);
|
||||
static xpcAccessibleDocument* GetCachedXPCDocument(const DocAccessibleParent* aDoc)
|
||||
{
|
||||
return sRemoteXPCDocumentCache ? sRemoteXPCDocumentCache->GetWeak(aDoc)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsProcessingRefreshDriverNotification() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
DocManager();
|
||||
virtual ~DocManager() { }
|
||||
|
||||
/**
|
||||
* Initialize the manager.
|
||||
*/
|
||||
bool Init();
|
||||
|
||||
/**
|
||||
* Shutdown the manager.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
DocManager(const DocManager&);
|
||||
DocManager& operator =(const DocManager&);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Create an accessible document if it was't created and fire accessibility
|
||||
* events if needed.
|
||||
*
|
||||
* @param aDocument [in] loaded DOM document
|
||||
* @param aLoadEventType [in] specifies the event type to fire load event,
|
||||
* if 0 then no event is fired
|
||||
*/
|
||||
void HandleDOMDocumentLoad(nsIDocument* aDocument,
|
||||
uint32_t aLoadEventType);
|
||||
|
||||
/**
|
||||
* Add/remove 'pagehide' and 'DOMContentLoaded' event listeners.
|
||||
*/
|
||||
void AddListeners(nsIDocument *aDocument, bool aAddPageShowListener);
|
||||
void RemoveListeners(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Create document or root accessible.
|
||||
*/
|
||||
DocAccessible* CreateDocOrRootAccessible(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Clear the cache and shutdown the document accessibles.
|
||||
*/
|
||||
void ClearDocCache();
|
||||
|
||||
typedef nsRefPtrHashtable<nsPtrHashKey<const nsIDocument>, DocAccessible>
|
||||
DocAccessibleHashtable;
|
||||
DocAccessibleHashtable mDocAccessibleCache;
|
||||
|
||||
typedef nsRefPtrHashtable<nsPtrHashKey<const DocAccessible>, xpcAccessibleDocument>
|
||||
XPCDocumentHashtable;
|
||||
XPCDocumentHashtable mXPCDocumentCache;
|
||||
static nsRefPtrHashtable<nsPtrHashKey<const DocAccessibleParent>, xpcAccessibleDocument>*
|
||||
sRemoteXPCDocumentCache;
|
||||
|
||||
/*
|
||||
* The list of remote top level documents.
|
||||
*/
|
||||
static StaticAutoPtr<nsTArray<DocAccessibleParent*>> sRemoteDocuments;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the existing document accessible for the document if any.
|
||||
* Note this returns the doc accessible for the primary pres shell if there is
|
||||
* more than one.
|
||||
*/
|
||||
inline DocAccessible*
|
||||
GetExistingDocAccessible(const nsIDocument* aDocument)
|
||||
{
|
||||
nsIPresShell* ps = aDocument->GetShell();
|
||||
return ps ? ps->GetDocAccessible() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11_DocManager_h_
|
||||
81
accessible/base/EmbeddedObjCollector.cpp
Normal file
81
accessible/base/EmbeddedObjCollector.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/* 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 "EmbeddedObjCollector.h"
|
||||
|
||||
#include "Accessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
uint32_t
|
||||
EmbeddedObjCollector::Count()
|
||||
{
|
||||
EnsureNGetIndex(nullptr);
|
||||
return mObjects.Length();
|
||||
}
|
||||
|
||||
Accessible*
|
||||
EmbeddedObjCollector::GetAccessibleAt(uint32_t aIndex)
|
||||
{
|
||||
Accessible* accessible = mObjects.SafeElementAt(aIndex, nullptr);
|
||||
if (accessible)
|
||||
return accessible;
|
||||
|
||||
return EnsureNGetObject(aIndex);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
EmbeddedObjCollector::EnsureNGetObject(uint32_t aIndex)
|
||||
{
|
||||
uint32_t childCount = mRoot->ChildCount();
|
||||
while (mRootChildIdx < childCount) {
|
||||
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
|
||||
if (child->IsText())
|
||||
continue;
|
||||
|
||||
AppendObject(child);
|
||||
if (mObjects.Length() - 1 == aIndex)
|
||||
return mObjects[aIndex];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int32_t
|
||||
EmbeddedObjCollector::EnsureNGetIndex(Accessible* aAccessible)
|
||||
{
|
||||
uint32_t childCount = mRoot->ChildCount();
|
||||
while (mRootChildIdx < childCount) {
|
||||
Accessible* child = mRoot->GetChildAt(mRootChildIdx++);
|
||||
if (child->IsText())
|
||||
continue;
|
||||
|
||||
AppendObject(child);
|
||||
if (child == aAccessible)
|
||||
return mObjects.Length() - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t
|
||||
EmbeddedObjCollector::GetIndexAt(Accessible* aAccessible)
|
||||
{
|
||||
if (aAccessible->mParent != mRoot)
|
||||
return -1;
|
||||
|
||||
MOZ_ASSERT(!aAccessible->IsProxy());
|
||||
if (aAccessible->mInt.mIndexOfEmbeddedChild != -1)
|
||||
return aAccessible->mInt.mIndexOfEmbeddedChild;
|
||||
|
||||
return !aAccessible->IsText() ? EnsureNGetIndex(aAccessible) : -1;
|
||||
}
|
||||
|
||||
void
|
||||
EmbeddedObjCollector::AppendObject(Accessible* aAccessible)
|
||||
{
|
||||
MOZ_ASSERT(!aAccessible->IsProxy());
|
||||
aAccessible->mInt.mIndexOfEmbeddedChild = mObjects.Length();
|
||||
mObjects.AppendElement(aAccessible);
|
||||
}
|
||||
69
accessible/base/EmbeddedObjCollector.h
Normal file
69
accessible/base/EmbeddedObjCollector.h
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/* 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_EmbeddedObjCollector_h__
|
||||
#define mozilla_a11y_EmbeddedObjCollector_h__
|
||||
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
|
||||
/**
|
||||
* Collect embedded objects. Provide quick access to accessible by index and
|
||||
* vice versa.
|
||||
*/
|
||||
class EmbeddedObjCollector final
|
||||
{
|
||||
public:
|
||||
~EmbeddedObjCollector() { }
|
||||
|
||||
/**
|
||||
* Return index of the given accessible within the collection.
|
||||
*/
|
||||
int32_t GetIndexAt(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Return accessible count within the collection.
|
||||
*/
|
||||
uint32_t Count();
|
||||
|
||||
/**
|
||||
* Return an accessible from the collection at the given index.
|
||||
*/
|
||||
Accessible* GetAccessibleAt(uint32_t aIndex);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Ensure accessible at the given index is stored and return it.
|
||||
*/
|
||||
Accessible* EnsureNGetObject(uint32_t aIndex);
|
||||
|
||||
/**
|
||||
* Ensure index for the given accessible is stored and return it.
|
||||
*/
|
||||
int32_t EnsureNGetIndex(Accessible* aAccessible);
|
||||
|
||||
// Make sure it's used by Accessible class only.
|
||||
explicit EmbeddedObjCollector(Accessible* aRoot) :
|
||||
mRoot(aRoot), mRootChildIdx(0) {}
|
||||
|
||||
/**
|
||||
* Append the object to collection.
|
||||
*/
|
||||
void AppendObject(Accessible* aAccessible);
|
||||
|
||||
friend class Accessible;
|
||||
|
||||
Accessible* mRoot;
|
||||
uint32_t mRootChildIdx;
|
||||
nsTArray<Accessible*> mObjects;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
344
accessible/base/EventQueue.cpp
Normal file
344
accessible/base/EventQueue.cpp
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "EventQueue.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsTextEquivUtils.h"
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
// Defines the number of selection add/remove events in the queue when they
|
||||
// aren't packed into single selection within event.
|
||||
const unsigned int kSelChangeCountToPack = 5;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool
|
||||
EventQueue::PushEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION((aEvent->mAccessible && aEvent->mAccessible->IsApplication()) ||
|
||||
aEvent->Document() == mDocument,
|
||||
"Queued event belongs to another document!");
|
||||
|
||||
if (!mEvents.AppendElement(aEvent))
|
||||
return false;
|
||||
|
||||
// Filter events.
|
||||
CoalesceEvents();
|
||||
|
||||
if (aEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
(aEvent->mEventType == nsIAccessibleEvent::EVENT_NAME_CHANGE ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_REMOVED ||
|
||||
aEvent->mEventType == nsIAccessibleEvent::EVENT_TEXT_INSERTED)) {
|
||||
PushNameChange(aEvent->mAccessible);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
EventQueue::PushNameChange(Accessible* aTarget)
|
||||
{
|
||||
// Fire name change event on parent given that this event hasn't been
|
||||
// coalesced, the parent's name was calculated from its subtree, and the
|
||||
// subtree was changed.
|
||||
if (aTarget->HasNameDependentParent()) {
|
||||
// Only continue traversing up the tree if it's possible that the parent
|
||||
// accessible's name can depend on this accessible's name.
|
||||
Accessible* parent = aTarget->Parent();
|
||||
while (parent &&
|
||||
nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeIfReqRule)) {
|
||||
// Test possible name dependent parent.
|
||||
if (nsTextEquivUtils::HasNameRule(parent, eNameFromSubtreeRule)) {
|
||||
nsAutoString name;
|
||||
ENameValueFlag nameFlag = parent->Name(name);
|
||||
// If name is obtained from subtree, fire name change event.
|
||||
if (nameFlag == eNameFromSubtree) {
|
||||
RefPtr<AccEvent> nameChangeEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_NAME_CHANGE, parent);
|
||||
return PushEvent(nameChangeEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = parent->Parent();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue: private
|
||||
|
||||
void
|
||||
EventQueue::CoalesceEvents()
|
||||
{
|
||||
NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
|
||||
uint32_t tail = mEvents.Length() - 1;
|
||||
AccEvent* tailEvent = mEvents[tail];
|
||||
|
||||
switch(tailEvent->mEventRule) {
|
||||
case AccEvent::eCoalesceReorder:
|
||||
{
|
||||
DebugOnly<Accessible*> target = tailEvent->mAccessible.get();
|
||||
MOZ_ASSERT(target->IsApplication() ||
|
||||
target->IsOuterDoc() ||
|
||||
target->IsXULTree(),
|
||||
"Only app or outerdoc accessible reorder events are in the queue");
|
||||
MOZ_ASSERT(tailEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER, "only reorder events should be queued");
|
||||
break; // case eCoalesceReorder
|
||||
}
|
||||
|
||||
case AccEvent::eCoalesceOfSameType:
|
||||
{
|
||||
// Coalesce old events by newer event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule) {
|
||||
accEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eCoalesceOfSameType
|
||||
|
||||
case AccEvent::eCoalesceSelectionChange:
|
||||
{
|
||||
AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule == tailEvent->mEventRule) {
|
||||
AccSelChangeEvent* thisSelChangeEvent =
|
||||
downcast_accEvent(thisEvent);
|
||||
|
||||
// Coalesce selection change events within same control.
|
||||
if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
|
||||
CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} break; // eCoalesceSelectionChange
|
||||
|
||||
case AccEvent::eCoalesceStateChange:
|
||||
{
|
||||
// If state change event is duped then ignore previous event. If state
|
||||
// change event is opposite to previous event then no event is emitted
|
||||
// (accessible state wasn't changed).
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
thisEvent->mEventType == tailEvent->mEventType &&
|
||||
thisEvent->mAccessible == tailEvent->mAccessible) {
|
||||
AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
|
||||
AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
|
||||
if (thisSCEvent->mState == tailSCEvent->mState) {
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
break; // eCoalesceStateChange
|
||||
}
|
||||
|
||||
case AccEvent::eCoalesceTextSelChange:
|
||||
{
|
||||
// Coalesce older event by newer event for the same selection or target.
|
||||
// Events for same selection may have different targets and vice versa one
|
||||
// target may be pointed by different selections (for latter see
|
||||
// bug 927159).
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* thisEvent = mEvents[index];
|
||||
if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
|
||||
thisEvent->mEventType == tailEvent->mEventType) {
|
||||
AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
|
||||
AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
|
||||
if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
|
||||
thisEvent->mAccessible == tailEvent->mAccessible)
|
||||
thisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
|
||||
}
|
||||
} break; // eCoalesceTextSelChange
|
||||
|
||||
case AccEvent::eRemoveDupes:
|
||||
{
|
||||
// Check for repeat events, coalesce newly appended event by more older
|
||||
// event.
|
||||
for (uint32_t index = tail - 1; index < tail; index--) {
|
||||
AccEvent* accEvent = mEvents[index];
|
||||
if (accEvent->mEventType == tailEvent->mEventType &&
|
||||
accEvent->mEventRule == tailEvent->mEventRule &&
|
||||
accEvent->mAccessible == tailEvent->mAccessible) {
|
||||
tailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break; // case eRemoveDupes
|
||||
|
||||
default:
|
||||
break; // case eAllowDupes, eDoNotEmit
|
||||
} // switch
|
||||
}
|
||||
|
||||
void
|
||||
EventQueue::CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex)
|
||||
{
|
||||
aTailEvent->mPreceedingCount = aThisEvent->mPreceedingCount + 1;
|
||||
|
||||
// Pack all preceding events into single selection within event
|
||||
// when we receive too much selection add/remove events.
|
||||
if (aTailEvent->mPreceedingCount >= kSelChangeCountToPack) {
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_WITHIN;
|
||||
aTailEvent->mAccessible = aTailEvent->mWidget;
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
|
||||
// Do not emit any preceding selection events for same widget if they
|
||||
// weren't coalesced yet.
|
||||
if (aThisEvent->mEventType != nsIAccessibleEvent::EVENT_SELECTION_WITHIN) {
|
||||
for (uint32_t jdx = aThisIndex - 1; jdx < aThisIndex; jdx--) {
|
||||
AccEvent* prevEvent = mEvents[jdx];
|
||||
if (prevEvent->mEventRule == aTailEvent->mEventRule) {
|
||||
AccSelChangeEvent* prevSelChangeEvent =
|
||||
downcast_accEvent(prevEvent);
|
||||
if (prevSelChangeEvent->mWidget == aTailEvent->mWidget)
|
||||
prevSelChangeEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Pack sequential selection remove and selection add events into
|
||||
// single selection change event.
|
||||
if (aTailEvent->mPreceedingCount == 1 &&
|
||||
aTailEvent->mItem != aThisEvent->mItem) {
|
||||
if (aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aThisEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aTailEvent->mPackedEvent = aThisEvent;
|
||||
return;
|
||||
}
|
||||
|
||||
if (aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd &&
|
||||
aTailEvent->mSelChangeType == AccSelChangeEvent::eSelectionRemove) {
|
||||
aTailEvent->mEventRule = AccEvent::eDoNotEmit;
|
||||
aThisEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION;
|
||||
aThisEvent->mPackedEvent = aTailEvent;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack the packed selection change event because we've got one
|
||||
// more selection add/remove.
|
||||
if (aThisEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
||||
if (aThisEvent->mPackedEvent) {
|
||||
aThisEvent->mPackedEvent->mEventType =
|
||||
aThisEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
aThisEvent->mPackedEvent->mEventRule =
|
||||
AccEvent::eCoalesceSelectionChange;
|
||||
|
||||
aThisEvent->mPackedEvent = nullptr;
|
||||
}
|
||||
|
||||
aThisEvent->mEventType =
|
||||
aThisEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd ?
|
||||
nsIAccessibleEvent::EVENT_SELECTION_ADD :
|
||||
nsIAccessibleEvent::EVENT_SELECTION_REMOVE;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert into selection add since control has single selection but other
|
||||
// selection events for this control are queued.
|
||||
if (aTailEvent->mEventType == nsIAccessibleEvent::EVENT_SELECTION)
|
||||
aTailEvent->mEventType = nsIAccessibleEvent::EVENT_SELECTION_ADD;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventQueue: event queue
|
||||
|
||||
void
|
||||
EventQueue::ProcessEventQueue()
|
||||
{
|
||||
// Process only currently queued events.
|
||||
nsTArray<RefPtr<AccEvent> > events;
|
||||
events.SwapElements(mEvents);
|
||||
|
||||
uint32_t eventCount = events.Length();
|
||||
#ifdef A11Y_LOG
|
||||
if (eventCount > 0 && logging::IsEnabled(logging::eEvents)) {
|
||||
logging::MsgBegin("EVENTS", "events processing");
|
||||
logging::Address("document", mDocument);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t idx = 0; idx < eventCount; idx++) {
|
||||
AccEvent* event = events[idx];
|
||||
if (event->mEventRule != AccEvent::eDoNotEmit) {
|
||||
Accessible* target = event->GetAccessible();
|
||||
if (!target || target->IsDefunct())
|
||||
continue;
|
||||
|
||||
// Dispatch the focus event if target is still focused.
|
||||
if (event->mEventType == nsIAccessibleEvent::EVENT_FOCUS) {
|
||||
FocusMgr()->ProcessFocusEvent(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dispatch caret moved and text selection change events.
|
||||
if (event->mEventType == nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED) {
|
||||
SelectionMgr()->ProcessTextSelChangeEvent(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fire selected state change events in support to selection events.
|
||||
if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_ADD) {
|
||||
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
|
||||
true, event->mIsFromUserInput);
|
||||
|
||||
} else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION_REMOVE) {
|
||||
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
|
||||
false, event->mIsFromUserInput);
|
||||
|
||||
} else if (event->mEventType == nsIAccessibleEvent::EVENT_SELECTION) {
|
||||
AccSelChangeEvent* selChangeEvent = downcast_accEvent(event);
|
||||
nsEventShell::FireEvent(event->mAccessible, states::SELECTED,
|
||||
(selChangeEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
|
||||
event->mIsFromUserInput);
|
||||
|
||||
if (selChangeEvent->mPackedEvent) {
|
||||
nsEventShell::FireEvent(selChangeEvent->mPackedEvent->mAccessible,
|
||||
states::SELECTED,
|
||||
(selChangeEvent->mPackedEvent->mSelChangeType == AccSelChangeEvent::eSelectionAdd),
|
||||
selChangeEvent->mPackedEvent->mIsFromUserInput);
|
||||
}
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
}
|
||||
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
}
|
||||
77
accessible/base/EventQueue.h
Normal file
77
accessible/base/EventQueue.h
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_EventQueue_h_
|
||||
#define mozilla_a11y_EventQueue_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessible;
|
||||
|
||||
/**
|
||||
* Used to organize and coalesce pending events.
|
||||
*/
|
||||
class EventQueue
|
||||
{
|
||||
protected:
|
||||
explicit EventQueue(DocAccessible* aDocument) : mDocument(aDocument) { }
|
||||
|
||||
/**
|
||||
* Put an accessible event into the queue to process it later.
|
||||
*/
|
||||
bool PushEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Puts a name change event into the queue, if needed.
|
||||
*/
|
||||
bool PushNameChange(Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Process events from the queue and fires events.
|
||||
*/
|
||||
void ProcessEventQueue();
|
||||
|
||||
private:
|
||||
EventQueue(const EventQueue&) = delete;
|
||||
EventQueue& operator = (const EventQueue&) = delete;
|
||||
|
||||
// Event queue processing
|
||||
/**
|
||||
* Coalesce redundant events from the queue.
|
||||
*/
|
||||
void CoalesceEvents();
|
||||
|
||||
/**
|
||||
* Coalesce events from the same subtree.
|
||||
*/
|
||||
void CoalesceReorderEvents(AccEvent* aTailEvent);
|
||||
|
||||
/**
|
||||
* Coalesce two selection change events within the same select control.
|
||||
*/
|
||||
void CoalesceSelChangeEvents(AccSelChangeEvent* aTailEvent,
|
||||
AccSelChangeEvent* aThisEvent,
|
||||
uint32_t aThisIndex);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* The document accessible reference owning this queue.
|
||||
*/
|
||||
DocAccessible* mDocument;
|
||||
|
||||
/**
|
||||
* Pending events array. Don't make this an AutoTArray; we use
|
||||
* SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<AccEvent>> mEvents;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_EventQueue_h_
|
||||
618
accessible/base/EventTree.cpp
Normal file
618
accessible/base/EventTree.cpp
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "EventTree.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "DocAccessible.h"
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TreeMutation class
|
||||
|
||||
EventTree* const TreeMutation::kNoEventTree = reinterpret_cast<EventTree*>(-1);
|
||||
|
||||
TreeMutation::TreeMutation(Accessible* aParent, bool aNoEvents) :
|
||||
mParent(aParent), mStartIdx(UINT32_MAX),
|
||||
mStateFlagsCopy(mParent->mStateFlags),
|
||||
mQueueEvents(!aNoEvents)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mIsDone = false;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree before");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
|
||||
if (logging::IsEnabled(logging::eVerbose)) {
|
||||
logging::Tree("EVENTS_TREE", "Container tree", mParent->Document(),
|
||||
PrefixLog, static_cast<void*>(this));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mParent->mStateFlags |= Accessible::eKidsMutating;
|
||||
}
|
||||
|
||||
TreeMutation::~TreeMutation()
|
||||
{
|
||||
MOZ_ASSERT(mIsDone, "Done() must be called explicitly");
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::AfterInsertion(Accessible* aChild)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent + 1;
|
||||
}
|
||||
|
||||
if (!mQueueEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
|
||||
DebugOnly<bool> added = Controller()->QueueMutationEvent(ev);
|
||||
MOZ_ASSERT(added);
|
||||
aChild->SetShowEventTarget(true);
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::BeforeRemoval(Accessible* aChild, bool aNoShutdown)
|
||||
{
|
||||
MOZ_ASSERT(aChild->Parent() == mParent);
|
||||
|
||||
if (static_cast<uint32_t>(aChild->mIndexInParent) < mStartIdx) {
|
||||
mStartIdx = aChild->mIndexInParent;
|
||||
}
|
||||
|
||||
if (!mQueueEvents) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, !aNoShutdown);
|
||||
if (Controller()->QueueMutationEvent(ev)) {
|
||||
aChild->SetHideEventTarget(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TreeMutation::Done()
|
||||
{
|
||||
MOZ_ASSERT(mParent->mStateFlags & Accessible::eKidsMutating);
|
||||
mParent->mStateFlags &= ~Accessible::eKidsMutating;
|
||||
|
||||
uint32_t length = mParent->mChildren.Length();
|
||||
#ifdef DEBUG
|
||||
for (uint32_t idx = 0; idx < mStartIdx && idx < length; idx++) {
|
||||
MOZ_ASSERT(mParent->mChildren[idx]->mIndexInParent == static_cast<int32_t>(idx),
|
||||
"Wrong index detected");
|
||||
}
|
||||
#endif
|
||||
|
||||
for (uint32_t idx = mStartIdx; idx < length; idx++) {
|
||||
mParent->mChildren[idx]->mInt.mIndexOfEmbeddedChild = -1;
|
||||
mParent->mChildren[idx]->mStateFlags |= Accessible::eGroupInfoDirty;
|
||||
}
|
||||
|
||||
mParent->mEmbeddedObjCollector = nullptr;
|
||||
mParent->mStateFlags |= mStateFlagsCopy & Accessible::eKidsMutating;
|
||||
|
||||
#ifdef DEBUG
|
||||
mIsDone = true;
|
||||
#endif
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (mQueueEvents && logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "reordering tree after");
|
||||
logging::AccessibleInfo("reordering for", mParent);
|
||||
Controller()->RootEventTree().Log();
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
const char*
|
||||
TreeMutation::PrefixLog(void* aData, Accessible* aAcc)
|
||||
{
|
||||
TreeMutation* thisObj = reinterpret_cast<TreeMutation*>(aData);
|
||||
if (thisObj->mParent == aAcc) {
|
||||
return "_X_";
|
||||
}
|
||||
const EventTree& ret = thisObj->Controller()->RootEventTree();
|
||||
if (ret.Find(aAcc)) {
|
||||
return "_с_";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// EventTree
|
||||
|
||||
void
|
||||
EventTree::Shown(Accessible* aChild)
|
||||
{
|
||||
RefPtr<AccShowEvent> ev = new AccShowEvent(aChild);
|
||||
Controller(aChild)->WithdrawPrecedingEvents(&ev->mPrecedingEvents);
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
void
|
||||
EventTree::Hidden(Accessible* aChild, bool aNeedsShutdown)
|
||||
{
|
||||
RefPtr<AccHideEvent> ev = new AccHideEvent(aChild, aNeedsShutdown);
|
||||
if (!aNeedsShutdown) {
|
||||
Controller(aChild)->StorePrecedingEvent(ev);
|
||||
}
|
||||
Mutated(ev);
|
||||
}
|
||||
|
||||
void
|
||||
EventTree::Process(const RefPtr<DocAccessible>& aDeathGrip)
|
||||
{
|
||||
while (mFirst) {
|
||||
// Skip a node and its subtree if its container is not in the document.
|
||||
if (mFirst->mContainer->IsInDocument()) {
|
||||
mFirst->Process(aDeathGrip);
|
||||
if (aDeathGrip->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mFirst = Move(mFirst->mNext);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
|
||||
"No container, no events");
|
||||
MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(),
|
||||
"Processing events for defunct container");
|
||||
MOZ_ASSERT(!mFireReorder || mContainer, "No target for reorder event");
|
||||
|
||||
// Fire mutation events.
|
||||
uint32_t eventsCount = mDependentEvents.Length();
|
||||
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
|
||||
AccMutationEvent* mtEvent = mDependentEvents[jdx];
|
||||
MOZ_ASSERT(mtEvent->Document(), "No document for event target");
|
||||
|
||||
// Fire all hide events that has to be fired before this show event.
|
||||
if (mtEvent->IsShow()) {
|
||||
AccShowEvent* showEv = downcast_accEvent(mtEvent);
|
||||
for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
|
||||
nsEventShell::FireEvent(showEv->mPrecedingEvents[i]);
|
||||
if (aDeathGrip->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(mtEvent);
|
||||
if (aDeathGrip->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mtEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
|
||||
if (aDeathGrip->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mtEvent->IsHide()) {
|
||||
// Fire menupopup end event before a hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
mtEvent->mAccessible);
|
||||
if (aDeathGrip->IsDefunct()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
aDeathGrip->ShutdownChildrenInSubtree(mtEvent->mAccessible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fire reorder event at last.
|
||||
if (mFireReorder) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
|
||||
mContainer->Document()->MaybeNotifyOfValueChange(mContainer);
|
||||
}
|
||||
|
||||
mDependentEvents.Clear();
|
||||
}
|
||||
|
||||
EventTree*
|
||||
EventTree::FindOrInsert(Accessible* aContainer)
|
||||
{
|
||||
if (!mFirst) {
|
||||
mFirst.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
|
||||
return mFirst.get();
|
||||
}
|
||||
|
||||
EventTree* prevNode = nullptr;
|
||||
EventTree* node = mFirst.get();
|
||||
do {
|
||||
MOZ_ASSERT(!node->mContainer->IsApplication(),
|
||||
"No event for application accessible is expected here");
|
||||
MOZ_ASSERT(!node->mContainer->IsDefunct(), "An event target has to be alive");
|
||||
|
||||
// Case of same target.
|
||||
if (node->mContainer == aContainer) {
|
||||
return node;
|
||||
}
|
||||
|
||||
// Check if the given container is contained by a current node
|
||||
Accessible* top = mContainer ? mContainer : aContainer->Document();
|
||||
Accessible* parent = aContainer;
|
||||
while (parent) {
|
||||
// Reached a top, no match for a current event.
|
||||
if (parent == top) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We got a match.
|
||||
if (parent->Parent() == node->mContainer) {
|
||||
// Reject the node if it's contained by a show/hide event target
|
||||
uint32_t evCount = node->mDependentEvents.Length();
|
||||
for (uint32_t idx = 0; idx < evCount; idx++) {
|
||||
AccMutationEvent* ev = node->mDependentEvents[idx];
|
||||
if (ev->GetAccessible() == parent) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE",
|
||||
"Rejecting node contained by show/hide");
|
||||
logging::AccessibleInfo("Node", aContainer);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
// If the node is rejected, then check if it has related hide event
|
||||
// on stack, and if so, then connect it to the parent show event.
|
||||
if (ev->IsShow()) {
|
||||
AccShowEvent* showEv = downcast_accEvent(ev);
|
||||
Controller(aContainer)->
|
||||
WithdrawPrecedingEvents(&showEv->mPrecedingEvents);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return node->FindOrInsert(aContainer);
|
||||
}
|
||||
|
||||
parent = parent->Parent();
|
||||
MOZ_ASSERT(parent, "Wrong tree");
|
||||
}
|
||||
|
||||
// If the given container contains a current node
|
||||
// then
|
||||
// if show or hide of the given node contains a grand parent of the current node
|
||||
// then ignore the current node and its show and hide events
|
||||
// otherwise ignore the current node, but not its show and hide events
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert the tail node into the hierarchy between the current node and
|
||||
// its parent.
|
||||
node->mFireReorder = false;
|
||||
UniquePtr<EventTree>& nodeOwnerRef = prevNode ? prevNode->mNext : mFirst;
|
||||
UniquePtr<EventTree> newNode(new EventTree(aContainer, mDependentEvents.IsEmpty()));
|
||||
newNode->mFirst = Move(nodeOwnerRef);
|
||||
nodeOwnerRef = Move(newNode);
|
||||
nodeOwnerRef->mNext = Move(node->mNext);
|
||||
|
||||
// Check if a next node is contained by the given node too, and move them
|
||||
// under the given node if so.
|
||||
prevNode = nodeOwnerRef.get();
|
||||
node = nodeOwnerRef->mNext.get();
|
||||
UniquePtr<EventTree>* nodeRef = &nodeOwnerRef->mNext;
|
||||
EventTree* insNode = nodeOwnerRef->mFirst.get();
|
||||
while (node) {
|
||||
Accessible* curParent = node->mContainer;
|
||||
while (curParent && !curParent->IsDoc()) {
|
||||
if (curParent->Parent() != aContainer) {
|
||||
curParent = curParent->Parent();
|
||||
continue;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!insNode->mNext);
|
||||
|
||||
node->mFireReorder = false;
|
||||
insNode->mNext = Move(*nodeRef);
|
||||
insNode = insNode->mNext.get();
|
||||
|
||||
prevNode->mNext = Move(node->mNext);
|
||||
node = prevNode;
|
||||
break;
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
nodeRef = &node->mNext;
|
||||
node = node->mNext.get();
|
||||
}
|
||||
|
||||
return nodeOwnerRef.get();
|
||||
}
|
||||
|
||||
prevNode = node;
|
||||
} while ((node = node->mNext.get()));
|
||||
|
||||
MOZ_ASSERT(prevNode, "Nowhere to insert");
|
||||
MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
|
||||
|
||||
// If 'this' node contains the given container accessible, then
|
||||
// do not emit a reorder event for the container
|
||||
// if a dependent show event target contains the given container then do not
|
||||
// emit show / hide events (see Process() method)
|
||||
|
||||
prevNode->mNext.reset(new EventTree(aContainer, mDependentEvents.IsEmpty()));
|
||||
return prevNode->mNext.get();
|
||||
}
|
||||
|
||||
void
|
||||
EventTree::Clear()
|
||||
{
|
||||
mFirst = nullptr;
|
||||
mNext = nullptr;
|
||||
mContainer = nullptr;
|
||||
|
||||
uint32_t eventsCount = mDependentEvents.Length();
|
||||
for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
|
||||
mDependentEvents[jdx]->mEventType = AccEvent::eDoNotEmit;
|
||||
AccHideEvent* ev = downcast_accEvent(mDependentEvents[jdx]);
|
||||
if (ev && ev->NeedsShutdown()) {
|
||||
ev->Document()->ShutdownChildrenInSubtree(ev->mAccessible);
|
||||
}
|
||||
}
|
||||
mDependentEvents.Clear();
|
||||
}
|
||||
|
||||
const EventTree*
|
||||
EventTree::Find(const Accessible* aContainer) const
|
||||
{
|
||||
const EventTree* et = this;
|
||||
while (et) {
|
||||
if (et->mContainer == aContainer) {
|
||||
return et;
|
||||
}
|
||||
|
||||
if (et->mFirst) {
|
||||
et = et->mFirst.get();
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
et = et->mNext.get();
|
||||
const EventTree* cet = et->Find(aContainer);
|
||||
if (cet) {
|
||||
return cet;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void
|
||||
EventTree::Log(uint32_t aLevel) const
|
||||
{
|
||||
if (aLevel == UINT32_MAX) {
|
||||
if (mFirst) {
|
||||
mFirst->Log(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < aLevel; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("container", mContainer);
|
||||
|
||||
for (uint32_t i = 0; i < mDependentEvents.Length(); i++) {
|
||||
AccMutationEvent* ev = mDependentEvents[i];
|
||||
if (ev->IsShow()) {
|
||||
for (uint32_t i = 0; i < aLevel + 1; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("shown", ev->mAccessible);
|
||||
|
||||
AccShowEvent* showEv = downcast_accEvent(ev);
|
||||
for (uint32_t i = 0; i < showEv->mPrecedingEvents.Length(); i++) {
|
||||
for (uint32_t j = 0; j < aLevel + 1; j++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("preceding",
|
||||
showEv->mPrecedingEvents[i]->mAccessible);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint32_t i = 0; i < aLevel + 1; i++) {
|
||||
printf(" ");
|
||||
}
|
||||
logging::AccessibleInfo("hidden", ev->mAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
if (mFirst) {
|
||||
mFirst->Log(aLevel + 1);
|
||||
}
|
||||
|
||||
if (mNext) {
|
||||
mNext->Log(aLevel);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
EventTree::Mutated(AccMutationEvent* aEv)
|
||||
{
|
||||
// If shown or hidden node is a root of previously mutated subtree, then
|
||||
// discard those subtree mutations as we are no longer interested in them.
|
||||
UniquePtr<EventTree>* node = &mFirst;
|
||||
while (*node) {
|
||||
Accessible* cntr = (*node)->mContainer;
|
||||
while (cntr != mContainer) {
|
||||
if (cntr == aEv->mAccessible) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "Trim subtree");
|
||||
logging::AccessibleInfo("Show/hide container", aEv->mAccessible);
|
||||
logging::AccessibleInfo("Trimmed subtree root", (*node)->mContainer);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the new hide is part of a move and it contains existing child
|
||||
// shows, then move preceding events from the child shows to the buffer,
|
||||
// so the ongoing show event will pick them up.
|
||||
if (aEv->IsHide()) {
|
||||
AccHideEvent* hideEv = downcast_accEvent(aEv);
|
||||
if (!hideEv->mNeedsShutdown) {
|
||||
for (uint32_t i = 0; i < (*node)->mDependentEvents.Length(); i++) {
|
||||
AccMutationEvent* childEv = (*node)->mDependentEvents[i];
|
||||
if (childEv->IsShow()) {
|
||||
AccShowEvent* childShowEv = downcast_accEvent(childEv);
|
||||
if (childShowEv->mPrecedingEvents.Length() > 0) {
|
||||
Controller(mContainer)->StorePrecedingEvents(
|
||||
mozilla::Move(childShowEv->mPrecedingEvents));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the new show contains existing child shows, then move preceding
|
||||
// events from the child shows to the new show.
|
||||
else if (aEv->IsShow()) {
|
||||
AccShowEvent* showEv = downcast_accEvent(aEv);
|
||||
for (uint32_t i = 0; (*node)->mDependentEvents.Length(); i++) {
|
||||
AccMutationEvent* childEv = (*node)->mDependentEvents[i];
|
||||
if (childEv->IsShow()) {
|
||||
AccShowEvent* showChildEv = downcast_accEvent(childEv);
|
||||
if (showChildEv->mPrecedingEvents.Length() > 0) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eEventTree)) {
|
||||
logging::MsgBegin("EVENTS_TREE", "Adopt preceding events");
|
||||
logging::AccessibleInfo("Parent", aEv->mAccessible);
|
||||
for (uint32_t j = 0; j < showChildEv->mPrecedingEvents.Length(); j++) {
|
||||
logging::AccessibleInfo("Adoptee",
|
||||
showChildEv->mPrecedingEvents[i]->mAccessible);
|
||||
}
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
showEv->mPrecedingEvents.AppendElements(showChildEv->mPrecedingEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*node = Move((*node)->mNext);
|
||||
break;
|
||||
}
|
||||
cntr = cntr->Parent();
|
||||
}
|
||||
if (cntr == aEv->mAccessible) {
|
||||
continue;
|
||||
}
|
||||
node = &(*node)->mNext;
|
||||
}
|
||||
|
||||
AccMutationEvent* prevEvent = mDependentEvents.SafeLastElement(nullptr);
|
||||
mDependentEvents.AppendElement(aEv);
|
||||
|
||||
// Coalesce text change events from this hide/show event and the previous one.
|
||||
if (prevEvent && aEv->mEventType == prevEvent->mEventType) {
|
||||
if (aEv->IsHide()) {
|
||||
// XXX: we need a way to ignore SplitNode and JoinNode() when they do not
|
||||
// affect the text within the hypertext.
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEv);
|
||||
AccHideEvent* prevHideEvent = downcast_accEvent(prevEvent);
|
||||
|
||||
if (prevHideEvent->mNextSibling == hideEvent->mAccessible) {
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (prevHideEvent->mPrevSibling == hideEvent->mAccessible) {
|
||||
uint32_t oldLen = prevTextEvent->GetLength();
|
||||
hideEvent->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
prevTextEvent->mStart -= prevTextEvent->GetLength() - oldLen;
|
||||
}
|
||||
|
||||
hideEvent->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
else {
|
||||
AccTextChangeEvent* prevTextEvent = prevEvent->mTextChangeEvent;
|
||||
if (prevTextEvent) {
|
||||
if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() + 1) {
|
||||
// If tail target was inserted after this target, i.e. tail target is next
|
||||
// sibling of this target.
|
||||
aEv->mAccessible->AppendTextTo(prevTextEvent->mModifiedText);
|
||||
}
|
||||
else if (aEv->mAccessible->IndexInParent() ==
|
||||
prevEvent->mAccessible->IndexInParent() - 1) {
|
||||
// If tail target was inserted before this target, i.e. tail target is
|
||||
// previous sibling of this target.
|
||||
nsAutoString startText;
|
||||
aEv->mAccessible->AppendTextTo(startText);
|
||||
prevTextEvent->mModifiedText = startText + prevTextEvent->mModifiedText;
|
||||
prevTextEvent->mStart -= startText.Length();
|
||||
}
|
||||
|
||||
aEv->mTextChangeEvent.swap(prevEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a text change event caused by this hide/show event. When a node is
|
||||
// hidden/removed or shown/appended, the text in an ancestor hyper text will
|
||||
// lose or get new characters.
|
||||
if (aEv->mTextChangeEvent || !mContainer->IsHyperText()) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString text;
|
||||
aEv->mAccessible->AppendTextTo(text);
|
||||
if (text.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t offset = mContainer->AsHyperText()->GetChildOffset(aEv->mAccessible);
|
||||
aEv->mTextChangeEvent =
|
||||
new AccTextChangeEvent(mContainer, offset, text, aEv->IsShow(),
|
||||
aEv->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
121
accessible/base/EventTree.h
Normal file
121
accessible/base/EventTree.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_EventTree_h_
|
||||
#define mozilla_a11y_EventTree_h_
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "Accessible.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* This class makes sure required tasks are done before and after tree
|
||||
* mutations. Currently this only includes group info invalidation. You must
|
||||
* have an object of this class on the stack when calling methods that mutate
|
||||
* the accessible tree.
|
||||
*/
|
||||
class TreeMutation final
|
||||
{
|
||||
public:
|
||||
static const bool kNoEvents = true;
|
||||
static const bool kNoShutdown = true;
|
||||
|
||||
explicit TreeMutation(Accessible* aParent, bool aNoEvents = false);
|
||||
~TreeMutation();
|
||||
|
||||
void AfterInsertion(Accessible* aChild);
|
||||
void BeforeRemoval(Accessible* aChild, bool aNoShutdown = false);
|
||||
void Done();
|
||||
|
||||
private:
|
||||
NotificationController* Controller() const
|
||||
{ return mParent->Document()->Controller(); }
|
||||
|
||||
static EventTree* const kNoEventTree;
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
static const char* PrefixLog(void* aData, Accessible*);
|
||||
#endif
|
||||
|
||||
Accessible* mParent;
|
||||
uint32_t mStartIdx;
|
||||
uint32_t mStateFlagsCopy;
|
||||
|
||||
/*
|
||||
* True if mutation events should be queued.
|
||||
*/
|
||||
bool mQueueEvents;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsDone;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A mutation events coalescence structure.
|
||||
*/
|
||||
class EventTree final {
|
||||
public:
|
||||
EventTree() :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(nullptr), mFireReorder(false) { }
|
||||
explicit EventTree(Accessible* aContainer, bool aFireReorder) :
|
||||
mFirst(nullptr), mNext(nullptr), mContainer(aContainer),
|
||||
mFireReorder(aFireReorder) { }
|
||||
~EventTree() { Clear(); }
|
||||
|
||||
void Shown(Accessible* aTarget);
|
||||
void Hidden(Accessible*, bool);
|
||||
|
||||
/**
|
||||
* Return an event tree node for the given accessible.
|
||||
*/
|
||||
const EventTree* Find(const Accessible* aContainer) const;
|
||||
|
||||
/**
|
||||
* Add a mutation event to this event tree.
|
||||
*/
|
||||
void Mutated(AccMutationEvent* aEv);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
void Log(uint32_t aLevel = UINT32_MAX) const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
/**
|
||||
* Processes the event queue and fires events.
|
||||
*/
|
||||
void Process(const RefPtr<DocAccessible>& aDeathGrip);
|
||||
|
||||
/**
|
||||
* Return an event subtree for the given accessible.
|
||||
*/
|
||||
EventTree* FindOrInsert(Accessible* aContainer);
|
||||
|
||||
void Clear();
|
||||
|
||||
UniquePtr<EventTree> mFirst;
|
||||
UniquePtr<EventTree> mNext;
|
||||
|
||||
Accessible* mContainer;
|
||||
nsTArray<RefPtr<AccMutationEvent>> mDependentEvents;
|
||||
bool mFireReorder;
|
||||
|
||||
static NotificationController* Controller(Accessible* aAcc)
|
||||
{ return aAcc->Document()->Controller(); }
|
||||
|
||||
friend class NotificationController;
|
||||
};
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_EventQueue_h_
|
||||
51
accessible/base/Filters.cpp
Normal file
51
accessible/base/Filters.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* 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 "Filters.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
using namespace mozilla::a11y::filters;
|
||||
|
||||
uint32_t
|
||||
filters::GetSelected(Accessible* aAccessible)
|
||||
{
|
||||
if (aAccessible->State() & states::SELECTED)
|
||||
return eMatch | eSkipSubtree;
|
||||
|
||||
return eSkip;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
filters::GetSelectable(Accessible* aAccessible)
|
||||
{
|
||||
if (aAccessible->InteractiveState() & states::SELECTABLE)
|
||||
return eMatch | eSkipSubtree;
|
||||
|
||||
return eSkip;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
filters::GetRow(Accessible* aAccessible)
|
||||
{
|
||||
a11y::role role = aAccessible->Role();
|
||||
if (role == roles::ROW)
|
||||
return eMatch | eSkipSubtree;
|
||||
|
||||
// Look for rows inside rowgroup.
|
||||
if (role == roles::GROUPING)
|
||||
return eSkip;
|
||||
|
||||
return eSkipSubtree;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
filters::GetCell(Accessible* aAccessible)
|
||||
{
|
||||
return aAccessible->IsTableCell() ? eMatch : eSkipSubtree;
|
||||
}
|
||||
50
accessible/base/Filters.h
Normal file
50
accessible/base/Filters.h
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/* 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_Filters_h__
|
||||
#define mozilla_a11y_Filters_h__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* Predefined filters used for nsAccIterator and nsAccCollector.
|
||||
*/
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
|
||||
namespace filters {
|
||||
|
||||
enum EResult {
|
||||
eSkip = 0,
|
||||
eMatch = 1,
|
||||
eSkipSubtree = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if the traversed accessible complies with filter.
|
||||
*/
|
||||
typedef uint32_t (*FilterFuncPtr) (Accessible*);
|
||||
|
||||
/**
|
||||
* Matches selected/selectable accessibles in subtree.
|
||||
*/
|
||||
uint32_t GetSelected(Accessible* aAccessible);
|
||||
uint32_t GetSelectable(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Matches row accessibles in subtree.
|
||||
*/
|
||||
uint32_t GetRow(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Matches cell accessibles in children.
|
||||
*/
|
||||
uint32_t GetCell(Accessible* aAccessible);
|
||||
} // namespace filters
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
404
accessible/base/FocusManager.cpp
Normal file
404
accessible/base/FocusManager.cpp
Normal file
|
|
@ -0,0 +1,404 @@
|
|||
/* 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 "FocusManager.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "AccIterator.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "Role.h"
|
||||
|
||||
#include "nsFocusManager.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
FocusManager::FocusManager()
|
||||
{
|
||||
}
|
||||
|
||||
FocusManager::~FocusManager()
|
||||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
FocusManager::FocusedAccessible() const
|
||||
{
|
||||
if (mActiveItem)
|
||||
return mActiveItem;
|
||||
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode) {
|
||||
DocAccessible* doc =
|
||||
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
|
||||
return doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
FocusManager::IsFocused(const Accessible* aAccessible) const
|
||||
{
|
||||
if (mActiveItem)
|
||||
return mActiveItem == aAccessible;
|
||||
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode) {
|
||||
// XXX: Before getting an accessible for node having a DOM focus make sure
|
||||
// they belong to the same document because it can trigger unwanted document
|
||||
// accessible creation for temporary about:blank document. Without this
|
||||
// peculiarity we would end up with plain implementation based on
|
||||
// FocusedAccessible() method call. Make sure this issue is fixed in
|
||||
// bug 638465.
|
||||
if (focusedNode->OwnerDoc() == aAccessible->GetNode()->OwnerDoc()) {
|
||||
DocAccessible* doc =
|
||||
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
|
||||
return aAccessible ==
|
||||
(doc ? doc->GetAccessibleEvenIfNotInMapOrContainer(focusedNode) : nullptr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
FocusManager::IsFocusWithin(const Accessible* aContainer) const
|
||||
{
|
||||
Accessible* child = FocusedAccessible();
|
||||
while (child) {
|
||||
if (child == aContainer)
|
||||
return true;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FocusManager::FocusDisposition
|
||||
FocusManager::IsInOrContainsFocus(const Accessible* aAccessible) const
|
||||
{
|
||||
Accessible* focus = FocusedAccessible();
|
||||
if (!focus)
|
||||
return eNone;
|
||||
|
||||
// If focused.
|
||||
if (focus == aAccessible)
|
||||
return eFocused;
|
||||
|
||||
// If contains the focus.
|
||||
Accessible* child = focus->Parent();
|
||||
while (child) {
|
||||
if (child == aAccessible)
|
||||
return eContainsFocus;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
|
||||
// If contained by focus.
|
||||
child = aAccessible->Parent();
|
||||
while (child) {
|
||||
if (child == focus)
|
||||
return eContainedByFocus;
|
||||
|
||||
child = child->Parent();
|
||||
}
|
||||
|
||||
return eNone;
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
|
||||
{
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusNotificationTarget("DOM focus", "Target", aTarget);
|
||||
#endif
|
||||
|
||||
mActiveItem = nullptr;
|
||||
|
||||
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
|
||||
if (targetNode) {
|
||||
DocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(targetNode->OwnerDoc());
|
||||
if (document) {
|
||||
// Set selection listener for focused element.
|
||||
if (targetNode->IsElement())
|
||||
SelectionMgr()->SetControlSelectionListener(targetNode->AsElement());
|
||||
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, targetNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
|
||||
{
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusNotificationTarget("DOM blur", "Target", aTarget);
|
||||
#endif
|
||||
|
||||
mActiveItem = nullptr;
|
||||
|
||||
// If DOM document stays focused then fire accessible focus event to process
|
||||
// the case when no element within this DOM document will be focused.
|
||||
nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
|
||||
if (targetNode && targetNode->OwnerDoc() == FocusedDOMDocument()) {
|
||||
nsIDocument* DOMDoc = targetNode->OwnerDoc();
|
||||
DocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(DOMDoc);
|
||||
if (document) {
|
||||
// Clear selection listener for previously focused element.
|
||||
if (targetNode->IsElement())
|
||||
SelectionMgr()->ClearControlSelectionListener();
|
||||
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, DOMDoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ActiveItemChanged(Accessible* aItem, bool aCheckIfActive)
|
||||
{
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusNotificationTarget("active item changed", "Item", aItem);
|
||||
#endif
|
||||
|
||||
// Nothing changed, happens for XUL trees and HTML selects.
|
||||
if (aItem && aItem == mActiveItem)
|
||||
return;
|
||||
|
||||
mActiveItem = nullptr;
|
||||
|
||||
if (aItem && aCheckIfActive) {
|
||||
Accessible* widget = aItem->ContainerWidget();
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::ActiveWidget(widget);
|
||||
#endif
|
||||
if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
|
||||
return;
|
||||
}
|
||||
mActiveItem = aItem;
|
||||
|
||||
// If active item is changed then fire accessible focus event on it, otherwise
|
||||
// if there's no an active item then fire focus event to accessible having
|
||||
// DOM focus.
|
||||
Accessible* target = FocusedAccessible();
|
||||
if (target)
|
||||
DispatchFocusEvent(target->Document(), target);
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ForceFocusEvent()
|
||||
{
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (focusedNode) {
|
||||
DocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(focusedNode->OwnerDoc());
|
||||
if (document) {
|
||||
document->HandleNotification<FocusManager, nsINode>
|
||||
(this, &FocusManager::ProcessDOMFocus, focusedNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::DispatchFocusEvent(DocAccessible* aDocument,
|
||||
Accessible* aTarget)
|
||||
{
|
||||
NS_PRECONDITION(aDocument, "No document for focused accessible!");
|
||||
if (aDocument) {
|
||||
RefPtr<AccEvent> event =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
|
||||
eAutoDetect, AccEvent::eCoalesceOfSameType);
|
||||
aDocument->FireDelayedEvent(event);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusDispatched(aTarget);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ProcessDOMFocus(nsINode* aTarget)
|
||||
{
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusNotificationTarget("process DOM focus", "Target", aTarget);
|
||||
#endif
|
||||
|
||||
DocAccessible* document =
|
||||
GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
|
||||
if (!document)
|
||||
return;
|
||||
|
||||
Accessible* target = document->GetAccessibleEvenIfNotInMapOrContainer(aTarget);
|
||||
if (target) {
|
||||
// Check if still focused. Otherwise we can end up with storing the active
|
||||
// item for control that isn't focused anymore.
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (!focusedNode)
|
||||
return;
|
||||
|
||||
Accessible* DOMFocus =
|
||||
document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
|
||||
if (target != DOMFocus)
|
||||
return;
|
||||
|
||||
Accessible* activeItem = target->CurrentItem();
|
||||
if (activeItem) {
|
||||
mActiveItem = activeItem;
|
||||
target = activeItem;
|
||||
}
|
||||
|
||||
DispatchFocusEvent(document, target);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FocusManager::ProcessFocusEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_PRECONDITION(aEvent->GetEventType() == nsIAccessibleEvent::EVENT_FOCUS,
|
||||
"Focus event is expected!");
|
||||
|
||||
// Emit focus event if event target is the active item. Otherwise then check
|
||||
// if it's still focused and then update active item and emit focus event.
|
||||
Accessible* target = aEvent->GetAccessible();
|
||||
if (target != mActiveItem) {
|
||||
|
||||
// Check if still focused. Otherwise we can end up with storing the active
|
||||
// item for control that isn't focused anymore.
|
||||
DocAccessible* document = aEvent->Document();
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
if (!focusedNode)
|
||||
return;
|
||||
|
||||
Accessible* DOMFocus =
|
||||
document->GetAccessibleEvenIfNotInMapOrContainer(focusedNode);
|
||||
if (target != DOMFocus)
|
||||
return;
|
||||
|
||||
Accessible* activeItem = target->CurrentItem();
|
||||
if (activeItem) {
|
||||
mActiveItem = activeItem;
|
||||
target = activeItem;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire menu start/end events for ARIA menus.
|
||||
if (target->IsARIARole(nsGkAtoms::menuitem)) {
|
||||
// The focus was moved into menu.
|
||||
Accessible* ARIAMenubar = nullptr;
|
||||
for (Accessible* parent = target->Parent(); parent; parent = parent->Parent()) {
|
||||
if (parent->IsARIARole(nsGkAtoms::menubar)) {
|
||||
ARIAMenubar = parent;
|
||||
break;
|
||||
}
|
||||
|
||||
// Go up in the parent chain of the menu hierarchy.
|
||||
if (!parent->IsARIARole(nsGkAtoms::menuitem) &&
|
||||
!parent->IsARIARole(nsGkAtoms::menu)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ARIAMenubar != mActiveARIAMenubar) {
|
||||
// Leaving ARIA menu. Fire menu_end event on current menubar.
|
||||
if (mActiveARIAMenubar) {
|
||||
RefPtr<AccEvent> menuEndEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
|
||||
aEvent->FromUserInput());
|
||||
nsEventShell::FireEvent(menuEndEvent);
|
||||
}
|
||||
|
||||
mActiveARIAMenubar = ARIAMenubar;
|
||||
|
||||
// Entering ARIA menu. Fire menu_start event.
|
||||
if (mActiveARIAMenubar) {
|
||||
RefPtr<AccEvent> menuStartEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_START,
|
||||
mActiveARIAMenubar, aEvent->FromUserInput());
|
||||
nsEventShell::FireEvent(menuStartEvent);
|
||||
}
|
||||
}
|
||||
} else if (mActiveARIAMenubar) {
|
||||
// Focus left a menu. Fire menu_end event.
|
||||
RefPtr<AccEvent> menuEndEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
|
||||
aEvent->FromUserInput());
|
||||
nsEventShell::FireEvent(menuEndEvent);
|
||||
|
||||
mActiveARIAMenubar = nullptr;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::FocusNotificationTarget("fire focus event", "Target", target);
|
||||
#endif
|
||||
|
||||
// Reset cached caret value. The cache will be updated upon processing the
|
||||
// next caret move event. This ensures that we will return the correct caret
|
||||
// offset before the caret move event is handled.
|
||||
SelectionMgr()->ResetCaretOffset();
|
||||
|
||||
RefPtr<AccEvent> focusEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, aEvent->FromUserInput());
|
||||
nsEventShell::FireEvent(focusEvent);
|
||||
|
||||
// Fire scrolling_start event when the document receives the focus if it has
|
||||
// an anchor jump. If an accessible within the document receive the focus
|
||||
// then null out the anchor jump because it no longer applies.
|
||||
DocAccessible* targetDocument = target->Document();
|
||||
Accessible* anchorJump = targetDocument->AnchorJump();
|
||||
if (anchorJump) {
|
||||
if (target == targetDocument) {
|
||||
// XXX: bug 625699, note in some cases the node could go away before we
|
||||
// we receive focus event, for example if the node is removed from DOM.
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
|
||||
anchorJump, aEvent->FromUserInput());
|
||||
}
|
||||
targetDocument->SetAnchorJump(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
nsINode*
|
||||
FocusManager::FocusedDOMNode() const
|
||||
{
|
||||
nsFocusManager* DOMFocusManager = nsFocusManager::GetFocusManager();
|
||||
nsIContent* focusedElm = DOMFocusManager->GetFocusedContent();
|
||||
|
||||
// No focus on remote target elements like xul:browser having DOM focus and
|
||||
// residing in chrome process because it means an element in content process
|
||||
// keeps the focus.
|
||||
if (focusedElm) {
|
||||
if (EventStateManager::IsRemoteTarget(focusedElm)) {
|
||||
return nullptr;
|
||||
}
|
||||
return focusedElm;
|
||||
}
|
||||
|
||||
// Otherwise the focus can be on DOM document.
|
||||
nsPIDOMWindowOuter* focusedWnd = DOMFocusManager->GetFocusedWindow();
|
||||
return focusedWnd ? focusedWnd->GetExtantDoc() : nullptr;
|
||||
}
|
||||
|
||||
nsIDocument*
|
||||
FocusManager::FocusedDOMDocument() const
|
||||
{
|
||||
nsINode* focusedNode = FocusedDOMNode();
|
||||
return focusedNode ? focusedNode->OwnerDoc() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
132
accessible/base/FocusManager.h
Normal file
132
accessible/base/FocusManager.h
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/* 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_FocusManager_h_
|
||||
#define mozilla_a11y_FocusManager_h_
|
||||
|
||||
class nsINode;
|
||||
class nsIDocument;
|
||||
class nsISupports;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class AccEvent;
|
||||
class Accessible;
|
||||
class DocAccessible;
|
||||
|
||||
/**
|
||||
* Manage the accessible focus. Used to fire and process accessible events.
|
||||
*/
|
||||
class FocusManager
|
||||
{
|
||||
public:
|
||||
virtual ~FocusManager();
|
||||
|
||||
/**
|
||||
* Return a focused accessible.
|
||||
*/
|
||||
Accessible* FocusedAccessible() const;
|
||||
|
||||
/**
|
||||
* Return true if given accessible is focused.
|
||||
*/
|
||||
bool IsFocused(const Accessible* aAccessible) const;
|
||||
|
||||
/**
|
||||
* Return true if the given accessible is an active item, i.e. an item that
|
||||
* is current within the active widget.
|
||||
*/
|
||||
inline bool IsActiveItem(const Accessible* aAccessible)
|
||||
{ return aAccessible == mActiveItem; }
|
||||
|
||||
/**
|
||||
* Return true if given DOM node has DOM focus.
|
||||
*/
|
||||
inline bool HasDOMFocus(const nsINode* aNode) const
|
||||
{ return aNode == FocusedDOMNode(); }
|
||||
|
||||
/**
|
||||
* Return true if focused accessible is within the given container.
|
||||
*/
|
||||
bool IsFocusWithin(const Accessible* aContainer) const;
|
||||
|
||||
/**
|
||||
* Return whether the given accessible is focused or contains the focus or
|
||||
* contained by focused accessible.
|
||||
*/
|
||||
enum FocusDisposition {
|
||||
eNone,
|
||||
eFocused,
|
||||
eContainsFocus,
|
||||
eContainedByFocus
|
||||
};
|
||||
FocusDisposition IsInOrContainsFocus(const Accessible* aAccessible) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Focus notifications and processing (don't use until you know what you do).
|
||||
|
||||
/**
|
||||
* Called when DOM focus event is fired.
|
||||
*/
|
||||
void NotifyOfDOMFocus(nsISupports* aTarget);
|
||||
|
||||
/**
|
||||
* Called when DOM blur event is fired.
|
||||
*/
|
||||
void NotifyOfDOMBlur(nsISupports* aTarget);
|
||||
|
||||
/**
|
||||
* Called when active item is changed. Note: must be called when accessible
|
||||
* tree is up to date.
|
||||
*/
|
||||
void ActiveItemChanged(Accessible* aItem, bool aCheckIfActive = true);
|
||||
|
||||
/**
|
||||
* Dispatch delayed focus event for the current focus accessible.
|
||||
*/
|
||||
void ForceFocusEvent();
|
||||
|
||||
/**
|
||||
* Dispatch delayed focus event for the given target.
|
||||
*/
|
||||
void DispatchFocusEvent(DocAccessible* aDocument, Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Process DOM focus notification.
|
||||
*/
|
||||
void ProcessDOMFocus(nsINode* aTarget);
|
||||
|
||||
/**
|
||||
* Process the delayed accessible event.
|
||||
* do.
|
||||
*/
|
||||
void ProcessFocusEvent(AccEvent* aEvent);
|
||||
|
||||
protected:
|
||||
FocusManager();
|
||||
|
||||
private:
|
||||
FocusManager(const FocusManager&);
|
||||
FocusManager& operator =(const FocusManager&);
|
||||
|
||||
/**
|
||||
* Return DOM node having DOM focus.
|
||||
*/
|
||||
nsINode* FocusedDOMNode() const;
|
||||
|
||||
/**
|
||||
* Return DOM document having DOM focus.
|
||||
*/
|
||||
nsIDocument* FocusedDOMDocument() const;
|
||||
|
||||
private:
|
||||
RefPtr<Accessible> mActiveItem;
|
||||
RefPtr<Accessible> mActiveARIAMenubar;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
1039
accessible/base/Logging.cpp
Normal file
1039
accessible/base/Logging.cpp
Normal file
File diff suppressed because it is too large
Load diff
225
accessible/base/Logging.h
Normal file
225
accessible/base/Logging.h
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/* -*- 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_logs_h__
|
||||
#define mozilla_a11y_logs_h__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
class nsIDocument;
|
||||
class nsINode;
|
||||
class nsIRequest;
|
||||
class nsISelection;
|
||||
class nsISupports;
|
||||
class nsIWebProgress;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class AccEvent;
|
||||
class Accessible;
|
||||
class DocAccessible;
|
||||
class OuterDocAccessible;
|
||||
|
||||
namespace logging {
|
||||
|
||||
enum EModules {
|
||||
eDocLoad = 1 << 0,
|
||||
eDocCreate = 1 << 1,
|
||||
eDocDestroy = 1 << 2,
|
||||
eDocLifeCycle = eDocLoad | eDocCreate | eDocDestroy,
|
||||
|
||||
eEvents = 1 << 3,
|
||||
eEventTree = 1 << 4,
|
||||
ePlatforms = 1 << 5,
|
||||
eText = 1 << 6,
|
||||
eTree = 1 << 7,
|
||||
|
||||
eDOMEvents = 1 << 8,
|
||||
eFocus = 1 << 9,
|
||||
eSelection = 1 << 10,
|
||||
eNotifications = eDOMEvents | eSelection | eFocus,
|
||||
|
||||
// extras
|
||||
eStack = 1 << 11,
|
||||
eVerbose = 1 << 12
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if any of the given modules is logged.
|
||||
*/
|
||||
bool IsEnabled(uint32_t aModules);
|
||||
|
||||
/**
|
||||
* Return true if all of the given modules are logged.
|
||||
*/
|
||||
bool IsEnabledAll(uint32_t aModules);
|
||||
|
||||
/**
|
||||
* Return true if the given module is logged.
|
||||
*/
|
||||
bool IsEnabled(const nsAString& aModules);
|
||||
|
||||
/**
|
||||
* Log the document loading progress.
|
||||
*/
|
||||
void DocLoad(const char* aMsg, nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest, uint32_t aStateFlags);
|
||||
void DocLoad(const char* aMsg, nsIDocument* aDocumentNode);
|
||||
void DocCompleteLoad(DocAccessible* aDocument, bool aIsLoadEventTarget);
|
||||
|
||||
/**
|
||||
* Log that document load event was fired.
|
||||
*/
|
||||
void DocLoadEventFired(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Log that document laod event was handled.
|
||||
*/
|
||||
void DocLoadEventHandled(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Log the document was created.
|
||||
*/
|
||||
void DocCreate(const char* aMsg, nsIDocument* aDocumentNode,
|
||||
DocAccessible* aDocument = nullptr);
|
||||
|
||||
/**
|
||||
* Log the document was destroyed.
|
||||
*/
|
||||
void DocDestroy(const char* aMsg, nsIDocument* aDocumentNode,
|
||||
DocAccessible* aDocument = nullptr);
|
||||
|
||||
/**
|
||||
* Log the outer document was destroyed.
|
||||
*/
|
||||
void OuterDocDestroy(OuterDocAccessible* OuterDoc);
|
||||
|
||||
/**
|
||||
* Log the focus notification target.
|
||||
*/
|
||||
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
|
||||
Accessible* aTarget);
|
||||
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
|
||||
nsINode* aTargetNode);
|
||||
void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
|
||||
nsISupports* aTargetThing);
|
||||
|
||||
/**
|
||||
* Log a cause of active item descendant change (submessage).
|
||||
*/
|
||||
void ActiveItemChangeCausedBy(const char* aMsg, Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Log the active widget (submessage).
|
||||
*/
|
||||
void ActiveWidget(Accessible* aWidget);
|
||||
|
||||
/**
|
||||
* Log the focus event was dispatched (submessage).
|
||||
*/
|
||||
void FocusDispatched(Accessible* aTarget);
|
||||
|
||||
/**
|
||||
* Log the selection change.
|
||||
*/
|
||||
void SelChange(nsISelection* aSelection, DocAccessible* aDocument,
|
||||
int16_t aReason);
|
||||
|
||||
/**
|
||||
* Log the given accessible elements info.
|
||||
*/
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...);
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags,
|
||||
const char* aMsg1, Accessible* aAcc,
|
||||
const char* aMsg2, nsINode* aNode);
|
||||
void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent);
|
||||
|
||||
/**
|
||||
* Log the accessible/DOM tree.
|
||||
*/
|
||||
typedef const char* (*GetTreePrefix)(void* aData, Accessible*);
|
||||
void Tree(const char* aTitle, const char* aMsgText, Accessible* aRoot,
|
||||
GetTreePrefix aPrefixFunc = nullptr, void* aGetTreePrefixData = nullptr);
|
||||
void DOMTree(const char* aTitle, const char* aMsgText, DocAccessible* aDoc);
|
||||
|
||||
/**
|
||||
* Log the message ('title: text' format) on new line. Print the start and end
|
||||
* boundaries of the message body designated by '{' and '}' (2 spaces indent for
|
||||
* body).
|
||||
*/
|
||||
void MsgBegin(const char* aTitle, const char* aMsgText, ...);
|
||||
void MsgEnd();
|
||||
|
||||
/**
|
||||
* Print start and end boundaries of the message body designated by '{' and '}'
|
||||
* (2 spaces indent for body).
|
||||
*/
|
||||
void SubMsgBegin();
|
||||
void SubMsgEnd();
|
||||
|
||||
/**
|
||||
* Log the entry into message body (4 spaces indent).
|
||||
*/
|
||||
void MsgEntry(const char* aEntryText, ...);
|
||||
|
||||
/**
|
||||
* Log the text, two spaces offset is used.
|
||||
*/
|
||||
void Text(const char* aText);
|
||||
|
||||
/**
|
||||
* Log the accessible object address as message entry (4 spaces indent).
|
||||
*/
|
||||
void Address(const char* aDescr, Accessible* aAcc);
|
||||
|
||||
/**
|
||||
* Log the DOM node info as message entry.
|
||||
*/
|
||||
void Node(const char* aDescr, nsINode* aNode);
|
||||
|
||||
/**
|
||||
* Log the document accessible info as message entry.
|
||||
*/
|
||||
void Document(DocAccessible* aDocument);
|
||||
|
||||
/**
|
||||
* Log the accessible and its DOM node as a message entry.
|
||||
*/
|
||||
void AccessibleInfo(const char* aDescr, Accessible* aAccessible);
|
||||
void AccessibleNNode(const char* aDescr, Accessible* aAccessible);
|
||||
void AccessibleNNode(const char* aDescr, nsINode* aNode);
|
||||
|
||||
/**
|
||||
* Log the DOM event.
|
||||
*/
|
||||
void DOMEvent(const char* aDescr, nsINode* aOrigTarget,
|
||||
const nsAString& aEventType);
|
||||
|
||||
/**
|
||||
* Log the call stack, two spaces offset is used.
|
||||
*/
|
||||
void Stack();
|
||||
|
||||
/**
|
||||
* Enable logging of the specified modules, all other modules aren't logged.
|
||||
*/
|
||||
void Enable(const nsAFlatCString& aModules);
|
||||
|
||||
/**
|
||||
* Enable logging of modules specified by A11YLOG environment variable,
|
||||
* all other modules aren't logged.
|
||||
*/
|
||||
void CheckEnv();
|
||||
|
||||
} // namespace logging
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
340
accessible/base/MarkupMap.h
Normal file
340
accessible/base/MarkupMap.h
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=2:
|
||||
*/
|
||||
/* 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/. */
|
||||
|
||||
MARKUPMAP(a,
|
||||
New_HTMLLink,
|
||||
roles::LINK)
|
||||
|
||||
MARKUPMAP(abbr,
|
||||
New_HyperText,
|
||||
0)
|
||||
|
||||
MARKUPMAP(acronym,
|
||||
New_HyperText,
|
||||
0)
|
||||
|
||||
MARKUPMAP(article,
|
||||
New_HyperText,
|
||||
roles::DOCUMENT,
|
||||
Attr(xmlroles, article))
|
||||
|
||||
MARKUPMAP(aside,
|
||||
New_HyperText,
|
||||
roles::NOTE)
|
||||
|
||||
MARKUPMAP(blockquote,
|
||||
New_HyperText,
|
||||
roles::SECTION)
|
||||
|
||||
MARKUPMAP(dd,
|
||||
New_HTMLDefinition,
|
||||
roles::DEFINITION)
|
||||
|
||||
MARKUPMAP(details,
|
||||
New_HyperText,
|
||||
roles::DETAILS)
|
||||
|
||||
MARKUPMAP(div,
|
||||
nullptr,
|
||||
roles::SECTION)
|
||||
|
||||
MARKUPMAP(dl,
|
||||
New_HTMLList,
|
||||
roles::DEFINITION_LIST)
|
||||
|
||||
MARKUPMAP(dt,
|
||||
New_HTMLListitem,
|
||||
roles::TERM)
|
||||
|
||||
MARKUPMAP(figcaption,
|
||||
New_HTMLFigcaption,
|
||||
roles::CAPTION)
|
||||
|
||||
MARKUPMAP(figure,
|
||||
New_HTMLFigure,
|
||||
roles::FIGURE,
|
||||
Attr(xmlroles, figure))
|
||||
|
||||
MARKUPMAP(form,
|
||||
New_HyperText,
|
||||
roles::FORM)
|
||||
|
||||
MARKUPMAP(footer,
|
||||
New_HyperText,
|
||||
roles::FOOTER)
|
||||
|
||||
MARKUPMAP(header,
|
||||
New_HyperText,
|
||||
roles::HEADER)
|
||||
|
||||
MARKUPMAP(h1,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(h2,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(h3,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(h4,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(h5,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(h6,
|
||||
New_HyperText,
|
||||
roles::HEADING)
|
||||
|
||||
MARKUPMAP(label,
|
||||
New_HTMLLabel,
|
||||
roles::LABEL)
|
||||
|
||||
MARKUPMAP(legend,
|
||||
New_HTMLLegend,
|
||||
roles::LABEL)
|
||||
|
||||
MARKUPMAP(li,
|
||||
New_HTMLListitem,
|
||||
0)
|
||||
|
||||
MARKUPMAP(map,
|
||||
nullptr,
|
||||
roles::TEXT_CONTAINER)
|
||||
|
||||
MARKUPMAP(math,
|
||||
New_HyperText,
|
||||
roles::MATHML_MATH)
|
||||
|
||||
MARKUPMAP(mi_,
|
||||
New_HyperText,
|
||||
roles::MATHML_IDENTIFIER)
|
||||
|
||||
MARKUPMAP(mn_,
|
||||
New_HyperText,
|
||||
roles::MATHML_NUMBER)
|
||||
|
||||
MARKUPMAP(mo_,
|
||||
New_HyperText,
|
||||
roles::MATHML_OPERATOR,
|
||||
AttrFromDOM(accent_, accent_),
|
||||
AttrFromDOM(fence_, fence_),
|
||||
AttrFromDOM(separator_, separator_),
|
||||
AttrFromDOM(largeop_, largeop_))
|
||||
|
||||
MARKUPMAP(mtext_,
|
||||
New_HyperText,
|
||||
roles::MATHML_TEXT)
|
||||
|
||||
MARKUPMAP(ms_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STRING_LITERAL)
|
||||
|
||||
MARKUPMAP(mglyph_,
|
||||
New_HyperText,
|
||||
roles::MATHML_GLYPH)
|
||||
|
||||
MARKUPMAP(mrow_,
|
||||
New_HyperText,
|
||||
roles::MATHML_ROW)
|
||||
|
||||
MARKUPMAP(mfrac_,
|
||||
New_HyperText,
|
||||
roles::MATHML_FRACTION,
|
||||
AttrFromDOM(bevelled_, bevelled_),
|
||||
AttrFromDOM(linethickness_, linethickness_))
|
||||
|
||||
MARKUPMAP(msqrt_,
|
||||
New_HyperText,
|
||||
roles::MATHML_SQUARE_ROOT)
|
||||
|
||||
MARKUPMAP(mroot_,
|
||||
New_HyperText,
|
||||
roles::MATHML_ROOT)
|
||||
|
||||
MARKUPMAP(mfenced_,
|
||||
New_HyperText,
|
||||
roles::MATHML_FENCED,
|
||||
AttrFromDOM(close, close),
|
||||
AttrFromDOM(open, open),
|
||||
AttrFromDOM(separators_, separators_))
|
||||
|
||||
MARKUPMAP(menclose_,
|
||||
New_HyperText,
|
||||
roles::MATHML_ENCLOSED,
|
||||
AttrFromDOM(notation_, notation_))
|
||||
|
||||
MARKUPMAP(mstyle_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STYLE)
|
||||
|
||||
MARKUPMAP(msub_,
|
||||
New_HyperText,
|
||||
roles::MATHML_SUB)
|
||||
|
||||
MARKUPMAP(msup_,
|
||||
New_HyperText,
|
||||
roles::MATHML_SUP)
|
||||
|
||||
MARKUPMAP(msubsup_,
|
||||
New_HyperText,
|
||||
roles::MATHML_SUB_SUP)
|
||||
|
||||
MARKUPMAP(munder_,
|
||||
New_HyperText,
|
||||
roles::MATHML_UNDER,
|
||||
AttrFromDOM(accentunder_, accentunder_),
|
||||
AttrFromDOM(align, align))
|
||||
|
||||
MARKUPMAP(mover_,
|
||||
New_HyperText,
|
||||
roles::MATHML_OVER,
|
||||
AttrFromDOM(accent_, accent_),
|
||||
AttrFromDOM(align, align))
|
||||
|
||||
MARKUPMAP(munderover_,
|
||||
New_HyperText,
|
||||
roles::MATHML_UNDER_OVER,
|
||||
AttrFromDOM(accent_, accent_),
|
||||
AttrFromDOM(accentunder_, accentunder_),
|
||||
AttrFromDOM(align, align))
|
||||
|
||||
MARKUPMAP(mmultiscripts_,
|
||||
New_HyperText,
|
||||
roles::MATHML_MULTISCRIPTS)
|
||||
|
||||
MARKUPMAP(mtable_,
|
||||
New_HTMLTableAccessible,
|
||||
roles::MATHML_TABLE,
|
||||
AttrFromDOM(align, align),
|
||||
AttrFromDOM(columnlines_, columnlines_),
|
||||
AttrFromDOM(rowlines_, rowlines_))
|
||||
|
||||
MARKUPMAP(mlabeledtr_,
|
||||
New_HTMLTableRowAccessible,
|
||||
roles::MATHML_LABELED_ROW)
|
||||
|
||||
MARKUPMAP(mtr_,
|
||||
New_HTMLTableRowAccessible,
|
||||
roles::MATHML_TABLE_ROW)
|
||||
|
||||
MARKUPMAP(mtd_,
|
||||
New_HTMLTableCellAccessible,
|
||||
roles::MATHML_CELL)
|
||||
|
||||
MARKUPMAP(maction_,
|
||||
New_HyperText,
|
||||
roles::MATHML_ACTION,
|
||||
AttrFromDOM(actiontype_, actiontype_),
|
||||
AttrFromDOM(selection_, selection_))
|
||||
|
||||
MARKUPMAP(merror_,
|
||||
New_HyperText,
|
||||
roles::MATHML_ERROR)
|
||||
|
||||
MARKUPMAP(mstack_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK,
|
||||
AttrFromDOM(align, align),
|
||||
AttrFromDOM(position, position))
|
||||
|
||||
MARKUPMAP(mlongdiv_,
|
||||
New_HyperText,
|
||||
roles::MATHML_LONG_DIVISION,
|
||||
AttrFromDOM(longdivstyle_, longdivstyle_))
|
||||
|
||||
MARKUPMAP(msgroup_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK_GROUP,
|
||||
AttrFromDOM(position, position),
|
||||
AttrFromDOM(shift_, shift_))
|
||||
|
||||
MARKUPMAP(msrow_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK_ROW,
|
||||
AttrFromDOM(position, position))
|
||||
|
||||
MARKUPMAP(mscarries_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK_CARRIES,
|
||||
AttrFromDOM(location_, location_),
|
||||
AttrFromDOM(position, position))
|
||||
|
||||
MARKUPMAP(mscarry_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK_CARRY,
|
||||
AttrFromDOM(crossout_, crossout_))
|
||||
|
||||
MARKUPMAP(msline_,
|
||||
New_HyperText,
|
||||
roles::MATHML_STACK_LINE,
|
||||
AttrFromDOM(position, position))
|
||||
|
||||
MARKUPMAP(nav,
|
||||
New_HyperText,
|
||||
roles::SECTION)
|
||||
|
||||
MARKUPMAP(ol,
|
||||
New_HTMLList,
|
||||
roles::LIST)
|
||||
|
||||
MARKUPMAP(option,
|
||||
New_HTMLOption,
|
||||
0)
|
||||
|
||||
MARKUPMAP(optgroup,
|
||||
New_HTMLOptgroup,
|
||||
0)
|
||||
|
||||
MARKUPMAP(output,
|
||||
New_HTMLOutput,
|
||||
roles::SECTION,
|
||||
Attr(live, polite))
|
||||
|
||||
MARKUPMAP(p,
|
||||
nullptr,
|
||||
roles::PARAGRAPH)
|
||||
|
||||
MARKUPMAP(progress,
|
||||
New_HTMLProgress,
|
||||
0)
|
||||
|
||||
MARKUPMAP(q,
|
||||
New_HyperText,
|
||||
0)
|
||||
|
||||
MARKUPMAP(section,
|
||||
New_HyperText,
|
||||
roles::SECTION,
|
||||
Attr(xmlroles, region))
|
||||
|
||||
MARKUPMAP(summary,
|
||||
New_HTMLSummary,
|
||||
roles::SUMMARY)
|
||||
|
||||
MARKUPMAP(time,
|
||||
New_HyperText,
|
||||
0,
|
||||
Attr(xmlroles, time),
|
||||
AttrFromDOM(datetime, datetime))
|
||||
|
||||
MARKUPMAP(td,
|
||||
New_HTMLTableHeaderCellIfScope,
|
||||
0)
|
||||
|
||||
MARKUPMAP(th,
|
||||
New_HTMLTableHeaderCell,
|
||||
0)
|
||||
|
||||
MARKUPMAP(ul,
|
||||
New_HTMLList,
|
||||
roles::LIST)
|
||||
947
accessible/base/NotificationController.cpp
Normal file
947
accessible/base/NotificationController.cpp
Normal file
|
|
@ -0,0 +1,947 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "NotificationController.h"
|
||||
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "DocAccessibleChild.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
#include "TextUpdater.h"
|
||||
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NotificationController::NotificationController(DocAccessible* aDocument,
|
||||
nsIPresShell* aPresShell) :
|
||||
EventQueue(aDocument), mObservingState(eNotObservingRefresh),
|
||||
mPresShell(aPresShell), mEventGeneration(0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
mMoveGuardOnStack = false;
|
||||
#endif
|
||||
|
||||
// Schedule initial accessible tree construction.
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
NotificationController::~NotificationController()
|
||||
{
|
||||
NS_ASSERTION(!mDocument, "Controller wasn't shutdown properly!");
|
||||
if (mDocument)
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: AddRef/Release and cycle collection
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_ADDREF(NotificationController)
|
||||
NS_IMPL_CYCLE_COLLECTING_NATIVE_RELEASE(NotificationController)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(NotificationController)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(NotificationController)
|
||||
if (tmp->mDocument)
|
||||
tmp->Shutdown();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(NotificationController)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHangingChildDocuments)
|
||||
for (auto it = tmp->mContentInsertions.ConstIter(); !it.Done(); it.Next()) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mContentInsertions key");
|
||||
cb.NoteXPCOMChild(it.Key());
|
||||
nsTArray<nsCOMPtr<nsIContent>>* list = it.UserData();
|
||||
for (uint32_t i = 0; i < list->Length(); i++) {
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mContentInsertions value item");
|
||||
cb.NoteXPCOMChild(list->ElementAt(i));
|
||||
}
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRelocations)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(NotificationController, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(NotificationController, Release)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: public
|
||||
|
||||
void
|
||||
NotificationController::Shutdown()
|
||||
{
|
||||
if (mObservingState != eNotObservingRefresh &&
|
||||
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingState = eNotObservingRefresh;
|
||||
}
|
||||
|
||||
// Shutdown handling child documents.
|
||||
int32_t childDocCount = mHangingChildDocuments.Length();
|
||||
for (int32_t idx = childDocCount - 1; idx >= 0; idx--) {
|
||||
if (!mHangingChildDocuments[idx]->IsDefunct())
|
||||
mHangingChildDocuments[idx]->Shutdown();
|
||||
}
|
||||
|
||||
mHangingChildDocuments.Clear();
|
||||
|
||||
mDocument = nullptr;
|
||||
mPresShell = nullptr;
|
||||
|
||||
mTextHash.Clear();
|
||||
mContentInsertions.Clear();
|
||||
mNotifications.Clear();
|
||||
mEvents.Clear();
|
||||
mRelocations.Clear();
|
||||
mEventTree.Clear();
|
||||
}
|
||||
|
||||
EventTree*
|
||||
NotificationController::QueueMutation(Accessible* aContainer)
|
||||
{
|
||||
EventTree* tree = mEventTree.FindOrInsert(aContainer);
|
||||
if (tree) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
bool
|
||||
NotificationController::QueueMutationEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
// We have to allow there to be a hide and then a show event for a target
|
||||
// because of targets getting moved. However we need to coalesce a show and
|
||||
// then a hide for a target which means we need to check for that here.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
|
||||
aEvent->GetAccessible()->ShowEventTarget()) {
|
||||
AccTreeMutationEvent* showEvent = mMutationMap.GetEvent(aEvent->GetAccessible(), EventMap::ShowEvent);
|
||||
DropMutationEvent(showEvent);
|
||||
return false;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(aEvent);
|
||||
mEventGeneration++;
|
||||
mutEvent->SetEventGeneration(mEventGeneration);
|
||||
|
||||
if (!mFirstMutationEvent) {
|
||||
mFirstMutationEvent = aEvent;
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
if (mLastMutationEvent) {
|
||||
NS_ASSERTION(!mLastMutationEvent->NextEvent(), "why isn't the last event the end?");
|
||||
mLastMutationEvent->SetNextEvent(aEvent);
|
||||
}
|
||||
|
||||
aEvent->SetPrevEvent(mLastMutationEvent);
|
||||
mLastMutationEvent = aEvent;
|
||||
mMutationMap.PutEvent(aEvent);
|
||||
|
||||
// Because we could be hiding the target of a show event we need to get rid
|
||||
// of any such events. It may be possible to do less than coallesce all
|
||||
// events, however that is easiest.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
CoalesceMutationEvents();
|
||||
|
||||
// mLastMutationEvent will point to something other than aEvent if and only
|
||||
// if aEvent was just coalesced away. In that case a parent accessible
|
||||
// must already have the required reorder and text change events so we are
|
||||
// done here.
|
||||
if (mLastMutationEvent != aEvent) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to fire a reorder event after all of the events targeted at shown or
|
||||
// hidden children of a container. So either queue a new one, or move an
|
||||
// existing one to the end of the queue if the container already has a
|
||||
// reorder event.
|
||||
Accessible* target = aEvent->GetAccessible();
|
||||
Accessible* container = aEvent->GetAccessible()->Parent();
|
||||
RefPtr<AccReorderEvent> reorder;
|
||||
if (!container->ReorderEventTarget()) {
|
||||
reorder = new AccReorderEvent(container);
|
||||
container->SetReorderEventTarget(true);
|
||||
mMutationMap.PutEvent(reorder);
|
||||
|
||||
// Since this is the first child of container that is changing, the name of
|
||||
// container may be changing.
|
||||
QueueNameChange(target);
|
||||
} else {
|
||||
AccReorderEvent* event = downcast_accEvent(mMutationMap.GetEvent(container, EventMap::ReorderEvent));
|
||||
reorder = event;
|
||||
if (mFirstMutationEvent == event) {
|
||||
mFirstMutationEvent = event->NextEvent();
|
||||
} else {
|
||||
event->PrevEvent()->SetNextEvent(event->NextEvent());
|
||||
}
|
||||
|
||||
event->NextEvent()->SetPrevEvent(event->PrevEvent());
|
||||
event->SetNextEvent(nullptr);
|
||||
}
|
||||
|
||||
reorder->SetEventGeneration(mEventGeneration);
|
||||
reorder->SetPrevEvent(mLastMutationEvent);
|
||||
mLastMutationEvent->SetNextEvent(reorder);
|
||||
mLastMutationEvent = reorder;
|
||||
|
||||
// It is not possible to have a text change event for something other than a
|
||||
// hyper text accessible.
|
||||
if (!container->IsHyperText()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mutEvent);
|
||||
|
||||
nsString text;
|
||||
aEvent->GetAccessible()->AppendTextTo(text);
|
||||
if (text.IsEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int32_t offset = container->AsHyperText()->GetChildOffset(target);
|
||||
AccTreeMutationEvent* prevEvent = aEvent->PrevEvent();
|
||||
while (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
prevEvent = prevEvent->PrevEvent();
|
||||
}
|
||||
|
||||
if (prevEvent && prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE &&
|
||||
mutEvent->IsHide()) {
|
||||
AccHideEvent* prevHide = downcast_accEvent(prevEvent);
|
||||
AccTextChangeEvent* prevTextChange = prevHide->mTextChangeEvent;
|
||||
if (prevTextChange && prevHide->Parent() == mutEvent->Parent()) {
|
||||
if (prevHide->mNextSibling == target) {
|
||||
target->AppendTextTo(prevTextChange->mModifiedText);
|
||||
prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
} else if (prevHide->mPrevSibling == target) {
|
||||
nsString temp;
|
||||
target->AppendTextTo(temp);
|
||||
|
||||
uint32_t extraLen = temp.Length();
|
||||
temp += prevTextChange->mModifiedText;;
|
||||
prevTextChange->mModifiedText = temp;
|
||||
prevTextChange->mStart -= extraLen;
|
||||
prevHide->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
} else if (prevEvent && mutEvent->IsShow() &&
|
||||
prevEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
AccShowEvent* prevShow = downcast_accEvent(prevEvent);
|
||||
AccTextChangeEvent* prevTextChange = prevShow->mTextChangeEvent;
|
||||
if (prevTextChange && prevShow->Parent() == target->Parent()) {
|
||||
int32_t index = target->IndexInParent();
|
||||
int32_t prevIndex = prevShow->GetAccessible()->IndexInParent();
|
||||
if (prevIndex + 1 == index) {
|
||||
target->AppendTextTo(prevTextChange->mModifiedText);
|
||||
prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
} else if (index + 1 == prevIndex) {
|
||||
nsString temp;
|
||||
target->AppendTextTo(temp);
|
||||
prevTextChange->mStart -= temp.Length();
|
||||
temp += prevTextChange->mModifiedText;
|
||||
prevTextChange->mModifiedText = temp;
|
||||
prevShow->mTextChangeEvent.swap(mutEvent->mTextChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!mutEvent->mTextChangeEvent) {
|
||||
mutEvent->mTextChangeEvent =
|
||||
new AccTextChangeEvent(container, offset, text, mutEvent->IsShow(),
|
||||
aEvent->mIsFromUserInput ? eFromUserInput : eNoUserInput);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::DropMutationEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
// unset the event bits since the event isn't being fired any more.
|
||||
if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
aEvent->GetAccessible()->SetReorderEventTarget(false);
|
||||
} else if (aEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
aEvent->GetAccessible()->SetShowEventTarget(false);
|
||||
} else {
|
||||
AccHideEvent* hideEvent = downcast_accEvent(aEvent);
|
||||
MOZ_ASSERT(hideEvent);
|
||||
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mDocument->ShutdownChildrenInSubtree(aEvent->GetAccessible());
|
||||
}
|
||||
}
|
||||
|
||||
// Do the work to splice the event out of the list.
|
||||
if (mFirstMutationEvent == aEvent) {
|
||||
mFirstMutationEvent = aEvent->NextEvent();
|
||||
} else {
|
||||
aEvent->PrevEvent()->SetNextEvent(aEvent->NextEvent());
|
||||
}
|
||||
|
||||
if (mLastMutationEvent == aEvent) {
|
||||
mLastMutationEvent = aEvent->PrevEvent();
|
||||
} else {
|
||||
aEvent->NextEvent()->SetPrevEvent(aEvent->PrevEvent());
|
||||
}
|
||||
|
||||
aEvent->SetPrevEvent(nullptr);
|
||||
aEvent->SetNextEvent(nullptr);
|
||||
mMutationMap.RemoveEvent(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::CoalesceMutationEvents()
|
||||
{
|
||||
AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
while (event) {
|
||||
AccTreeMutationEvent* nextEvent = event->NextEvent();
|
||||
uint32_t eventType = event->GetEventType();
|
||||
if (event->GetEventType() == nsIAccessibleEvent::EVENT_REORDER) {
|
||||
Accessible* acc = event->GetAccessible();
|
||||
while (acc) {
|
||||
if (acc->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if a parent of the reorder event's target is being hidden that
|
||||
// hide event's target must have a parent that is also a reorder event
|
||||
// target. That means we don't need this reorder event.
|
||||
if (acc->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
Accessible* parent = acc->Parent();
|
||||
if (parent->ReorderEventTarget()) {
|
||||
AccReorderEvent* reorder = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ReorderEvent));
|
||||
|
||||
// We want to make sure that a reorder event comes after any show or
|
||||
// hide events targeted at the children of its target. We keep the
|
||||
// invariant that event generation goes up as you are farther in the
|
||||
// queue, so we want to use the spot of the event with the higher
|
||||
// generation number, and keep that generation number.
|
||||
if (reorder && reorder->EventGeneration() < event->EventGeneration()) {
|
||||
reorder->SetEventGeneration(event->EventGeneration());
|
||||
|
||||
// It may be true that reorder was before event, and we coalesced
|
||||
// away all the show / hide events between them. In that case
|
||||
// event is already immediately after reorder in the queue and we
|
||||
// do not need to rearrange the list of events.
|
||||
if (event != reorder->NextEvent()) {
|
||||
// There really should be a show or hide event before the first
|
||||
// reorder event.
|
||||
if (reorder->PrevEvent()) {
|
||||
reorder->PrevEvent()->SetNextEvent(reorder->NextEvent());
|
||||
} else {
|
||||
mFirstMutationEvent = reorder->NextEvent();
|
||||
}
|
||||
|
||||
reorder->NextEvent()->SetPrevEvent(reorder->PrevEvent());
|
||||
event->PrevEvent()->SetNextEvent(reorder);
|
||||
reorder->SetPrevEvent(event->PrevEvent());
|
||||
event->SetPrevEvent(reorder);
|
||||
reorder->SetNextEvent(event);
|
||||
}
|
||||
}
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
acc = parent;
|
||||
}
|
||||
} else if (eventType == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
Accessible* parent = event->GetAccessible()->Parent();
|
||||
while (parent) {
|
||||
if (parent->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// if the parent of a show event is being either shown or hidden then
|
||||
// we don't need to fire a show event for a subtree of that change.
|
||||
if (parent->ShowEventTarget() || parent->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
parent = parent->Parent();
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(eventType == nsIAccessibleEvent::EVENT_HIDE, "mutation event list has an invalid event");
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(event);
|
||||
Accessible* parent = hideEvent->Parent();
|
||||
while (parent) {
|
||||
if (parent->IsDoc()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent->HideEventTarget()) {
|
||||
DropMutationEvent(event);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent->ShowEventTarget()) {
|
||||
AccShowEvent* showEvent = downcast_accEvent(mMutationMap.GetEvent(parent, EventMap::ShowEvent));
|
||||
if (showEvent->EventGeneration() < hideEvent->EventGeneration()) {
|
||||
DropMutationEvent(hideEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
parent = parent->Parent();
|
||||
}
|
||||
}
|
||||
|
||||
event = nextEvent;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ScheduleChildDocBinding(DocAccessible* aDocument)
|
||||
{
|
||||
// Schedule child document binding to the tree.
|
||||
mHangingChildDocuments.AppendElement(aDocument);
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ScheduleContentInsertion(Accessible* aContainer,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode)
|
||||
{
|
||||
nsTArray<nsCOMPtr<nsIContent>>* list =
|
||||
mContentInsertions.LookupOrAdd(aContainer);
|
||||
|
||||
bool needsProcessing = false;
|
||||
nsIContent* node = aStartChildNode;
|
||||
while (node != aEndChildNode) {
|
||||
// Notification triggers for content insertion even if no content was
|
||||
// actually inserted, check if the given content has a frame to discard
|
||||
// this case early.
|
||||
if (node->GetPrimaryFrame()) {
|
||||
if (list->AppendElement(node))
|
||||
needsProcessing = true;
|
||||
}
|
||||
node = node->GetNextSibling();
|
||||
}
|
||||
|
||||
if (needsProcessing) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ScheduleProcessing()
|
||||
{
|
||||
// If notification flush isn't planed yet start notification flush
|
||||
// asynchronously (after style and layout).
|
||||
if (mObservingState == eNotObservingRefresh) {
|
||||
if (mPresShell->AddRefreshObserver(this, Flush_Display))
|
||||
mObservingState = eRefreshObserving;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: protected
|
||||
|
||||
bool
|
||||
NotificationController::IsUpdatePending()
|
||||
{
|
||||
return mPresShell->IsLayoutFlushObserver() ||
|
||||
mObservingState == eRefreshProcessingForUpdate ||
|
||||
mContentInsertions.Count() != 0 || mNotifications.Length() != 0 ||
|
||||
mTextHash.Count() != 0 ||
|
||||
!mDocument->HasLoadState(DocAccessible::eTreeConstructed);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::ProcessMutationEvents()
|
||||
{
|
||||
// there is no reason to fire a hide event for a child of a show event
|
||||
// target. That can happen if something is inserted into the tree and
|
||||
// removed before the next refresh driver tick, but it should not be
|
||||
// observable outside gecko so it should be safe to coalesce away any such
|
||||
// events. This means that it should be fine to fire all of the hide events
|
||||
// first, and then deal with any shown subtrees.
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_HIDE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(event);
|
||||
if (mutEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire menupopup end event before a hide event if a menu goes away.
|
||||
|
||||
// XXX: We don't look into children of hidden subtree to find hiding
|
||||
// menupopup (as we did prior bug 570275) because we don't do that when
|
||||
// menu is showing (and that's impossible until bug 606924 is fixed).
|
||||
// Nevertheless we should do this at least because layout coalesces
|
||||
// the changes before our processing and we may miss some menupopup
|
||||
// events. Now we just want to be consistent in content insertion/removal
|
||||
// handling.
|
||||
if (event->mAccessible->ARIARole() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
event->mAccessible);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AccHideEvent* hideEvent = downcast_accEvent(event);
|
||||
if (hideEvent->NeedsShutdown()) {
|
||||
mDocument->ShutdownChildrenInSubtree(event->mAccessible);
|
||||
}
|
||||
}
|
||||
|
||||
// Group the show events by the parent of their target.
|
||||
nsDataHashtable<nsPtrHashKey<Accessible>, nsTArray<AccTreeMutationEvent*>> showEvents;
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_SHOW) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* parent = event->GetAccessible()->Parent();
|
||||
showEvents.GetOrInsert(parent).AppendElement(event);
|
||||
}
|
||||
|
||||
// We need to fire show events for the children of an accessible in the order
|
||||
// of their indices at this point. So sort each set of events for the same
|
||||
// container by the index of their target.
|
||||
for (auto iter = showEvents.Iter(); !iter.Done(); iter.Next()) {
|
||||
struct AccIdxComparator {
|
||||
bool LessThan(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
|
||||
{
|
||||
int32_t aIdx = a->GetAccessible()->IndexInParent();
|
||||
int32_t bIdx = b->GetAccessible()->IndexInParent();
|
||||
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
|
||||
return aIdx < bIdx;
|
||||
}
|
||||
bool Equals(const AccTreeMutationEvent* a, const AccTreeMutationEvent* b) const
|
||||
{
|
||||
DebugOnly<int32_t> aIdx = a->GetAccessible()->IndexInParent();
|
||||
DebugOnly<int32_t> bIdx = b->GetAccessible()->IndexInParent();
|
||||
MOZ_ASSERT(aIdx >= 0 && bIdx >= 0 && aIdx != bIdx);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
nsTArray<AccTreeMutationEvent*>& events = iter.Data();
|
||||
events.Sort(AccIdxComparator());
|
||||
for (AccTreeMutationEvent* event: events) {
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccMutationEvent* mutEvent = downcast_accEvent(event);
|
||||
if (mutEvent->mTextChangeEvent) {
|
||||
nsEventShell::FireEvent(mutEvent->mTextChangeEvent);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now we can fire the reorder events after all the show and hide events.
|
||||
for (AccTreeMutationEvent* event = mFirstMutationEvent;
|
||||
event; event = event->NextEvent()) {
|
||||
if (event->GetEventType() != nsIAccessibleEvent::EVENT_REORDER) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsEventShell::FireEvent(event);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
|
||||
Accessible* target = event->GetAccessible();
|
||||
target->Document()->MaybeNotifyOfValueChange(target);
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// NotificationCollector: private
|
||||
|
||||
void
|
||||
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
|
||||
Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
|
||||
|
||||
// If the document accessible that notification collector was created for is
|
||||
// now shut down, don't process notifications anymore.
|
||||
NS_ASSERTION(mDocument,
|
||||
"The document was shut down while refresh observer is attached!");
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
if (mObservingState == eRefreshProcessing ||
|
||||
mObservingState == eRefreshProcessingForUpdate)
|
||||
return;
|
||||
|
||||
// Any generic notifications should be queued if we're processing content
|
||||
// insertions or generic notifications.
|
||||
mObservingState = eRefreshProcessingForUpdate;
|
||||
|
||||
// Initial accessible tree construction.
|
||||
if (!mDocument->HasLoadState(DocAccessible::eTreeConstructed)) {
|
||||
// If document is not bound to parent at this point then the document is not
|
||||
// ready yet (process notifications later).
|
||||
if (!mDocument->IsBoundToParent()) {
|
||||
mObservingState = eRefreshObserving;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree)) {
|
||||
logging::MsgBegin("TREE", "initial tree created");
|
||||
logging::Address("document", mDocument);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
mDocument->DoInitialUpdate();
|
||||
|
||||
NS_ASSERTION(mContentInsertions.Count() == 0,
|
||||
"Pending content insertions while initial accessible tree isn't created!");
|
||||
}
|
||||
|
||||
// Initialize scroll support if needed.
|
||||
if (!(mDocument->mDocFlags & DocAccessible::eScrollInitialized))
|
||||
mDocument->AddScrollListener();
|
||||
|
||||
// Process rendered text change notifications.
|
||||
for (auto iter = mTextHash.Iter(); !iter.Done(); iter.Next()) {
|
||||
nsCOMPtrHashKey<nsIContent>* entry = iter.Get();
|
||||
nsIContent* textNode = entry->GetKey();
|
||||
Accessible* textAcc = mDocument->GetAccessible(textNode);
|
||||
|
||||
// If the text node is not in tree or doesn't have frame then this case should
|
||||
// have been handled already by content removal notifications.
|
||||
nsINode* containerNode = textNode->GetParentNode();
|
||||
if (!containerNode) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node was removed but accessible is kept alive!");
|
||||
continue;
|
||||
}
|
||||
|
||||
nsIFrame* textFrame = textNode->GetPrimaryFrame();
|
||||
if (!textFrame) {
|
||||
NS_ASSERTION(!textAcc,
|
||||
"Text node isn't rendered but accessible is kept alive!");
|
||||
continue;
|
||||
}
|
||||
|
||||
nsIContent* containerElm = containerNode->IsElement() ?
|
||||
containerNode->AsElement() : nullptr;
|
||||
|
||||
nsIFrame::RenderedText text = textFrame->GetRenderedText(0,
|
||||
UINT32_MAX, nsIFrame::TextOffsetType::OFFSETS_IN_CONTENT_TEXT,
|
||||
nsIFrame::TrailingWhitespace::DONT_TRIM_TRAILING_WHITESPACE);
|
||||
|
||||
// Remove text accessible if rendered text is empty.
|
||||
if (textAcc) {
|
||||
if (text.mString.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node lost its content; doc: %p", mDocument);
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
mDocument->ContentRemoved(containerElm, textNode);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Update text of the accessible and fire text change events.
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eText)) {
|
||||
logging::MsgBegin("TEXT", "text may be changed; doc: %p", mDocument);
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEntry("old text '%s'",
|
||||
NS_ConvertUTF16toUTF8(textAcc->AsTextLeaf()->Text()).get());
|
||||
logging::MsgEntry("new text: '%s'",
|
||||
NS_ConvertUTF16toUTF8(text.mString).get());
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
TextUpdater::Run(mDocument, textAcc->AsTextLeaf(), text.mString);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Append an accessible if rendered text is not empty.
|
||||
if (!text.mString.IsEmpty()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eTree | logging::eText)) {
|
||||
logging::MsgBegin("TREE", "text node gains new content; doc: %p", mDocument);
|
||||
logging::Node("container", containerElm);
|
||||
logging::Node("content", textNode);
|
||||
logging::MsgEnd();
|
||||
}
|
||||
#endif
|
||||
|
||||
Accessible* container = mDocument->AccessibleOrTrueContainer(containerNode);
|
||||
MOZ_ASSERT(container,
|
||||
"Text node having rendered text hasn't accessible document!");
|
||||
if (container) {
|
||||
nsTArray<nsCOMPtr<nsIContent>>* list =
|
||||
mContentInsertions.LookupOrAdd(container);
|
||||
list->AppendElement(textNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
mTextHash.Clear();
|
||||
|
||||
// Process content inserted notifications to update the tree.
|
||||
for (auto iter = mContentInsertions.ConstIter(); !iter.Done(); iter.Next()) {
|
||||
mDocument->ProcessContentInserted(iter.Key(), iter.UserData());
|
||||
if (!mDocument) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mContentInsertions.Clear();
|
||||
|
||||
// Bind hanging child documents.
|
||||
uint32_t hangingDocCnt = mHangingChildDocuments.Length();
|
||||
nsTArray<RefPtr<DocAccessible>> newChildDocs;
|
||||
for (uint32_t idx = 0; idx < hangingDocCnt; idx++) {
|
||||
DocAccessible* childDoc = mHangingChildDocuments[idx];
|
||||
if (childDoc->IsDefunct())
|
||||
continue;
|
||||
|
||||
nsIContent* ownerContent = mDocument->DocumentNode()->
|
||||
FindContentForSubDocument(childDoc->DocumentNode());
|
||||
if (ownerContent) {
|
||||
Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
|
||||
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
|
||||
if (mDocument->AppendChildDocument(childDoc)) {
|
||||
newChildDocs.AppendElement(Move(mHangingChildDocuments[idx]));
|
||||
continue;
|
||||
}
|
||||
|
||||
outerDocAcc->RemoveChild(childDoc);
|
||||
}
|
||||
|
||||
// Failed to bind the child document, destroy it.
|
||||
childDoc->Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
mHangingChildDocuments.Clear();
|
||||
|
||||
// If the document is ready and all its subdocuments are completely loaded
|
||||
// then process the document load.
|
||||
if (mDocument->HasLoadState(DocAccessible::eReady) &&
|
||||
!mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
|
||||
hangingDocCnt == 0) {
|
||||
uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
|
||||
for (; childDocIdx < childDocCnt; childDocIdx++) {
|
||||
DocAccessible* childDoc = mDocument->GetChildDocumentAt(childDocIdx);
|
||||
if (!childDoc->HasLoadState(DocAccessible::eCompletelyLoaded))
|
||||
break;
|
||||
}
|
||||
|
||||
if (childDocIdx == childDocCnt) {
|
||||
mDocument->ProcessLoad();
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Process only currently queued generic notifications.
|
||||
nsTArray < RefPtr<Notification> > notifications;
|
||||
notifications.SwapElements(mNotifications);
|
||||
|
||||
uint32_t notificationCount = notifications.Length();
|
||||
for (uint32_t idx = 0; idx < notificationCount; idx++) {
|
||||
notifications[idx]->Process();
|
||||
if (!mDocument)
|
||||
return;
|
||||
}
|
||||
|
||||
// Process invalidation list of the document after all accessible tree
|
||||
// modification are done.
|
||||
mDocument->ProcessInvalidationList();
|
||||
|
||||
// We cannot rely on DOM tree to keep aria-owns relations updated. Make
|
||||
// a validation to remove dead links.
|
||||
mDocument->ValidateARIAOwned();
|
||||
|
||||
// Process relocation list.
|
||||
for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
|
||||
if (mRelocations[idx]->IsInDocument()) {
|
||||
mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
|
||||
}
|
||||
}
|
||||
mRelocations.Clear();
|
||||
|
||||
// If a generic notification occurs after this point then we may be allowed to
|
||||
// process it synchronously. However we do not want to reenter if fireing
|
||||
// events causes script to run.
|
||||
mObservingState = eRefreshProcessing;
|
||||
|
||||
CoalesceMutationEvents();
|
||||
ProcessMutationEvents();
|
||||
mEventGeneration = 0;
|
||||
|
||||
// Now that we are done with them get rid of the events we fired.
|
||||
RefPtr<AccTreeMutationEvent> mutEvent = Move(mFirstMutationEvent);
|
||||
mLastMutationEvent = nullptr;
|
||||
mFirstMutationEvent = nullptr;
|
||||
while (mutEvent) {
|
||||
RefPtr<AccTreeMutationEvent> nextEvent = mutEvent->NextEvent();
|
||||
Accessible* target = mutEvent->GetAccessible();
|
||||
|
||||
// We need to be careful here, while it may seem that we can simply 0 all
|
||||
// the pending event bits that is not true. Because accessibles may be
|
||||
// reparented they may be the target of both a hide event and a show event
|
||||
// at the same time.
|
||||
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_SHOW) {
|
||||
target->SetShowEventTarget(false);
|
||||
}
|
||||
|
||||
if (mutEvent->GetEventType() == nsIAccessibleEvent::EVENT_HIDE) {
|
||||
target->SetHideEventTarget(false);
|
||||
}
|
||||
|
||||
// However it is not possible for a reorder event target to also be the
|
||||
// target of a show or hide, so we can just zero that.
|
||||
target->SetReorderEventTarget(false);
|
||||
|
||||
mutEvent->SetPrevEvent(nullptr);
|
||||
mutEvent->SetNextEvent(nullptr);
|
||||
mMutationMap.RemoveEvent(mutEvent);
|
||||
mutEvent = nextEvent;
|
||||
}
|
||||
|
||||
ProcessEventQueue();
|
||||
|
||||
if (IPCAccessibilityActive()) {
|
||||
size_t newDocCount = newChildDocs.Length();
|
||||
for (size_t i = 0; i < newDocCount; i++) {
|
||||
DocAccessible* childDoc = newChildDocs[i];
|
||||
if (childDoc->IsDefunct()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Accessible* parent = childDoc->Parent();
|
||||
DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
|
||||
uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
|
||||
MOZ_ASSERT(id);
|
||||
DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
|
||||
if (ipcDoc) {
|
||||
parentIPCDoc->SendBindChildDoc(ipcDoc, id);
|
||||
continue;
|
||||
}
|
||||
|
||||
ipcDoc = new DocAccessibleChild(childDoc);
|
||||
childDoc->SetIPCDoc(ipcDoc);
|
||||
|
||||
#if defined(XP_WIN)
|
||||
MOZ_ASSERT(parentIPCDoc);
|
||||
parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
|
||||
AccessibleWrap::GetChildIDFor(childDoc));
|
||||
#else
|
||||
nsCOMPtr<nsITabChild> tabChild =
|
||||
do_GetInterface(mDocument->DocumentNode()->GetDocShell());
|
||||
if (tabChild) {
|
||||
MOZ_ASSERT(parentIPCDoc);
|
||||
static_cast<TabChild*>(tabChild.get())->
|
||||
SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
mObservingState = eRefreshObserving;
|
||||
if (!mDocument)
|
||||
return;
|
||||
|
||||
// Stop further processing if there are no new notifications of any kind or
|
||||
// events and document load is processed.
|
||||
if (mContentInsertions.Count() == 0 && mNotifications.IsEmpty() &&
|
||||
mEvents.IsEmpty() && mTextHash.Count() == 0 &&
|
||||
mHangingChildDocuments.IsEmpty() &&
|
||||
mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
|
||||
mPresShell->RemoveRefreshObserver(this, Flush_Display)) {
|
||||
mObservingState = eNotObservingRefresh;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::EventMap::PutEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
EventType type = GetEventType(aEvent);
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
|
||||
addr |= type;
|
||||
mTable.Put(addr, aEvent);
|
||||
}
|
||||
|
||||
AccTreeMutationEvent*
|
||||
NotificationController::EventMap::GetEvent(Accessible* aTarget, EventType aType)
|
||||
{
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aTarget);
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "target is not 4 byte aligned");
|
||||
|
||||
addr |= aType;
|
||||
return mTable.GetWeak(addr);
|
||||
}
|
||||
|
||||
void
|
||||
NotificationController::EventMap::RemoveEvent(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
EventType type = GetEventType(aEvent);
|
||||
uint64_t addr = reinterpret_cast<uintptr_t>(aEvent->GetAccessible());
|
||||
MOZ_ASSERT((addr & 0x3) == 0, "accessible is not 4 byte aligned");
|
||||
addr |= type;
|
||||
|
||||
MOZ_ASSERT(mTable.GetWeak(addr) == aEvent, "mTable has the wrong event");
|
||||
mTable.Remove(addr);
|
||||
}
|
||||
|
||||
NotificationController::EventMap::EventType
|
||||
NotificationController::EventMap::GetEventType(AccTreeMutationEvent* aEvent)
|
||||
{
|
||||
switch(aEvent->GetEventType())
|
||||
{
|
||||
case nsIAccessibleEvent::EVENT_SHOW:
|
||||
return ShowEvent;
|
||||
case nsIAccessibleEvent::EVENT_HIDE:
|
||||
return HideEvent;
|
||||
case nsIAccessibleEvent::EVENT_REORDER:
|
||||
return ReorderEvent;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("event has invalid type");
|
||||
return ShowEvent;
|
||||
}
|
||||
}
|
||||
439
accessible/base/NotificationController.h
Normal file
439
accessible/base/NotificationController.h
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_NotificationController_h_
|
||||
#define mozilla_a11y_NotificationController_h_
|
||||
|
||||
#include "EventQueue.h"
|
||||
#include "EventTree.h"
|
||||
|
||||
#include "mozilla/IndexSequence.h"
|
||||
#include "mozilla/Tuple.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
#include "Logging.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessible;
|
||||
|
||||
/**
|
||||
* Notification interface.
|
||||
*/
|
||||
class Notification
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(mozilla::a11y::Notification)
|
||||
|
||||
/**
|
||||
* Process notification.
|
||||
*/
|
||||
virtual void Process() = 0;
|
||||
|
||||
protected:
|
||||
Notification() { }
|
||||
|
||||
/**
|
||||
* Protected destructor, to discourage deletion outside of Release():
|
||||
*/
|
||||
virtual ~Notification() { }
|
||||
|
||||
private:
|
||||
Notification(const Notification&);
|
||||
Notification& operator = (const Notification&);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Template class for generic notification.
|
||||
*
|
||||
* @note Instance is kept as a weak ref, the caller must guarantee it exists
|
||||
* longer than the document accessible owning the notification controller
|
||||
* that this notification is processed by.
|
||||
*/
|
||||
template<class Class, class ... Args>
|
||||
class TNotification : public Notification
|
||||
{
|
||||
public:
|
||||
typedef void (Class::*Callback)(Args* ...);
|
||||
|
||||
TNotification(Class* aInstance, Callback aCallback, Args* ... aArgs) :
|
||||
mInstance(aInstance), mCallback(aCallback), mArgs(aArgs...) { }
|
||||
virtual ~TNotification() { mInstance = nullptr; }
|
||||
|
||||
virtual void Process() override
|
||||
{ ProcessHelper(typename IndexSequenceFor<Args...>::Type()); }
|
||||
|
||||
private:
|
||||
TNotification(const TNotification&);
|
||||
TNotification& operator = (const TNotification&);
|
||||
|
||||
template <size_t... Indices>
|
||||
void ProcessHelper(IndexSequence<Indices...>)
|
||||
{
|
||||
(mInstance->*mCallback)(Get<Indices>(mArgs)...);
|
||||
}
|
||||
|
||||
Class* mInstance;
|
||||
Callback mCallback;
|
||||
Tuple<RefPtr<Args> ...> mArgs;
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to process notifications from core for the document accessible.
|
||||
*/
|
||||
class NotificationController final : public EventQueue,
|
||||
public nsARefreshObserver
|
||||
{
|
||||
public:
|
||||
NotificationController(DocAccessible* aDocument, nsIPresShell* aPresShell);
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release(void) override;
|
||||
|
||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(NotificationController)
|
||||
|
||||
/**
|
||||
* Shutdown the notification controller.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Add an accessible event into the queue to process it later.
|
||||
*/
|
||||
void QueueEvent(AccEvent* aEvent)
|
||||
{
|
||||
if (PushEvent(aEvent)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a name change event into the queue for a container of
|
||||
* the given accessible, if the accessible is a part of name computation of
|
||||
* the container.
|
||||
*/
|
||||
void QueueNameChange(Accessible* aChangeTarget)
|
||||
{
|
||||
if (PushNameChange(aChangeTarget)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns existing event tree for the given the accessible or creates one if
|
||||
* it doesn't exists yet.
|
||||
*/
|
||||
EventTree* QueueMutation(Accessible* aContainer);
|
||||
|
||||
class MoveGuard final {
|
||||
public:
|
||||
explicit MoveGuard(NotificationController* aController) :
|
||||
mController(aController)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(!mController->mMoveGuardOnStack,
|
||||
"Move guard is on stack already!");
|
||||
mController->mMoveGuardOnStack = true;
|
||||
#endif
|
||||
}
|
||||
~MoveGuard() {
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(mController->mMoveGuardOnStack, "No move guard on stack!");
|
||||
mController->mMoveGuardOnStack = false;
|
||||
#endif
|
||||
mController->mPrecedingEvents.Clear();
|
||||
}
|
||||
|
||||
private:
|
||||
NotificationController* mController;
|
||||
};
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
const EventTree& RootEventTree() const { return mEventTree; };
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Queue a mutation event to emit if not coalesced away. Returns true if the
|
||||
* event was queued and has not yet been coalesced.
|
||||
*/
|
||||
bool QueueMutationEvent(AccTreeMutationEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Coalesce all queued mutation events.
|
||||
*/
|
||||
void CoalesceMutationEvents();
|
||||
|
||||
/**
|
||||
* Schedule binding the child document to the tree of this document.
|
||||
*/
|
||||
void ScheduleChildDocBinding(DocAccessible* aDocument);
|
||||
|
||||
/**
|
||||
* Schedule the accessible tree update because of rendered text changes.
|
||||
*/
|
||||
inline void ScheduleTextUpdate(nsIContent* aTextNode)
|
||||
{
|
||||
// Make sure we are not called with a node that is not in the DOM tree or
|
||||
// not visible.
|
||||
MOZ_ASSERT(aTextNode->GetParentNode(), "A text node is not in DOM");
|
||||
MOZ_ASSERT(aTextNode->GetPrimaryFrame(), "A text node doesn't have a frame");
|
||||
MOZ_ASSERT(aTextNode->GetPrimaryFrame()->StyleVisibility()->IsVisible(),
|
||||
"A text node is not visible");
|
||||
|
||||
mTextHash.PutEntry(aTextNode);
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pend accessible tree update for content insertion.
|
||||
*/
|
||||
void ScheduleContentInsertion(Accessible* aContainer,
|
||||
nsIContent* aStartChildNode,
|
||||
nsIContent* aEndChildNode);
|
||||
|
||||
/**
|
||||
* Pend an accessible subtree relocation.
|
||||
*/
|
||||
void ScheduleRelocation(Accessible* aOwner)
|
||||
{
|
||||
if (!mRelocations.Contains(aOwner) && mRelocations.AppendElement(aOwner)) {
|
||||
ScheduleProcessing();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start to observe refresh to make notifications and events processing after
|
||||
* layout.
|
||||
*/
|
||||
void ScheduleProcessing();
|
||||
|
||||
/**
|
||||
* Process the generic notification synchronously if there are no pending
|
||||
* layout changes and no notifications are pending or being processed right
|
||||
* now. Otherwise, queue it up to process asynchronously.
|
||||
*
|
||||
* @note The caller must guarantee that the given instance still exists when
|
||||
* the notification is processed.
|
||||
*/
|
||||
template<class Class, class Arg>
|
||||
inline void HandleNotification(Class* aInstance,
|
||||
typename TNotification<Class, Arg>::Callback aMethod,
|
||||
Arg* aArg)
|
||||
{
|
||||
if (!IsUpdatePending()) {
|
||||
#ifdef A11Y_LOG
|
||||
if (mozilla::a11y::logging::IsEnabled(mozilla::a11y::logging::eNotifications))
|
||||
mozilla::a11y::logging::Text("sync notification processing");
|
||||
#endif
|
||||
(aInstance->*aMethod)(aArg);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Notification> notification =
|
||||
new TNotification<Class, Arg>(aInstance, aMethod, aArg);
|
||||
if (notification && mNotifications.AppendElement(notification))
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule the generic notification to process asynchronously.
|
||||
*
|
||||
* @note The caller must guarantee that the given instance still exists when
|
||||
* the notification is processed.
|
||||
*/
|
||||
template<class Class>
|
||||
inline void ScheduleNotification(Class* aInstance,
|
||||
typename TNotification<Class>::Callback aMethod)
|
||||
{
|
||||
RefPtr<Notification> notification =
|
||||
new TNotification<Class>(aInstance, aMethod);
|
||||
if (notification && mNotifications.AppendElement(notification))
|
||||
ScheduleProcessing();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsUpdating() const
|
||||
{ return mObservingState == eRefreshProcessingForUpdate; }
|
||||
#endif
|
||||
|
||||
protected:
|
||||
virtual ~NotificationController();
|
||||
|
||||
nsCycleCollectingAutoRefCnt mRefCnt;
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
/**
|
||||
* Return true if the accessible tree state update is pending.
|
||||
*/
|
||||
bool IsUpdatePending();
|
||||
|
||||
private:
|
||||
NotificationController(const NotificationController&);
|
||||
NotificationController& operator = (const NotificationController&);
|
||||
|
||||
// nsARefreshObserver
|
||||
virtual void WillRefresh(mozilla::TimeStamp aTime) override;
|
||||
|
||||
/**
|
||||
* Set and returns a hide event, paired with a show event, for the move.
|
||||
*/
|
||||
void WithdrawPrecedingEvents(nsTArray<RefPtr<AccHideEvent>>* aEvs)
|
||||
{
|
||||
if (mPrecedingEvents.Length() > 0) {
|
||||
aEvs->AppendElements(mozilla::Move(mPrecedingEvents));
|
||||
}
|
||||
}
|
||||
void StorePrecedingEvent(AccHideEvent* aEv)
|
||||
{
|
||||
MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
|
||||
mPrecedingEvents.AppendElement(aEv);
|
||||
}
|
||||
void StorePrecedingEvents(nsTArray<RefPtr<AccHideEvent>>&& aEvs)
|
||||
{
|
||||
MOZ_ASSERT(mMoveGuardOnStack, "No move guard on stack!");
|
||||
mPrecedingEvents.InsertElementsAt(0, aEvs);
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* get rid of a mutation event that is no longer necessary.
|
||||
*/
|
||||
void DropMutationEvent(AccTreeMutationEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Fire all necessary mutation events.
|
||||
*/
|
||||
void ProcessMutationEvents();
|
||||
|
||||
/**
|
||||
* Indicates whether we're waiting on an event queue processing from our
|
||||
* notification controller to flush events.
|
||||
*/
|
||||
enum eObservingState {
|
||||
eNotObservingRefresh,
|
||||
eRefreshObserving,
|
||||
eRefreshProcessing,
|
||||
eRefreshProcessingForUpdate
|
||||
};
|
||||
eObservingState mObservingState;
|
||||
|
||||
/**
|
||||
* The presshell of the document accessible.
|
||||
*/
|
||||
nsIPresShell* mPresShell;
|
||||
|
||||
/**
|
||||
* Child documents that needs to be bound to the tree.
|
||||
*/
|
||||
nsTArray<RefPtr<DocAccessible> > mHangingChildDocuments;
|
||||
|
||||
/**
|
||||
* Pending accessible tree update notifications for content insertions.
|
||||
*/
|
||||
nsClassHashtable<nsRefPtrHashKey<Accessible>,
|
||||
nsTArray<nsCOMPtr<nsIContent>>> mContentInsertions;
|
||||
|
||||
template<class T>
|
||||
class nsCOMPtrHashKey : public PLDHashEntryHdr
|
||||
{
|
||||
public:
|
||||
typedef T* KeyType;
|
||||
typedef const T* KeyTypePointer;
|
||||
|
||||
explicit nsCOMPtrHashKey(const T* aKey) : mKey(const_cast<T*>(aKey)) {}
|
||||
explicit nsCOMPtrHashKey(const nsPtrHashKey<T> &aToCopy) : mKey(aToCopy.mKey) {}
|
||||
~nsCOMPtrHashKey() { }
|
||||
|
||||
KeyType GetKey() const { return mKey; }
|
||||
bool KeyEquals(KeyTypePointer aKey) const { return aKey == mKey; }
|
||||
|
||||
static KeyTypePointer KeyToPointer(KeyType aKey) { return aKey; }
|
||||
static PLDHashNumber HashKey(KeyTypePointer aKey)
|
||||
{ return NS_PTR_TO_INT32(aKey) >> 2; }
|
||||
|
||||
enum { ALLOW_MEMMOVE = true };
|
||||
|
||||
protected:
|
||||
nsCOMPtr<T> mKey;
|
||||
};
|
||||
|
||||
/**
|
||||
* Pending accessible tree update notifications for rendered text changes.
|
||||
*/
|
||||
nsTHashtable<nsCOMPtrHashKey<nsIContent> > mTextHash;
|
||||
|
||||
/**
|
||||
* Other notifications like DOM events. Don't make this an AutoTArray; we
|
||||
* use SwapElements() on it.
|
||||
*/
|
||||
nsTArray<RefPtr<Notification> > mNotifications;
|
||||
|
||||
/**
|
||||
* Holds all scheduled relocations.
|
||||
*/
|
||||
nsTArray<RefPtr<Accessible> > mRelocations;
|
||||
|
||||
/**
|
||||
* Holds all mutation events.
|
||||
*/
|
||||
EventTree mEventTree;
|
||||
|
||||
/**
|
||||
* A temporary collection of hide events that should be fired before related
|
||||
* show event. Used by EventTree.
|
||||
*/
|
||||
nsTArray<RefPtr<AccHideEvent>> mPrecedingEvents;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mMoveGuardOnStack;
|
||||
#endif
|
||||
|
||||
friend class MoveGuard;
|
||||
friend class EventTree;
|
||||
|
||||
/**
|
||||
* A list of all mutation events we may want to emit. Ordered from the first
|
||||
* event that should be emitted to the last one to emit.
|
||||
*/
|
||||
RefPtr<AccTreeMutationEvent> mFirstMutationEvent;
|
||||
RefPtr<AccTreeMutationEvent> mLastMutationEvent;
|
||||
|
||||
/**
|
||||
* A class to map an accessible and event type to an event.
|
||||
*/
|
||||
class EventMap
|
||||
{
|
||||
public:
|
||||
enum EventType
|
||||
{
|
||||
ShowEvent = 0x0,
|
||||
HideEvent = 0x1,
|
||||
ReorderEvent = 0x2,
|
||||
};
|
||||
|
||||
void PutEvent(AccTreeMutationEvent* aEvent);
|
||||
AccTreeMutationEvent* GetEvent(Accessible* aTarget, EventType aType);
|
||||
void RemoveEvent(AccTreeMutationEvent* aEvent);
|
||||
void Clear() { mTable.Clear(); }
|
||||
|
||||
private:
|
||||
EventType GetEventType(AccTreeMutationEvent* aEvent);
|
||||
|
||||
nsRefPtrHashtable<nsUint64HashKey, AccTreeMutationEvent> mTable;
|
||||
};
|
||||
|
||||
EventMap mMutationMap;
|
||||
uint32_t mEventGeneration;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_NotificationController_h_
|
||||
88
accessible/base/Platform.h
Normal file
88
accessible/base/Platform.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* -*- 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_Platform_h
|
||||
#define mozilla_a11y_Platform_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class nsString;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class ProxyAccessible;
|
||||
|
||||
enum EPlatformDisabledState {
|
||||
ePlatformIsForceEnabled = -1,
|
||||
ePlatformIsEnabled = 0,
|
||||
ePlatformIsDisabled = 1
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the platform disabled state.
|
||||
*/
|
||||
EPlatformDisabledState PlatformDisabledState();
|
||||
|
||||
#ifdef MOZ_ACCESSIBILITY_ATK
|
||||
/**
|
||||
* Perform initialization that should be done as soon as possible, in order
|
||||
* to minimize startup time.
|
||||
* XXX: this function and the next defined in ApplicationAccessibleWrap.cpp
|
||||
*/
|
||||
void PreInit();
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_ACCESSIBILITY_ATK) || defined(XP_MACOSX)
|
||||
/**
|
||||
* Is platform accessibility enabled.
|
||||
* Only used on linux with atk and MacOS for now.
|
||||
*/
|
||||
bool ShouldA11yBeEnabled();
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Called to initialize platform specific accessibility support.
|
||||
* Note this is called after internal accessibility support is initialized.
|
||||
*/
|
||||
void PlatformInit();
|
||||
|
||||
/**
|
||||
* Shutdown platform accessibility.
|
||||
* Note this is called before internal accessibility support is shutdown.
|
||||
*/
|
||||
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* aProxy, uint32_t aInterfaces);
|
||||
|
||||
/**
|
||||
* Called just before a ProxyAccessible is destroyed so its wrapper can be
|
||||
* disposed of and other action taken.
|
||||
*/
|
||||
void ProxyDestroyed(ProxyAccessible*);
|
||||
|
||||
/**
|
||||
* Callied when an event is fired on a proxied accessible.
|
||||
*/
|
||||
void ProxyEvent(ProxyAccessible* aTarget, uint32_t aEventType);
|
||||
void ProxyStateChangeEvent(ProxyAccessible* aTarget, uint64_t aState,
|
||||
bool aEnabled);
|
||||
void ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset);
|
||||
void ProxyTextChangeEvent(ProxyAccessible* aTarget, const nsString& aStr,
|
||||
int32_t aStart, uint32_t aLen, bool aIsInsert,
|
||||
bool aFromUser);
|
||||
void ProxyShowHideEvent(ProxyAccessible* aTarget, ProxyAccessible* aParent,
|
||||
bool aInsert, bool aFromUser);
|
||||
void ProxySelectionEvent(ProxyAccessible* aTarget, ProxyAccessible* aWidget,
|
||||
uint32_t aType);
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_Platform_h
|
||||
108
accessible/base/Relation.h
Normal file
108
accessible/base/Relation.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/* -*- 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_relation_h_
|
||||
#define mozilla_a11y_relation_h_
|
||||
|
||||
#include "AccIterator.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* A collection of relation targets of a certain type. Targets are computed
|
||||
* lazily while enumerating.
|
||||
*/
|
||||
class Relation
|
||||
{
|
||||
public:
|
||||
Relation() : mFirstIter(nullptr), mLastIter(nullptr) { }
|
||||
|
||||
explicit Relation(AccIterable* aIter) :
|
||||
mFirstIter(aIter), mLastIter(aIter) { }
|
||||
|
||||
explicit Relation(Accessible* aAcc) :
|
||||
mFirstIter(nullptr), mLastIter(nullptr)
|
||||
{ AppendTarget(aAcc); }
|
||||
|
||||
Relation(DocAccessible* aDocument, nsIContent* aContent) :
|
||||
mFirstIter(nullptr), mLastIter(nullptr)
|
||||
{ AppendTarget(aDocument, aContent); }
|
||||
|
||||
Relation(Relation&& aOther) :
|
||||
mFirstIter(Move(aOther.mFirstIter)), mLastIter(aOther.mLastIter)
|
||||
{
|
||||
aOther.mLastIter = nullptr;
|
||||
}
|
||||
|
||||
Relation& operator = (Relation&& aRH)
|
||||
{
|
||||
mFirstIter = Move(aRH.mFirstIter);
|
||||
mLastIter = aRH.mLastIter;
|
||||
aRH.mLastIter = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void AppendIter(AccIterable* aIter)
|
||||
{
|
||||
if (mLastIter)
|
||||
mLastIter->mNextIter.reset(aIter);
|
||||
else
|
||||
mFirstIter.reset(aIter);
|
||||
|
||||
mLastIter = aIter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the given accessible to the set of related accessibles.
|
||||
*/
|
||||
inline void AppendTarget(Accessible* aAcc)
|
||||
{
|
||||
if (aAcc)
|
||||
AppendIter(new SingleAccIterator(aAcc));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the one accessible for this content node to the set of related
|
||||
* accessibles.
|
||||
*/
|
||||
void AppendTarget(DocAccessible* aDocument, nsIContent* aContent)
|
||||
{
|
||||
if (aContent)
|
||||
AppendTarget(aDocument->GetAccessible(aContent));
|
||||
}
|
||||
|
||||
/**
|
||||
* compute and return the next related accessible.
|
||||
*/
|
||||
inline Accessible* Next()
|
||||
{
|
||||
Accessible* target = nullptr;
|
||||
|
||||
while (mFirstIter && !(target = mFirstIter->Next()))
|
||||
mFirstIter = std::move(mFirstIter->mNextIter);
|
||||
|
||||
if (!mFirstIter)
|
||||
mLastIter = nullptr;
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
private:
|
||||
Relation& operator = (const Relation&) = delete;
|
||||
Relation(const Relation&) = delete;
|
||||
|
||||
std::unique_ptr<AccIterable> mFirstIter;
|
||||
AccIterable* mLastIter;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
163
accessible/base/RelationType.h
Normal file
163
accessible/base/RelationType.h
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/* -*- 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_relationtype_h_
|
||||
#define mozilla_a11y_relationtype_h_
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
enum class RelationType {
|
||||
|
||||
/**
|
||||
* This object is labelled by a target object.
|
||||
*/
|
||||
LABELLED_BY = 0x00,
|
||||
|
||||
/**
|
||||
* This object is label for a target object.
|
||||
*/
|
||||
LABEL_FOR = 0x01,
|
||||
|
||||
/**
|
||||
* This object is described by the target object.
|
||||
*/
|
||||
DESCRIBED_BY = 0x02,
|
||||
|
||||
/**
|
||||
* This object is describes the target object.
|
||||
*/
|
||||
DESCRIPTION_FOR = 0x3,
|
||||
|
||||
/**
|
||||
* This object is a child of a target object.
|
||||
*/
|
||||
NODE_CHILD_OF = 0x4,
|
||||
|
||||
/**
|
||||
* This object is a parent of a target object. A dual relation to
|
||||
* NODE_CHILD_OF.
|
||||
*/
|
||||
NODE_PARENT_OF = 0x5,
|
||||
|
||||
/**
|
||||
* Some attribute of this object is affected by a target object.
|
||||
*/
|
||||
CONTROLLED_BY = 0x06,
|
||||
|
||||
/**
|
||||
* This object is interactive and controls some attribute of a target object.
|
||||
*/
|
||||
CONTROLLER_FOR = 0x07,
|
||||
|
||||
/**
|
||||
* Content flows from this object to a target object, i.e. has content that
|
||||
* flows logically to another object in a sequential way, e.g. text flow.
|
||||
*/
|
||||
FLOWS_TO = 0x08,
|
||||
|
||||
/**
|
||||
* Content flows to this object from a target object, i.e. has content that
|
||||
* flows logically from another object in a sequential way, e.g. text flow.
|
||||
*/
|
||||
FLOWS_FROM = 0x09,
|
||||
|
||||
/**
|
||||
* This object is a member of a group of one or more objects. When there is
|
||||
* more than one object in the group each member may have one and the same
|
||||
* target, e.g. a grouping object. It is also possible that each member has
|
||||
* multiple additional targets, e.g. one for every other member in the group.
|
||||
*/
|
||||
MEMBER_OF = 0x0a,
|
||||
|
||||
/**
|
||||
* This object is a sub window of a target object.
|
||||
*/
|
||||
SUBWINDOW_OF = 0x0b,
|
||||
|
||||
/**
|
||||
* This object embeds a target object. This relation can be used on the
|
||||
* OBJID_CLIENT accessible for a top level window to show where the content
|
||||
* areas are.
|
||||
*/
|
||||
EMBEDS = 0x0c,
|
||||
|
||||
/**
|
||||
* This object is embedded by a target object.
|
||||
*/
|
||||
EMBEDDED_BY = 0x0d,
|
||||
|
||||
/**
|
||||
* This object is a transient component related to the target object. When
|
||||
* this object is activated the target object doesn't lose focus.
|
||||
*/
|
||||
POPUP_FOR = 0x0e,
|
||||
|
||||
/**
|
||||
* This object is a parent window of the target object.
|
||||
*/
|
||||
PARENT_WINDOW_OF = 0x0f,
|
||||
|
||||
/**
|
||||
* Part of a form/dialog with a related default button. It is used for
|
||||
* MSAA/XPCOM, it isn't for IA2 or ATK.
|
||||
*/
|
||||
DEFAULT_BUTTON = 0x10,
|
||||
|
||||
/**
|
||||
* The target object is the containing document object.
|
||||
*/
|
||||
CONTAINING_DOCUMENT = 0x11,
|
||||
|
||||
/**
|
||||
* The target object is the topmost containing document object in the tab pane.
|
||||
*/
|
||||
CONTAINING_TAB_PANE = 0x12,
|
||||
|
||||
/**
|
||||
* The target object is the containing window object.
|
||||
*/
|
||||
CONTAINING_WINDOW = 0x13,
|
||||
|
||||
/**
|
||||
* The target object is the containing application object.
|
||||
*/
|
||||
CONTAINING_APPLICATION = 0x14,
|
||||
|
||||
|
||||
/**
|
||||
* The target object provides the detailed, extended description for this
|
||||
* object. It provides more detailed information than would normally be
|
||||
* provided using the DESCRIBED_BY relation. A common use for this relation is
|
||||
* in digital publishing where an extended description needs to be conveyed in
|
||||
* a book that requires structural markup or the embedding of other technology
|
||||
* to provide illustrative content.
|
||||
*/
|
||||
DETAILS = 0x15,
|
||||
|
||||
/**
|
||||
* This object provides the detailed, extended description for the target
|
||||
* object. See DETAILS relation.
|
||||
*/
|
||||
DETAILS_FOR = 0x16,
|
||||
|
||||
/**
|
||||
* The target object is the error message for this object.
|
||||
*/
|
||||
ERRORMSG = 0x17,
|
||||
|
||||
/**
|
||||
* This object is the error message for the target object.
|
||||
*/
|
||||
ERRORMSG_FOR = 0x18,
|
||||
|
||||
LAST = ERRORMSG_FOR
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
154
accessible/base/RelationTypeMap.h
Normal file
154
accessible/base/RelationTypeMap.h
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
/**
|
||||
* Usage: declare the macro RELATIONTYPE()with the following arguments:
|
||||
* RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type)
|
||||
*/
|
||||
|
||||
RELATIONTYPE(LABELLED_BY,
|
||||
"labelled by",
|
||||
ATK_RELATION_LABELLED_BY,
|
||||
NAVRELATION_LABELLED_BY,
|
||||
IA2_RELATION_LABELLED_BY)
|
||||
|
||||
RELATIONTYPE(LABEL_FOR,
|
||||
"label for",
|
||||
ATK_RELATION_LABEL_FOR,
|
||||
NAVRELATION_LABEL_FOR,
|
||||
IA2_RELATION_LABEL_FOR)
|
||||
|
||||
RELATIONTYPE(DESCRIBED_BY,
|
||||
"described by",
|
||||
ATK_RELATION_DESCRIBED_BY,
|
||||
NAVRELATION_DESCRIBED_BY,
|
||||
IA2_RELATION_DESCRIBED_BY)
|
||||
|
||||
RELATIONTYPE(DESCRIPTION_FOR,
|
||||
"description for",
|
||||
ATK_RELATION_DESCRIPTION_FOR,
|
||||
NAVRELATION_DESCRIPTION_FOR,
|
||||
IA2_RELATION_DESCRIPTION_FOR)
|
||||
|
||||
RELATIONTYPE(NODE_CHILD_OF,
|
||||
"node child of",
|
||||
ATK_RELATION_NODE_CHILD_OF,
|
||||
NAVRELATION_NODE_CHILD_OF,
|
||||
IA2_RELATION_NODE_CHILD_OF)
|
||||
|
||||
RELATIONTYPE(NODE_PARENT_OF,
|
||||
"node parent of",
|
||||
ATK_RELATION_NODE_PARENT_OF,
|
||||
NAVRELATION_NODE_PARENT_OF,
|
||||
IA2_RELATION_NODE_PARENT_OF)
|
||||
|
||||
RELATIONTYPE(CONTROLLED_BY,
|
||||
"controlled by",
|
||||
ATK_RELATION_CONTROLLED_BY,
|
||||
NAVRELATION_CONTROLLED_BY,
|
||||
IA2_RELATION_CONTROLLED_BY)
|
||||
|
||||
RELATIONTYPE(CONTROLLER_FOR,
|
||||
"controller for",
|
||||
ATK_RELATION_CONTROLLER_FOR,
|
||||
NAVRELATION_CONTROLLER_FOR,
|
||||
IA2_RELATION_CONTROLLER_FOR)
|
||||
|
||||
RELATIONTYPE(FLOWS_TO,
|
||||
"flows to",
|
||||
ATK_RELATION_FLOWS_TO,
|
||||
NAVRELATION_FLOWS_TO,
|
||||
IA2_RELATION_FLOWS_TO)
|
||||
|
||||
RELATIONTYPE(FLOWS_FROM,
|
||||
"flows from",
|
||||
ATK_RELATION_FLOWS_FROM,
|
||||
NAVRELATION_FLOWS_FROM,
|
||||
IA2_RELATION_FLOWS_FROM)
|
||||
|
||||
RELATIONTYPE(MEMBER_OF,
|
||||
"member of",
|
||||
ATK_RELATION_MEMBER_OF,
|
||||
NAVRELATION_MEMBER_OF,
|
||||
IA2_RELATION_MEMBER_OF)
|
||||
|
||||
RELATIONTYPE(SUBWINDOW_OF,
|
||||
"subwindow of",
|
||||
ATK_RELATION_SUBWINDOW_OF,
|
||||
NAVRELATION_SUBWINDOW_OF,
|
||||
IA2_RELATION_SUBWINDOW_OF)
|
||||
|
||||
RELATIONTYPE(EMBEDS,
|
||||
"embeds",
|
||||
ATK_RELATION_EMBEDS,
|
||||
NAVRELATION_EMBEDS,
|
||||
IA2_RELATION_EMBEDS)
|
||||
|
||||
RELATIONTYPE(EMBEDDED_BY,
|
||||
"embedded by",
|
||||
ATK_RELATION_EMBEDDED_BY,
|
||||
NAVRELATION_EMBEDDED_BY,
|
||||
IA2_RELATION_EMBEDDED_BY)
|
||||
|
||||
RELATIONTYPE(POPUP_FOR,
|
||||
"popup for",
|
||||
ATK_RELATION_POPUP_FOR,
|
||||
NAVRELATION_POPUP_FOR,
|
||||
IA2_RELATION_POPUP_FOR)
|
||||
|
||||
RELATIONTYPE(PARENT_WINDOW_OF,
|
||||
"parent window of",
|
||||
ATK_RELATION_PARENT_WINDOW_OF,
|
||||
NAVRELATION_PARENT_WINDOW_OF,
|
||||
IA2_RELATION_PARENT_WINDOW_OF)
|
||||
|
||||
RELATIONTYPE(DEFAULT_BUTTON,
|
||||
"default button",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_DEFAULT_BUTTON,
|
||||
IA2_RELATION_NULL)
|
||||
|
||||
RELATIONTYPE(CONTAINING_DOCUMENT,
|
||||
"containing document",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_CONTAINING_DOCUMENT,
|
||||
IA2_RELATION_CONTAINING_DOCUMENT)
|
||||
|
||||
RELATIONTYPE(CONTAINING_TAB_PANE,
|
||||
"containing tab pane",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_CONTAINING_TAB_PANE,
|
||||
IA2_RELATION_CONTAINING_TAB_PANE)
|
||||
|
||||
RELATIONTYPE(CONTAINING_APPLICATION,
|
||||
"containing application",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_CONTAINING_APPLICATION,
|
||||
IA2_RELATION_CONTAINING_APPLICATION)
|
||||
|
||||
RELATIONTYPE(DETAILS,
|
||||
"details",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_DETAILS,
|
||||
IA2_RELATION_DETAILS)
|
||||
|
||||
RELATIONTYPE(DETAILS_FOR,
|
||||
"details for",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_DETAILS_FOR,
|
||||
IA2_RELATION_DETAILS_FOR)
|
||||
|
||||
RELATIONTYPE(ERRORMSG,
|
||||
"error",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_ERROR,
|
||||
IA2_RELATION_ERROR)
|
||||
|
||||
RELATIONTYPE(ERRORMSG_FOR,
|
||||
"error for",
|
||||
ATK_RELATION_NULL,
|
||||
NAVRELATION_ERROR_FOR,
|
||||
IA2_RELATION_ERROR_FOR)
|
||||
995
accessible/base/Role.h
Normal file
995
accessible/base/Role.h
Normal file
|
|
@ -0,0 +1,995 @@
|
|||
/* -*- 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 _role_h_
|
||||
#define _role_h_
|
||||
|
||||
/**
|
||||
* @note Make sure to update the localized role names when changing the list.
|
||||
* @note When adding a new role, be sure to also add it to base/RoleMap.h and
|
||||
* update nsIAccessibleRole.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace roles {
|
||||
|
||||
enum Role {
|
||||
/**
|
||||
* Used when accessible hans't strong defined role.
|
||||
*/
|
||||
NOTHING = 0,
|
||||
|
||||
/**
|
||||
* Represents a title or caption bar for a window. It is used by MSAA only,
|
||||
* supported automatically by MS Windows.
|
||||
*/
|
||||
TITLEBAR = 1,
|
||||
|
||||
/**
|
||||
* Represents the menu bar (positioned beneath the title bar of a window)
|
||||
* from which menus are selected by the user. The role is used by
|
||||
* xul:menubar or role="menubar".
|
||||
*/
|
||||
MENUBAR = 2,
|
||||
|
||||
/**
|
||||
* Represents a vertical or horizontal scroll bar, which is part of the client
|
||||
* area or used in a control.
|
||||
*/
|
||||
SCROLLBAR = 3,
|
||||
|
||||
/**
|
||||
* Represents a special mouse pointer, which allows a user to manipulate user
|
||||
* interface elements such as windows. For example, a user clicks and drags
|
||||
* a sizing grip in the lower-right corner of a window to resize it.
|
||||
*/
|
||||
GRIP = 4,
|
||||
|
||||
/**
|
||||
* Represents a system sound, which is associated with various system events.
|
||||
*/
|
||||
SOUND = 5,
|
||||
|
||||
/**
|
||||
* Represents the system mouse pointer.
|
||||
*/
|
||||
CURSOR = 6,
|
||||
|
||||
/**
|
||||
* Represents the system caret. The role is supported for caret.
|
||||
*/
|
||||
CARET = 7,
|
||||
|
||||
/**
|
||||
* Represents an alert or a condition that a user should be notified about.
|
||||
* Assistive Technologies typically respond to the role by reading the entire
|
||||
* onscreen contents of containers advertising this role. Should be used for
|
||||
* warning dialogs, etc. The role is used by xul:browsermessage,
|
||||
* role="alert".
|
||||
*/
|
||||
ALERT = 8,
|
||||
|
||||
/**
|
||||
* Represents the window frame, which contains child objects such as
|
||||
* a title bar, client, and other objects contained in a window. The role
|
||||
* is supported automatically by MS Windows.
|
||||
*/
|
||||
WINDOW = 9,
|
||||
|
||||
/**
|
||||
* A sub-document (<frame> or <iframe>)
|
||||
*/
|
||||
INTERNAL_FRAME = 10,
|
||||
|
||||
/**
|
||||
* Represents a menu, which presents a list of options from which the user can
|
||||
* make a selection to perform an action. It is used for role="menu".
|
||||
*/
|
||||
MENUPOPUP = 11,
|
||||
|
||||
/**
|
||||
* Represents a menu item, which is an entry in a menu that a user can choose
|
||||
* to carry out a command, select an option. It is used for xul:menuitem,
|
||||
* role="menuitem".
|
||||
*/
|
||||
MENUITEM = 12,
|
||||
|
||||
/**
|
||||
* Represents a ToolTip that provides helpful hints.
|
||||
*/
|
||||
TOOLTIP = 13,
|
||||
|
||||
/**
|
||||
* Represents a main window for an application. It is used for
|
||||
* role="application". Also refer to APP_ROOT
|
||||
*/
|
||||
APPLICATION = 14,
|
||||
|
||||
/**
|
||||
* Represents a document window. A document window is always contained within
|
||||
* an application window. It is used for role="document".
|
||||
*/
|
||||
DOCUMENT = 15,
|
||||
|
||||
/**
|
||||
* Represents a pane within a frame or document window. Users can navigate
|
||||
* between panes and within the contents of the current pane, but cannot
|
||||
* navigate between items in different panes. Thus, panes represent a level
|
||||
* of grouping lower than frame windows or documents, but above individual
|
||||
* controls. It is used for the first child of a <frame> or <iframe>.
|
||||
*/
|
||||
PANE = 16,
|
||||
|
||||
/**
|
||||
* Represents a graphical image used to represent data.
|
||||
*/
|
||||
CHART = 17,
|
||||
|
||||
/**
|
||||
* Represents a dialog box or message box. It is used for xul:dialog,
|
||||
* role="dialog".
|
||||
*/
|
||||
DIALOG = 18,
|
||||
|
||||
/**
|
||||
* Represents a window border.
|
||||
*/
|
||||
BORDER = 19,
|
||||
|
||||
/**
|
||||
* Logically groups other objects. There is not always a parent-child
|
||||
* relationship between the grouping object and the objects it contains. It
|
||||
* is used for html:textfield, xul:groupbox, role="group".
|
||||
*/
|
||||
GROUPING = 20,
|
||||
|
||||
/**
|
||||
* Used to visually divide a space into two regions, such as a separator menu
|
||||
* item or a bar that divides split panes within a window. It is used for
|
||||
* xul:separator, html:hr, role="separator".
|
||||
*/
|
||||
SEPARATOR = 21,
|
||||
|
||||
/**
|
||||
* Represents a toolbar, which is a grouping of controls (push buttons or
|
||||
* toggle buttons) that provides easy access to frequently used features. It
|
||||
* is used for xul:toolbar, role="toolbar".
|
||||
*/
|
||||
TOOLBAR = 22,
|
||||
|
||||
/**
|
||||
* Represents a status bar, which is an area at the bottom of a window that
|
||||
* displays information about the current operation, state of the application,
|
||||
* or selected object. The status bar has multiple fields, which display
|
||||
* different kinds of information. It is used for xul:statusbar.
|
||||
*/
|
||||
STATUSBAR = 23,
|
||||
|
||||
/**
|
||||
* Represents a table that contains rows and columns of cells, and optionally,
|
||||
* row headers and column headers. It is used for html:table,
|
||||
* role="grid". Also refer to the following role: COLUMNHEADER,
|
||||
* ROWHEADER, COLUMN, ROW, CELL.
|
||||
*/
|
||||
TABLE = 24,
|
||||
|
||||
/**
|
||||
* Represents a column header, providing a visual label for a column in
|
||||
* a table. It is used for XUL tree column headers, html:th,
|
||||
* role="colheader". Also refer to TABLE.
|
||||
*/
|
||||
COLUMNHEADER = 25,
|
||||
|
||||
/**
|
||||
* Represents a row header, which provides a visual label for a table row.
|
||||
* It is used for role="rowheader". Also, see TABLE.
|
||||
*/
|
||||
ROWHEADER = 26,
|
||||
|
||||
/**
|
||||
* Represents a column of cells within a table. Also, see TABLE.
|
||||
*/
|
||||
COLUMN = 27,
|
||||
|
||||
/**
|
||||
* Represents a row of cells within a table. Also, see TABLE.
|
||||
*/
|
||||
ROW = 28,
|
||||
|
||||
/**
|
||||
* Represents a cell within a table. It is used for html:td,
|
||||
* xul:tree cell and xul:listcell. Also, see TABLE.
|
||||
*/
|
||||
CELL = 29,
|
||||
|
||||
/**
|
||||
* Represents a link to something else. This object might look like text or
|
||||
* a graphic, but it acts like a button. It is used for
|
||||
* xul:label@class="text-link", html:a, html:area.
|
||||
*/
|
||||
LINK = 30,
|
||||
|
||||
/**
|
||||
* Displays a Help topic in the form of a ToolTip or Help balloon.
|
||||
*/
|
||||
HELPBALLOON = 31,
|
||||
|
||||
/**
|
||||
* Represents a cartoon-like graphic object, such as Microsoft Office
|
||||
* Assistant, which is displayed to provide help to users of an application.
|
||||
*/
|
||||
CHARACTER = 32,
|
||||
|
||||
/**
|
||||
* Represents a list box, allowing the user to select one or more items. It
|
||||
* is used for xul:listbox, html:select@size, role="list". See also
|
||||
* LIST_ITEM.
|
||||
*/
|
||||
LIST = 33,
|
||||
|
||||
/**
|
||||
* Represents an item in a list. See also LIST.
|
||||
*/
|
||||
LISTITEM = 34,
|
||||
|
||||
/**
|
||||
* Represents an outline or tree structure, such as a tree view control,
|
||||
* that displays a hierarchical list and allows the user to expand and
|
||||
* collapse branches. Is is used for role="tree".
|
||||
*/
|
||||
OUTLINE = 35,
|
||||
|
||||
/**
|
||||
* Represents an item in an outline or tree structure. It is used for
|
||||
* role="treeitem".
|
||||
*/
|
||||
OUTLINEITEM = 36,
|
||||
|
||||
/**
|
||||
* Represents a page tab, it is a child of a page tab list. It is used for
|
||||
* xul:tab, role="treeitem". Also refer to PAGETABLIST.
|
||||
*/
|
||||
PAGETAB = 37,
|
||||
|
||||
/**
|
||||
* Represents a property sheet. It is used for xul:tabpanel,
|
||||
* role="tabpanel".
|
||||
*/
|
||||
PROPERTYPAGE = 38,
|
||||
|
||||
/**
|
||||
* Represents an indicator, such as a pointer graphic, that points to the
|
||||
* current item.
|
||||
*/
|
||||
INDICATOR = 39,
|
||||
|
||||
/**
|
||||
* Represents a picture. Is is used for xul:image, html:img.
|
||||
*/
|
||||
GRAPHIC = 40,
|
||||
|
||||
/**
|
||||
* Represents read-only text, such as labels for other controls or
|
||||
* instructions in a dialog box. Static text cannot be modified or selected.
|
||||
* Is is used for xul:label, xul:description, html:label, role="label".
|
||||
*/
|
||||
STATICTEXT = 41,
|
||||
|
||||
/**
|
||||
* Represents selectable text that allows edits or is designated read-only.
|
||||
*/
|
||||
TEXT_LEAF = 42,
|
||||
|
||||
/**
|
||||
* Represents a push button control. It is used for xul:button, html:button,
|
||||
* role="button".
|
||||
*/
|
||||
PUSHBUTTON = 43,
|
||||
|
||||
/**
|
||||
* Represents a check box control. It is used for xul:checkbox,
|
||||
* html:input@type="checkbox", role="checkbox".
|
||||
*/
|
||||
CHECKBUTTON = 44,
|
||||
|
||||
/**
|
||||
* Represents an option button, also called a radio button. It is one of a
|
||||
* group of mutually exclusive options. All objects sharing a single parent
|
||||
* that have this attribute are assumed to be part of single mutually
|
||||
* exclusive group. It is used for xul:radio, html:input@type="radio",
|
||||
* role="radio".
|
||||
*/
|
||||
RADIOBUTTON = 45,
|
||||
|
||||
/**
|
||||
* Represents a combo box; an edit control with an associated list box that
|
||||
* provides a set of predefined choices. It is used for html:select,
|
||||
* xul:menulist, role="combobox".
|
||||
*/
|
||||
COMBOBOX = 46,
|
||||
|
||||
/**
|
||||
* Represents the calendar control.
|
||||
*/
|
||||
DROPLIST = 47,
|
||||
|
||||
/**
|
||||
* Represents a progress bar, dynamically showing the user the percent
|
||||
* complete of an operation in progress. It is used for xul:progressmeter,
|
||||
* role="progressbar".
|
||||
*/
|
||||
PROGRESSBAR = 48,
|
||||
|
||||
/**
|
||||
* Represents a dial or knob whose purpose is to allow a user to set a value.
|
||||
*/
|
||||
DIAL = 49,
|
||||
|
||||
/**
|
||||
* Represents a hot-key field that allows the user to enter a combination or
|
||||
* sequence of keystrokes.
|
||||
*/
|
||||
HOTKEYFIELD = 50,
|
||||
|
||||
/**
|
||||
* Represents a slider, which allows the user to adjust a setting in given
|
||||
* increments between minimum and maximum values. It is used by xul:scale,
|
||||
* role="slider".
|
||||
*/
|
||||
SLIDER = 51,
|
||||
|
||||
/**
|
||||
* Represents a spin box, which is a control that allows the user to increment
|
||||
* or decrement the value displayed in a separate "buddy" control associated
|
||||
* with the spin box. It is used for xul:spinbuttons.
|
||||
*/
|
||||
SPINBUTTON = 52,
|
||||
|
||||
/**
|
||||
* Represents a graphical image used to diagram data. It is used for svg:svg.
|
||||
*/
|
||||
DIAGRAM = 53,
|
||||
|
||||
/**
|
||||
* Represents an animation control, which contains content that changes over
|
||||
* time, such as a control that displays a series of bitmap frames.
|
||||
*/
|
||||
ANIMATION = 54,
|
||||
|
||||
/**
|
||||
* Represents a mathematical equation. It is used by MATHML, where there is a
|
||||
* rich DOM subtree for an equation. Use FLAT_EQUATION for <img role="math" alt="[TeX]"/>
|
||||
*/
|
||||
EQUATION = 55,
|
||||
|
||||
/**
|
||||
* Represents a button that drops down a list of items.
|
||||
*/
|
||||
BUTTONDROPDOWN = 56,
|
||||
|
||||
/**
|
||||
* Represents a button that drops down a menu.
|
||||
*/
|
||||
BUTTONMENU = 57,
|
||||
|
||||
/**
|
||||
* Represents a button that drops down a grid. It is used for xul:colorpicker.
|
||||
*/
|
||||
BUTTONDROPDOWNGRID = 58,
|
||||
|
||||
/**
|
||||
* Represents blank space between other objects.
|
||||
*/
|
||||
WHITESPACE = 59,
|
||||
|
||||
/**
|
||||
* Represents a container of page tab controls. Is it used for xul:tabs,
|
||||
* DHTML: role="tabs". Also refer to PAGETAB.
|
||||
*/
|
||||
PAGETABLIST = 60,
|
||||
|
||||
/**
|
||||
* Represents a control that displays time.
|
||||
*/
|
||||
CLOCK = 61,
|
||||
|
||||
/**
|
||||
* Represents a button on a toolbar that has a drop-down list icon directly
|
||||
* adjacent to the button.
|
||||
*/
|
||||
SPLITBUTTON = 62,
|
||||
|
||||
/**
|
||||
* Represents an edit control designed for an Internet Protocol (IP) address.
|
||||
* The edit control is divided into sections for the different parts of the
|
||||
* IP address.
|
||||
*/
|
||||
IPADDRESS = 63,
|
||||
|
||||
/**
|
||||
* Represents a label control that has an accelerator.
|
||||
*/
|
||||
ACCEL_LABEL = 64,
|
||||
|
||||
/**
|
||||
* Represents an arrow in one of the four cardinal directions.
|
||||
*/
|
||||
ARROW = 65,
|
||||
|
||||
/**
|
||||
* Represents a control that can be drawn into and is used to trap events.
|
||||
* It is used for html:canvas.
|
||||
*/
|
||||
CANVAS = 66,
|
||||
|
||||
/**
|
||||
* Represents a menu item with a check box.
|
||||
*/
|
||||
CHECK_MENU_ITEM = 67,
|
||||
|
||||
/**
|
||||
* Represents a specialized dialog that lets the user choose a color.
|
||||
*/
|
||||
COLOR_CHOOSER = 68,
|
||||
|
||||
/**
|
||||
* Represents control whose purpose is to allow a user to edit a date.
|
||||
*/
|
||||
DATE_EDITOR = 69,
|
||||
|
||||
/**
|
||||
* An iconified internal frame in an DESKTOP_PANE. Also refer to
|
||||
* INTERNAL_FRAME.
|
||||
*/
|
||||
DESKTOP_ICON = 70,
|
||||
|
||||
/**
|
||||
* A desktop pane. A pane that supports internal frames and iconified
|
||||
* versions of those internal frames.
|
||||
*/
|
||||
DESKTOP_FRAME = 71,
|
||||
|
||||
/**
|
||||
* A directory pane. A pane that allows the user to navigate through
|
||||
* and select the contents of a directory. May be used by a file chooser.
|
||||
* Also refer to FILE_CHOOSER.
|
||||
*/
|
||||
DIRECTORY_PANE = 72,
|
||||
|
||||
/**
|
||||
* A file chooser. A specialized dialog that displays the files in the
|
||||
* directory and lets the user select a file, browse a different directory,
|
||||
* or specify a filename. May use the directory pane to show the contents of
|
||||
* a directory. Also refer to DIRECTORY_PANE.
|
||||
*/
|
||||
FILE_CHOOSER = 73,
|
||||
|
||||
/**
|
||||
* A font chooser. A font chooser is a component that lets the user pick
|
||||
* various attributes for fonts.
|
||||
*/
|
||||
FONT_CHOOSER = 74,
|
||||
|
||||
/**
|
||||
* Frame role. A top level window with a title bar, border, menu bar, etc.
|
||||
* It is often used as the primary window for an application.
|
||||
*/
|
||||
CHROME_WINDOW = 75,
|
||||
|
||||
/**
|
||||
* A glass pane. A pane that is guaranteed to be painted on top of all
|
||||
* panes beneath it. Also refer to ROOT_PANE.
|
||||
*/
|
||||
GLASS_PANE = 76,
|
||||
|
||||
/**
|
||||
* A document container for HTML, whose children represent the document
|
||||
* content.
|
||||
*/
|
||||
HTML_CONTAINER = 77,
|
||||
|
||||
/**
|
||||
* A small fixed size picture, typically used to decorate components.
|
||||
*/
|
||||
ICON = 78,
|
||||
|
||||
/**
|
||||
* Presents an icon or short string in an interface.
|
||||
*/
|
||||
LABEL = 79,
|
||||
|
||||
/**
|
||||
* A layered pane. A specialized pane that allows its children to be drawn
|
||||
* in layers, providing a form of stacking order. This is usually the pane
|
||||
* that holds the menu bar as well as the pane that contains most of the
|
||||
* visual components in a window. Also refer to GLASS_PANE and
|
||||
* ROOT_PANE.
|
||||
*/
|
||||
LAYERED_PANE = 80,
|
||||
|
||||
/**
|
||||
* A specialized pane whose primary use is inside a dialog.
|
||||
*/
|
||||
OPTION_PANE = 81,
|
||||
|
||||
/**
|
||||
* A text object uses for passwords, or other places where the text content
|
||||
* is not shown visibly to the user.
|
||||
*/
|
||||
PASSWORD_TEXT = 82,
|
||||
|
||||
/**
|
||||
* A temporary window that is usually used to offer the user a list of
|
||||
* choices, and then hides when the user selects one of those choices.
|
||||
*/
|
||||
POPUP_MENU = 83,
|
||||
|
||||
/**
|
||||
* A radio button that is a menu item.
|
||||
*/
|
||||
RADIO_MENU_ITEM = 84,
|
||||
|
||||
/**
|
||||
* A root pane. A specialized pane that has a glass pane and a layered pane
|
||||
* as its children. Also refer to GLASS_PANE and LAYERED_PANE.
|
||||
*/
|
||||
ROOT_PANE = 85,
|
||||
|
||||
/**
|
||||
* A scroll pane. An object that allows a user to incrementally view a large
|
||||
* amount of information. Its children can include scroll bars and a
|
||||
* viewport. Also refer to VIEW_PORT.
|
||||
*/
|
||||
SCROLL_PANE = 86,
|
||||
|
||||
/**
|
||||
* A split pane. A specialized panel that presents two other panels at the
|
||||
* same time. Between the two panels is a divider the user can manipulate to
|
||||
* make one panel larger and the other panel smaller.
|
||||
*/
|
||||
SPLIT_PANE = 87,
|
||||
|
||||
/**
|
||||
* The header for a column of a table.
|
||||
* XXX: it looks this role is dupe of COLUMNHEADER.
|
||||
*/
|
||||
TABLE_COLUMN_HEADER = 88,
|
||||
|
||||
/**
|
||||
* The header for a row of a table.
|
||||
* XXX: it looks this role is dupe of ROWHEADER
|
||||
*/
|
||||
TABLE_ROW_HEADER = 89,
|
||||
|
||||
/**
|
||||
* A menu item used to tear off and reattach its menu.
|
||||
*/
|
||||
TEAR_OFF_MENU_ITEM = 90,
|
||||
|
||||
/**
|
||||
* Represents an accessible terminal.
|
||||
*/
|
||||
TERMINAL = 91,
|
||||
|
||||
/**
|
||||
* Collection of objects that constitute a logical text entity.
|
||||
*/
|
||||
TEXT_CONTAINER = 92,
|
||||
|
||||
/**
|
||||
* A toggle button. A specialized push button that can be checked or
|
||||
* unchecked, but does not provide a separate indicator for the current state.
|
||||
*/
|
||||
TOGGLE_BUTTON = 93,
|
||||
|
||||
/**
|
||||
* Represent a control that is capable of expanding and collapsing rows as
|
||||
* well as showing multiple columns of data.
|
||||
*/
|
||||
TREE_TABLE = 94,
|
||||
|
||||
/**
|
||||
* A viewport. An object usually used in a scroll pane. It represents the
|
||||
* portion of the entire data that the user can see. As the user manipulates
|
||||
* the scroll bars, the contents of the viewport can change. Also refer to
|
||||
* SCROLL_PANE.
|
||||
*/
|
||||
VIEWPORT = 95,
|
||||
|
||||
/**
|
||||
* Header of a document page. Also refer to FOOTER.
|
||||
*/
|
||||
HEADER = 96,
|
||||
|
||||
/**
|
||||
* Footer of a document page. Also refer to HEADER.
|
||||
*/
|
||||
FOOTER = 97,
|
||||
|
||||
/**
|
||||
* A paragraph of text.
|
||||
*/
|
||||
PARAGRAPH = 98,
|
||||
|
||||
/**
|
||||
* A ruler such as those used in word processors.
|
||||
*/
|
||||
RULER = 99,
|
||||
|
||||
/**
|
||||
* A text entry having dialog or list containing items for insertion into
|
||||
* an entry widget, for instance a list of words for completion of a
|
||||
* text entry. It is used for xul:textbox@autocomplete
|
||||
*/
|
||||
AUTOCOMPLETE = 100,
|
||||
|
||||
/**
|
||||
* An editable text object in a toolbar.
|
||||
*/
|
||||
EDITBAR = 101,
|
||||
|
||||
/**
|
||||
* An control whose textual content may be entered or modified by the user.
|
||||
*/
|
||||
ENTRY = 102,
|
||||
|
||||
/**
|
||||
* A caption describing another object.
|
||||
*/
|
||||
CAPTION = 103,
|
||||
|
||||
/**
|
||||
* A visual frame or container which contains a view of document content.
|
||||
* Document frames may occur within another Document instance, in which case
|
||||
* the second document may be said to be embedded in the containing instance.
|
||||
* HTML frames are often DOCUMENT_FRAME. Either this object, or a
|
||||
* singleton descendant, should implement the Document interface.
|
||||
*/
|
||||
DOCUMENT_FRAME = 104,
|
||||
|
||||
/**
|
||||
* Heading.
|
||||
*/
|
||||
HEADING = 105,
|
||||
|
||||
/**
|
||||
* An object representing a page of document content. It is used in documents
|
||||
* which are accessed by the user on a page by page basis.
|
||||
*/
|
||||
PAGE = 106,
|
||||
|
||||
/**
|
||||
* A container of document content. An example of the use of this role is to
|
||||
* represent an html:div.
|
||||
*/
|
||||
SECTION = 107,
|
||||
|
||||
/**
|
||||
* An object which is redundant with another object in the accessible
|
||||
* hierarchy. ATs typically ignore objects with this role.
|
||||
*/
|
||||
REDUNDANT_OBJECT = 108,
|
||||
|
||||
/**
|
||||
* A container of form controls. An example of the use of this role is to
|
||||
* represent an html:form.
|
||||
*/
|
||||
FORM = 109,
|
||||
|
||||
/**
|
||||
* An object which is used to allow input of characters not found on a
|
||||
* keyboard, such as the input of Chinese characters on a Western keyboard.
|
||||
*/
|
||||
IME = 110,
|
||||
|
||||
/**
|
||||
* XXX: document this.
|
||||
*/
|
||||
APP_ROOT = 111,
|
||||
|
||||
/**
|
||||
* Represents a menu item, which is an entry in a menu that a user can choose
|
||||
* to display another menu.
|
||||
*/
|
||||
PARENT_MENUITEM = 112,
|
||||
|
||||
/**
|
||||
* A calendar that allows the user to select a date.
|
||||
*/
|
||||
CALENDAR = 113,
|
||||
|
||||
/**
|
||||
* A list of items that is shown by combobox.
|
||||
*/
|
||||
COMBOBOX_LIST = 114,
|
||||
|
||||
/**
|
||||
* A item of list that is shown by combobox.
|
||||
*/
|
||||
COMBOBOX_OPTION = 115,
|
||||
|
||||
/**
|
||||
* An image map -- has child links representing the areas
|
||||
*/
|
||||
IMAGE_MAP = 116,
|
||||
|
||||
/**
|
||||
* An option in a listbox
|
||||
*/
|
||||
OPTION = 117,
|
||||
|
||||
/**
|
||||
* A rich option in a listbox, it can have other widgets as children
|
||||
*/
|
||||
RICH_OPTION = 118,
|
||||
|
||||
/**
|
||||
* A list of options
|
||||
*/
|
||||
LISTBOX = 119,
|
||||
|
||||
/**
|
||||
* Represents a mathematical equation in the accessible name
|
||||
*/
|
||||
FLAT_EQUATION = 120,
|
||||
|
||||
/**
|
||||
* Represents a cell within a grid. It is used for role="gridcell". Unlike
|
||||
* CELL, it allows the calculation of the accessible name from subtree.
|
||||
* Also, see TABLE.
|
||||
*/
|
||||
GRID_CELL = 121,
|
||||
|
||||
/**
|
||||
* Represents an embedded object. It is used for html:object or html:embed.
|
||||
*/
|
||||
EMBEDDED_OBJECT = 122,
|
||||
|
||||
/**
|
||||
* A note. Originally intended to be hidden until activated, but now also used
|
||||
* for things like html 'aside'.
|
||||
*/
|
||||
NOTE = 123,
|
||||
|
||||
/**
|
||||
* A figure. Used for things like HTML5 figure element.
|
||||
*/
|
||||
FIGURE = 124,
|
||||
|
||||
/**
|
||||
* Represents a rich item with a check box.
|
||||
*/
|
||||
CHECK_RICH_OPTION = 125,
|
||||
|
||||
/**
|
||||
* Represent a definition list (dl in HTML).
|
||||
*/
|
||||
DEFINITION_LIST = 126,
|
||||
|
||||
/**
|
||||
* Represent a term in a definition list (dt in HTML).
|
||||
*/
|
||||
TERM = 127,
|
||||
|
||||
/**
|
||||
* Represent a definition in a definition list (dd in HTML)
|
||||
*/
|
||||
DEFINITION = 128,
|
||||
|
||||
/**
|
||||
* Represent a keyboard or keypad key (ARIA role "key").
|
||||
*/
|
||||
KEY = 129,
|
||||
|
||||
/**
|
||||
* Represent a switch control widget (ARIA role "switch").
|
||||
*/
|
||||
SWITCH = 130,
|
||||
|
||||
/**
|
||||
* A block of MathML code (math).
|
||||
*/
|
||||
MATHML_MATH = 131,
|
||||
|
||||
/**
|
||||
* A MathML identifier (mi in MathML).
|
||||
*/
|
||||
MATHML_IDENTIFIER = 132,
|
||||
|
||||
/**
|
||||
* A MathML number (mn in MathML).
|
||||
*/
|
||||
MATHML_NUMBER = 133,
|
||||
|
||||
/**
|
||||
* A MathML operator (mo in MathML).
|
||||
*/
|
||||
MATHML_OPERATOR = 134,
|
||||
|
||||
/**
|
||||
* A MathML text (mtext in MathML).
|
||||
*/
|
||||
MATHML_TEXT = 135,
|
||||
|
||||
/**
|
||||
* A MathML string literal (ms in MathML).
|
||||
*/
|
||||
MATHML_STRING_LITERAL = 136,
|
||||
|
||||
/**
|
||||
* A MathML glyph (mglyph in MathML).
|
||||
*/
|
||||
MATHML_GLYPH = 137,
|
||||
|
||||
/**
|
||||
* A MathML row (mrow in MathML).
|
||||
*/
|
||||
MATHML_ROW = 138,
|
||||
|
||||
/**
|
||||
* A MathML fraction (mfrac in MathML).
|
||||
*/
|
||||
MATHML_FRACTION = 139,
|
||||
|
||||
/**
|
||||
* A MathML square root (msqrt in MathML).
|
||||
*/
|
||||
MATHML_SQUARE_ROOT = 140,
|
||||
|
||||
/**
|
||||
* A MathML root (mroot in MathML).
|
||||
*/
|
||||
MATHML_ROOT = 141,
|
||||
|
||||
/**
|
||||
* A MathML fenced element (mfenced in MathML).
|
||||
*/
|
||||
MATHML_FENCED = 142,
|
||||
|
||||
/**
|
||||
* A MathML enclosed element (menclose in MathML).
|
||||
*/
|
||||
MATHML_ENCLOSED = 143,
|
||||
|
||||
/**
|
||||
* A MathML styling element (mstyle in MathML).
|
||||
*/
|
||||
MATHML_STYLE = 144,
|
||||
|
||||
/**
|
||||
* A MathML subscript (msub in MathML).
|
||||
*/
|
||||
MATHML_SUB = 145,
|
||||
|
||||
/**
|
||||
* A MathML superscript (msup in MathML).
|
||||
*/
|
||||
MATHML_SUP = 146,
|
||||
|
||||
/**
|
||||
* A MathML subscript and superscript (msubsup in MathML).
|
||||
*/
|
||||
MATHML_SUB_SUP = 147,
|
||||
|
||||
/**
|
||||
* A MathML underscript (munder in MathML).
|
||||
*/
|
||||
MATHML_UNDER = 148,
|
||||
|
||||
/**
|
||||
* A MathML overscript (mover in MathML).
|
||||
*/
|
||||
MATHML_OVER = 149,
|
||||
|
||||
/**
|
||||
* A MathML underscript and overscript (munderover in MathML).
|
||||
*/
|
||||
MATHML_UNDER_OVER = 150,
|
||||
|
||||
/**
|
||||
* A MathML multiple subscript and superscript element (mmultiscripts in
|
||||
* MathML).
|
||||
*/
|
||||
MATHML_MULTISCRIPTS = 151,
|
||||
|
||||
/**
|
||||
* A MathML table (mtable in MathML).
|
||||
*/
|
||||
MATHML_TABLE = 152,
|
||||
|
||||
/**
|
||||
* A MathML labelled table row (mlabeledtr in MathML).
|
||||
*/
|
||||
MATHML_LABELED_ROW = 153,
|
||||
|
||||
/**
|
||||
* A MathML table row (mtr in MathML).
|
||||
*/
|
||||
MATHML_TABLE_ROW = 154,
|
||||
|
||||
/**
|
||||
* A MathML table entry or cell (mtd in MathML).
|
||||
*/
|
||||
MATHML_CELL = 155,
|
||||
|
||||
/**
|
||||
* A MathML interactive element (maction in MathML).
|
||||
*/
|
||||
MATHML_ACTION = 156,
|
||||
|
||||
/**
|
||||
* A MathML error message (merror in MathML).
|
||||
*/
|
||||
MATHML_ERROR = 157,
|
||||
|
||||
/**
|
||||
* A MathML stacked (rows of numbers) element (mstack in MathML).
|
||||
*/
|
||||
MATHML_STACK = 158,
|
||||
|
||||
/**
|
||||
* A MathML long division element (mlongdiv in MathML).
|
||||
*/
|
||||
MATHML_LONG_DIVISION = 159,
|
||||
|
||||
/**
|
||||
* A MathML stack group (msgroup in MathML).
|
||||
*/
|
||||
MATHML_STACK_GROUP = 160,
|
||||
|
||||
/**
|
||||
* A MathML stack row (msrow in MathML).
|
||||
*/
|
||||
MATHML_STACK_ROW = 161,
|
||||
|
||||
/**
|
||||
* MathML carries, borrows, or crossouts for a row (mscarries in MathML).
|
||||
*/
|
||||
MATHML_STACK_CARRIES = 162,
|
||||
|
||||
/**
|
||||
* A MathML carry, borrow, or crossout for a column (mscarry in MathML).
|
||||
*/
|
||||
MATHML_STACK_CARRY = 163,
|
||||
|
||||
/**
|
||||
* A MathML line in a stack (msline in MathML).
|
||||
*/
|
||||
MATHML_STACK_LINE = 164,
|
||||
|
||||
/**
|
||||
* A group containing radio buttons
|
||||
*/
|
||||
RADIO_GROUP = 165,
|
||||
|
||||
/**
|
||||
* A text container exposing brief amount of information. See related
|
||||
* TEXT_CONTAINER role.
|
||||
*/
|
||||
TEXT = 166,
|
||||
|
||||
/**
|
||||
* The html:details element.
|
||||
*/
|
||||
DETAILS = 167,
|
||||
|
||||
/**
|
||||
* The html:summary element.
|
||||
*/
|
||||
SUMMARY = 168,
|
||||
|
||||
LAST_ROLE = SUMMARY
|
||||
};
|
||||
|
||||
} // namespace role
|
||||
|
||||
typedef enum mozilla::a11y::roles::Role role;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
1370
accessible/base/RoleMap.h
Normal file
1370
accessible/base/RoleMap.h
Normal file
File diff suppressed because it is too large
Load diff
232
accessible/base/SelectionManager.cpp
Normal file
232
accessible/base/SelectionManager.cpp
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "mozilla/a11y/SelectionManager.h"
|
||||
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "HyperTextAccessible-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "nsEventShell.h"
|
||||
#include "nsFrameSelection.h"
|
||||
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIPresShell.h"
|
||||
#include "mozilla/dom/Selection.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
using mozilla::dom::Selection;
|
||||
|
||||
struct mozilla::a11y::SelData final
|
||||
{
|
||||
SelData(Selection* aSel, int32_t aReason) :
|
||||
mSel(aSel), mReason(aReason) {}
|
||||
|
||||
RefPtr<Selection> mSel;
|
||||
int16_t mReason;
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(SelData)
|
||||
|
||||
private:
|
||||
// Private destructor, to discourage deletion outside of Release():
|
||||
~SelData() {}
|
||||
};
|
||||
|
||||
SelectionManager::SelectionManager() :
|
||||
mCaretOffset(-1), mAccWithCaret(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::ClearControlSelectionListener()
|
||||
{
|
||||
|
||||
// Remove 'this' registered as selection listener for the normal selection.
|
||||
nsCOMPtr<nsISelection> normalSel = do_QueryReferent(mCurrCtrlNormalSel);
|
||||
if (normalSel) {
|
||||
normalSel->AsSelection()->RemoveSelectionListener(this);
|
||||
mCurrCtrlNormalSel = nullptr;
|
||||
}
|
||||
|
||||
// Remove 'this' registered as selection listener for the spellcheck
|
||||
// selection.
|
||||
nsCOMPtr<nsISelection> spellSel = do_QueryReferent(mCurrCtrlSpellSel);
|
||||
if (spellSel) {
|
||||
spellSel->AsSelection()->RemoveSelectionListener(this);
|
||||
mCurrCtrlSpellSel = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::SetControlSelectionListener(dom::Element* aFocusedElm)
|
||||
{
|
||||
// When focus moves such that the caret is part of a new frame selection
|
||||
// this removes the old selection listener and attaches a new one for
|
||||
// the current focus.
|
||||
ClearControlSelectionListener();
|
||||
|
||||
nsIFrame* controlFrame = aFocusedElm->GetPrimaryFrame();
|
||||
if (!controlFrame)
|
||||
return;
|
||||
|
||||
const nsFrameSelection* frameSel = controlFrame->GetConstFrameSelection();
|
||||
NS_ASSERTION(frameSel, "No frame selection for focused element!");
|
||||
if (!frameSel)
|
||||
return;
|
||||
|
||||
// Register 'this' as selection listener for the normal selection.
|
||||
nsCOMPtr<nsISelection> normalSel = frameSel->GetSelection(SelectionType::eNormal);
|
||||
normalSel->AsSelection()->AddSelectionListener(this);
|
||||
mCurrCtrlNormalSel = do_GetWeakReference(normalSel);
|
||||
|
||||
// Register 'this' as selection listener for the spell check selection.
|
||||
nsCOMPtr<nsISelection> spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
|
||||
spellSel->AsSelection()->AddSelectionListener(this);
|
||||
mCurrCtrlSpellSel = do_GetWeakReference(spellSel);
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::AddDocSelectionListener(nsIPresShell* aPresShell)
|
||||
{
|
||||
const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
|
||||
|
||||
// Register 'this' as selection listener for the normal selection.
|
||||
Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
|
||||
normalSel->AddSelectionListener(this);
|
||||
|
||||
// Register 'this' as selection listener for the spell check selection.
|
||||
Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
|
||||
spellSel->AddSelectionListener(this);
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::RemoveDocSelectionListener(nsIPresShell* aPresShell)
|
||||
{
|
||||
const nsFrameSelection* frameSel = aPresShell->ConstFrameSelection();
|
||||
|
||||
// Remove 'this' registered as selection listener for the normal selection.
|
||||
Selection* normalSel = frameSel->GetSelection(SelectionType::eNormal);
|
||||
normalSel->RemoveSelectionListener(this);
|
||||
|
||||
// Remove 'this' registered as selection listener for the spellcheck
|
||||
// selection.
|
||||
Selection* spellSel = frameSel->GetSelection(SelectionType::eSpellCheck);
|
||||
spellSel->RemoveSelectionListener(this);
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::ProcessTextSelChangeEvent(AccEvent* aEvent)
|
||||
{
|
||||
// Fire selection change event if it's not pure caret-move selection change,
|
||||
// i.e. the accessible has or had not collapsed selection.
|
||||
AccTextSelChangeEvent* event = downcast_accEvent(aEvent);
|
||||
if (!event->IsCaretMoveOnly())
|
||||
nsEventShell::FireEvent(aEvent);
|
||||
|
||||
// Fire caret move event if there's a caret in the selection.
|
||||
nsINode* caretCntrNode =
|
||||
nsCoreUtils::GetDOMNodeFromDOMPoint(event->mSel->GetFocusNode(),
|
||||
event->mSel->FocusOffset());
|
||||
if (!caretCntrNode)
|
||||
return;
|
||||
|
||||
HyperTextAccessible* caretCntr = nsAccUtils::GetTextContainer(caretCntrNode);
|
||||
NS_ASSERTION(caretCntr,
|
||||
"No text container for focus while there's one for common ancestor?!");
|
||||
if (!caretCntr)
|
||||
return;
|
||||
|
||||
Selection* selection = caretCntr->DOMSelection();
|
||||
|
||||
// XXX Sometimes we can't get a selection for caretCntr, in that case assume
|
||||
// event->mSel is correct.
|
||||
if (!selection)
|
||||
selection = event->mSel;
|
||||
|
||||
mCaretOffset = caretCntr->DOMPointToOffset(selection->GetFocusNode(),
|
||||
selection->FocusOffset());
|
||||
mAccWithCaret = caretCntr;
|
||||
if (mCaretOffset != -1) {
|
||||
RefPtr<AccCaretMoveEvent> caretMoveEvent =
|
||||
new AccCaretMoveEvent(caretCntr, mCaretOffset, aEvent->FromUserInput());
|
||||
nsEventShell::FireEvent(caretMoveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
SelectionManager::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
|
||||
nsISelection* aSelection,
|
||||
int16_t aReason)
|
||||
{
|
||||
if (NS_WARN_IF(!aDOMDocument) || NS_WARN_IF(!aSelection)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> documentNode(do_QueryInterface(aDOMDocument));
|
||||
DocAccessible* document = GetAccService()->GetDocAccessible(documentNode);
|
||||
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eSelection))
|
||||
logging::SelChange(aSelection, document, aReason);
|
||||
#endif
|
||||
|
||||
if (document) {
|
||||
// Selection manager has longer lifetime than any document accessible,
|
||||
// so that we are guaranteed that the notification is processed before
|
||||
// the selection manager is destroyed.
|
||||
RefPtr<SelData> selData =
|
||||
new SelData(aSelection->AsSelection(), aReason);
|
||||
document->HandleNotification<SelectionManager, SelData>
|
||||
(this, &SelectionManager::ProcessSelectionChanged, selData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SelectionManager::ProcessSelectionChanged(SelData* aSelData)
|
||||
{
|
||||
Selection* selection = aSelData->mSel;
|
||||
if (!selection->GetPresShell())
|
||||
return;
|
||||
|
||||
const nsRange* range = selection->GetAnchorFocusRange();
|
||||
nsINode* cntrNode = nullptr;
|
||||
if (range)
|
||||
cntrNode = range->GetCommonAncestor();
|
||||
|
||||
if (!cntrNode) {
|
||||
cntrNode = selection->GetFrameSelection()->GetAncestorLimiter();
|
||||
if (!cntrNode) {
|
||||
cntrNode = selection->GetPresShell()->GetDocument();
|
||||
NS_ASSERTION(aSelData->mSel->GetPresShell()->ConstFrameSelection() == selection->GetFrameSelection(),
|
||||
"Wrong selection container was used!");
|
||||
}
|
||||
}
|
||||
|
||||
HyperTextAccessible* text = nsAccUtils::GetTextContainer(cntrNode);
|
||||
if (!text) {
|
||||
NS_NOTREACHED("We must reach document accessible implementing text interface!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selection->GetType() == SelectionType::eNormal) {
|
||||
RefPtr<AccEvent> event =
|
||||
new AccTextSelChangeEvent(text, selection, aSelData->mReason);
|
||||
text->Document()->FireDelayedEvent(event);
|
||||
|
||||
} else if (selection->GetType() == SelectionType::eSpellCheck) {
|
||||
// XXX: fire an event for container accessible of the focus/anchor range
|
||||
// of the spelcheck selection.
|
||||
text->Document()->FireDelayedEvent(nsIAccessibleEvent::EVENT_TEXT_ATTRIBUTE_CHANGED,
|
||||
text);
|
||||
}
|
||||
}
|
||||
133
accessible/base/SelectionManager.h
Normal file
133
accessible/base/SelectionManager.h
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_SelectionManager_h__
|
||||
#define mozilla_a11y_SelectionManager_h__
|
||||
|
||||
#include "nsIFrame.h"
|
||||
#include "nsISelectionListener.h"
|
||||
|
||||
class nsIPresShell;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Element;
|
||||
}
|
||||
|
||||
namespace a11y {
|
||||
|
||||
class AccEvent;
|
||||
class HyperTextAccessible;
|
||||
|
||||
/**
|
||||
* This special accessibility class is for the caret and selection management.
|
||||
* There is only 1 visible caret per top level window. However, there may be
|
||||
* several visible selections.
|
||||
*
|
||||
* The important selections are the one owned by each document, and the one in
|
||||
* the currently focused control.
|
||||
*
|
||||
* On Windows this class is used to move an invisible system caret that
|
||||
* shadows the Mozilla caret. Windows will also automatically map this to
|
||||
* the MSAA caret accessible object (via OBJID_CARET) (as opposed to the root
|
||||
* accessible tree for a window which is retrieved with OBJID_CLIENT).
|
||||
*
|
||||
* For ATK and IAccessible2, this class is used to fire caret move and
|
||||
* selection change events.
|
||||
*/
|
||||
|
||||
struct SelData;
|
||||
|
||||
class SelectionManager : public nsISelectionListener
|
||||
{
|
||||
public:
|
||||
// nsISupports
|
||||
// implemented by derived nsAccessibilityService
|
||||
|
||||
// nsISelectionListener
|
||||
NS_DECL_NSISELECTIONLISTENER
|
||||
|
||||
// SelectionManager
|
||||
void Shutdown() { ClearControlSelectionListener(); }
|
||||
|
||||
/**
|
||||
* Listen to selection events on the focused control.
|
||||
*
|
||||
* Note: only one control's selection events are listened to at a time. This
|
||||
* will remove the previous control's selection listener.
|
||||
*/
|
||||
void SetControlSelectionListener(dom::Element* aFocusedElm);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events on the control.
|
||||
*/
|
||||
void ClearControlSelectionListener();
|
||||
|
||||
/**
|
||||
* Listen to selection events on the document.
|
||||
*/
|
||||
void AddDocSelectionListener(nsIPresShell* aPresShell);
|
||||
|
||||
/**
|
||||
* Stop listening to selection events for a given document
|
||||
*/
|
||||
void RemoveDocSelectionListener(nsIPresShell* aShell);
|
||||
|
||||
/**
|
||||
* Process delayed event, results in caret move and text selection change
|
||||
* events.
|
||||
*/
|
||||
void ProcessTextSelChangeEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Gets the current caret offset/hypertext accessible pair. If there is no
|
||||
* current pair, then returns -1 for the offset and a nullptr for the
|
||||
* accessible.
|
||||
*/
|
||||
inline HyperTextAccessible* AccessibleWithCaret(int32_t* aCaret)
|
||||
{
|
||||
if (aCaret)
|
||||
*aCaret = mCaretOffset;
|
||||
|
||||
return mAccWithCaret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update caret offset when it doesn't go through a caret move event.
|
||||
*/
|
||||
inline void UpdateCaretOffset(HyperTextAccessible* aItem, int32_t aOffset)
|
||||
{
|
||||
mAccWithCaret = aItem;
|
||||
mCaretOffset = aOffset;
|
||||
}
|
||||
|
||||
inline void ResetCaretOffset()
|
||||
{
|
||||
mCaretOffset = -1;
|
||||
mAccWithCaret = nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
SelectionManager();
|
||||
|
||||
/**
|
||||
* Process DOM selection change. Fire selection and caret move events.
|
||||
*/
|
||||
void ProcessSelectionChanged(SelData* aSelData);
|
||||
|
||||
private:
|
||||
int32_t mCaretOffset;
|
||||
HyperTextAccessible* mAccWithCaret;
|
||||
// Currently focused controls.
|
||||
nsWeakPtr mCurrCtrlNormalSel;
|
||||
nsWeakPtr mCurrCtrlSpellSel;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
285
accessible/base/States.h
Normal file
285
accessible/base/States.h
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set expandtab shiftwidth=2 tabstop=2: */
|
||||
/* 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 _states_h_
|
||||
#define _states_h_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace states {
|
||||
|
||||
/**
|
||||
* The object is disabled, opposite to enabled and sensitive.
|
||||
*/
|
||||
const uint64_t UNAVAILABLE = ((uint64_t) 0x1) << 0;
|
||||
|
||||
/**
|
||||
* The object is selected.
|
||||
*/
|
||||
const uint64_t SELECTED = ((uint64_t) 0x1) << 1;
|
||||
|
||||
/**
|
||||
* The object has the keyboard focus.
|
||||
*/
|
||||
const uint64_t FOCUSED = ((uint64_t) 0x1) << 2;
|
||||
|
||||
/**
|
||||
* The object is pressed.
|
||||
*/
|
||||
const uint64_t PRESSED = ((uint64_t) 0x1) << 3;
|
||||
|
||||
/**
|
||||
* The checkable object is checked, applied to check box controls,
|
||||
* @see CHECKABLE and MIXED states.
|
||||
*/
|
||||
const uint64_t CHECKED = ((uint64_t) 0x1) << 4;
|
||||
|
||||
/**
|
||||
* Indicates that the state of a three-state check box or tool bar button is
|
||||
* undetermined. The check box is neither checked or unchecked, and is
|
||||
* in the third or mixed state.
|
||||
*/
|
||||
const uint64_t MIXED = ((uint64_t) 0x1) << 5;
|
||||
|
||||
/**
|
||||
* The object is designated read-only, so it can't be edited.
|
||||
*/
|
||||
const uint64_t READONLY = ((uint64_t) 0x1) << 6;
|
||||
|
||||
/**
|
||||
* The object is hot-tracked by the mouse, which means that its appearance
|
||||
* has changed to indicate that the mouse pointer is located over it.
|
||||
*
|
||||
* This is currently unused.
|
||||
*/
|
||||
const uint64_t HOTTRACKED = ((uint64_t) 0x1) << 7;
|
||||
|
||||
/**
|
||||
* This object is the default button in a window.
|
||||
*/
|
||||
const uint64_t DEFAULT = ((uint64_t) 0x1) << 8;
|
||||
|
||||
/**
|
||||
* The expandable object's children are displayed, the opposite of collapsed,
|
||||
* applied to trees, list and other controls.
|
||||
* @see COLLAPSED state
|
||||
*/
|
||||
const uint64_t EXPANDED = ((uint64_t) 0x1) << 9;
|
||||
|
||||
/**
|
||||
* The expandable object's children are not displayed, the opposite of
|
||||
* expanded, applied to tree lists and other controls,
|
||||
* @see EXPANDED state.
|
||||
*/
|
||||
const uint64_t COLLAPSED = ((uint64_t) 0x1) << 10;
|
||||
|
||||
/**
|
||||
* The control or document can not accept input at this time.
|
||||
*/
|
||||
const uint64_t BUSY = ((uint64_t) 0x1) << 11;
|
||||
|
||||
/**
|
||||
* The object is out of normal flow, may be outside of boundaries of its
|
||||
* parent.
|
||||
*/
|
||||
const uint64_t FLOATING = ((uint64_t) 0x1) << 12;
|
||||
|
||||
/**
|
||||
* The object can be checked.
|
||||
*/
|
||||
const uint64_t CHECKABLE = ((uint64_t) 0x1) << 13;
|
||||
|
||||
/**
|
||||
* This object is a graphic which is rapidly changing appearance.
|
||||
*/
|
||||
const uint64_t ANIMATED = ((uint64_t) 0x1) << 14;
|
||||
|
||||
/**
|
||||
* The object is programmatically hidden.
|
||||
* So user action like scrolling or switching tabs won't make this visible.
|
||||
*/
|
||||
const uint64_t INVISIBLE = ((uint64_t) 0x1) << 15;
|
||||
|
||||
/**
|
||||
* The object is scrolled off screen.
|
||||
* User action such as scrolling or changing tab may make the object
|
||||
* visible.
|
||||
*/
|
||||
const uint64_t OFFSCREEN = ((uint64_t) 0x1) << 16;
|
||||
|
||||
/**
|
||||
* The object can be resized.
|
||||
*/
|
||||
const uint64_t SIZEABLE = ((uint64_t) 0x1) << 17;
|
||||
|
||||
/**
|
||||
* The object can be moved to a different position.
|
||||
*/
|
||||
const uint64_t MOVEABLE = ((uint64_t) 0x1) << 18;
|
||||
|
||||
/**
|
||||
* The object describes itself with speech.
|
||||
* Other speech related assistive technology may want to avoid speaking
|
||||
* information about this object, because the object is already doing this.
|
||||
*/
|
||||
const uint64_t SELFVOICING = ((uint64_t) 0x1) << 19;
|
||||
|
||||
/**
|
||||
* The object can have the focus and become focused.
|
||||
*/
|
||||
const uint64_t FOCUSABLE = ((uint64_t) 0x1) << 20;
|
||||
|
||||
/**
|
||||
* The object can be selected.
|
||||
*/
|
||||
const uint64_t SELECTABLE = ((uint64_t) 0x1) << 21;
|
||||
|
||||
/**
|
||||
* This object is a link.
|
||||
*/
|
||||
const uint64_t LINKED = ((uint64_t) 0x1) << 22;
|
||||
|
||||
/**
|
||||
* This is used for links that have been traversed
|
||||
* i.e. the linked page has been visited.
|
||||
*/
|
||||
const uint64_t TRAVERSED = ((uint64_t) 0x1) << 23;
|
||||
|
||||
/**
|
||||
* Supports multiple selection.
|
||||
*/
|
||||
const uint64_t MULTISELECTABLE = ((uint64_t) 0x1) << 24;
|
||||
|
||||
/**
|
||||
* Supports extended selection.
|
||||
* All objects supporting this are also multipselectable.
|
||||
* This only makes sense for msaa see bug 635690.
|
||||
*/
|
||||
const uint64_t EXTSELECTABLE = ((uint64_t) 0x1) << 25;
|
||||
|
||||
/**
|
||||
* The user is required to interact with this object.
|
||||
*/
|
||||
const uint64_t REQUIRED = ((uint64_t) 0x1) << 26;
|
||||
|
||||
/**
|
||||
* The object is an alert, notifying the user of something important.
|
||||
*/
|
||||
const uint64_t ALERT = ((uint64_t) 0x1) << 27;
|
||||
|
||||
/**
|
||||
* Used for text fields containing invalid values.
|
||||
*/
|
||||
const uint64_t INVALID = ((uint64_t) 0x1) << 28;
|
||||
|
||||
/**
|
||||
* The controls value can not be obtained, and is returned as a set of "*"s.
|
||||
*/
|
||||
const uint64_t PROTECTED = ((uint64_t) 0x1) << 29;
|
||||
|
||||
/**
|
||||
* The object can be invoked to show a pop up menu or window.
|
||||
*/
|
||||
const uint64_t HASPOPUP = ((uint64_t) 0x1) << 30;
|
||||
|
||||
/**
|
||||
* The editable area has some kind of autocompletion.
|
||||
*/
|
||||
const uint64_t SUPPORTS_AUTOCOMPLETION = ((uint64_t) 0x1) << 31;
|
||||
|
||||
/**
|
||||
* The object is no longer available to be queried.
|
||||
*/
|
||||
const uint64_t DEFUNCT = ((uint64_t) 0x1) << 32;
|
||||
|
||||
/**
|
||||
* The text is selectable, the object must implement the text interface.
|
||||
*/
|
||||
const uint64_t SELECTABLE_TEXT = ((uint64_t) 0x1) << 33;
|
||||
|
||||
/**
|
||||
* The text in this object can be edited.
|
||||
*/
|
||||
const uint64_t EDITABLE = ((uint64_t) 0x1) << 34;
|
||||
|
||||
/**
|
||||
* This window is currently the active window.
|
||||
*/
|
||||
const uint64_t ACTIVE = ((uint64_t) 0x1) << 35;
|
||||
|
||||
/**
|
||||
* Indicates that the object is modal. Modal objects have the behavior
|
||||
* that something must be done with the object before the user can
|
||||
* interact with an object in a different window.
|
||||
*/
|
||||
const uint64_t MODAL = ((uint64_t) 0x1) << 36;
|
||||
|
||||
/**
|
||||
* Edit control that can take multiple lines.
|
||||
*/
|
||||
const uint64_t MULTI_LINE = ((uint64_t) 0x1) << 37;
|
||||
|
||||
/**
|
||||
* Uses horizontal layout.
|
||||
*/
|
||||
const uint64_t HORIZONTAL = ((uint64_t) 0x1) << 38;
|
||||
|
||||
/**
|
||||
* Indicates this object paints every pixel within its rectangular region.
|
||||
*/
|
||||
const uint64_t OPAQUE1 = ((uint64_t) 0x1) << 39;
|
||||
|
||||
/**
|
||||
* This text object can only contain 1 line of text.
|
||||
*/
|
||||
const uint64_t SINGLE_LINE = ((uint64_t) 0x1) << 40;
|
||||
|
||||
/**
|
||||
* The parent object manages descendants, and this object may only exist
|
||||
* while it is visible or has focus.
|
||||
* For example the focused cell of a table or the current element of a list box may have this state.
|
||||
*/
|
||||
const uint64_t TRANSIENT = ((uint64_t) 0x1) << 41;
|
||||
|
||||
/**
|
||||
* Uses vertical layout.
|
||||
* Especially used for sliders and scrollbars.
|
||||
*/
|
||||
const uint64_t VERTICAL = ((uint64_t) 0x1) << 42;
|
||||
|
||||
/**
|
||||
* Object not dead, but not up-to-date either.
|
||||
*/
|
||||
const uint64_t STALE = ((uint64_t) 0x1) << 43;
|
||||
|
||||
/**
|
||||
* A widget that is not unavailable.
|
||||
*/
|
||||
const uint64_t ENABLED = ((uint64_t) 0x1) << 44;
|
||||
|
||||
/**
|
||||
* Same as ENABLED state for now see bug 636158
|
||||
*/
|
||||
const uint64_t SENSITIVE = ((uint64_t) 0x1) << 45;
|
||||
|
||||
/**
|
||||
* The object is expandable, provides a UI to expand/collapse its children
|
||||
* @see EXPANDED and COLLAPSED states.
|
||||
*/
|
||||
const uint64_t EXPANDABLE = ((uint64_t) 0x1) << 46;
|
||||
|
||||
/**
|
||||
* The object is pinned, usually indicating it is fixed in place and has permanence.
|
||||
*/
|
||||
const uint64_t PINNED = ((uint64_t) 0x1) << 47;
|
||||
} // namespace states
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
39
accessible/base/Statistics.h
Normal file
39
accessible/base/Statistics.h
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* -*- 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 A11Y_STATISTICS_H_
|
||||
#define A11Y_STATISTICS_H_
|
||||
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace statistics {
|
||||
|
||||
inline void A11yInitialized()
|
||||
{ Telemetry::Accumulate(Telemetry::A11Y_INSTANTIATED_FLAG, true); }
|
||||
|
||||
inline void A11yConsumers(uint32_t aConsumer)
|
||||
{ Telemetry::Accumulate(Telemetry::A11Y_CONSUMERS, aConsumer); }
|
||||
|
||||
/**
|
||||
* Report that ISimpleDOM* has been used.
|
||||
*/
|
||||
inline void ISimpleDOMUsed()
|
||||
{ Telemetry::Accumulate(Telemetry::A11Y_ISIMPLEDOM_USAGE_FLAG, true); }
|
||||
|
||||
/**
|
||||
* Report that IAccessibleTable has been used.
|
||||
*/
|
||||
inline void IAccessibleTableUsed()
|
||||
{ Telemetry::Accumulate(Telemetry::A11Y_IATABLE_USAGE_FLAG, true); }
|
||||
|
||||
} // namespace statistics
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
122
accessible/base/StyleInfo.cpp
Normal file
122
accessible/base/StyleInfo.cpp
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set expandtab shiftwidth=2 tabstop=2: */
|
||||
/* 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 "StyleInfo.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsComputedDOMStyle.h"
|
||||
#include "nsCSSProps.h"
|
||||
#include "nsIFrame.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
StyleInfo::StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell) :
|
||||
mElement(aElement)
|
||||
{
|
||||
mStyleContext =
|
||||
nsComputedDOMStyle::GetStyleContextForElementNoFlush(aElement,
|
||||
nullptr,
|
||||
aPresShell);
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::Display(nsAString& aValue)
|
||||
{
|
||||
aValue.Truncate();
|
||||
AppendASCIItoUTF16(
|
||||
nsCSSProps::ValueToKeyword(mStyleContext->StyleDisplay()->mDisplay,
|
||||
nsCSSProps::kDisplayKTable), aValue);
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::TextAlign(nsAString& aValue)
|
||||
{
|
||||
aValue.Truncate();
|
||||
AppendASCIItoUTF16(
|
||||
nsCSSProps::ValueToKeyword(mStyleContext->StyleText()->mTextAlign,
|
||||
nsCSSProps::kTextAlignKTable), aValue);
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::TextIndent(nsAString& aValue)
|
||||
{
|
||||
aValue.Truncate();
|
||||
|
||||
const nsStyleCoord& styleCoord =
|
||||
mStyleContext->StyleText()->mTextIndent;
|
||||
|
||||
nscoord coordVal = 0;
|
||||
switch (styleCoord.GetUnit()) {
|
||||
case eStyleUnit_Coord:
|
||||
coordVal = styleCoord.GetCoordValue();
|
||||
aValue.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(coordVal));
|
||||
aValue.AppendLiteral("px");
|
||||
break;
|
||||
|
||||
case eStyleUnit_Percent:
|
||||
aValue.AppendFloat(styleCoord.GetPercentValue() * 100);
|
||||
aValue.AppendLiteral("%");
|
||||
break;
|
||||
|
||||
case eStyleUnit_Null:
|
||||
case eStyleUnit_Normal:
|
||||
case eStyleUnit_Auto:
|
||||
case eStyleUnit_None:
|
||||
case eStyleUnit_Factor:
|
||||
case eStyleUnit_Degree:
|
||||
case eStyleUnit_Grad:
|
||||
case eStyleUnit_Radian:
|
||||
case eStyleUnit_Turn:
|
||||
case eStyleUnit_FlexFraction:
|
||||
case eStyleUnit_Integer:
|
||||
case eStyleUnit_Enumerated:
|
||||
case eStyleUnit_Calc:
|
||||
aValue.AppendLiteral("0px");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::Margin(css::Side aSide, nsAString& aValue)
|
||||
{
|
||||
MOZ_ASSERT(mElement->GetPrimaryFrame(), " mElement->GetPrimaryFrame() needs to be valid pointer");
|
||||
aValue.Truncate();
|
||||
|
||||
nscoord coordVal = mElement->GetPrimaryFrame()->GetUsedMargin().Side(aSide);
|
||||
aValue.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(coordVal));
|
||||
aValue.AppendLiteral("px");
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::FormatColor(const nscolor& aValue, nsString& aFormattedValue)
|
||||
{
|
||||
// Combine the string like rgb(R, G, B) from nscolor.
|
||||
aFormattedValue.AppendLiteral("rgb(");
|
||||
aFormattedValue.AppendInt(NS_GET_R(aValue));
|
||||
aFormattedValue.AppendLiteral(", ");
|
||||
aFormattedValue.AppendInt(NS_GET_G(aValue));
|
||||
aFormattedValue.AppendLiteral(", ");
|
||||
aFormattedValue.AppendInt(NS_GET_B(aValue));
|
||||
aFormattedValue.Append(')');
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue)
|
||||
{
|
||||
nsCSSKeyword keyword =
|
||||
nsCSSProps::ValueToKeywordEnum(aValue, nsCSSProps::kFontStyleKTable);
|
||||
AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(keyword), aFormattedValue);
|
||||
}
|
||||
|
||||
void
|
||||
StyleInfo::FormatTextDecorationStyle(uint8_t aValue, nsAString& aFormattedValue)
|
||||
{
|
||||
nsCSSKeyword keyword =
|
||||
nsCSSProps::ValueToKeywordEnum(aValue,
|
||||
nsCSSProps::kTextDecorationStyleKTable);
|
||||
AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(keyword), aFormattedValue);
|
||||
}
|
||||
48
accessible/base/StyleInfo.h
Normal file
48
accessible/base/StyleInfo.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set expandtab shiftwidth=2 tabstop=2: */
|
||||
/* 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_style_h_
|
||||
#define _mozilla_a11y_style_h_
|
||||
|
||||
#include "mozilla/gfx/Types.h"
|
||||
#include "nsStyleContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class StyleInfo
|
||||
{
|
||||
public:
|
||||
StyleInfo(dom::Element* aElement, nsIPresShell* aPresShell);
|
||||
~StyleInfo() { }
|
||||
|
||||
void Display(nsAString& aValue);
|
||||
void TextAlign(nsAString& aValue);
|
||||
void TextIndent(nsAString& aValue);
|
||||
void MarginLeft(nsAString& aValue) { Margin(eSideLeft, aValue); }
|
||||
void MarginRight(nsAString& aValue) { Margin(eSideRight, aValue); }
|
||||
void MarginTop(nsAString& aValue) { Margin(eSideTop, aValue); }
|
||||
void MarginBottom(nsAString& aValue) { Margin(eSideBottom, aValue); }
|
||||
|
||||
static void FormatColor(const nscolor& aValue, nsString& aFormattedValue);
|
||||
static void FormatFontStyle(const nscoord& aValue, nsAString& aFormattedValue);
|
||||
static void FormatTextDecorationStyle(uint8_t aValue, nsAString& aFormattedValue);
|
||||
|
||||
private:
|
||||
StyleInfo() = delete;
|
||||
StyleInfo(const StyleInfo&) = delete;
|
||||
StyleInfo& operator = (const StyleInfo&) = delete;
|
||||
|
||||
void Margin(Side aSide, nsAString& aValue);
|
||||
|
||||
dom::Element* mElement;
|
||||
RefPtr<nsStyleContext> mStyleContext;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
913
accessible/base/TextAttrs.cpp
Normal file
913
accessible/base/TextAttrs.cpp
Normal file
|
|
@ -0,0 +1,913 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "TextAttrs.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "StyleInfo.h"
|
||||
|
||||
#include "gfxFont.h"
|
||||
#include "nsFontMetrics.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsContainerFrame.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "mozilla/AppUnits.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
#include "gfxPlatformGtk.h" // xxx - for UseFcFontList
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextAttrsMgr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
TextAttrsMgr::GetAttributes(nsIPersistentProperties* aAttributes,
|
||||
uint32_t* aStartOffset,
|
||||
uint32_t* aEndOffset)
|
||||
{
|
||||
// 1. Hyper text accessible must be specified always.
|
||||
// 2. Offset accessible and result hyper text offsets must be specified in
|
||||
// the case of text attributes.
|
||||
// 3. Offset accessible and result hyper text offsets must not be specified
|
||||
// but include default text attributes flag and attributes list must be
|
||||
// specified in the case of default text attributes.
|
||||
NS_PRECONDITION(mHyperTextAcc &&
|
||||
((mOffsetAcc && mOffsetAccIdx != -1 &&
|
||||
aStartOffset && aEndOffset) ||
|
||||
(!mOffsetAcc && mOffsetAccIdx == -1 &&
|
||||
!aStartOffset && !aEndOffset &&
|
||||
mIncludeDefAttrs && aAttributes)),
|
||||
"Wrong usage of TextAttrsMgr!");
|
||||
|
||||
// Embedded objects are combined into own range with empty attributes set.
|
||||
if (mOffsetAcc && !mOffsetAcc->IsText()) {
|
||||
for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
|
||||
Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
|
||||
if (currAcc->IsText())
|
||||
break;
|
||||
|
||||
(*aStartOffset)--;
|
||||
}
|
||||
|
||||
uint32_t childCount = mHyperTextAcc->ChildCount();
|
||||
for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childCount;
|
||||
childIdx++) {
|
||||
Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
|
||||
if (currAcc->IsText())
|
||||
break;
|
||||
|
||||
(*aEndOffset)++;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the content and frame of the accessible. In the case of document
|
||||
// accessible it's role content and root frame.
|
||||
nsIContent* hyperTextElm = mHyperTextAcc->GetContent();
|
||||
if (!hyperTextElm)
|
||||
return; // XXX: we don't support text attrs on document with no body
|
||||
|
||||
nsIFrame* rootFrame = mHyperTextAcc->GetFrame();
|
||||
MOZ_ASSERT(rootFrame, "No frame for accessible!");
|
||||
if (!rootFrame)
|
||||
return;
|
||||
|
||||
nsIContent *offsetNode = nullptr, *offsetElm = nullptr;
|
||||
nsIFrame *frame = nullptr;
|
||||
if (mOffsetAcc) {
|
||||
offsetNode = mOffsetAcc->GetContent();
|
||||
offsetElm = nsCoreUtils::GetDOMElementFor(offsetNode);
|
||||
MOZ_ASSERT(offsetElm, "No element for offset accessible!");
|
||||
if (!offsetElm)
|
||||
return;
|
||||
|
||||
frame = offsetElm->GetPrimaryFrame();
|
||||
}
|
||||
|
||||
// "language" text attribute
|
||||
LangTextAttr langTextAttr(mHyperTextAcc, hyperTextElm, offsetNode);
|
||||
|
||||
// "aria-invalid" text attribute
|
||||
InvalidTextAttr invalidTextAttr(hyperTextElm, offsetNode);
|
||||
|
||||
// "background-color" text attribute
|
||||
BGColorTextAttr bgColorTextAttr(rootFrame, frame);
|
||||
|
||||
// "color" text attribute
|
||||
ColorTextAttr colorTextAttr(rootFrame, frame);
|
||||
|
||||
// "font-family" text attribute
|
||||
FontFamilyTextAttr fontFamilyTextAttr(rootFrame, frame);
|
||||
|
||||
// "font-size" text attribute
|
||||
FontSizeTextAttr fontSizeTextAttr(rootFrame, frame);
|
||||
|
||||
// "font-style" text attribute
|
||||
FontStyleTextAttr fontStyleTextAttr(rootFrame, frame);
|
||||
|
||||
// "font-weight" text attribute
|
||||
FontWeightTextAttr fontWeightTextAttr(rootFrame, frame);
|
||||
|
||||
// "auto-generated" text attribute
|
||||
AutoGeneratedTextAttr autoGenTextAttr(mHyperTextAcc, mOffsetAcc);
|
||||
|
||||
// "text-underline(line-through)-style(color)" text attributes
|
||||
TextDecorTextAttr textDecorTextAttr(rootFrame, frame);
|
||||
|
||||
// "text-position" text attribute
|
||||
TextPosTextAttr textPosTextAttr(rootFrame, frame);
|
||||
|
||||
TextAttr* attrArray[] =
|
||||
{
|
||||
&langTextAttr,
|
||||
&invalidTextAttr,
|
||||
&bgColorTextAttr,
|
||||
&colorTextAttr,
|
||||
&fontFamilyTextAttr,
|
||||
&fontSizeTextAttr,
|
||||
&fontStyleTextAttr,
|
||||
&fontWeightTextAttr,
|
||||
&autoGenTextAttr,
|
||||
&textDecorTextAttr,
|
||||
&textPosTextAttr
|
||||
};
|
||||
|
||||
// Expose text attributes if applicable.
|
||||
if (aAttributes) {
|
||||
for (uint32_t idx = 0; idx < ArrayLength(attrArray); idx++)
|
||||
attrArray[idx]->Expose(aAttributes, mIncludeDefAttrs);
|
||||
}
|
||||
|
||||
// Expose text attributes range where they are applied if applicable.
|
||||
if (mOffsetAcc)
|
||||
GetRange(attrArray, ArrayLength(attrArray), aStartOffset, aEndOffset);
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
|
||||
uint32_t* aStartOffset, uint32_t* aEndOffset)
|
||||
{
|
||||
// Navigate backward from anchor accessible to find start offset.
|
||||
for (int32_t childIdx = mOffsetAccIdx - 1; childIdx >= 0; childIdx--) {
|
||||
Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
|
||||
|
||||
// Stop on embedded accessible since embedded accessibles are combined into
|
||||
// own range.
|
||||
if (!currAcc->IsText())
|
||||
break;
|
||||
|
||||
MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
|
||||
"Text accessible has to have an associated DOM element");
|
||||
|
||||
bool offsetFound = false;
|
||||
for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
|
||||
TextAttr* textAttr = aAttrArray[attrIdx];
|
||||
if (!textAttr->Equal(currAcc)) {
|
||||
offsetFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetFound)
|
||||
break;
|
||||
|
||||
*(aStartOffset) -= nsAccUtils::TextLength(currAcc);
|
||||
}
|
||||
|
||||
// Navigate forward from anchor accessible to find end offset.
|
||||
uint32_t childLen = mHyperTextAcc->ChildCount();
|
||||
for (uint32_t childIdx = mOffsetAccIdx + 1; childIdx < childLen; childIdx++) {
|
||||
Accessible* currAcc = mHyperTextAcc->GetChildAt(childIdx);
|
||||
if (!currAcc->IsText())
|
||||
break;
|
||||
|
||||
MOZ_ASSERT(nsCoreUtils::GetDOMElementFor(currAcc->GetContent()),
|
||||
"Text accessible has to have an associated DOM element");
|
||||
|
||||
bool offsetFound = false;
|
||||
for (uint32_t attrIdx = 0; attrIdx < aAttrArrayLen; attrIdx++) {
|
||||
TextAttr* textAttr = aAttrArray[attrIdx];
|
||||
|
||||
// Alter the end offset when text attribute changes its value and stop
|
||||
// the search.
|
||||
if (!textAttr->Equal(currAcc)) {
|
||||
offsetFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (offsetFound)
|
||||
break;
|
||||
|
||||
(*aEndOffset) += nsAccUtils::TextLength(currAcc);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// LangTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::LangTextAttr::
|
||||
LangTextAttr(HyperTextAccessible* aRoot,
|
||||
nsIContent* aRootElm, nsIContent* aElm) :
|
||||
TTextAttr<nsString>(!aElm), mRootContent(aRootElm)
|
||||
{
|
||||
aRoot->Language(mRootNativeValue);
|
||||
mIsRootDefined = !mRootNativeValue.IsEmpty();
|
||||
|
||||
if (aElm) {
|
||||
nsCoreUtils::GetLanguageFor(aElm, mRootContent, mNativeValue);
|
||||
mIsDefined = !mNativeValue.IsEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
TextAttrsMgr::LangTextAttr::
|
||||
~LangTextAttr() {}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::LangTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nsString* aValue)
|
||||
{
|
||||
nsCoreUtils::GetLanguageFor(aAccessible->GetContent(), mRootContent, *aValue);
|
||||
return !aValue->IsEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::LangTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
|
||||
{
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::language, aValue);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// InvalidTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::InvalidTextAttr::
|
||||
InvalidTextAttr(nsIContent* aRootElm, nsIContent* aElm) :
|
||||
TTextAttr<uint32_t>(!aElm), mRootElm(aRootElm)
|
||||
{
|
||||
mIsRootDefined = GetValue(mRootElm, &mRootNativeValue);
|
||||
if (aElm)
|
||||
mIsDefined = GetValue(aElm, &mNativeValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::InvalidTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, uint32_t* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
return elm ? GetValue(elm, aValue) : false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::InvalidTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const uint32_t& aValue)
|
||||
{
|
||||
switch (aValue) {
|
||||
case eFalse:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
|
||||
NS_LITERAL_STRING("false"));
|
||||
break;
|
||||
|
||||
case eGrammar:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
|
||||
NS_LITERAL_STRING("grammar"));
|
||||
break;
|
||||
|
||||
case eSpelling:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
|
||||
NS_LITERAL_STRING("spelling"));
|
||||
break;
|
||||
|
||||
case eTrue:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::invalid,
|
||||
NS_LITERAL_STRING("true"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::InvalidTextAttr::
|
||||
GetValue(nsIContent* aElm, uint32_t* aValue)
|
||||
{
|
||||
nsIContent* elm = aElm;
|
||||
do {
|
||||
if (nsAccUtils::HasDefinedARIAToken(elm, nsGkAtoms::aria_invalid)) {
|
||||
static nsIContent::AttrValuesArray tokens[] =
|
||||
{ &nsGkAtoms::_false, &nsGkAtoms::grammar, &nsGkAtoms::spelling,
|
||||
nullptr };
|
||||
|
||||
int32_t idx = elm->FindAttrValueIn(kNameSpaceID_None,
|
||||
nsGkAtoms::aria_invalid, tokens,
|
||||
eCaseMatters);
|
||||
switch (idx) {
|
||||
case 0:
|
||||
*aValue = eFalse;
|
||||
return true;
|
||||
case 1:
|
||||
*aValue = eGrammar;
|
||||
return true;
|
||||
case 2:
|
||||
*aValue = eSpelling;
|
||||
return true;
|
||||
default:
|
||||
*aValue = eTrue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} while ((elm = elm->GetParent()) && elm != mRootElm);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// BGColorTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::BGColorTextAttr::
|
||||
BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<nscolor>(!aFrame), mRootFrame(aRootFrame)
|
||||
{
|
||||
mIsRootDefined = GetColor(mRootFrame, &mRootNativeValue);
|
||||
if (aFrame)
|
||||
mIsDefined = GetColor(aFrame, &mNativeValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::BGColorTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nscolor* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
return GetColor(frame, aValue);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::BGColorTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nscolor& aValue)
|
||||
{
|
||||
nsAutoString formattedValue;
|
||||
StyleInfo::FormatColor(aValue, formattedValue);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::backgroundColor,
|
||||
formattedValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::BGColorTextAttr::
|
||||
GetColor(nsIFrame* aFrame, nscolor* aColor)
|
||||
{
|
||||
const nsStyleBackground* styleBackground = aFrame->StyleBackground();
|
||||
|
||||
if (NS_GET_A(styleBackground->mBackgroundColor) > 0) {
|
||||
*aColor = styleBackground->mBackgroundColor;
|
||||
return true;
|
||||
}
|
||||
|
||||
nsContainerFrame *parentFrame = aFrame->GetParent();
|
||||
if (!parentFrame) {
|
||||
*aColor = aFrame->PresContext()->DefaultBackgroundColor();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Each frame of parents chain for the initially passed 'aFrame' has
|
||||
// transparent background color. So background color isn't changed from
|
||||
// 'mRootFrame' to initially passed 'aFrame'.
|
||||
if (parentFrame == mRootFrame)
|
||||
return false;
|
||||
|
||||
return GetColor(parentFrame, aColor);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// ColorTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::ColorTextAttr::
|
||||
ColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<nscolor>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = aRootFrame->StyleColor()->mColor;
|
||||
mIsRootDefined = true;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = aFrame->StyleColor()->mColor;
|
||||
mIsDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::ColorTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nscolor* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = frame->StyleColor()->mColor;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::ColorTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nscolor& aValue)
|
||||
{
|
||||
nsAutoString formattedValue;
|
||||
StyleInfo::FormatColor(aValue, formattedValue);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::color, formattedValue);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FontFamilyTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::FontFamilyTextAttr::
|
||||
FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<nsString>(!aFrame)
|
||||
{
|
||||
mIsRootDefined = GetFontFamily(aRootFrame, mRootNativeValue);
|
||||
|
||||
if (aFrame)
|
||||
mIsDefined = GetFontFamily(aFrame, mNativeValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::FontFamilyTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nsString* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
return GetFontFamily(frame, *aValue);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::FontFamilyTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nsString& aValue)
|
||||
{
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_family, aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::FontFamilyTextAttr::
|
||||
GetFontFamily(nsIFrame* aFrame, nsString& aFamily)
|
||||
{
|
||||
RefPtr<nsFontMetrics> fm =
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
|
||||
|
||||
gfxFontGroup* fontGroup = fm->GetThebesFontGroup();
|
||||
gfxFont* font = fontGroup->GetFirstValidFont();
|
||||
gfxFontEntry* fontEntry = font->GetFontEntry();
|
||||
aFamily = fontEntry->FamilyName();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FontSizeTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::FontSizeTextAttr::
|
||||
FontSizeTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<nscoord>(!aFrame)
|
||||
{
|
||||
mDC = aRootFrame->PresContext()->DeviceContext();
|
||||
|
||||
mRootNativeValue = aRootFrame->StyleFont()->mSize;
|
||||
mIsRootDefined = true;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = aFrame->StyleFont()->mSize;
|
||||
mIsDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::FontSizeTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nscoord* aValue)
|
||||
{
|
||||
nsIContent* el = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (el) {
|
||||
nsIFrame* frame = el->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = frame->StyleFont()->mSize;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::FontSizeTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nscoord& aValue)
|
||||
{
|
||||
// Convert from nscoord to pt.
|
||||
//
|
||||
// Note: according to IA2, "The conversion doesn't have to be exact.
|
||||
// The intent is to give the user a feel for the size of the text."
|
||||
//
|
||||
// ATK does not specify a unit and will likely follow IA2 here.
|
||||
//
|
||||
// XXX todo: consider sharing this code with layout module? (bug 474621)
|
||||
float px =
|
||||
NSAppUnitsToFloatPixels(aValue, mozilla::AppUnitsPerCSSPixel());
|
||||
// Each pt is 4/3 of a CSS pixel.
|
||||
int pts = NS_lround(px*3/4);
|
||||
|
||||
nsAutoString value;
|
||||
value.AppendInt(pts);
|
||||
value.AppendLiteral("pt");
|
||||
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_size, value);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FontStyleTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::FontStyleTextAttr::
|
||||
FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<nscoord>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = aRootFrame->StyleFont()->mFont.style;
|
||||
mIsRootDefined = true;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = aFrame->StyleFont()->mFont.style;
|
||||
mIsDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::FontStyleTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, nscoord* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = frame->StyleFont()->mFont.style;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::FontStyleTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const nscoord& aValue)
|
||||
{
|
||||
nsAutoString formattedValue;
|
||||
StyleInfo::FormatFontStyle(aValue, formattedValue);
|
||||
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::font_style, formattedValue);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FontWeightTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::FontWeightTextAttr::
|
||||
FontWeightTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<int32_t>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = GetFontWeight(aRootFrame);
|
||||
mIsRootDefined = true;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = GetFontWeight(aFrame);
|
||||
mIsDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::FontWeightTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, int32_t* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = GetFontWeight(frame);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::FontWeightTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const int32_t& aValue)
|
||||
{
|
||||
nsAutoString formattedValue;
|
||||
formattedValue.AppendInt(aValue);
|
||||
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::fontWeight, formattedValue);
|
||||
}
|
||||
|
||||
int32_t
|
||||
TextAttrsMgr::FontWeightTextAttr::
|
||||
GetFontWeight(nsIFrame* aFrame)
|
||||
{
|
||||
// nsFont::width isn't suitable here because it's necessary to expose real
|
||||
// value of font weight (used font might not have some font weight values).
|
||||
RefPtr<nsFontMetrics> fm =
|
||||
nsLayoutUtils::GetFontMetricsForFrame(aFrame, 1.0f);
|
||||
|
||||
gfxFontGroup *fontGroup = fm->GetThebesFontGroup();
|
||||
gfxFont *font = fontGroup->GetFirstValidFont();
|
||||
|
||||
// When there doesn't exist a bold font in the family and so the rendering of
|
||||
// a non-bold font face is changed so that the user sees what looks like a
|
||||
// bold font, i.e. synthetic bolding is used. IsSyntheticBold method is only
|
||||
// needed on Mac, but it is "safe" to use on all platforms. (For non-Mac
|
||||
// platforms it always return false.)
|
||||
if (font->IsSyntheticBold())
|
||||
return 700;
|
||||
|
||||
bool useFontEntryWeight = true;
|
||||
|
||||
// Under Linux, when gfxPangoFontGroup code is used,
|
||||
// font->GetStyle()->weight will give the absolute weight requested of the
|
||||
// font face. The gfxPangoFontGroup code uses the gfxFontEntry constructor
|
||||
// which doesn't initialize the weight field.
|
||||
#if defined(MOZ_WIDGET_GTK)
|
||||
useFontEntryWeight = gfxPlatformGtk::UseFcFontList();
|
||||
#endif
|
||||
|
||||
if (useFontEntryWeight) {
|
||||
// On Windows, font->GetStyle()->weight will give the same weight as
|
||||
// fontEntry->Weight(), the weight of the first font in the font group,
|
||||
// which may not be the weight of the font face used to render the
|
||||
// characters. On Mac, font->GetStyle()->weight will just give the same
|
||||
// number as getComputedStyle(). fontEntry->Weight() will give the weight
|
||||
// of the font face used.
|
||||
gfxFontEntry *fontEntry = font->GetFontEntry();
|
||||
return fontEntry->Weight();
|
||||
} else {
|
||||
return font->GetStyle()->weight;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AutoGeneratedTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
TextAttrsMgr::AutoGeneratedTextAttr::
|
||||
AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
|
||||
Accessible* aAccessible) :
|
||||
TTextAttr<bool>(!aAccessible)
|
||||
{
|
||||
mRootNativeValue = false;
|
||||
mIsRootDefined = false;
|
||||
|
||||
if (aAccessible)
|
||||
mIsDefined = mNativeValue = (aAccessible->NativeRole() == roles::STATICTEXT);
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::AutoGeneratedTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, bool* aValue)
|
||||
{
|
||||
return *aValue = (aAccessible->NativeRole() == roles::STATICTEXT);
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::AutoGeneratedTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const bool& aValue)
|
||||
{
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::auto_generated,
|
||||
aValue ? NS_LITERAL_STRING("true") : NS_LITERAL_STRING("false"));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextDecorTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::TextDecorValue::
|
||||
TextDecorValue(nsIFrame* aFrame)
|
||||
{
|
||||
const nsStyleTextReset* textReset = aFrame->StyleTextReset();
|
||||
mStyle = textReset->mTextDecorationStyle;
|
||||
mColor = aFrame->StyleColor()->
|
||||
CalcComplexColor(textReset->mTextDecorationColor);
|
||||
mLine = textReset->mTextDecorationLine &
|
||||
(NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE |
|
||||
NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH);
|
||||
}
|
||||
|
||||
TextAttrsMgr::TextDecorTextAttr::
|
||||
TextDecorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<TextDecorValue>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = TextDecorValue(aRootFrame);
|
||||
mIsRootDefined = mRootNativeValue.IsDefined();
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = TextDecorValue(aFrame);
|
||||
mIsDefined = mNativeValue.IsDefined();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::TextDecorTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, TextDecorValue* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = TextDecorValue(frame);
|
||||
return aValue->IsDefined();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::TextDecorTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const TextDecorValue& aValue)
|
||||
{
|
||||
if (aValue.IsUnderline()) {
|
||||
nsAutoString formattedStyle;
|
||||
StyleInfo::FormatTextDecorationStyle(aValue.Style(), formattedStyle);
|
||||
nsAccUtils::SetAccAttr(aAttributes,
|
||||
nsGkAtoms::textUnderlineStyle,
|
||||
formattedStyle);
|
||||
|
||||
nsAutoString formattedColor;
|
||||
StyleInfo::FormatColor(aValue.Color(), formattedColor);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textUnderlineColor,
|
||||
formattedColor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aValue.IsLineThrough()) {
|
||||
nsAutoString formattedStyle;
|
||||
StyleInfo::FormatTextDecorationStyle(aValue.Style(), formattedStyle);
|
||||
nsAccUtils::SetAccAttr(aAttributes,
|
||||
nsGkAtoms::textLineThroughStyle,
|
||||
formattedStyle);
|
||||
|
||||
nsAutoString formattedColor;
|
||||
StyleInfo::FormatColor(aValue.Color(), formattedColor);
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textLineThroughColor,
|
||||
formattedColor);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextPosTextAttr
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame) :
|
||||
TTextAttr<TextPosValue>(!aFrame)
|
||||
{
|
||||
mRootNativeValue = GetTextPosValue(aRootFrame);
|
||||
mIsRootDefined = mRootNativeValue != eTextPosNone;
|
||||
|
||||
if (aFrame) {
|
||||
mNativeValue = GetTextPosValue(aFrame);
|
||||
mIsDefined = mNativeValue != eTextPosNone;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
GetValueFor(Accessible* aAccessible, TextPosValue* aValue)
|
||||
{
|
||||
nsIContent* elm = nsCoreUtils::GetDOMElementFor(aAccessible->GetContent());
|
||||
if (elm) {
|
||||
nsIFrame* frame = elm->GetPrimaryFrame();
|
||||
if (frame) {
|
||||
*aValue = GetTextPosValue(frame);
|
||||
return *aValue != eTextPosNone;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
ExposeValue(nsIPersistentProperties* aAttributes, const TextPosValue& aValue)
|
||||
{
|
||||
switch (aValue) {
|
||||
case eTextPosBaseline:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("baseline"));
|
||||
break;
|
||||
|
||||
case eTextPosSub:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("sub"));
|
||||
break;
|
||||
|
||||
case eTextPosSuper:
|
||||
nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::textPosition,
|
||||
NS_LITERAL_STRING("super"));
|
||||
break;
|
||||
|
||||
case eTextPosNone:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TextAttrsMgr::TextPosValue
|
||||
TextAttrsMgr::TextPosTextAttr::
|
||||
GetTextPosValue(nsIFrame* aFrame) const
|
||||
{
|
||||
const nsStyleCoord& styleCoord = aFrame->StyleDisplay()->mVerticalAlign;
|
||||
switch (styleCoord.GetUnit()) {
|
||||
case eStyleUnit_Enumerated:
|
||||
switch (styleCoord.GetIntValue()) {
|
||||
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
|
||||
return eTextPosBaseline;
|
||||
case NS_STYLE_VERTICAL_ALIGN_SUB:
|
||||
return eTextPosSub;
|
||||
case NS_STYLE_VERTICAL_ALIGN_SUPER:
|
||||
return eTextPosSuper;
|
||||
|
||||
// No good guess for these:
|
||||
// NS_STYLE_VERTICAL_ALIGN_TOP
|
||||
// NS_STYLE_VERTICAL_ALIGN_TEXT_TOP
|
||||
// NS_STYLE_VERTICAL_ALIGN_MIDDLE
|
||||
// NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM
|
||||
// NS_STYLE_VERTICAL_ALIGN_BOTTOM
|
||||
// NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE
|
||||
// Do not expose value of text-position attribute.
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return eTextPosNone;
|
||||
|
||||
case eStyleUnit_Percent:
|
||||
{
|
||||
float percentValue = styleCoord.GetPercentValue();
|
||||
return percentValue > 0 ?
|
||||
eTextPosSuper :
|
||||
(percentValue < 0 ? eTextPosSub : eTextPosBaseline);
|
||||
}
|
||||
|
||||
case eStyleUnit_Coord:
|
||||
{
|
||||
nscoord coordValue = styleCoord.GetCoordValue();
|
||||
return coordValue > 0 ?
|
||||
eTextPosSuper :
|
||||
(coordValue < 0 ? eTextPosSub : eTextPosBaseline);
|
||||
}
|
||||
|
||||
case eStyleUnit_Null:
|
||||
case eStyleUnit_Normal:
|
||||
case eStyleUnit_Auto:
|
||||
case eStyleUnit_None:
|
||||
case eStyleUnit_Factor:
|
||||
case eStyleUnit_Degree:
|
||||
case eStyleUnit_Grad:
|
||||
case eStyleUnit_Radian:
|
||||
case eStyleUnit_Turn:
|
||||
case eStyleUnit_FlexFraction:
|
||||
case eStyleUnit_Integer:
|
||||
case eStyleUnit_Calc:
|
||||
break;
|
||||
}
|
||||
|
||||
const nsIContent* content = aFrame->GetContent();
|
||||
if (content) {
|
||||
if (content->IsHTMLElement(nsGkAtoms::sup))
|
||||
return eTextPosSuper;
|
||||
if (content->IsHTMLElement(nsGkAtoms::sub))
|
||||
return eTextPosSub;
|
||||
}
|
||||
|
||||
return eTextPosNone;
|
||||
}
|
||||
478
accessible/base/TextAttrs.h
Normal file
478
accessible/base/TextAttrs.h
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsTextAttrs_h_
|
||||
#define nsTextAttrs_h_
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsColor.h"
|
||||
#include "nsStyleConsts.h"
|
||||
|
||||
class nsIFrame;
|
||||
class nsIPersistentProperties;
|
||||
class nsIContent;
|
||||
class nsDeviceContext;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class HyperTextAccessible;
|
||||
|
||||
/**
|
||||
* Used to expose text attributes for the hyper text accessible (see
|
||||
* HyperTextAccessible class).
|
||||
*
|
||||
* @note "invalid: spelling" text attribute is implemented entirely in
|
||||
* HyperTextAccessible class.
|
||||
*/
|
||||
class TextAttrsMgr
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor. Used to expose default text attributes.
|
||||
*/
|
||||
explicit TextAttrsMgr(HyperTextAccessible* aHyperTextAcc) :
|
||||
mOffsetAcc(nullptr), mHyperTextAcc(aHyperTextAcc),
|
||||
mOffsetAccIdx(-1), mIncludeDefAttrs(true) { }
|
||||
|
||||
/**
|
||||
* Constructor. Used to expose text attributes at the given offset.
|
||||
*
|
||||
* @param aHyperTextAcc [in] hyper text accessible text attributes are
|
||||
* calculated for
|
||||
* @param aIncludeDefAttrs [optional] indicates whether default text
|
||||
* attributes should be included into list of exposed
|
||||
* text attributes
|
||||
* @param oOffsetAcc [optional] offset an accessible the text attributes
|
||||
* should be calculated for
|
||||
* @param oOffsetAccIdx [optional] index in parent of offset accessible
|
||||
*/
|
||||
TextAttrsMgr(HyperTextAccessible* aHyperTextAcc,
|
||||
bool aIncludeDefAttrs,
|
||||
Accessible* aOffsetAcc,
|
||||
int32_t aOffsetAccIdx) :
|
||||
mOffsetAcc(aOffsetAcc), mHyperTextAcc(aHyperTextAcc),
|
||||
mOffsetAccIdx(aOffsetAccIdx), mIncludeDefAttrs(aIncludeDefAttrs) { }
|
||||
|
||||
/*
|
||||
* Return text attributes and hyper text offsets where these attributes are
|
||||
* applied. Offsets are calculated in the case of non default attributes.
|
||||
*
|
||||
* @note In the case of default attributes pointers on hyper text offsets
|
||||
* must be skipped.
|
||||
*
|
||||
* @param aAttributes [in, out] text attributes list
|
||||
* @param aStartHTOffset [out, optional] start hyper text offset
|
||||
* @param aEndHTOffset [out, optional] end hyper text offset
|
||||
*/
|
||||
void GetAttributes(nsIPersistentProperties* aAttributes,
|
||||
uint32_t* aStartHTOffset = nullptr,
|
||||
uint32_t* aEndHTOffset = nullptr);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Calculates range (start and end offsets) of text where the text attributes
|
||||
* are stretched. New offsets may be smaller if one of text attributes changes
|
||||
* its value before or after the given offsets.
|
||||
*
|
||||
* @param aTextAttrArray [in] text attributes array
|
||||
* @param aAttrArrayLen [in] text attributes array length
|
||||
* @param aStartHTOffset [in, out] the start offset
|
||||
* @param aEndHTOffset [in, out] the end offset
|
||||
*/
|
||||
class TextAttr;
|
||||
void GetRange(TextAttr* aAttrArray[], uint32_t aAttrArrayLen,
|
||||
uint32_t* aStartOffset, uint32_t* aEndOffset);
|
||||
|
||||
private:
|
||||
Accessible* mOffsetAcc;
|
||||
HyperTextAccessible* mHyperTextAcc;
|
||||
int32_t mOffsetAccIdx;
|
||||
bool mIncludeDefAttrs;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Interface class of text attribute class implementations.
|
||||
*/
|
||||
class TextAttr
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Expose the text attribute to the given attribute set.
|
||||
*
|
||||
* @param aAttributes [in] the given attribute set
|
||||
* @param aIncludeDefAttrValue [in] if true then attribute is exposed even
|
||||
* if its value is the same as default one
|
||||
*/
|
||||
virtual void Expose(nsIPersistentProperties* aAttributes,
|
||||
bool aIncludeDefAttrValue) = 0;
|
||||
|
||||
/**
|
||||
* Return true if the text attribute value on the given element equals with
|
||||
* predefined attribute value.
|
||||
*/
|
||||
virtual bool Equal(Accessible* aAccessible) = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base class to work with text attributes. See derived classes below.
|
||||
*/
|
||||
template<class T>
|
||||
class TTextAttr : public TextAttr
|
||||
{
|
||||
public:
|
||||
explicit TTextAttr(bool aGetRootValue) : mGetRootValue(aGetRootValue) {}
|
||||
|
||||
// TextAttr
|
||||
virtual void Expose(nsIPersistentProperties* aAttributes,
|
||||
bool aIncludeDefAttrValue) override
|
||||
{
|
||||
if (mGetRootValue) {
|
||||
if (mIsRootDefined)
|
||||
ExposeValue(aAttributes, mRootNativeValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsDefined) {
|
||||
if (aIncludeDefAttrValue || mRootNativeValue != mNativeValue)
|
||||
ExposeValue(aAttributes, mNativeValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aIncludeDefAttrValue && mIsRootDefined)
|
||||
ExposeValue(aAttributes, mRootNativeValue);
|
||||
}
|
||||
|
||||
virtual bool Equal(Accessible* aAccessible) override
|
||||
{
|
||||
T nativeValue;
|
||||
bool isDefined = GetValueFor(aAccessible, &nativeValue);
|
||||
|
||||
if (!mIsDefined && !isDefined)
|
||||
return true;
|
||||
|
||||
if (mIsDefined && isDefined)
|
||||
return nativeValue == mNativeValue;
|
||||
|
||||
if (mIsDefined)
|
||||
return mNativeValue == mRootNativeValue;
|
||||
|
||||
return nativeValue == mRootNativeValue;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// Expose the text attribute with the given value to attribute set.
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const T& aValue) = 0;
|
||||
|
||||
// Return native value for the given DOM element.
|
||||
virtual bool GetValueFor(Accessible* aAccessible, T* aValue) = 0;
|
||||
|
||||
// Indicates if root value should be exposed.
|
||||
bool mGetRootValue;
|
||||
|
||||
// Native value and flag indicating if the value is defined (initialized in
|
||||
// derived classes). Note, undefined native value means it is inherited
|
||||
// from root.
|
||||
MOZ_INIT_OUTSIDE_CTOR T mNativeValue;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mIsDefined;
|
||||
|
||||
// Native root value and flag indicating if the value is defined (initialized
|
||||
// in derived classes).
|
||||
MOZ_INIT_OUTSIDE_CTOR T mRootNativeValue;
|
||||
MOZ_INIT_OUTSIDE_CTOR bool mIsRootDefined;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with 'language' text attribute.
|
||||
*/
|
||||
class LangTextAttr : public TTextAttr<nsString>
|
||||
{
|
||||
public:
|
||||
LangTextAttr(HyperTextAccessible* aRoot, nsIContent* aRootElm,
|
||||
nsIContent* aElm);
|
||||
virtual ~LangTextAttr();
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue) override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nsString& aValue) override;
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIContent> mRootContent;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the 'invalid' text attribute. Note, it calculated
|
||||
* the attribute from aria-invalid attribute only; invalid:spelling attribute
|
||||
* calculated from misspelled text in the editor is managed by
|
||||
* HyperTextAccessible and applied on top of the value from aria-invalid.
|
||||
*/
|
||||
class InvalidTextAttr : public TTextAttr<uint32_t>
|
||||
{
|
||||
public:
|
||||
InvalidTextAttr(nsIContent* aRootElm, nsIContent* aElm);
|
||||
virtual ~InvalidTextAttr() { };
|
||||
|
||||
protected:
|
||||
|
||||
enum {
|
||||
eFalse,
|
||||
eGrammar,
|
||||
eSpelling,
|
||||
eTrue
|
||||
};
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, uint32_t* aValue) override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const uint32_t& aValue) override;
|
||||
|
||||
private:
|
||||
bool GetValue(nsIContent* aElm, uint32_t* aValue);
|
||||
nsIContent* mRootElm;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with 'background-color' text attribute.
|
||||
*/
|
||||
class BGColorTextAttr : public TTextAttr<nscolor>
|
||||
{
|
||||
public:
|
||||
BGColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~BGColorTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nscolor& aValue) override;
|
||||
|
||||
private:
|
||||
bool GetColor(nsIFrame* aFrame, nscolor* aColor);
|
||||
nsIFrame* mRootFrame;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with 'color' text attribute.
|
||||
*/
|
||||
class ColorTextAttr : public TTextAttr<nscolor>
|
||||
{
|
||||
public:
|
||||
ColorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~ColorTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TTextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, nscolor* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nscolor& aValue) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with "font-family" text attribute.
|
||||
*/
|
||||
class FontFamilyTextAttr : public TTextAttr<nsString>
|
||||
{
|
||||
public:
|
||||
FontFamilyTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~FontFamilyTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TTextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, nsString* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nsString& aValue) override;
|
||||
|
||||
private:
|
||||
|
||||
bool GetFontFamily(nsIFrame* aFrame, nsString& aFamily);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with "font-size" text attribute.
|
||||
*/
|
||||
class FontSizeTextAttr : public TTextAttr<nscoord>
|
||||
{
|
||||
public:
|
||||
FontSizeTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~FontSizeTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TTextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, nscoord* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nscoord& aValue) override;
|
||||
|
||||
private:
|
||||
nsDeviceContext* mDC;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with "font-style" text attribute.
|
||||
*/
|
||||
class FontStyleTextAttr : public TTextAttr<nscoord>
|
||||
{
|
||||
public:
|
||||
FontStyleTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~FontStyleTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TTextAttr
|
||||
virtual bool GetValueFor(Accessible* aContent, nscoord* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const nscoord& aValue) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class is used for the work with "font-weight" text attribute.
|
||||
*/
|
||||
class FontWeightTextAttr : public TTextAttr<int32_t>
|
||||
{
|
||||
public:
|
||||
FontWeightTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~FontWeightTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TTextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, int32_t* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const int32_t& aValue) override;
|
||||
|
||||
private:
|
||||
int32_t GetFontWeight(nsIFrame* aFrame);
|
||||
};
|
||||
|
||||
/**
|
||||
* Class is used for the work with 'auto-generated' text attribute.
|
||||
*/
|
||||
class AutoGeneratedTextAttr : public TTextAttr<bool>
|
||||
{
|
||||
public:
|
||||
AutoGeneratedTextAttr(HyperTextAccessible* aHyperTextAcc,
|
||||
Accessible* aAccessible);
|
||||
virtual ~AutoGeneratedTextAttr() { }
|
||||
|
||||
protected:
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, bool* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const bool& aValue) override;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TextDecorTextAttr class is used for the work with
|
||||
* "text-line-through-style", "text-line-through-color",
|
||||
* "text-underline-style" and "text-underline-color" text attributes.
|
||||
*/
|
||||
|
||||
class TextDecorValue
|
||||
{
|
||||
public:
|
||||
TextDecorValue() { }
|
||||
explicit TextDecorValue(nsIFrame* aFrame);
|
||||
|
||||
nscolor Color() const { return mColor; }
|
||||
uint8_t Style() const { return mStyle; }
|
||||
|
||||
bool IsDefined() const
|
||||
{ return IsUnderline() || IsLineThrough(); }
|
||||
bool IsUnderline() const
|
||||
{ return mLine & NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE; }
|
||||
bool IsLineThrough() const
|
||||
{ return mLine & NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH; }
|
||||
|
||||
bool operator ==(const TextDecorValue& aValue)
|
||||
{
|
||||
return mColor == aValue.mColor && mLine == aValue.mLine &&
|
||||
mStyle == aValue.mStyle;
|
||||
}
|
||||
bool operator !=(const TextDecorValue& aValue)
|
||||
{ return !(*this == aValue); }
|
||||
|
||||
private:
|
||||
nscolor mColor;
|
||||
uint8_t mLine;
|
||||
uint8_t mStyle;
|
||||
};
|
||||
|
||||
class TextDecorTextAttr : public TTextAttr<TextDecorValue>
|
||||
{
|
||||
public:
|
||||
TextDecorTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~TextDecorTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, TextDecorValue* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const TextDecorValue& aValue) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Class is used for the work with "text-position" text attribute.
|
||||
*/
|
||||
|
||||
enum TextPosValue {
|
||||
eTextPosNone = 0,
|
||||
eTextPosBaseline,
|
||||
eTextPosSub,
|
||||
eTextPosSuper
|
||||
};
|
||||
|
||||
class TextPosTextAttr : public TTextAttr<TextPosValue>
|
||||
{
|
||||
public:
|
||||
TextPosTextAttr(nsIFrame* aRootFrame, nsIFrame* aFrame);
|
||||
virtual ~TextPosTextAttr() { }
|
||||
|
||||
protected:
|
||||
|
||||
// TextAttr
|
||||
virtual bool GetValueFor(Accessible* aAccessible, TextPosValue* aValue)
|
||||
override;
|
||||
virtual void ExposeValue(nsIPersistentProperties* aAttributes,
|
||||
const TextPosValue& aValue) override;
|
||||
|
||||
private:
|
||||
TextPosValue GetTextPosValue(nsIFrame* aFrame) const;
|
||||
};
|
||||
|
||||
}; // TextAttrMgr
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
29
accessible/base/TextRange-inl.h
Normal file
29
accessible/base/TextRange-inl.h
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- 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_TextRange_inl_h__
|
||||
#define mozilla_a11y_TextRange_inl_h__
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
inline Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
return CommonParent(mStartContainer, mEndContainer,
|
||||
&parents1, &pos1, &parents2, &pos2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
380
accessible/base/TextRange.cpp
Normal file
380
accessible/base/TextRange.cpp
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
/* -*- 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 "TextRange-inl.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextPoint
|
||||
|
||||
bool
|
||||
TextPoint::operator <(const TextPoint& aPoint) const
|
||||
{
|
||||
if (mContainer == aPoint.mContainer)
|
||||
return mOffset < aPoint.mOffset;
|
||||
|
||||
// Build the chain of parents
|
||||
Accessible* p1 = mContainer;
|
||||
Accessible* p2 = aPoint.mContainer;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
parents2.AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
uint32_t pos1 = parents1.Length(), pos2 = parents2.Length();
|
||||
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
|
||||
Accessible* child1 = parents1.ElementAt(--pos1);
|
||||
Accessible* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2)
|
||||
return child1->IndexInParent() < child2->IndexInParent();
|
||||
}
|
||||
|
||||
NS_ERROR("Broken tree?!");
|
||||
return false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TextRange
|
||||
|
||||
TextRange::TextRange(HyperTextAccessible* aRoot,
|
||||
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
|
||||
HyperTextAccessible* aEndContainer, int32_t aEndOffset) :
|
||||
mRoot(aRoot), mStartContainer(aStartContainer), mEndContainer(aEndContainer),
|
||||
mStartOffset(aStartOffset), mEndOffset(aEndOffset)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
||||
{
|
||||
if (mStartContainer == mEndContainer) {
|
||||
int32_t startIdx = mStartContainer->GetChildIndexAtOffset(mStartOffset);
|
||||
int32_t endIdx = mStartContainer->GetChildIndexAtOffset(mEndOffset);
|
||||
for (int32_t idx = startIdx; idx <= endIdx; idx++) {
|
||||
Accessible* child = mStartContainer->GetChildAt(idx);
|
||||
if (!child->IsText()) {
|
||||
aChildren->AppendElement(child);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
AutoTArray<Accessible*, 30> parents1, parents2;
|
||||
Accessible* container =
|
||||
CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
|
||||
|
||||
// Traverse the tree up to the container and collect embedded objects.
|
||||
for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
|
||||
Accessible* parent = parents1[idx + 1];
|
||||
Accessible* child = parents1[idx];
|
||||
uint32_t childCount = parent->ChildCount();
|
||||
for (uint32_t childIdx = child->IndexInParent(); childIdx < childCount; childIdx++) {
|
||||
Accessible* next = parent->GetChildAt(childIdx);
|
||||
if (!next->IsText()) {
|
||||
aChildren->AppendElement(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse through direct children in the container.
|
||||
int32_t endIdx = parents2[pos2 - 1]->IndexInParent();
|
||||
int32_t childIdx = parents1[pos1 - 1]->IndexInParent() + 1;
|
||||
for (; childIdx < endIdx; childIdx++) {
|
||||
Accessible* next = container->GetChildAt(childIdx);
|
||||
if (!next->IsText()) {
|
||||
aChildren->AppendElement(next);
|
||||
}
|
||||
}
|
||||
|
||||
// Traverse down from the container to end point.
|
||||
for (int32_t idx = pos2 - 2; idx > 0; idx--) {
|
||||
Accessible* parent = parents2[idx];
|
||||
Accessible* child = parents2[idx - 1];
|
||||
int32_t endIdx = child->IndexInParent();
|
||||
for (int32_t childIdx = 0; childIdx < endIdx; childIdx++) {
|
||||
Accessible* next = parent->GetChildAt(childIdx);
|
||||
if (!next->IsText()) {
|
||||
aChildren->AppendElement(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::Text(nsAString& aText) const
|
||||
{
|
||||
Accessible* current = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
uint32_t startIntlOffset =
|
||||
mStartOffset - mStartContainer->GetChildOffset(current);
|
||||
|
||||
while (current && TextInternal(aText, current, startIntlOffset)) {
|
||||
current = current->Parent();
|
||||
if (!current)
|
||||
break;
|
||||
|
||||
current = current->NextSibling();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::Bounds(nsTArray<nsIntRect> aRects) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::Normalize(ETextUnit aUnit)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
TextRange::Crop(Accessible* aContainer)
|
||||
{
|
||||
uint32_t boundaryPos = 0, containerPos = 0;
|
||||
AutoTArray<Accessible*, 30> boundaryParents, containerParents;
|
||||
|
||||
// Crop the start boundary.
|
||||
Accessible* container = nullptr;
|
||||
Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
if (boundary != aContainer) {
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
// The container is contained by the start boundary, reduce the range to
|
||||
// the point starting at the container.
|
||||
aContainer->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
static_cast<Accessible*>(mStartContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
// The start boundary and the container are siblings.
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
// The container does not contain the start boundary.
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (container) {
|
||||
// If the range start is after the container, then make the range invalid.
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
// If the range starts before the container, then reduce the range to
|
||||
// the point starting at the container.
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
container->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
mStartContainer.get()->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
boundaryParents.SetLengthAndRetainStorage(0);
|
||||
containerParents.SetLengthAndRetainStorage(0);
|
||||
}
|
||||
|
||||
boundary = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
if (boundary == aContainer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Crop the end boundary.
|
||||
container = nullptr;
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::FindText(const nsAString& aText, EDirection aDirection,
|
||||
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
|
||||
TextRange* aFoundRange) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::AddToSelection() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::RemoveFromSelection() const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::Select() const
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::ScrollIntoView(EHowToAlign aHow) const
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// pivate
|
||||
|
||||
void
|
||||
TextRange::Set(HyperTextAccessible* aRoot,
|
||||
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
|
||||
HyperTextAccessible* aEndContainer, int32_t aEndOffset)
|
||||
{
|
||||
mRoot = aRoot;
|
||||
mStartContainer = aStartContainer;
|
||||
mEndContainer = aEndContainer;
|
||||
mStartOffset = aStartOffset;
|
||||
mEndOffset = aEndOffset;
|
||||
}
|
||||
|
||||
bool
|
||||
TextRange::TextInternal(nsAString& aText, Accessible* aCurrent,
|
||||
uint32_t aStartIntlOffset) const
|
||||
{
|
||||
bool moveNext = true;
|
||||
int32_t endIntlOffset = -1;
|
||||
if (aCurrent->Parent() == mEndContainer &&
|
||||
mEndContainer->GetChildAtOffset(mEndOffset) == aCurrent) {
|
||||
|
||||
uint32_t currentStartOffset = mEndContainer->GetChildOffset(aCurrent);
|
||||
endIntlOffset = mEndOffset - currentStartOffset;
|
||||
if (endIntlOffset == 0)
|
||||
return false;
|
||||
|
||||
moveNext = false;
|
||||
}
|
||||
|
||||
if (aCurrent->IsTextLeaf()) {
|
||||
aCurrent->AppendTextTo(aText, aStartIntlOffset,
|
||||
endIntlOffset - aStartIntlOffset);
|
||||
if (!moveNext)
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible* next = aCurrent->FirstChild();
|
||||
if (next) {
|
||||
if (!TextInternal(aText, next, 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
next = aCurrent->NextSibling();
|
||||
if (next) {
|
||||
if (!TextInternal(aText, next, 0))
|
||||
return false;
|
||||
}
|
||||
|
||||
return moveNext;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
|
||||
HyperTextAccessible& aContainer, int32_t aOffset,
|
||||
HyperTextAccessible* aStopContainer, int32_t aStopOffset)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const
|
||||
{
|
||||
if (aAcc1 == aAcc2) {
|
||||
return aAcc1;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
|
||||
"Wrong arguments");
|
||||
|
||||
// Build the chain of parents.
|
||||
Accessible* p1 = aAcc1;
|
||||
Accessible* p2 = aAcc2;
|
||||
do {
|
||||
aParents1->AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
aParents2->AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
*aPos1 = aParents1->Length();
|
||||
*aPos2 = aParents2->Length();
|
||||
Accessible* parent = nullptr;
|
||||
uint32_t len = 0;
|
||||
for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
|
||||
Accessible* child1 = aParents1->ElementAt(--(*aPos1));
|
||||
Accessible* child2 = aParents2->ElementAt(--(*aPos2));
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
270
accessible/base/TextRange.h
Normal file
270
accessible/base/TextRange.h
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/* -*- 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_TextRange_h__
|
||||
#define mozilla_a11y_TextRange_h__
|
||||
|
||||
#include "mozilla/Move.h"
|
||||
#include "nsCaseTreatment.h"
|
||||
#include "nsRect.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIVariant;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class HyperTextAccessible;
|
||||
|
||||
/**
|
||||
* A text point (hyper text + offset), represents a boundary of text range.
|
||||
*/
|
||||
struct TextPoint final
|
||||
{
|
||||
TextPoint(HyperTextAccessible* aContainer, int32_t aOffset) :
|
||||
mContainer(aContainer), mOffset(aOffset) { }
|
||||
TextPoint(const TextPoint& aPoint) :
|
||||
mContainer(aPoint.mContainer), mOffset(aPoint.mOffset) { }
|
||||
|
||||
HyperTextAccessible* mContainer;
|
||||
int32_t mOffset;
|
||||
|
||||
bool operator ==(const TextPoint& aPoint) const
|
||||
{ return mContainer == aPoint.mContainer && mOffset == aPoint.mOffset; }
|
||||
bool operator <(const TextPoint& aPoint) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a text range within the text control or document.
|
||||
*/
|
||||
class TextRange final
|
||||
{
|
||||
public:
|
||||
TextRange(HyperTextAccessible* aRoot,
|
||||
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
|
||||
HyperTextAccessible* aEndContainer, int32_t aEndOffset);
|
||||
TextRange() {}
|
||||
TextRange(TextRange&& aRange) :
|
||||
mRoot(mozilla::Move(aRange.mRoot)),
|
||||
mStartContainer(mozilla::Move(aRange.mStartContainer)),
|
||||
mEndContainer(mozilla::Move(aRange.mEndContainer)),
|
||||
mStartOffset(aRange.mStartOffset), mEndOffset(aRange.mEndOffset) {}
|
||||
|
||||
TextRange& operator= (TextRange&& aRange)
|
||||
{
|
||||
mRoot = mozilla::Move(aRange.mRoot);
|
||||
mStartContainer = mozilla::Move(aRange.mStartContainer);
|
||||
mEndContainer = mozilla::Move(aRange.mEndContainer);
|
||||
mStartOffset = aRange.mStartOffset;
|
||||
mEndOffset = aRange.mEndOffset;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HyperTextAccessible* StartContainer() const { return mStartContainer; }
|
||||
int32_t StartOffset() const { return mStartOffset; }
|
||||
HyperTextAccessible* EndContainer() const { return mEndContainer; }
|
||||
int32_t EndOffset() const { return mEndOffset; }
|
||||
|
||||
bool operator ==(const TextRange& aRange) const
|
||||
{
|
||||
return mStartContainer == aRange.mStartContainer &&
|
||||
mStartOffset == aRange.mStartOffset &&
|
||||
mEndContainer == aRange.mEndContainer && mEndOffset == aRange.mEndOffset;
|
||||
}
|
||||
|
||||
TextPoint StartPoint() const { return TextPoint(mStartContainer, mStartOffset); }
|
||||
TextPoint EndPoint() const { return TextPoint(mEndContainer, mEndOffset); }
|
||||
|
||||
/**
|
||||
* Return a container containing both start and end points.
|
||||
*/
|
||||
Accessible* Container() const;
|
||||
|
||||
/**
|
||||
* Return a list of embedded objects enclosed by the text range (includes
|
||||
* partially overlapped objects).
|
||||
*/
|
||||
void EmbeddedChildren(nsTArray<Accessible*>* aChildren) const;
|
||||
|
||||
/**
|
||||
* Return text enclosed by the range.
|
||||
*/
|
||||
void Text(nsAString& aText) const;
|
||||
|
||||
/**
|
||||
* Return list of bounding rects of the text range by lines.
|
||||
*/
|
||||
void Bounds(nsTArray<nsIntRect> aRects) const;
|
||||
|
||||
enum ETextUnit {
|
||||
eFormat,
|
||||
eWord,
|
||||
eLine,
|
||||
eParagraph,
|
||||
ePage,
|
||||
eDocument
|
||||
};
|
||||
|
||||
/**
|
||||
* Move the range or its points on specified amount of given units.
|
||||
*/
|
||||
void Move(ETextUnit aUnit, int32_t aCount)
|
||||
{
|
||||
MoveEnd(aUnit, aCount);
|
||||
MoveStart(aUnit, aCount);
|
||||
}
|
||||
void MoveStart(ETextUnit aUnit, int32_t aCount)
|
||||
{
|
||||
MoveInternal(aUnit, aCount, *mStartContainer, mStartOffset,
|
||||
mEndContainer, mEndOffset);
|
||||
}
|
||||
void MoveEnd(ETextUnit aUnit, int32_t aCount)
|
||||
{ MoveInternal(aUnit, aCount, *mEndContainer, mEndOffset); }
|
||||
|
||||
/**
|
||||
* Move the range points to the closest unit boundaries.
|
||||
*/
|
||||
void Normalize(ETextUnit aUnit);
|
||||
|
||||
/**
|
||||
* Crops the range if it overlaps the given accessible element boundaries,
|
||||
* returns true if the range was cropped successfully.
|
||||
*/
|
||||
bool Crop(Accessible* aContainer);
|
||||
|
||||
enum EDirection {
|
||||
eBackward,
|
||||
eForward
|
||||
};
|
||||
|
||||
/**
|
||||
* Return range enclosing the found text.
|
||||
*/
|
||||
void FindText(const nsAString& aText, EDirection aDirection,
|
||||
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const;
|
||||
|
||||
enum EAttr {
|
||||
eAnimationStyleAttr,
|
||||
eAnnotationObjectsAttr,
|
||||
eAnnotationTypesAttr,
|
||||
eBackgroundColorAttr,
|
||||
eBulletStyleAttr,
|
||||
eCapStyleAttr,
|
||||
eCaretBidiModeAttr,
|
||||
eCaretPositionAttr,
|
||||
eCultureAttr,
|
||||
eFontNameAttr,
|
||||
eFontSizeAttr,
|
||||
eFontWeightAttr,
|
||||
eForegroundColorAttr,
|
||||
eHorizontalTextAlignmentAttr,
|
||||
eIndentationFirstLineAttr,
|
||||
eIndentationLeadingAttr,
|
||||
eIndentationTrailingAttr,
|
||||
eIsActiveAttr,
|
||||
eIsHiddenAttr,
|
||||
eIsItalicAttr,
|
||||
eIsReadOnlyAttr,
|
||||
eIsSubscriptAttr,
|
||||
eIsSuperscriptAttr,
|
||||
eLinkAttr,
|
||||
eMarginBottomAttr,
|
||||
eMarginLeadingAttr,
|
||||
eMarginTopAttr,
|
||||
eMarginTrailingAttr,
|
||||
eOutlineStylesAttr,
|
||||
eOverlineColorAttr,
|
||||
eOverlineStyleAttr,
|
||||
eSelectionActiveEndAttr,
|
||||
eStrikethroughColorAttr,
|
||||
eStrikethroughStyleAttr,
|
||||
eStyleIdAttr,
|
||||
eStyleNameAttr,
|
||||
eTabsAttr,
|
||||
eTextFlowDirectionsAttr,
|
||||
eUnderlineColorAttr,
|
||||
eUnderlineStyleAttr
|
||||
};
|
||||
|
||||
/**
|
||||
* Return range enclosing text having requested attribute.
|
||||
*/
|
||||
void FindAttr(EAttr aAttr, nsIVariant* aValue, EDirection aDirection,
|
||||
TextRange* aFoundRange) const;
|
||||
|
||||
/**
|
||||
* Add/remove the text range from selection.
|
||||
*/
|
||||
void AddToSelection() const;
|
||||
void RemoveFromSelection() const;
|
||||
void Select() const;
|
||||
|
||||
/**
|
||||
* Scroll the text range into view.
|
||||
*/
|
||||
enum EHowToAlign {
|
||||
eAlignToTop,
|
||||
eAlignToBottom
|
||||
};
|
||||
void ScrollIntoView(EHowToAlign aHow) const;
|
||||
|
||||
/**
|
||||
* Return true if this TextRange object represents an actual range of text.
|
||||
*/
|
||||
bool IsValid() const { return mRoot; }
|
||||
|
||||
void SetStartPoint(HyperTextAccessible* aContainer, int32_t aOffset)
|
||||
{ mStartContainer = aContainer; mStartOffset = aOffset; }
|
||||
void SetEndPoint(HyperTextAccessible* aContainer, int32_t aOffset)
|
||||
{ mStartContainer = aContainer; mStartOffset = aOffset; }
|
||||
|
||||
private:
|
||||
TextRange(const TextRange& aRange) = delete;
|
||||
TextRange& operator=(const TextRange& aRange) = delete;
|
||||
|
||||
friend class HyperTextAccessible;
|
||||
friend class xpcAccessibleTextRange;
|
||||
|
||||
void Set(HyperTextAccessible* aRoot,
|
||||
HyperTextAccessible* aStartContainer, int32_t aStartOffset,
|
||||
HyperTextAccessible* aEndContainer, int32_t aEndOffset);
|
||||
|
||||
/**
|
||||
* Text() method helper.
|
||||
* @param aText [in,out] calculated text
|
||||
* @param aCurrent [in] currently traversed node
|
||||
* @param aStartIntlOffset [in] start offset if current node is a text node
|
||||
* @return true if calculation is not finished yet
|
||||
*/
|
||||
bool TextInternal(nsAString& aText, Accessible* aCurrent,
|
||||
uint32_t aStartIntlOffset) const;
|
||||
|
||||
void MoveInternal(ETextUnit aUnit, int32_t aCount,
|
||||
HyperTextAccessible& aContainer, int32_t aOffset,
|
||||
HyperTextAccessible* aStopContainer = nullptr,
|
||||
int32_t aStopOffset = 0);
|
||||
|
||||
/**
|
||||
* A helper method returning a common parent for two given accessible
|
||||
* elements.
|
||||
*/
|
||||
Accessible* CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const;
|
||||
|
||||
RefPtr<HyperTextAccessible> mRoot;
|
||||
RefPtr<HyperTextAccessible> mStartContainer;
|
||||
RefPtr<HyperTextAccessible> mEndContainer;
|
||||
int32_t mStartOffset;
|
||||
int32_t mEndOffset;
|
||||
};
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
202
accessible/base/TextUpdater.cpp
Normal file
202
accessible/base/TextUpdater.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "TextUpdater.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "DocAccessible-inl.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
#include <algorithm>
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
void
|
||||
TextUpdater::Run(DocAccessible* aDocument, TextLeafAccessible* aTextLeaf,
|
||||
const nsAString& aNewText)
|
||||
{
|
||||
NS_ASSERTION(aTextLeaf, "No text leaf accessible?");
|
||||
|
||||
const nsString& oldText = aTextLeaf->Text();
|
||||
uint32_t oldLen = oldText.Length(), newLen = aNewText.Length();
|
||||
uint32_t minLen = std::min(oldLen, newLen);
|
||||
|
||||
// Skip coinciding begin substrings.
|
||||
uint32_t skipStart = 0;
|
||||
for (; skipStart < minLen; skipStart++) {
|
||||
if (aNewText[skipStart] != oldText[skipStart])
|
||||
break;
|
||||
}
|
||||
|
||||
// The text was changed. Do update.
|
||||
if (skipStart != minLen || oldLen != newLen) {
|
||||
TextUpdater updater(aDocument, aTextLeaf);
|
||||
updater.DoUpdate(aNewText, oldText, skipStart);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TextUpdater::DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
|
||||
uint32_t aSkipStart)
|
||||
{
|
||||
Accessible* parent = mTextLeaf->Parent();
|
||||
if (!parent)
|
||||
return;
|
||||
|
||||
mHyperText = parent->AsHyperText();
|
||||
if (!mHyperText) {
|
||||
NS_ERROR("Text leaf parent is not hypertext!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the text leaf accessible offset and invalidate cached offsets after it.
|
||||
mTextOffset = mHyperText->GetChildOffset(mTextLeaf, true);
|
||||
NS_ASSERTION(mTextOffset != -1,
|
||||
"Text leaf hasn't offset within hyper text!");
|
||||
|
||||
uint32_t oldLen = aOldText.Length(), newLen = aNewText.Length();
|
||||
uint32_t minLen = std::min(oldLen, newLen);
|
||||
|
||||
// Trim coinciding substrings from the end.
|
||||
uint32_t skipEnd = 0;
|
||||
while (minLen - skipEnd > aSkipStart &&
|
||||
aNewText[newLen - skipEnd - 1] == aOldText[oldLen - skipEnd - 1]) {
|
||||
skipEnd++;
|
||||
}
|
||||
|
||||
uint32_t strLen1 = oldLen - aSkipStart - skipEnd;
|
||||
uint32_t strLen2 = newLen - aSkipStart - skipEnd;
|
||||
|
||||
const nsAString& str1 = Substring(aOldText, aSkipStart, strLen1);
|
||||
const nsAString& str2 = Substring(aNewText, aSkipStart, strLen2);
|
||||
|
||||
// Increase offset of the text leaf on skipped characters amount.
|
||||
mTextOffset += aSkipStart;
|
||||
|
||||
// It could be single insertion or removal or the case of long strings. Do not
|
||||
// calculate the difference between long strings and prefer to fire pair of
|
||||
// insert/remove events as the old string was replaced on the new one.
|
||||
if (strLen1 == 0 || strLen2 == 0 ||
|
||||
strLen1 > kMaxStrLen || strLen2 > kMaxStrLen) {
|
||||
if (strLen1 > 0) {
|
||||
// Fire text change event for removal.
|
||||
RefPtr<AccEvent> textRemoveEvent =
|
||||
new AccTextChangeEvent(mHyperText, mTextOffset, str1, false);
|
||||
mDocument->FireDelayedEvent(textRemoveEvent);
|
||||
}
|
||||
|
||||
if (strLen2 > 0) {
|
||||
// Fire text change event for insertion.
|
||||
RefPtr<AccEvent> textInsertEvent =
|
||||
new AccTextChangeEvent(mHyperText, mTextOffset, str2, true);
|
||||
mDocument->FireDelayedEvent(textInsertEvent);
|
||||
}
|
||||
|
||||
mDocument->MaybeNotifyOfValueChange(mHyperText);
|
||||
|
||||
// Update the text.
|
||||
mTextLeaf->SetText(aNewText);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise find the difference between strings and fire events.
|
||||
// Note: we can skip initial and final coinciding characters since they don't
|
||||
// affect the Levenshtein distance.
|
||||
|
||||
// Compute the flat structured matrix need to compute the difference.
|
||||
uint32_t len1 = strLen1 + 1, len2 = strLen2 + 1;
|
||||
uint32_t* entries = new uint32_t[len1 * len2];
|
||||
|
||||
for (uint32_t colIdx = 0; colIdx < len1; colIdx++)
|
||||
entries[colIdx] = colIdx;
|
||||
|
||||
uint32_t* row = entries;
|
||||
for (uint32_t rowIdx = 1; rowIdx < len2; rowIdx++) {
|
||||
uint32_t* prevRow = row;
|
||||
row += len1;
|
||||
row[0] = rowIdx;
|
||||
for (uint32_t colIdx = 1; colIdx < len1; colIdx++) {
|
||||
if (str1[colIdx - 1] != str2[rowIdx - 1]) {
|
||||
uint32_t left = row[colIdx - 1];
|
||||
uint32_t up = prevRow[colIdx];
|
||||
uint32_t upleft = prevRow[colIdx - 1];
|
||||
row[colIdx] = std::min(upleft, std::min(left, up)) + 1;
|
||||
} else {
|
||||
row[colIdx] = prevRow[colIdx - 1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute events based on the difference.
|
||||
nsTArray<RefPtr<AccEvent> > events;
|
||||
ComputeTextChangeEvents(str1, str2, entries, events);
|
||||
|
||||
delete [] entries;
|
||||
|
||||
// Fire events.
|
||||
for (int32_t idx = events.Length() - 1; idx >= 0; idx--)
|
||||
mDocument->FireDelayedEvent(events[idx]);
|
||||
|
||||
mDocument->MaybeNotifyOfValueChange(mHyperText);
|
||||
|
||||
// Update the text.
|
||||
mTextLeaf->SetText(aNewText);
|
||||
}
|
||||
|
||||
void
|
||||
TextUpdater::ComputeTextChangeEvents(const nsAString& aStr1,
|
||||
const nsAString& aStr2,
|
||||
uint32_t* aEntries,
|
||||
nsTArray<RefPtr<AccEvent> >& aEvents)
|
||||
{
|
||||
int32_t colIdx = aStr1.Length(), rowIdx = aStr2.Length();
|
||||
|
||||
// Point at which strings last matched.
|
||||
int32_t colEnd = colIdx;
|
||||
int32_t rowEnd = rowIdx;
|
||||
|
||||
int32_t colLen = colEnd + 1;
|
||||
uint32_t* row = aEntries + rowIdx * colLen;
|
||||
uint32_t dist = row[colIdx]; // current Levenshtein distance
|
||||
while (rowIdx && colIdx) { // stop when we can't move diagonally
|
||||
if (aStr1[colIdx - 1] == aStr2[rowIdx - 1]) { // match
|
||||
if (rowIdx < rowEnd) { // deal with any pending insertion
|
||||
FireInsertEvent(Substring(aStr2, rowIdx, rowEnd - rowIdx),
|
||||
rowIdx, aEvents);
|
||||
}
|
||||
if (colIdx < colEnd) { // deal with any pending deletion
|
||||
FireDeleteEvent(Substring(aStr1, colIdx, colEnd - colIdx),
|
||||
rowIdx, aEvents);
|
||||
}
|
||||
|
||||
colEnd = --colIdx; // reset the match point
|
||||
rowEnd = --rowIdx;
|
||||
row -= colLen;
|
||||
continue;
|
||||
}
|
||||
--dist;
|
||||
if (dist == row[colIdx - 1 - colLen]) { // substitution
|
||||
--colIdx;
|
||||
--rowIdx;
|
||||
row -= colLen;
|
||||
continue;
|
||||
}
|
||||
if (dist == row[colIdx - colLen]) { // insertion
|
||||
--rowIdx;
|
||||
row -= colLen;
|
||||
continue;
|
||||
}
|
||||
if (dist == row[colIdx - 1]) { // deletion
|
||||
--colIdx;
|
||||
continue;
|
||||
}
|
||||
NS_NOTREACHED("huh?");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rowEnd)
|
||||
FireInsertEvent(Substring(aStr2, 0, rowEnd), 0, aEvents);
|
||||
if (colEnd)
|
||||
FireDeleteEvent(Substring(aStr1, 0, colEnd), 0, aEvents);
|
||||
}
|
||||
96
accessible/base/TextUpdater.h
Normal file
96
accessible/base/TextUpdater.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_TextUpdater_h__
|
||||
#define mozilla_a11y_TextUpdater_h__
|
||||
|
||||
#include "AccEvent.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
/**
|
||||
* Used to find a difference between old and new text and fire text change
|
||||
* events.
|
||||
*/
|
||||
class TextUpdater
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Start text of the text leaf update.
|
||||
*/
|
||||
static void Run(DocAccessible* aDocument, TextLeafAccessible* aTextLeaf,
|
||||
const nsAString& aNewText);
|
||||
|
||||
private:
|
||||
TextUpdater(DocAccessible* aDocument, TextLeafAccessible* aTextLeaf) :
|
||||
mDocument(aDocument), mTextLeaf(aTextLeaf), mHyperText(nullptr),
|
||||
mTextOffset(-1) { }
|
||||
|
||||
~TextUpdater()
|
||||
{ mDocument = nullptr; mTextLeaf = nullptr; mHyperText = nullptr; }
|
||||
|
||||
/**
|
||||
* Update text of the text leaf accessible, fire text change and value change
|
||||
* (if applicable) events for its container hypertext accessible.
|
||||
*/
|
||||
void DoUpdate(const nsAString& aNewText, const nsAString& aOldText,
|
||||
uint32_t aSkipStart);
|
||||
|
||||
private:
|
||||
TextUpdater();
|
||||
TextUpdater(const TextUpdater&);
|
||||
TextUpdater& operator=(const TextUpdater&);
|
||||
|
||||
/**
|
||||
* Fire text change events based on difference between strings.
|
||||
*/
|
||||
void ComputeTextChangeEvents(const nsAString& aStr1,
|
||||
const nsAString& aStr2,
|
||||
uint32_t* aEntries,
|
||||
nsTArray<RefPtr<AccEvent> >& aEvents);
|
||||
|
||||
/**
|
||||
* Helper to create text change events for inserted text.
|
||||
*/
|
||||
inline void FireInsertEvent(const nsAString& aText, uint32_t aAddlOffset,
|
||||
nsTArray<RefPtr<AccEvent> >& aEvents)
|
||||
{
|
||||
RefPtr<AccEvent> event =
|
||||
new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset,
|
||||
aText, true);
|
||||
aEvents.AppendElement(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to create text change events for removed text.
|
||||
*/
|
||||
inline void FireDeleteEvent(const nsAString& aText, uint32_t aAddlOffset,
|
||||
nsTArray<RefPtr<AccEvent> >& aEvents)
|
||||
{
|
||||
RefPtr<AccEvent> event =
|
||||
new AccTextChangeEvent(mHyperText, mTextOffset + aAddlOffset,
|
||||
aText, false);
|
||||
aEvents.AppendElement(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* The constant used to skip string difference calculation in case of long
|
||||
* strings.
|
||||
*/
|
||||
const static uint32_t kMaxStrLen = 1 << 6;
|
||||
|
||||
private:
|
||||
DocAccessible* mDocument;
|
||||
TextLeafAccessible* mTextLeaf;
|
||||
HyperTextAccessible* mHyperText;
|
||||
int32_t mTextOffset;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
325
accessible/base/TreeWalker.cpp
Normal file
325
accessible/base/TreeWalker.cpp
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "TreeWalker.h"
|
||||
|
||||
#include "Accessible.h"
|
||||
#include "AccIterator.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "DocAccessible.h"
|
||||
|
||||
#include "mozilla/dom/ChildIterator.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TreeWalker
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TreeWalker::
|
||||
TreeWalker(Accessible* aContext) :
|
||||
mDoc(aContext->Document()), mContext(aContext), mAnchorNode(nullptr),
|
||||
mARIAOwnsIdx(0),
|
||||
mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(0),
|
||||
mPhase(eAtStart)
|
||||
{
|
||||
mChildFilter |= mContext->NoXBLKids() ?
|
||||
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||
|
||||
mAnchorNode = mContext->IsDoc() ?
|
||||
mDoc->DocumentNode()->GetRootElement() : mContext->GetContent();
|
||||
|
||||
MOZ_COUNT_CTOR(TreeWalker);
|
||||
}
|
||||
|
||||
TreeWalker::
|
||||
TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags) :
|
||||
mDoc(aContext->Document()), mContext(aContext), mAnchorNode(aAnchorNode),
|
||||
mARIAOwnsIdx(0),
|
||||
mChildFilter(nsIContent::eSkipPlaceholderContent), mFlags(aFlags),
|
||||
mPhase(eAtStart)
|
||||
{
|
||||
MOZ_ASSERT(mFlags & eWalkCache, "This constructor cannot be used for tree creation");
|
||||
MOZ_ASSERT(aAnchorNode, "No anchor node for the accessible tree walker");
|
||||
|
||||
mChildFilter |= mContext->NoXBLKids() ?
|
||||
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||
|
||||
MOZ_COUNT_CTOR(TreeWalker);
|
||||
}
|
||||
|
||||
TreeWalker::~TreeWalker()
|
||||
{
|
||||
MOZ_COUNT_DTOR(TreeWalker);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TreeWalker::Scope(nsIContent* aAnchorNode)
|
||||
{
|
||||
Reset();
|
||||
|
||||
mAnchorNode = aAnchorNode;
|
||||
|
||||
bool skipSubtree = false;
|
||||
Accessible* acc = AccessibleFor(aAnchorNode, 0, &skipSubtree);
|
||||
if (acc) {
|
||||
mPhase = eAtEnd;
|
||||
return acc;
|
||||
}
|
||||
|
||||
return skipSubtree ? nullptr : Next();
|
||||
}
|
||||
|
||||
bool
|
||||
TreeWalker::Seek(nsIContent* aChildNode)
|
||||
{
|
||||
MOZ_ASSERT(aChildNode, "Child cannot be null");
|
||||
|
||||
Reset();
|
||||
|
||||
if (mAnchorNode == aChildNode) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIContent* childNode = nullptr;
|
||||
nsINode* parentNode = aChildNode;
|
||||
do {
|
||||
childNode = parentNode->AsContent();
|
||||
parentNode = childNode->HasFlag(NODE_MAY_BE_IN_BINDING_MNGR) &&
|
||||
(mChildFilter & nsIContent::eAllButXBL) ?
|
||||
childNode->GetParentNode() : childNode->GetFlattenedTreeParent();
|
||||
|
||||
if (!parentNode || !parentNode->IsElement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If ARIA owned child.
|
||||
Accessible* child = mDoc->GetAccessible(childNode);
|
||||
if (child && child->IsRelocated()) {
|
||||
if (child->Parent() != mContext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible* ownedChild = nullptr;
|
||||
while ((ownedChild = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx++)) &&
|
||||
ownedChild != child);
|
||||
|
||||
MOZ_ASSERT(ownedChild, "A child has to be in ARIA owned elements");
|
||||
mPhase = eAtARIAOwns;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Look in DOM.
|
||||
dom::AllChildrenIterator* iter = PrependState(parentNode->AsElement(), true);
|
||||
if (!iter->Seek(childNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parentNode == mAnchorNode) {
|
||||
mPhase = eAtDOM;
|
||||
return true;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TreeWalker::Next()
|
||||
{
|
||||
if (mStateStack.IsEmpty()) {
|
||||
if (mPhase == eAtEnd) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mPhase == eAtDOM || mPhase == eAtARIAOwns) {
|
||||
mPhase = eAtARIAOwns;
|
||||
Accessible* child = mDoc->ARIAOwnedAt(mContext, mARIAOwnsIdx);
|
||||
if (child) {
|
||||
mARIAOwnsIdx++;
|
||||
return child;
|
||||
}
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mAnchorNode) {
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPhase = eAtDOM;
|
||||
PushState(mAnchorNode, true);
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
|
||||
while (top) {
|
||||
while (nsIContent* childNode = top->GetNextChild()) {
|
||||
bool skipSubtree = false;
|
||||
Accessible* child = AccessibleFor(childNode, mFlags, &skipSubtree);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// Walk down the subtree if allowed.
|
||||
if (!skipSubtree && childNode->IsElement()) {
|
||||
top = PushState(childNode, true);
|
||||
}
|
||||
}
|
||||
top = PopState();
|
||||
}
|
||||
|
||||
// If we traversed the whole subtree of the anchor node. Move to next node
|
||||
// relative anchor node within the context subtree if asked.
|
||||
if (mFlags != eWalkContextTree) {
|
||||
// eWalkCache flag presence indicates that the search is scoped to the
|
||||
// anchor (no ARIA owns stuff).
|
||||
if (mFlags & eWalkCache) {
|
||||
mPhase = eAtEnd;
|
||||
return nullptr;
|
||||
}
|
||||
return Next();
|
||||
}
|
||||
|
||||
nsINode* contextNode = mContext->GetNode();
|
||||
while (mAnchorNode != contextNode) {
|
||||
nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
|
||||
if (!parentNode || !parentNode->IsElement())
|
||||
return nullptr;
|
||||
|
||||
nsIContent* parent = parentNode->AsElement();
|
||||
top = PushState(parent, true);
|
||||
if (top->Seek(mAnchorNode)) {
|
||||
mAnchorNode = parent;
|
||||
return Next();
|
||||
}
|
||||
|
||||
// XXX We really should never get here, it means we're trying to find an
|
||||
// accessible for a dom node where iterating over its parent's children
|
||||
// doesn't return it. However this sometimes happens when we're asked for
|
||||
// the nearest accessible to place holder content which we ignore.
|
||||
mAnchorNode = parent;
|
||||
}
|
||||
|
||||
return Next();
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TreeWalker::Prev()
|
||||
{
|
||||
if (mStateStack.IsEmpty()) {
|
||||
if (mPhase == eAtStart || mPhase == eAtDOM) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mPhase == eAtEnd) {
|
||||
mARIAOwnsIdx = mDoc->ARIAOwnedCount(mContext);
|
||||
mPhase = eAtARIAOwns;
|
||||
}
|
||||
|
||||
if (mPhase == eAtARIAOwns) {
|
||||
if (mARIAOwnsIdx > 0) {
|
||||
return mDoc->ARIAOwnedAt(mContext, --mARIAOwnsIdx);
|
||||
}
|
||||
|
||||
if (!mAnchorNode) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mPhase = eAtDOM;
|
||||
PushState(mAnchorNode, false);
|
||||
}
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator* top = &mStateStack[mStateStack.Length() - 1];
|
||||
while (top) {
|
||||
while (nsIContent* childNode = top->GetPreviousChild()) {
|
||||
// No accessible creation on the way back.
|
||||
bool skipSubtree = false;
|
||||
Accessible* child = AccessibleFor(childNode, eWalkCache, &skipSubtree);
|
||||
if (child) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// Walk down into subtree to find accessibles.
|
||||
if (!skipSubtree && childNode->IsElement()) {
|
||||
top = PushState(childNode, false);
|
||||
}
|
||||
}
|
||||
top = PopState();
|
||||
}
|
||||
|
||||
// Move to a previous node relative the anchor node within the context
|
||||
// subtree if asked.
|
||||
if (mFlags != eWalkContextTree) {
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsINode* contextNode = mContext->GetNode();
|
||||
while (mAnchorNode != contextNode) {
|
||||
nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
|
||||
if (!parentNode || !parentNode->IsElement()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIContent* parent = parentNode->AsElement();
|
||||
top = PushState(parent, true);
|
||||
if (top->Seek(mAnchorNode)) {
|
||||
mAnchorNode = parent;
|
||||
return Prev();
|
||||
}
|
||||
|
||||
mAnchorNode = parent;
|
||||
}
|
||||
|
||||
mPhase = eAtStart;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TreeWalker::AccessibleFor(nsIContent* aNode, uint32_t aFlags, bool* aSkipSubtree)
|
||||
{
|
||||
// Ignore the accessible and its subtree if it was repositioned by means
|
||||
// of aria-owns.
|
||||
Accessible* child = mDoc->GetAccessible(aNode);
|
||||
if (child) {
|
||||
if (child->IsRelocated()) {
|
||||
*aSkipSubtree = true;
|
||||
return nullptr;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
// Create an accessible if allowed.
|
||||
if (!(aFlags & eWalkCache) && mContext->IsAcceptableChild(aNode)) {
|
||||
// We may have ARIA owned element in the dependent attributes map, but the
|
||||
// element may be not allowed for this ARIA owns relation, if the relation
|
||||
// crosses out XBL anonymous content boundaries. In this case we won't
|
||||
// create an accessible object for it, when aria-owns is processed, which
|
||||
// may make the element subtree inaccessible. To avoid that let's create
|
||||
// an accessible object now, and later, if allowed, move it in the tree,
|
||||
// when aria-owns relation is processed.
|
||||
if (mDoc->RelocateARIAOwnedIfNeeded(aNode) && !aNode->IsXULElement()) {
|
||||
*aSkipSubtree = true;
|
||||
return nullptr;
|
||||
}
|
||||
return GetAccService()->CreateAccessible(aNode, mContext, aSkipSubtree);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
dom::AllChildrenIterator*
|
||||
TreeWalker::PopState()
|
||||
{
|
||||
size_t length = mStateStack.Length();
|
||||
mStateStack.RemoveElementAt(length - 1);
|
||||
return mStateStack.IsEmpty() ? nullptr : &mStateStack.LastElement();
|
||||
}
|
||||
144
accessible/base/TreeWalker.h
Normal file
144
accessible/base/TreeWalker.h
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_TreeWalker_h_
|
||||
#define mozilla_a11y_TreeWalker_h_
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include <stdint.h>
|
||||
#include "mozilla/dom/ChildIterator.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
class nsIContent;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class Accessible;
|
||||
class DocAccessible;
|
||||
|
||||
/**
|
||||
* This class is used to walk the DOM tree to create accessible tree.
|
||||
*/
|
||||
class TreeWalker final
|
||||
{
|
||||
public:
|
||||
enum {
|
||||
// used to walk the existing tree of the given node
|
||||
eWalkCache = 1,
|
||||
// used to walk the context tree starting from given node
|
||||
eWalkContextTree = 2 | eWalkCache
|
||||
};
|
||||
|
||||
/**
|
||||
* Used to navigate and create if needed the accessible children.
|
||||
*/
|
||||
explicit TreeWalker(Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Used to navigate the accessible children relative to the anchor.
|
||||
*
|
||||
* @param aContext [in] container accessible for the given node, used to
|
||||
* define accessible context
|
||||
* @param aAnchorNode [in] the node the search will be prepared relative to
|
||||
* @param aFlags [in] flags (see enum above)
|
||||
*/
|
||||
TreeWalker(Accessible* aContext, nsIContent* aAnchorNode, uint32_t aFlags = eWalkCache);
|
||||
|
||||
~TreeWalker();
|
||||
|
||||
/**
|
||||
* Resets the walker state, and sets the given node as an anchor. Returns a
|
||||
* first accessible element within the node including the node itself.
|
||||
*/
|
||||
Accessible* Scope(nsIContent* aAnchorNode);
|
||||
|
||||
/**
|
||||
* Resets the walker state.
|
||||
*/
|
||||
void Reset()
|
||||
{
|
||||
mPhase = eAtStart;
|
||||
mStateStack.Clear();
|
||||
mARIAOwnsIdx = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the walker state to the given child node if it's within the anchor.
|
||||
*/
|
||||
bool Seek(nsIContent* aChildNode);
|
||||
|
||||
/**
|
||||
* Return the next/prev accessible.
|
||||
*
|
||||
* @note Returned accessible is bound to the document, if the accessible is
|
||||
* rejected during tree creation then the caller should be unbind it
|
||||
* from the document.
|
||||
*/
|
||||
Accessible* Next();
|
||||
Accessible* Prev();
|
||||
|
||||
Accessible* Context() const { return mContext; }
|
||||
DocAccessible* Document() const { return mDoc; }
|
||||
|
||||
private:
|
||||
TreeWalker();
|
||||
TreeWalker(const TreeWalker&);
|
||||
TreeWalker& operator =(const TreeWalker&);
|
||||
|
||||
/**
|
||||
* Return an accessible for the given node if any.
|
||||
*/
|
||||
Accessible* AccessibleFor(nsIContent* aNode, uint32_t aFlags,
|
||||
bool* aSkipSubtree);
|
||||
|
||||
/**
|
||||
* Create new state for the given node and push it on top of stack / at bottom
|
||||
* of stack.
|
||||
*
|
||||
* @note State stack is used to navigate up/down the DOM subtree during
|
||||
* accessible children search.
|
||||
*/
|
||||
dom::AllChildrenIterator* PushState(nsIContent* aContent,
|
||||
bool aStartAtBeginning)
|
||||
{
|
||||
return mStateStack.AppendElement(
|
||||
dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
|
||||
}
|
||||
dom::AllChildrenIterator* PrependState(nsIContent* aContent,
|
||||
bool aStartAtBeginning)
|
||||
{
|
||||
return mStateStack.InsertElementAt(0,
|
||||
dom::AllChildrenIterator(aContent, mChildFilter, aStartAtBeginning));
|
||||
}
|
||||
|
||||
/**
|
||||
* Pop state from stack.
|
||||
*/
|
||||
dom::AllChildrenIterator* PopState();
|
||||
|
||||
DocAccessible* mDoc;
|
||||
Accessible* mContext;
|
||||
nsIContent* mAnchorNode;
|
||||
|
||||
AutoTArray<dom::AllChildrenIterator, 20> mStateStack;
|
||||
uint32_t mARIAOwnsIdx;
|
||||
|
||||
int32_t mChildFilter;
|
||||
uint32_t mFlags;
|
||||
|
||||
enum Phase {
|
||||
eAtStart,
|
||||
eAtDOM,
|
||||
eAtARIAOwns,
|
||||
eAtEnd
|
||||
};
|
||||
Phase mPhase;
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_a11y_TreeWalker_h_
|
||||
114
accessible/base/moz.build
Normal file
114
accessible/base/moz.build
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
# -*- Mode: python; 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'AccEvent.h',
|
||||
'nsAccessibilityService.h'
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'AccTypes.h',
|
||||
'DocManager.h',
|
||||
'FocusManager.h',
|
||||
'Platform.h',
|
||||
'RelationType.h',
|
||||
'Role.h',
|
||||
'SelectionManager.h',
|
||||
'States.h',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_DEBUG']:
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'Logging.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'AccessibleOrProxy.cpp',
|
||||
'AccEvent.cpp',
|
||||
'AccGroupInfo.cpp',
|
||||
'AccIterator.cpp',
|
||||
'ARIAMap.cpp',
|
||||
'ARIAStateMap.cpp',
|
||||
'Asserts.cpp',
|
||||
'DocManager.cpp',
|
||||
'EmbeddedObjCollector.cpp',
|
||||
'EventQueue.cpp',
|
||||
'EventTree.cpp',
|
||||
'Filters.cpp',
|
||||
'FocusManager.cpp',
|
||||
'NotificationController.cpp',
|
||||
'nsAccessibilityService.cpp',
|
||||
'nsAccessiblePivot.cpp',
|
||||
'nsAccUtils.cpp',
|
||||
'nsCoreUtils.cpp',
|
||||
'nsEventShell.cpp',
|
||||
'nsTextEquivUtils.cpp',
|
||||
'SelectionManager.cpp',
|
||||
'StyleInfo.cpp',
|
||||
'TextAttrs.cpp',
|
||||
'TextRange.cpp',
|
||||
'TextUpdater.cpp',
|
||||
'TreeWalker.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['A11Y_LOG']:
|
||||
UNIFIED_SOURCES += [
|
||||
'Logging.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
]
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/ipc/win',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/ipc/other',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/xpcom',
|
||||
'/accessible/xul',
|
||||
'/dom/base',
|
||||
'/dom/xbl',
|
||||
'/ipc/chromium/src',
|
||||
'/layout/generic',
|
||||
'/layout/style',
|
||||
'/layout/svg',
|
||||
'/layout/xul',
|
||||
'/layout/xul/tree/',
|
||||
]
|
||||
|
||||
if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/atk',
|
||||
]
|
||||
CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
28
accessible/base/nsAccCache.h
Normal file
28
accessible/base/nsAccCache.h
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 _nsAccCache_H_
|
||||
#define _nsAccCache_H_
|
||||
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Accessible cache utils
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T>
|
||||
void
|
||||
UnbindCacheEntriesFromDocument(
|
||||
nsRefPtrHashtable<nsPtrHashKey<const void>, T>& aCache)
|
||||
{
|
||||
for (auto iter = aCache.Iter(); !iter.Done(); iter.Next()) {
|
||||
T* accessible = iter.Data();
|
||||
MOZ_ASSERT(accessible && !accessible->IsDefunct());
|
||||
accessible->Document()->UnbindFromDocument(accessible);
|
||||
iter.Remove();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
447
accessible/base/nsAccUtils.cpp
Normal file
447
accessible/base/nsAccUtils.cpp
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "nsAccUtils.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "ARIAMap.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsCoreUtils.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "nsIAccessibleTypes.h"
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
|
||||
#include "nsIDOMXULContainerElement.h"
|
||||
#include "nsIPersistentProperties2.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
void
|
||||
nsAccUtils::GetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom *aAttrName, nsAString& aAttrValue)
|
||||
{
|
||||
aAttrValue.Truncate();
|
||||
|
||||
aAttributes->GetStringProperty(nsAtomCString(aAttrName), aAttrValue);
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom *aAttrName, const nsAString& aAttrValue)
|
||||
{
|
||||
nsAutoString oldValue;
|
||||
aAttributes->SetStringProperty(nsAtomCString(aAttrName), aAttrValue, oldValue);
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::SetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom* aAttrName, nsIAtom* aAttrValue)
|
||||
{
|
||||
nsAutoString oldValue;
|
||||
aAttributes->SetStringProperty(nsAtomCString(aAttrName),
|
||||
nsAtomString(aAttrValue), oldValue);
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
|
||||
int32_t aLevel, int32_t aSetSize,
|
||||
int32_t aPosInSet)
|
||||
{
|
||||
nsAutoString value;
|
||||
|
||||
if (aLevel) {
|
||||
value.AppendInt(aLevel);
|
||||
SetAccAttr(aAttributes, nsGkAtoms::level, value);
|
||||
}
|
||||
|
||||
if (aSetSize && aPosInSet) {
|
||||
value.Truncate();
|
||||
value.AppendInt(aPosInSet);
|
||||
SetAccAttr(aAttributes, nsGkAtoms::posinset, value);
|
||||
|
||||
value.Truncate();
|
||||
value.AppendInt(aSetSize);
|
||||
SetAccAttr(aAttributes, nsGkAtoms::setsize, value);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsAccUtils::GetDefaultLevel(Accessible* aAccessible)
|
||||
{
|
||||
roles::Role role = aAccessible->Role();
|
||||
|
||||
if (role == roles::OUTLINEITEM)
|
||||
return 1;
|
||||
|
||||
if (role == roles::ROW) {
|
||||
Accessible* parent = aAccessible->Parent();
|
||||
// It is a row inside flatten treegrid. Group level is always 1 until it
|
||||
// is overriden by aria-level attribute.
|
||||
if (parent && parent->Role() == roles::TREE_TABLE)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsAccUtils::GetARIAOrDefaultLevel(Accessible* aAccessible)
|
||||
{
|
||||
int32_t level = 0;
|
||||
nsCoreUtils::GetUIntAttr(aAccessible->GetContent(),
|
||||
nsGkAtoms::aria_level, &level);
|
||||
|
||||
if (level != 0)
|
||||
return level;
|
||||
|
||||
return GetDefaultLevel(aAccessible);
|
||||
}
|
||||
|
||||
int32_t
|
||||
nsAccUtils::GetLevelForXULContainerItem(nsIContent *aContent)
|
||||
{
|
||||
nsCOMPtr<nsIDOMXULContainerItemElement> item(do_QueryInterface(aContent));
|
||||
if (!item)
|
||||
return 0;
|
||||
|
||||
nsCOMPtr<nsIDOMXULContainerElement> container;
|
||||
item->GetParentContainer(getter_AddRefs(container));
|
||||
if (!container)
|
||||
return 0;
|
||||
|
||||
// Get level of the item.
|
||||
int32_t level = -1;
|
||||
while (container) {
|
||||
level++;
|
||||
|
||||
nsCOMPtr<nsIDOMXULContainerElement> parentContainer;
|
||||
container->GetParentContainer(getter_AddRefs(parentContainer));
|
||||
parentContainer.swap(container);
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
nsIContent* aStartContent,
|
||||
dom::Element* aTopEl)
|
||||
{
|
||||
nsAutoString live, relevant, busy;
|
||||
nsIContent* ancestor = aStartContent;
|
||||
while (ancestor) {
|
||||
|
||||
// container-relevant attribute
|
||||
if (relevant.IsEmpty() &&
|
||||
HasDefinedARIAToken(ancestor, nsGkAtoms::aria_relevant) &&
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_relevant, relevant))
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerRelevant, relevant);
|
||||
|
||||
// container-live, and container-live-role attributes
|
||||
if (live.IsEmpty()) {
|
||||
const nsRoleMapEntry* role = nullptr;
|
||||
if (ancestor->IsElement()) {
|
||||
role = aria::GetRoleMap(ancestor->AsElement());
|
||||
}
|
||||
if (HasDefinedARIAToken(ancestor, nsGkAtoms::aria_live)) {
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_live, live);
|
||||
} else if (role) {
|
||||
GetLiveAttrValue(role->liveAttRule, live);
|
||||
}
|
||||
if (!live.IsEmpty()) {
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerLive, live);
|
||||
if (role) {
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerLiveRole,
|
||||
role->ARIARoleString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// container-atomic attribute
|
||||
if (ancestor->AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_atomic,
|
||||
nsGkAtoms::_true, eCaseMatters)) {
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerAtomic,
|
||||
NS_LITERAL_STRING("true"));
|
||||
}
|
||||
|
||||
// container-busy attribute
|
||||
if (busy.IsEmpty() &&
|
||||
HasDefinedARIAToken(ancestor, nsGkAtoms::aria_busy) &&
|
||||
ancestor->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_busy, busy))
|
||||
SetAccAttr(aAttributes, nsGkAtoms::containerBusy, busy);
|
||||
|
||||
if (ancestor == aTopEl)
|
||||
break;
|
||||
|
||||
ancestor = ancestor->GetParent();
|
||||
if (!ancestor)
|
||||
ancestor = aTopEl; // Use <body>/<frameset>
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccUtils::HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom)
|
||||
{
|
||||
NS_ASSERTION(aContent, "aContent is null in call to HasDefinedARIAToken!");
|
||||
|
||||
if (!aContent->HasAttr(kNameSpaceID_None, aAtom) ||
|
||||
aContent->AttrValueIs(kNameSpaceID_None, aAtom,
|
||||
nsGkAtoms::_empty, eCaseMatters) ||
|
||||
aContent->AttrValueIs(kNameSpaceID_None, aAtom,
|
||||
nsGkAtoms::_undefined, eCaseMatters)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsIAtom*
|
||||
nsAccUtils::GetARIAToken(dom::Element* aElement, nsIAtom* aAttr)
|
||||
{
|
||||
if (!HasDefinedARIAToken(aElement, aAttr))
|
||||
return nsGkAtoms::_empty;
|
||||
|
||||
static nsIContent::AttrValuesArray tokens[] =
|
||||
{ &nsGkAtoms::_false, &nsGkAtoms::_true,
|
||||
&nsGkAtoms::mixed, nullptr};
|
||||
|
||||
int32_t idx = aElement->FindAttrValueIn(kNameSpaceID_None,
|
||||
aAttr, tokens, eCaseMatters);
|
||||
if (idx >= 0)
|
||||
return *(tokens[idx]);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Accessible*
|
||||
nsAccUtils::GetSelectableContainer(Accessible* aAccessible, uint64_t aState)
|
||||
{
|
||||
if (!aAccessible)
|
||||
return nullptr;
|
||||
|
||||
if (!(aState & states::SELECTABLE))
|
||||
return nullptr;
|
||||
|
||||
Accessible* parent = aAccessible;
|
||||
while ((parent = parent->Parent()) && !parent->IsSelect()) {
|
||||
if (parent->Role() == roles::PANE)
|
||||
return nullptr;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccUtils::IsARIASelected(Accessible* aAccessible)
|
||||
{
|
||||
return aAccessible->GetContent()->
|
||||
AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected,
|
||||
nsGkAtoms::_true, eCaseMatters);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
nsAccUtils::TableFor(Accessible* aRow)
|
||||
{
|
||||
if (aRow) {
|
||||
Accessible* table = aRow->Parent();
|
||||
if (table) {
|
||||
roles::Role tableRole = table->Role();
|
||||
if (tableRole == roles::GROUPING) { // if there's a rowgroup.
|
||||
table = table->Parent();
|
||||
if (table)
|
||||
tableRole = table->Role();
|
||||
}
|
||||
|
||||
return (tableRole == roles::TABLE || tableRole == roles::TREE_TABLE ||
|
||||
tableRole == roles::MATHML_TABLE) ? table : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HyperTextAccessible*
|
||||
nsAccUtils::GetTextContainer(nsINode* aNode)
|
||||
{
|
||||
// Get text accessible containing the result node.
|
||||
DocAccessible* doc =
|
||||
GetAccService()->GetDocAccessible(aNode->OwnerDoc());
|
||||
Accessible* accessible =
|
||||
doc ? doc->GetAccessibleOrContainer(aNode) : nullptr;
|
||||
if (!accessible)
|
||||
return nullptr;
|
||||
|
||||
do {
|
||||
HyperTextAccessible* textAcc = accessible->AsHyperText();
|
||||
if (textAcc)
|
||||
return textAcc;
|
||||
|
||||
accessible = accessible->Parent();
|
||||
} while (accessible);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
nsAccUtils::ConvertToScreenCoords(int32_t aX, int32_t aY,
|
||||
uint32_t aCoordinateType,
|
||||
Accessible* aAccessible)
|
||||
{
|
||||
nsIntPoint coords(aX, aY);
|
||||
|
||||
switch (aCoordinateType) {
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
|
||||
break;
|
||||
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
|
||||
{
|
||||
coords += nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
|
||||
{
|
||||
coords += GetScreenCoordsForParent(aAccessible);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("invalid coord type!");
|
||||
}
|
||||
|
||||
return coords;
|
||||
}
|
||||
|
||||
void
|
||||
nsAccUtils::ConvertScreenCoordsTo(int32_t *aX, int32_t *aY,
|
||||
uint32_t aCoordinateType,
|
||||
Accessible* aAccessible)
|
||||
{
|
||||
switch (aCoordinateType) {
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_SCREEN_RELATIVE:
|
||||
break;
|
||||
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_WINDOW_RELATIVE:
|
||||
{
|
||||
nsIntPoint coords = nsCoreUtils::GetScreenCoordsForWindow(aAccessible->GetNode());
|
||||
*aX -= coords.x;
|
||||
*aY -= coords.y;
|
||||
break;
|
||||
}
|
||||
|
||||
case nsIAccessibleCoordinateType::COORDTYPE_PARENT_RELATIVE:
|
||||
{
|
||||
nsIntPoint coords = GetScreenCoordsForParent(aAccessible);
|
||||
*aX -= coords.x;
|
||||
*aY -= coords.y;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_NOTREACHED("invalid coord type!");
|
||||
}
|
||||
}
|
||||
|
||||
nsIntPoint
|
||||
nsAccUtils::GetScreenCoordsForParent(Accessible* aAccessible)
|
||||
{
|
||||
Accessible* parent = aAccessible->Parent();
|
||||
if (!parent)
|
||||
return nsIntPoint(0, 0);
|
||||
|
||||
nsIFrame *parentFrame = parent->GetFrame();
|
||||
if (!parentFrame)
|
||||
return nsIntPoint(0, 0);
|
||||
|
||||
nsRect rect = parentFrame->GetScreenRectInAppUnits();
|
||||
return nsPoint(rect.x, rect.y).
|
||||
ToNearestPixels(parentFrame->PresContext()->AppUnitsPerDevPixel());
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccUtils::GetLiveAttrValue(uint32_t aRule, nsAString& aValue)
|
||||
{
|
||||
switch (aRule) {
|
||||
case eOffLiveAttr:
|
||||
aValue = NS_LITERAL_STRING("off");
|
||||
return true;
|
||||
case ePoliteLiveAttr:
|
||||
aValue = NS_LITERAL_STRING("polite");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
bool
|
||||
nsAccUtils::IsTextInterfaceSupportCorrect(Accessible* aAccessible)
|
||||
{
|
||||
// Don't test for accessible docs, it makes us create accessibles too
|
||||
// early and fire mutation events before we need to
|
||||
if (aAccessible->IsDoc())
|
||||
return true;
|
||||
|
||||
bool foundText = false;
|
||||
uint32_t childCount = aAccessible->ChildCount();
|
||||
for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
|
||||
Accessible* child = aAccessible->GetChildAt(childIdx);
|
||||
if (child->IsText()) {
|
||||
foundText = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return !foundText || aAccessible->IsHyperText();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t
|
||||
nsAccUtils::TextLength(Accessible* aAccessible)
|
||||
{
|
||||
if (!aAccessible->IsText()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
TextLeafAccessible* textLeaf = aAccessible->AsTextLeaf();
|
||||
if (textLeaf)
|
||||
return textLeaf->Text().Length();
|
||||
|
||||
// For list bullets (or anything other accessible which would compute its own
|
||||
// text. They don't have their own frame.
|
||||
// XXX In the future, list bullets may have frame and anon content, so
|
||||
// we should be able to remove this at that point
|
||||
nsAutoString text;
|
||||
aAccessible->AppendTextTo(text); // Get all the text
|
||||
return text.Length();
|
||||
}
|
||||
|
||||
bool
|
||||
nsAccUtils::MustPrune(Accessible* aAccessible)
|
||||
{
|
||||
roles::Role role = aAccessible->Role();
|
||||
|
||||
// Don't prune the tree for certain roles if the tree is more complex than
|
||||
// a single text leaf.
|
||||
return
|
||||
(role == roles::MENUITEM ||
|
||||
role == roles::COMBOBOX_OPTION ||
|
||||
role == roles::OPTION ||
|
||||
role == roles::ENTRY ||
|
||||
role == roles::FLAT_EQUATION ||
|
||||
role == roles::PASSWORD_TEXT ||
|
||||
role == roles::PUSHBUTTON ||
|
||||
role == roles::TOGGLE_BUTTON ||
|
||||
role == roles::GRAPHIC ||
|
||||
role == roles::SLIDER ||
|
||||
role == roles::PROGRESSBAR ||
|
||||
role == roles::SEPARATOR) &&
|
||||
aAccessible->ContentChildCount() == 1 &&
|
||||
aAccessible->ContentChildAt(0)->IsTextLeaf();
|
||||
}
|
||||
243
accessible/base/nsAccUtils.h
Normal file
243
accessible/base/nsAccUtils.h
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 nsAccUtils_h_
|
||||
#define nsAccUtils_h_
|
||||
|
||||
#include "mozilla/a11y/Accessible.h"
|
||||
|
||||
#include "nsAccessibilityService.h"
|
||||
#include "nsCoreUtils.h"
|
||||
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsPoint.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace dom {
|
||||
class Element;
|
||||
}
|
||||
|
||||
namespace a11y {
|
||||
|
||||
class HyperTextAccessible;
|
||||
class DocAccessible;
|
||||
|
||||
class nsAccUtils
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Returns value of attribute from the given attributes container.
|
||||
*
|
||||
* @param aAttributes - attributes container
|
||||
* @param aAttrName - the name of requested attribute
|
||||
* @param aAttrValue - value of attribute
|
||||
*/
|
||||
static void GetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom *aAttrName,
|
||||
nsAString& aAttrValue);
|
||||
|
||||
/**
|
||||
* Set value of attribute for the given attributes container.
|
||||
*
|
||||
* @param aAttributes - attributes container
|
||||
* @param aAttrName - the name of requested attribute
|
||||
* @param aAttrValue - new value of attribute
|
||||
*/
|
||||
static void SetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom *aAttrName,
|
||||
const nsAString& aAttrValue);
|
||||
|
||||
static void SetAccAttr(nsIPersistentProperties *aAttributes,
|
||||
nsIAtom* aAttrName,
|
||||
nsIAtom* aAttrValue);
|
||||
|
||||
/**
|
||||
* Set group attributes ('level', 'setsize', 'posinset').
|
||||
*/
|
||||
static void SetAccGroupAttrs(nsIPersistentProperties *aAttributes,
|
||||
int32_t aLevel, int32_t aSetSize,
|
||||
int32_t aPosInSet);
|
||||
|
||||
/**
|
||||
* Get default value of the level for the given accessible.
|
||||
*/
|
||||
static int32_t GetDefaultLevel(Accessible* aAcc);
|
||||
|
||||
/**
|
||||
* Return ARIA level value or the default one if ARIA is missed for the
|
||||
* given accessible.
|
||||
*/
|
||||
static int32_t GetARIAOrDefaultLevel(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Compute group level for nsIDOMXULContainerItemElement node.
|
||||
*/
|
||||
static int32_t GetLevelForXULContainerItem(nsIContent *aContent);
|
||||
|
||||
/**
|
||||
* Set container-foo live region attributes for the given node.
|
||||
*
|
||||
* @param aAttributes where to store the attributes
|
||||
* @param aStartContent node to start from
|
||||
* @param aTopContent node to end at
|
||||
*/
|
||||
static void SetLiveContainerAttributes(nsIPersistentProperties *aAttributes,
|
||||
nsIContent* aStartContent,
|
||||
mozilla::dom::Element* aTopEl);
|
||||
|
||||
/**
|
||||
* Any ARIA property of type boolean or NMTOKEN is undefined if the ARIA
|
||||
* property is not present, or is "" or "undefined". Do not call
|
||||
* this method for properties of type string, decimal, IDREF or IDREFS.
|
||||
*
|
||||
* Return true if the ARIA property is defined, otherwise false
|
||||
*/
|
||||
static bool HasDefinedARIAToken(nsIContent *aContent, nsIAtom *aAtom);
|
||||
|
||||
/**
|
||||
* Return atomic value of ARIA attribute of boolean or NMTOKEN type.
|
||||
*/
|
||||
static nsIAtom* GetARIAToken(mozilla::dom::Element* aElement, nsIAtom* aAttr);
|
||||
|
||||
/**
|
||||
* Return document accessible for the given DOM node.
|
||||
*/
|
||||
static DocAccessible* GetDocAccessibleFor(nsINode* aNode)
|
||||
{
|
||||
nsIPresShell *presShell = nsCoreUtils::GetPresShellFor(aNode);
|
||||
return GetAccService()->GetDocAccessible(presShell);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return document accessible for the given docshell.
|
||||
*/
|
||||
static DocAccessible* GetDocAccessibleFor(nsIDocShellTreeItem* aContainer)
|
||||
{
|
||||
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
|
||||
return GetAccService()->GetDocAccessible(docShell->GetPresShell());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return single or multi selectable container for the given item.
|
||||
*
|
||||
* @param aAccessible [in] the item accessible
|
||||
* @param aState [in] the state of the item accessible
|
||||
*/
|
||||
static Accessible* GetSelectableContainer(Accessible* aAccessible,
|
||||
uint64_t aState);
|
||||
|
||||
/**
|
||||
* Return a text container accessible for the given node.
|
||||
*/
|
||||
static HyperTextAccessible* GetTextContainer(nsINode* aNode);
|
||||
|
||||
static Accessible* TableFor(Accessible* aRow);
|
||||
|
||||
/**
|
||||
* Return true if the DOM node of given accessible has aria-selected="true"
|
||||
* attribute.
|
||||
*/
|
||||
static bool IsARIASelected(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Converts the given coordinates to coordinates relative screen.
|
||||
*
|
||||
* @param aX [in] the given x coord
|
||||
* @param aY [in] the given y coord
|
||||
* @param aCoordinateType [in] specifies coordinates origin (refer to
|
||||
* nsIAccessibleCoordinateType)
|
||||
* @param aAccessible [in] the accessible if coordinates are given
|
||||
* relative it.
|
||||
* @return converted coordinates
|
||||
*/
|
||||
static nsIntPoint ConvertToScreenCoords(int32_t aX, int32_t aY,
|
||||
uint32_t aCoordinateType,
|
||||
Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Converts the given coordinates relative screen to another coordinate
|
||||
* system.
|
||||
*
|
||||
* @param aX [in, out] the given x coord
|
||||
* @param aY [in, out] the given y coord
|
||||
* @param aCoordinateType [in] specifies coordinates origin (refer to
|
||||
* nsIAccessibleCoordinateType)
|
||||
* @param aAccessible [in] the accessible if coordinates are given
|
||||
* relative it
|
||||
*/
|
||||
static void ConvertScreenCoordsTo(int32_t* aX, int32_t* aY,
|
||||
uint32_t aCoordinateType,
|
||||
Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Returns coordinates relative screen for the parent of the given accessible.
|
||||
*
|
||||
* @param [in] aAccessible the accessible
|
||||
*/
|
||||
static nsIntPoint GetScreenCoordsForParent(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Get the 'live' or 'container-live' object attribute value from the given
|
||||
* ELiveAttrRule constant.
|
||||
*
|
||||
* @param aRule [in] rule constant (see ELiveAttrRule in nsAccMap.h)
|
||||
* @param aValue [out] object attribute value
|
||||
*
|
||||
* @return true if object attribute should be exposed
|
||||
*/
|
||||
static bool GetLiveAttrValue(uint32_t aRule, nsAString& aValue);
|
||||
|
||||
#ifdef DEBUG
|
||||
/**
|
||||
* Detect whether the given accessible object implements nsIAccessibleText,
|
||||
* when it is text or has text child node.
|
||||
*/
|
||||
static bool IsTextInterfaceSupportCorrect(Accessible* aAccessible);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return text length of the given accessible, return 0 on failure.
|
||||
*/
|
||||
static uint32_t TextLength(Accessible* aAccessible);
|
||||
|
||||
/**
|
||||
* Transform nsIAccessibleStates constants to internal state constant.
|
||||
*/
|
||||
static inline uint64_t To64State(uint32_t aState1, uint32_t aState2)
|
||||
{
|
||||
return static_cast<uint64_t>(aState1) +
|
||||
(static_cast<uint64_t>(aState2) << 31);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform internal state constant to nsIAccessibleStates constants.
|
||||
*/
|
||||
static inline void To32States(uint64_t aState64,
|
||||
uint32_t* aState1, uint32_t* aState2)
|
||||
{
|
||||
*aState1 = aState64 & 0x7fffffff;
|
||||
if (aState2)
|
||||
*aState2 = static_cast<uint32_t>(aState64 >> 31);
|
||||
}
|
||||
|
||||
static uint32_t To32States(uint64_t aState, bool* aIsExtra)
|
||||
{
|
||||
uint32_t extraState = aState >> 31;
|
||||
*aIsExtra = !!extraState;
|
||||
return aState | extraState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given accessible can't have children. Used when exposing
|
||||
* to platform accessibility APIs, should the children be pruned off?
|
||||
*/
|
||||
static bool MustPrune(Accessible* aAccessible);
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
1885
accessible/base/nsAccessibilityService.cpp
Normal file
1885
accessible/base/nsAccessibilityService.cpp
Normal file
File diff suppressed because it is too large
Load diff
442
accessible/base/nsAccessibilityService.h
Normal file
442
accessible/base/nsAccessibilityService.h
Normal file
|
|
@ -0,0 +1,442 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 __nsAccessibilityService_h__
|
||||
#define __nsAccessibilityService_h__
|
||||
|
||||
#include "mozilla/a11y/DocManager.h"
|
||||
#include "mozilla/a11y/FocusManager.h"
|
||||
#include "mozilla/a11y/Role.h"
|
||||
#include "mozilla/a11y/SelectionManager.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIAccessibleEvent.h"
|
||||
#include "nsIEventListenerService.h"
|
||||
#include "xpcAccessibilityService.h"
|
||||
|
||||
class nsImageFrame;
|
||||
class nsIArray;
|
||||
class nsIPersistentProperties;
|
||||
class nsPluginFrame;
|
||||
class nsITreeView;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class ApplicationAccessible;
|
||||
class xpcAccessibleApplication;
|
||||
|
||||
/**
|
||||
* Return focus manager.
|
||||
*/
|
||||
FocusManager* FocusMgr();
|
||||
|
||||
/**
|
||||
* Return selection manager.
|
||||
*/
|
||||
SelectionManager* SelectionMgr();
|
||||
|
||||
/**
|
||||
* Returns the application accessible.
|
||||
*/
|
||||
ApplicationAccessible* ApplicationAcc();
|
||||
xpcAccessibleApplication* XPCApplicationAcc();
|
||||
|
||||
typedef Accessible* (New_Accessible)(nsIContent* aContent, Accessible* aContext);
|
||||
|
||||
struct MarkupAttrInfo {
|
||||
nsIAtom** name;
|
||||
nsIAtom** value;
|
||||
|
||||
nsIAtom** DOMAttrName;
|
||||
nsIAtom** DOMAttrValue;
|
||||
};
|
||||
|
||||
struct MarkupMapInfo {
|
||||
nsIAtom** tag;
|
||||
New_Accessible* new_func;
|
||||
a11y::role role;
|
||||
MarkupAttrInfo attrs[4];
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
class nsAccessibilityService final : public mozilla::a11y::DocManager,
|
||||
public mozilla::a11y::FocusManager,
|
||||
public mozilla::a11y::SelectionManager,
|
||||
public nsIListenerChangeListener,
|
||||
public nsIObserver
|
||||
{
|
||||
public:
|
||||
typedef mozilla::a11y::Accessible Accessible;
|
||||
typedef mozilla::a11y::DocAccessible DocAccessible;
|
||||
|
||||
// nsIListenerChangeListener
|
||||
NS_IMETHOD ListenersChanged(nsIArray* aEventChanges) override;
|
||||
|
||||
protected:
|
||||
~nsAccessibilityService();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
Accessible* GetRootDocumentAccessible(nsIPresShell* aPresShell,
|
||||
bool aCanCreate);
|
||||
already_AddRefed<Accessible>
|
||||
CreatePluginAccessible(nsPluginFrame* aFrame, nsIContent* aContent,
|
||||
Accessible* aContext);
|
||||
|
||||
/**
|
||||
* Adds/remove ATK root accessible for gtk+ native window to/from children
|
||||
* of the application accessible.
|
||||
*/
|
||||
Accessible* AddNativeRootAccessible(void* aAtkAccessible);
|
||||
void RemoveNativeRootAccessible(Accessible* aRootAccessible);
|
||||
|
||||
bool HasAccessible(nsIDOMNode* aDOMNode);
|
||||
|
||||
/**
|
||||
* Get a string equivalent for an accessilbe role value.
|
||||
*/
|
||||
void GetStringRole(uint32_t aRole, nsAString& aString);
|
||||
|
||||
/**
|
||||
* Get a string equivalent for an accessible state/extra state.
|
||||
*/
|
||||
void GetStringStates(uint32_t aState, uint32_t aExtraState,
|
||||
nsISupports **aStringStates);
|
||||
|
||||
/**
|
||||
* Get a string equivalent for an accessible event value.
|
||||
*/
|
||||
void GetStringEventType(uint32_t aEventType, nsAString& aString);
|
||||
|
||||
/**
|
||||
* Get a string equivalent for an accessible relation type.
|
||||
*/
|
||||
void GetStringRelationType(uint32_t aRelationType, nsAString& aString);
|
||||
|
||||
// nsAccesibilityService
|
||||
/**
|
||||
* Notification used to update the accessible tree when deck panel is
|
||||
* switched.
|
||||
*/
|
||||
void DeckPanelSwitched(nsIPresShell* aPresShell, nsIContent* aDeckNode,
|
||||
nsIFrame* aPrevBoxFrame, nsIFrame* aCurrentBoxFrame);
|
||||
|
||||
/**
|
||||
* Notification used to update the accessible tree when new content is
|
||||
* inserted.
|
||||
*/
|
||||
void ContentRangeInserted(nsIPresShell* aPresShell, nsIContent* aContainer,
|
||||
nsIContent* aStartChild, nsIContent* aEndChild);
|
||||
|
||||
/**
|
||||
* Notification used to update the accessible tree when content is removed.
|
||||
*/
|
||||
void ContentRemoved(nsIPresShell* aPresShell, nsIContent* aChild);
|
||||
|
||||
void UpdateText(nsIPresShell* aPresShell, nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Update XUL:tree accessible tree when treeview is changed.
|
||||
*/
|
||||
void TreeViewChanged(nsIPresShell* aPresShell, nsIContent* aContent,
|
||||
nsITreeView* aView);
|
||||
|
||||
/**
|
||||
* Notify of input@type="element" value change.
|
||||
*/
|
||||
void RangeValueChanged(nsIPresShell* aPresShell, nsIContent* aContent);
|
||||
|
||||
/**
|
||||
* Update list bullet accessible.
|
||||
*/
|
||||
void UpdateListBullet(nsIPresShell* aPresShell,
|
||||
nsIContent* aHTMLListItemContent,
|
||||
bool aHasBullet);
|
||||
|
||||
/**
|
||||
* Update the image map.
|
||||
*/
|
||||
void UpdateImageMap(nsImageFrame* aImageFrame);
|
||||
|
||||
/**
|
||||
* Update the label accessible tree when rendered @value is changed.
|
||||
*/
|
||||
void UpdateLabelValue(nsIPresShell* aPresShell, nsIContent* aLabelElm,
|
||||
const nsString& aNewValue);
|
||||
|
||||
/**
|
||||
* Notify accessibility that anchor jump has been accomplished to the given
|
||||
* target. Used by layout.
|
||||
*/
|
||||
void NotifyOfAnchorJumpTo(nsIContent *aTarget);
|
||||
|
||||
/**
|
||||
* Notify that presshell is activated.
|
||||
*/
|
||||
void PresShellActivated(nsIPresShell* aPresShell);
|
||||
|
||||
/**
|
||||
* Recreate an accessible for the given content node in the presshell.
|
||||
*/
|
||||
void RecreateAccessible(nsIPresShell* aPresShell, nsIContent* aContent);
|
||||
|
||||
void FireAccessibleEvent(uint32_t aEvent, Accessible* aTarget);
|
||||
|
||||
// nsAccessibiltiyService
|
||||
|
||||
/**
|
||||
* Return true if accessibility service has been shutdown.
|
||||
*/
|
||||
static bool IsShutdown()
|
||||
{
|
||||
return gConsumers == 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an accessible for the given DOM node.
|
||||
*
|
||||
* @param aNode [in] the given node
|
||||
* @param aContext [in] context the accessible is created in
|
||||
* @param aIsSubtreeHidden [out, optional] indicates whether the node's
|
||||
* frame and its subtree is hidden
|
||||
*/
|
||||
Accessible* CreateAccessible(nsINode* aNode, Accessible* aContext,
|
||||
bool* aIsSubtreeHidden = nullptr);
|
||||
|
||||
mozilla::a11y::role MarkupRole(const nsIContent* aContent) const
|
||||
{
|
||||
const mozilla::a11y::MarkupMapInfo* markupMap =
|
||||
mMarkupMaps.Get(aContent->NodeInfo()->NameAtom());
|
||||
return markupMap ? markupMap->role : mozilla::a11y::roles::NOTHING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the object attribute defined by markup for the given element.
|
||||
*/
|
||||
void MarkupAttributes(const nsIContent* aContent,
|
||||
nsIPersistentProperties* aAttributes) const;
|
||||
|
||||
/**
|
||||
* A list of possible accessibility service consumers. Accessibility service
|
||||
* can only be shut down when there are no remaining consumers.
|
||||
*
|
||||
* eXPCOM - accessibility service is used by XPCOM.
|
||||
*
|
||||
* eMainProcess - accessibility service was started by main process in the
|
||||
* content process.
|
||||
*
|
||||
* ePlatformAPI - accessibility service is used by the platform api in the
|
||||
* main process.
|
||||
*/
|
||||
enum ServiceConsumer
|
||||
{
|
||||
eXPCOM = 1 << 0,
|
||||
eMainProcess = 1 << 1,
|
||||
ePlatformAPI = 1 << 2,
|
||||
};
|
||||
|
||||
private:
|
||||
// nsAccessibilityService creation is controlled by friend
|
||||
// GetOrCreateAccService, keep constructors private.
|
||||
nsAccessibilityService();
|
||||
nsAccessibilityService(const nsAccessibilityService&);
|
||||
nsAccessibilityService& operator =(const nsAccessibilityService&);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialize accessibility service.
|
||||
*/
|
||||
bool Init();
|
||||
|
||||
/**
|
||||
* Shutdowns accessibility service.
|
||||
*/
|
||||
void Shutdown();
|
||||
|
||||
/**
|
||||
* Create accessible for the element having XBL bindings.
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateAccessibleByType(nsIContent* aContent, DocAccessible* aDoc);
|
||||
|
||||
/**
|
||||
* Create an accessible whose type depends on the given frame.
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent,
|
||||
Accessible* aContext);
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
/**
|
||||
* Create accessible for XUL tree element.
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateAccessibleForXULTree(nsIContent* aContent, DocAccessible* aDoc);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reference for accessibility service instance.
|
||||
*/
|
||||
static nsAccessibilityService* gAccessibilityService;
|
||||
|
||||
/**
|
||||
* Reference for application accessible instance.
|
||||
*/
|
||||
static mozilla::a11y::ApplicationAccessible* gApplicationAccessible;
|
||||
static mozilla::a11y::xpcAccessibleApplication* gXPCApplicationAccessible;
|
||||
|
||||
/**
|
||||
* Contains a set of accessibility service consumers.
|
||||
*/
|
||||
static uint32_t gConsumers;
|
||||
|
||||
nsDataHashtable<nsPtrHashKey<const nsIAtom>, const mozilla::a11y::MarkupMapInfo*> mMarkupMaps;
|
||||
|
||||
friend nsAccessibilityService* GetAccService();
|
||||
friend nsAccessibilityService* GetOrCreateAccService(uint32_t);
|
||||
friend void MaybeShutdownAccService(uint32_t);
|
||||
friend mozilla::a11y::FocusManager* mozilla::a11y::FocusMgr();
|
||||
friend mozilla::a11y::SelectionManager* mozilla::a11y::SelectionMgr();
|
||||
friend mozilla::a11y::ApplicationAccessible* mozilla::a11y::ApplicationAcc();
|
||||
friend mozilla::a11y::xpcAccessibleApplication* mozilla::a11y::XPCApplicationAcc();
|
||||
friend class xpcAccessibilityService;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the accessibility service instance. (Handy global function)
|
||||
*/
|
||||
inline nsAccessibilityService*
|
||||
GetAccService()
|
||||
{
|
||||
return nsAccessibilityService::gAccessibilityService;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return accessibility service instance; creating one if necessary.
|
||||
*/
|
||||
nsAccessibilityService* GetOrCreateAccService(
|
||||
uint32_t aNewConsumer = nsAccessibilityService::ePlatformAPI);
|
||||
|
||||
/**
|
||||
* Shutdown accessibility service if needed.
|
||||
*/
|
||||
void MaybeShutdownAccService(uint32_t aFormerConsumer);
|
||||
|
||||
/**
|
||||
* Return true if we're in a content process and not B2G.
|
||||
*/
|
||||
inline bool
|
||||
IPCAccessibilityActive()
|
||||
{
|
||||
#ifdef MOZ_B2G
|
||||
return false;
|
||||
#else
|
||||
return XRE_IsContentProcess() &&
|
||||
mozilla::Preferences::GetBool("accessibility.ipc_architecture.enabled", true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Map nsIAccessibleEvents constants to strings. Used by
|
||||
* nsAccessibilityService::GetStringEventType() method.
|
||||
*/
|
||||
static const char kEventTypeNames[][40] = {
|
||||
"unknown", //
|
||||
"show", // EVENT_SHOW
|
||||
"hide", // EVENT_HIDE
|
||||
"reorder", // EVENT_REORDER
|
||||
"active decendent change", // EVENT_ACTIVE_DECENDENT_CHANGED
|
||||
"focus", // EVENT_FOCUS
|
||||
"state change", // EVENT_STATE_CHANGE
|
||||
"location change", // EVENT_LOCATION_CHANGE
|
||||
"name changed", // EVENT_NAME_CHANGE
|
||||
"description change", // EVENT_DESCRIPTION_CHANGE
|
||||
"value change", // EVENT_VALUE_CHANGE
|
||||
"help change", // EVENT_HELP_CHANGE
|
||||
"default action change", // EVENT_DEFACTION_CHANGE
|
||||
"action change", // EVENT_ACTION_CHANGE
|
||||
"accelerator change", // EVENT_ACCELERATOR_CHANGE
|
||||
"selection", // EVENT_SELECTION
|
||||
"selection add", // EVENT_SELECTION_ADD
|
||||
"selection remove", // EVENT_SELECTION_REMOVE
|
||||
"selection within", // EVENT_SELECTION_WITHIN
|
||||
"alert", // EVENT_ALERT
|
||||
"foreground", // EVENT_FOREGROUND
|
||||
"menu start", // EVENT_MENU_START
|
||||
"menu end", // EVENT_MENU_END
|
||||
"menupopup start", // EVENT_MENUPOPUP_START
|
||||
"menupopup end", // EVENT_MENUPOPUP_END
|
||||
"capture start", // EVENT_CAPTURE_START
|
||||
"capture end", // EVENT_CAPTURE_END
|
||||
"movesize start", // EVENT_MOVESIZE_START
|
||||
"movesize end", // EVENT_MOVESIZE_END
|
||||
"contexthelp start", // EVENT_CONTEXTHELP_START
|
||||
"contexthelp end", // EVENT_CONTEXTHELP_END
|
||||
"dragdrop start", // EVENT_DRAGDROP_START
|
||||
"dragdrop end", // EVENT_DRAGDROP_END
|
||||
"dialog start", // EVENT_DIALOG_START
|
||||
"dialog end", // EVENT_DIALOG_END
|
||||
"scrolling start", // EVENT_SCROLLING_START
|
||||
"scrolling end", // EVENT_SCROLLING_END
|
||||
"minimize start", // EVENT_MINIMIZE_START
|
||||
"minimize end", // EVENT_MINIMIZE_END
|
||||
"document load complete", // EVENT_DOCUMENT_LOAD_COMPLETE
|
||||
"document reload", // EVENT_DOCUMENT_RELOAD
|
||||
"document load stopped", // EVENT_DOCUMENT_LOAD_STOPPED
|
||||
"document attributes changed", // EVENT_DOCUMENT_ATTRIBUTES_CHANGED
|
||||
"document content changed", // EVENT_DOCUMENT_CONTENT_CHANGED
|
||||
"property changed", // EVENT_PROPERTY_CHANGED
|
||||
"page changed", // EVENT_PAGE_CHANGED
|
||||
"text attribute changed", // EVENT_TEXT_ATTRIBUTE_CHANGED
|
||||
"text caret moved", // EVENT_TEXT_CARET_MOVED
|
||||
"text changed", // EVENT_TEXT_CHANGED
|
||||
"text inserted", // EVENT_TEXT_INSERTED
|
||||
"text removed", // EVENT_TEXT_REMOVED
|
||||
"text updated", // EVENT_TEXT_UPDATED
|
||||
"text selection changed", // EVENT_TEXT_SELECTION_CHANGED
|
||||
"visible data changed", // EVENT_VISIBLE_DATA_CHANGED
|
||||
"text column changed", // EVENT_TEXT_COLUMN_CHANGED
|
||||
"section changed", // EVENT_SECTION_CHANGED
|
||||
"table caption changed", // EVENT_TABLE_CAPTION_CHANGED
|
||||
"table model changed", // EVENT_TABLE_MODEL_CHANGED
|
||||
"table summary changed", // EVENT_TABLE_SUMMARY_CHANGED
|
||||
"table row description changed", // EVENT_TABLE_ROW_DESCRIPTION_CHANGED
|
||||
"table row header changed", // EVENT_TABLE_ROW_HEADER_CHANGED
|
||||
"table row insert", // EVENT_TABLE_ROW_INSERT
|
||||
"table row delete", // EVENT_TABLE_ROW_DELETE
|
||||
"table row reorder", // EVENT_TABLE_ROW_REORDER
|
||||
"table column description changed", // EVENT_TABLE_COLUMN_DESCRIPTION_CHANGED
|
||||
"table column header changed", // EVENT_TABLE_COLUMN_HEADER_CHANGED
|
||||
"table column insert", // EVENT_TABLE_COLUMN_INSERT
|
||||
"table column delete", // EVENT_TABLE_COLUMN_DELETE
|
||||
"table column reorder", // EVENT_TABLE_COLUMN_REORDER
|
||||
"window activate", // EVENT_WINDOW_ACTIVATE
|
||||
"window create", // EVENT_WINDOW_CREATE
|
||||
"window deactivate", // EVENT_WINDOW_DEACTIVATE
|
||||
"window destroy", // EVENT_WINDOW_DESTROY
|
||||
"window maximize", // EVENT_WINDOW_MAXIMIZE
|
||||
"window minimize", // EVENT_WINDOW_MINIMIZE
|
||||
"window resize", // EVENT_WINDOW_RESIZE
|
||||
"window restore", // EVENT_WINDOW_RESTORE
|
||||
"hyperlink end index changed", // EVENT_HYPERLINK_END_INDEX_CHANGED
|
||||
"hyperlink number of anchors changed", // EVENT_HYPERLINK_NUMBER_OF_ANCHORS_CHANGED
|
||||
"hyperlink selected link changed", // EVENT_HYPERLINK_SELECTED_LINK_CHANGED
|
||||
"hypertext link activated", // EVENT_HYPERTEXT_LINK_ACTIVATED
|
||||
"hypertext link selected", // EVENT_HYPERTEXT_LINK_SELECTED
|
||||
"hyperlink start index changed", // EVENT_HYPERLINK_START_INDEX_CHANGED
|
||||
"hypertext changed", // EVENT_HYPERTEXT_CHANGED
|
||||
"hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED
|
||||
"object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED
|
||||
"virtual cursor changed", // EVENT_VIRTUALCURSOR_CHANGED
|
||||
"text value change", // EVENT_TEXT_VALUE_CHANGE
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue