summaryrefslogtreecommitdiff
path: root/accessible
diff options
context:
space:
mode:
authorBrian Smith <brian@dbsoft.org>2022-04-26 09:34:34 -0500
committerBrian Smith <brian@dbsoft.org>2022-04-26 10:19:00 -0500
commit378738aaa9924d0b95e2c57f27cbad2b2e644282 (patch)
tree34ce9c4ce3995576604fb4bc47d9405e661daf39 /accessible
parent82f11ad8aaeff395629c3a3f72ece43712fd8e72 (diff)
downloaduxp-378738aaa9924d0b95e2c57f27cbad2b2e644282.tar.gz
Issue #1829 - Revert “Issue #1751 - Remove Mac code behind MOZ_WIDGET_TOOLKIT == 'cocoa’”
This reverts commit 1fe9c19305dadf2d5bcaa0e589fcd250389dfa8a.
Diffstat (limited to 'accessible')
-rw-r--r--accessible/base/moz.build4
-rw-r--r--accessible/generic/moz.build4
-rw-r--r--accessible/html/moz.build4
-rw-r--r--accessible/ipc/moz.build4
-rw-r--r--accessible/ipc/other/moz.build4
-rw-r--r--accessible/mac/ARIAGridAccessibleWrap.h22
-rw-r--r--accessible/mac/AccessibleWrap.h103
-rw-r--r--accessible/mac/AccessibleWrap.mm256
-rw-r--r--accessible/mac/ApplicationAccessibleWrap.h22
-rw-r--r--accessible/mac/DocAccessibleWrap.h25
-rw-r--r--accessible/mac/DocAccessibleWrap.mm21
-rw-r--r--accessible/mac/HTMLTableAccessibleWrap.h24
-rw-r--r--accessible/mac/HyperTextAccessibleWrap.h20
-rw-r--r--accessible/mac/ImageAccessibleWrap.h22
-rw-r--r--accessible/mac/MacUtils.h26
-rw-r--r--accessible/mac/MacUtils.mm32
-rw-r--r--accessible/mac/Platform.mm174
-rw-r--r--accessible/mac/RootAccessibleWrap.h34
-rw-r--r--accessible/mac/RootAccessibleWrap.mm53
-rw-r--r--accessible/mac/TextLeafAccessibleWrap.h19
-rw-r--r--accessible/mac/XULListboxAccessibleWrap.h20
-rw-r--r--accessible/mac/XULMenuAccessibleWrap.h19
-rw-r--r--accessible/mac/XULTreeGridAccessibleWrap.h20
-rw-r--r--accessible/mac/moz.build44
-rw-r--r--accessible/mac/mozAccessible.h181
-rw-r--r--accessible/mac/mozAccessible.mm1197
-rw-r--r--accessible/mac/mozAccessibleProtocol.h69
-rw-r--r--accessible/mac/mozActionElements.h37
-rw-r--r--accessible/mac/mozActionElements.mm340
-rw-r--r--accessible/mac/mozDocAccessible.h31
-rw-r--r--accessible/mac/mozDocAccessible.mm111
-rw-r--r--accessible/mac/mozHTMLAccessible.h16
-rw-r--r--accessible/mac/mozHTMLAccessible.mm141
-rw-r--r--accessible/mac/mozTableAccessible.h28
-rw-r--r--accessible/mac/mozTableAccessible.mm281
-rw-r--r--accessible/mac/mozTextAccessible.h17
-rw-r--r--accessible/mac/mozTextAccessible.mm627
-rw-r--r--accessible/moz.build2
-rw-r--r--accessible/xpcom/moz.build4
-rw-r--r--accessible/xul/moz.build4
40 files changed, 4062 insertions, 0 deletions
diff --git a/accessible/base/moz.build b/accessible/base/moz.build
index 54627ca50c..ea9b67aee9 100644
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -96,6 +96,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build
index 720d9bf01b..6855daf909 100644
--- a/accessible/generic/moz.build
+++ b/accessible/generic/moz.build
@@ -52,6 +52,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/html/moz.build b/accessible/html/moz.build
index a18c4e59b1..e486f10456 100644
--- a/accessible/html/moz.build
+++ b/accessible/html/moz.build
@@ -32,6 +32,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build
index 91fd1fa4d3..cb852de271 100644
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -19,6 +19,10 @@ else:
LOCAL_INCLUDES += [
'/accessible/atk',
]
+ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/ipc/other/moz.build b/accessible/ipc/other/moz.build
index 489520cef6..50f96de040 100644
--- a/accessible/ipc/other/moz.build
+++ b/accessible/ipc/other/moz.build
@@ -28,6 +28,10 @@ if CONFIG['ACCESSIBILITY']:
LOCAL_INCLUDES += [
'/accessible/atk',
]
+ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/mac/ARIAGridAccessibleWrap.h b/accessible/mac/ARIAGridAccessibleWrap.h
new file mode 100644
index 0000000000..5d397e915c
--- /dev/null
+++ b/accessible/mac/ARIAGridAccessibleWrap.h
@@ -0,0 +1,22 @@
+/* -*- 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_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
diff --git a/accessible/mac/AccessibleWrap.h b/accessible/mac/AccessibleWrap.h
new file mode 100644
index 0000000000..6c746ff0dc
--- /dev/null
+++ b/accessible/mac/AccessibleWrap.h
@@ -0,0 +1,103 @@
+/* -*- Mode: Objective-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/. */
+
+/* For documentation of the accessibility architecture,
+ * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
+ */
+
+#ifndef _AccessibleWrap_H_
+#define _AccessibleWrap_H_
+
+#include <objc/objc.h>
+
+#include "Accessible.h"
+#include "States.h"
+
+#include "nsCOMPtr.h"
+
+#include "nsTArray.h"
+
+#if defined(__OBJC__)
+@class mozAccessible;
+#endif
+
+namespace mozilla {
+namespace a11y {
+
+class AccessibleWrap : public Accessible
+{
+public: // construction, destruction
+ AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
+ virtual ~AccessibleWrap();
+
+ /**
+ * Get the native Obj-C object (mozAccessible).
+ */
+ virtual void GetNativeInterface(void** aOutAccessible) override;
+
+ /**
+ * The objective-c |Class| type that this accessible's native object
+ * should be instantied with. used on runtime to determine the
+ * right type for this accessible's associated native object.
+ */
+ virtual Class GetNativeType ();
+
+ virtual void Shutdown () override;
+
+ virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
+ virtual bool RemoveChild(Accessible* aAccessible) override;
+
+ virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
+
+protected:
+
+ /**
+ * Return true if the parent doesn't have children to expose to AT.
+ */
+ bool AncestorIsFlat();
+
+ /**
+ * Get the native object. Create it if needed.
+ */
+#if defined(__OBJC__)
+ mozAccessible* GetNativeObject();
+#else
+ id GetNativeObject();
+#endif
+
+private:
+
+ /**
+ * Our native object. Private because its creation is done lazily.
+ * Don't access it directly. Ever. Unless you are GetNativeObject() or
+ * Shutdown()
+ */
+#if defined(__OBJC__)
+ // if we are in Objective-C, we use the actual Obj-C class.
+ mozAccessible* mNativeObject;
+#else
+ id mNativeObject;
+#endif
+
+ /**
+ * We have created our native. This does not mean there is one.
+ * This can never go back to false.
+ * We need it because checking whether we need a native object cost time.
+ */
+ bool mNativeInited;
+};
+
+#if defined(__OBJC__)
+ void FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType);
+#else
+ void FireNativeEvent(id aNativeAcc, uint32_t aEventType);
+#endif
+
+Class GetTypeFromRole(roles::Role aRole);
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/mac/AccessibleWrap.mm b/accessible/mac/AccessibleWrap.mm
new file mode 100644
index 0000000000..65f2e1db42
--- /dev/null
+++ b/accessible/mac/AccessibleWrap.mm
@@ -0,0 +1,256 @@
+/* -*- 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 "DocAccessible.h"
+#include "nsObjCExceptions.h"
+
+#include "Accessible-inl.h"
+#include "nsAccUtils.h"
+#include "Role.h"
+
+#import "mozAccessible.h"
+#import "mozActionElements.h"
+#import "mozHTMLAccessible.h"
+#import "mozTableAccessible.h"
+#import "mozTextAccessible.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+AccessibleWrap::
+ AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
+ Accessible(aContent, aDoc), mNativeObject(nil),
+ mNativeInited(false)
+{
+}
+
+AccessibleWrap::~AccessibleWrap()
+{
+}
+
+mozAccessible*
+AccessibleWrap::GetNativeObject()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) {
+ uintptr_t accWrap = reinterpret_cast<uintptr_t>(this);
+ mNativeObject = [[GetNativeType() alloc] initWithAccessible:accWrap];
+ }
+
+ mNativeInited = true;
+
+ return mNativeObject;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+void
+AccessibleWrap::GetNativeInterface(void** aOutInterface)
+{
+ *aOutInterface = static_cast<void*>(GetNativeObject());
+}
+
+// overridden in subclasses to create the right kind of object. by default we create a generic
+// 'mozAccessible' node.
+Class
+AccessibleWrap::GetNativeType ()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (IsXULTabpanels())
+ return [mozPaneAccessible class];
+
+ if (IsTable())
+ return [mozTableAccessible class];
+
+ if (IsTableRow())
+ return [mozTableRowAccessible class];
+
+ if (IsTableCell())
+ return [mozTableCellAccessible class];
+
+ return GetTypeFromRole(Role());
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// this method is very important. it is fired when an accessible object "dies". after this point
+// the object might still be around (because some 3rd party still has a ref to it), but it is
+// in fact 'dead'.
+void
+AccessibleWrap::Shutdown ()
+{
+ // this ensure we will not try to re-create the native object.
+ mNativeInited = true;
+
+ // we really intend to access the member directly.
+ if (mNativeObject) {
+ [mNativeObject expire];
+ [mNativeObject release];
+ mNativeObject = nil;
+ }
+
+ Accessible::Shutdown();
+}
+
+nsresult
+AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv = Accessible::HandleAccEvent(aEvent);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (IPCAccessibilityActive()) {
+ return NS_OK;
+ }
+
+ uint32_t eventType = aEvent->GetEventType();
+
+ // ignore everything but focus-changed, value-changed, caret, selection
+ // and document load complete events for now.
+ if (eventType != nsIAccessibleEvent::EVENT_FOCUS &&
+ eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
+ eventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
+ eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
+ eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED &&
+ eventType != nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE)
+ return NS_OK;
+
+ Accessible* accessible = aEvent->GetAccessible();
+ NS_ENSURE_STATE(accessible);
+
+ mozAccessible *nativeAcc = nil;
+ accessible->GetNativeInterface((void**)&nativeAcc);
+ if (!nativeAcc)
+ return NS_ERROR_FAILURE;
+
+ FireNativeEvent(nativeAcc, eventType);
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+bool
+AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
+{
+ bool inserted = Accessible::InsertChildAt(aIdx, aAccessible);
+ if (inserted && mNativeObject)
+ [mNativeObject appendChild:aAccessible];
+
+ return inserted;
+}
+
+bool
+AccessibleWrap::RemoveChild(Accessible* aAccessible)
+{
+ bool removed = Accessible::RemoveChild(aAccessible);
+
+ if (removed && mNativeObject)
+ [mNativeObject invalidateChildren];
+
+ return removed;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// AccessibleWrap protected
+
+bool
+AccessibleWrap::AncestorIsFlat()
+{
+ // We don't create a native object if we're child of a "flat" accessible;
+ // for example, on OS X buttons shouldn't have any children, because that
+ // makes the OS confused.
+ //
+ // To maintain a scripting environment where the XPCOM accessible hierarchy
+ // look the same on all platforms, we still let the C++ objects be created
+ // though.
+
+ Accessible* parent = Parent();
+ while (parent) {
+ if (nsAccUtils::MustPrune(parent))
+ return true;
+
+ parent = parent->Parent();
+ }
+ // no parent was flat
+ return false;
+}
+
+void
+a11y::FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ switch (aEventType) {
+ case nsIAccessibleEvent::EVENT_FOCUS:
+ [aNativeAcc didReceiveFocus];
+ break;
+ case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
+ case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
+ [aNativeAcc valueDidChange];
+ break;
+ case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
+ case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
+ [aNativeAcc selectedTextDidChange];
+ break;
+ case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
+ [aNativeAcc documentLoadComplete];
+ break;
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+Class
+a11y::GetTypeFromRole(roles::Role aRole)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ switch (aRole) {
+ case roles::COMBOBOX:
+ case roles::PUSHBUTTON:
+ case roles::SPLITBUTTON:
+ case roles::TOGGLE_BUTTON:
+ {
+ return [mozButtonAccessible class];
+ }
+
+ case roles::PAGETAB:
+ return [mozButtonAccessible class];
+
+ case roles::CHECKBUTTON:
+ return [mozCheckboxAccessible class];
+
+ case roles::HEADING:
+ return [mozHeadingAccessible class];
+
+ case roles::PAGETABLIST:
+ return [mozTabsAccessible class];
+
+ case roles::ENTRY:
+ case roles::STATICTEXT:
+ case roles::CAPTION:
+ case roles::ACCEL_LABEL:
+ case roles::PASSWORD_TEXT:
+ // normal textfield (static or editable)
+ return [mozTextAccessible class];
+
+ case roles::TEXT_LEAF:
+ return [mozTextLeafAccessible class];
+
+ case roles::LINK:
+ return [mozLinkAccessible class];
+
+ default:
+ return [mozAccessible class];
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
diff --git a/accessible/mac/ApplicationAccessibleWrap.h b/accessible/mac/ApplicationAccessibleWrap.h
new file mode 100644
index 0000000000..9343c29ddc
--- /dev/null
+++ b/accessible/mac/ApplicationAccessibleWrap.h
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* 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 {
+
+typedef ApplicationAccessible ApplicationAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
+
diff --git a/accessible/mac/DocAccessibleWrap.h b/accessible/mac/DocAccessibleWrap.h
new file mode 100644
index 0000000000..3e80a0d33c
--- /dev/null
+++ b/accessible/mac/DocAccessibleWrap.h
@@ -0,0 +1,25 @@
+/* -*- 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_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();
+
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/mac/DocAccessibleWrap.mm b/accessible/mac/DocAccessibleWrap.mm
new file mode 100644
index 0000000000..8a513f485a
--- /dev/null
+++ b/accessible/mac/DocAccessibleWrap.mm
@@ -0,0 +1,21 @@
+/* -*- 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 "DocAccessibleWrap.h"
+
+#import "mozAccessible.h"
+
+using namespace mozilla::a11y;
+
+DocAccessibleWrap::
+ DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
+ DocAccessible(aDocument, aPresShell)
+{
+}
+
+DocAccessibleWrap::~DocAccessibleWrap()
+{
+}
+
diff --git a/accessible/mac/HTMLTableAccessibleWrap.h b/accessible/mac/HTMLTableAccessibleWrap.h
new file mode 100644
index 0000000000..4f158e241d
--- /dev/null
+++ b/accessible/mac/HTMLTableAccessibleWrap.h
@@ -0,0 +1,24 @@
+/* -*- 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_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
+
diff --git a/accessible/mac/HyperTextAccessibleWrap.h b/accessible/mac/HyperTextAccessibleWrap.h
new file mode 100644
index 0000000000..fb335ef0f7
--- /dev/null
+++ b/accessible/mac/HyperTextAccessibleWrap.h
@@ -0,0 +1,20 @@
+/* -*- 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_HyperTextAccessibleWrap_h__
+#define mozilla_a11y_HyperTextAccessibleWrap_h__
+
+#include "HyperTextAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef class HyperTextAccessible HyperTextAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
+
diff --git a/accessible/mac/ImageAccessibleWrap.h b/accessible/mac/ImageAccessibleWrap.h
new file mode 100644
index 0000000000..069efb6511
--- /dev/null
+++ b/accessible/mac/ImageAccessibleWrap.h
@@ -0,0 +1,22 @@
+/* -*- 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_ImageAccessibleWrap_h__
+#define mozilla_a11y_ImageAccessibleWrap_h__
+
+#include "ImageAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef class ImageAccessible ImageAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
+
diff --git a/accessible/mac/MacUtils.h b/accessible/mac/MacUtils.h
new file mode 100644
index 0000000000..f88a27ee58
--- /dev/null
+++ b/accessible/mac/MacUtils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: Objective-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 _MacUtils_H_
+#define _MacUtils_H_
+
+@class NSString;
+class nsString;
+
+namespace mozilla {
+namespace a11y {
+namespace utils {
+
+/**
+ * Get a localized string from the string bundle.
+ * Return nil if not found.
+ */
+NSString* LocalizedString(const nsString& aString);
+
+}
+}
+}
+
+#endif
diff --git a/accessible/mac/MacUtils.mm b/accessible/mac/MacUtils.mm
new file mode 100644
index 0000000000..2ce03fe966
--- /dev/null
+++ b/accessible/mac/MacUtils.mm
@@ -0,0 +1,32 @@
+/* -*- Mode: Objective-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/. */
+
+#import "MacUtils.h"
+
+#include "Accessible.h"
+
+#include "nsCocoaUtils.h"
+
+namespace mozilla {
+namespace a11y {
+namespace utils {
+
+/**
+ * Get a localized string from the a11y string bundle.
+ * Return nil if not found.
+ */
+NSString*
+LocalizedString(const nsString& aString)
+{
+ nsString text;
+
+ Accessible::TranslateString(aString, text);
+
+ return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
+}
+
+}
+}
+}
diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm
new file mode 100644
index 0000000000..a104bf904c
--- /dev/null
+++ b/accessible/mac/Platform.mm
@@ -0,0 +1,174 @@
+/* -*- Mode: Objective-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/. */
+
+#import <Cocoa/Cocoa.h>
+
+#include "Platform.h"
+#include "ProxyAccessible.h"
+#include "DocAccessibleParent.h"
+#include "mozTableAccessible.h"
+
+#include "nsAppShell.h"
+
+namespace mozilla {
+namespace a11y {
+
+// Mac a11y whitelisting
+static bool sA11yShouldBeEnabled = false;
+
+bool
+ShouldA11yBeEnabled()
+{
+ EPlatformDisabledState disabledState = PlatformDisabledState();
+ return (disabledState == ePlatformIsForceEnabled) || ((disabledState == ePlatformIsEnabled) && sA11yShouldBeEnabled);
+}
+
+void
+PlatformInit()
+{
+}
+
+void
+PlatformShutdown()
+{
+}
+
+void
+ProxyCreated(ProxyAccessible* aProxy, uint32_t)
+{
+ // Pass in dummy state for now as retrieving proxy state requires IPC.
+ // Note that we can use ProxyAccessible::IsTable* functions here because they
+ // do not use IPC calls but that might change after bug 1210477.
+ Class type;
+ if (aProxy->IsTable())
+ type = [mozTableAccessible class];
+ else if (aProxy->IsTableRow())
+ type = [mozTableRowAccessible class];
+ else if (aProxy->IsTableCell())
+ type = [mozTableCellAccessible class];
+ else
+ type = GetTypeFromRole(aProxy->Role());
+
+ uintptr_t accWrap = reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY;
+ mozAccessible* mozWrapper = [[type alloc] initWithAccessible:accWrap];
+ aProxy->SetWrapper(reinterpret_cast<uintptr_t>(mozWrapper));
+
+ mozAccessible* nativeParent = nullptr;
+ if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
+ // If proxy is top level, the parent we need to invalidate the children of
+ // will be a non-remote accessible.
+ Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
+ if (outerDoc) {
+ nativeParent = GetNativeFromGeckoAccessible(outerDoc);
+ }
+ } else {
+ // Non-top level proxies need proxy parents' children invalidated.
+ ProxyAccessible* parent = aProxy->Parent();
+ nativeParent = GetNativeFromProxy(parent);
+ NS_ASSERTION(parent, "a non-top-level proxy is missing a parent?");
+ }
+
+ if (nativeParent) {
+ [nativeParent invalidateChildren];
+ }
+}
+
+void
+ProxyDestroyed(ProxyAccessible* aProxy)
+{
+ mozAccessible* nativeParent = nil;
+ if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
+ // Invalidate native parent in parent process's children on proxy destruction
+ Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
+ if (outerDoc) {
+ nativeParent = GetNativeFromGeckoAccessible(outerDoc);
+ }
+ } else {
+ if (!aProxy->Document()->IsShutdown()) {
+ // Only do if the document has not been shut down, else parent will return
+ // garbage since we don't shut down children from top down.
+ ProxyAccessible* parent = aProxy->Parent();
+ // Invalidate proxy parent's children.
+ if (parent) {
+ nativeParent = GetNativeFromProxy(parent);
+ }
+ }
+ }
+
+ mozAccessible* wrapper = GetNativeFromProxy(aProxy);
+ [wrapper expire];
+ [wrapper release];
+ aProxy->SetWrapper(0);
+
+ if (nativeParent) {
+ [nativeParent invalidateChildren];
+ }
+}
+
+void
+ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType)
+{
+ // ignore everything but focus-changed, value-changed, caret and selection
+ // events for now.
+ if (aEventType != nsIAccessibleEvent::EVENT_FOCUS &&
+ aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
+ aEventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
+ aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
+ aEventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
+ return;
+
+ mozAccessible* wrapper = GetNativeFromProxy(aProxy);
+ if (wrapper)
+ FireNativeEvent(wrapper, aEventType);
+}
+
+void
+ProxyStateChangeEvent(ProxyAccessible* aProxy, uint64_t, bool)
+{
+ // mac doesn't care about state change events
+}
+
+void
+ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
+{
+ mozAccessible* wrapper = GetNativeFromProxy(aTarget);
+ if (wrapper)
+ [wrapper selectedTextDidChange];
+}
+
+void
+ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
+ bool, bool)
+{
+}
+
+void
+ProxyShowHideEvent(ProxyAccessible*, ProxyAccessible*, bool, bool)
+{
+}
+
+void
+ProxySelectionEvent(ProxyAccessible*, ProxyAccessible*, uint32_t)
+{
+}
+} // namespace a11y
+} // namespace mozilla
+
+@interface GeckoNSApplication(a11y)
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
+@end
+
+@implementation GeckoNSApplication(a11y)
+
+-(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
+{
+ if ([attribute isEqualToString:@"AXEnhancedUserInterface"])
+ mozilla::a11y::sA11yShouldBeEnabled = ([value intValue] == 1);
+
+ return [super accessibilitySetValue:value forAttribute:attribute];
+}
+
+@end
+
diff --git a/accessible/mac/RootAccessibleWrap.h b/accessible/mac/RootAccessibleWrap.h
new file mode 100644
index 0000000000..aa53e06ac0
--- /dev/null
+++ b/accessible/mac/RootAccessibleWrap.h
@@ -0,0 +1,34 @@
+/* -*- 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/. */
+
+/* For documentation of the accessibility architecture,
+ * see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
+ */
+
+#ifndef mozilla_a11y_RootAccessibleWrap_h__
+#define mozilla_a11y_RootAccessibleWrap_h__
+
+#include "RootAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+class RootAccessibleWrap : public RootAccessible
+{
+public:
+ RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
+ virtual ~RootAccessibleWrap();
+
+ Class GetNativeType ();
+
+ // let's our native accessible get in touch with the
+ // native cocoa view that is our accessible parent.
+ void GetNativeWidget (void **aOutView);
+};
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/mac/RootAccessibleWrap.mm b/accessible/mac/RootAccessibleWrap.mm
new file mode 100644
index 0000000000..037545cce2
--- /dev/null
+++ b/accessible/mac/RootAccessibleWrap.mm
@@ -0,0 +1,53 @@
+/* -*- 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 "RootAccessibleWrap.h"
+
+#include "mozDocAccessible.h"
+
+#include "nsCOMPtr.h"
+#include "nsObjCExceptions.h"
+#include "nsIFrame.h"
+#include "nsView.h"
+#include "nsIWidget.h"
+
+using namespace mozilla::a11y;
+
+RootAccessibleWrap::
+ RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
+ RootAccessible(aDocument, aPresShell)
+{
+}
+
+RootAccessibleWrap::~RootAccessibleWrap()
+{
+}
+
+Class
+RootAccessibleWrap::GetNativeType()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ return [mozRootAccessible class];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+void
+RootAccessibleWrap::GetNativeWidget(void** aOutView)
+{
+ nsIFrame *frame = GetFrame();
+ if (frame) {
+ nsView *view = frame->GetView();
+ if (view) {
+ nsIWidget *widget = view->GetWidget();
+ if (widget) {
+ *aOutView = (void**)widget->GetNativeData (NS_NATIVE_WIDGET);
+ NS_ASSERTION (*aOutView,
+ "Couldn't get the native NSView parent we need to connect the accessibility hierarchy!");
+ }
+ }
+ }
+}
diff --git a/accessible/mac/TextLeafAccessibleWrap.h b/accessible/mac/TextLeafAccessibleWrap.h
new file mode 100644
index 0000000000..d07b9defec
--- /dev/null
+++ b/accessible/mac/TextLeafAccessibleWrap.h
@@ -0,0 +1,19 @@
+/* -*- 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_TextLeafAccessibleWrap_h__
+#define mozilla_a11y_TextLeafAccessibleWrap_h__
+
+#include "TextLeafAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef class TextLeafAccessible TextLeafAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/mac/XULListboxAccessibleWrap.h b/accessible/mac/XULListboxAccessibleWrap.h
new file mode 100644
index 0000000000..f7dc6cc547
--- /dev/null
+++ b/accessible/mac/XULListboxAccessibleWrap.h
@@ -0,0 +1,20 @@
+/* -*- 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_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
diff --git a/accessible/mac/XULMenuAccessibleWrap.h b/accessible/mac/XULMenuAccessibleWrap.h
new file mode 100644
index 0000000000..6efcf007eb
--- /dev/null
+++ b/accessible/mac/XULMenuAccessibleWrap.h
@@ -0,0 +1,19 @@
+/* -*- 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_XULMenuAccessibleWrap_h__
+#define mozilla_a11y_XULMenuAccessibleWrap_h__
+
+#include "XULMenuAccessible.h"
+
+namespace mozilla {
+namespace a11y {
+
+typedef class XULMenuitemAccessible XULMenuitemAccessibleWrap;
+
+} // namespace a11y
+} // namespace mozilla
+
+#endif
diff --git a/accessible/mac/XULTreeGridAccessibleWrap.h b/accessible/mac/XULTreeGridAccessibleWrap.h
new file mode 100644
index 0000000000..b3631e9adb
--- /dev/null
+++ b/accessible/mac/XULTreeGridAccessibleWrap.h
@@ -0,0 +1,20 @@
+/* -*- 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_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
diff --git a/accessible/mac/moz.build b/accessible/mac/moz.build
new file mode 100644
index 0000000000..8d2e7b391f
--- /dev/null
+++ b/accessible/mac/moz.build
@@ -0,0 +1,44 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 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/.
+
+EXPORTS += [
+ 'mozAccessibleProtocol.h',
+]
+
+EXPORTS.mozilla.a11y += [
+ 'AccessibleWrap.h',
+ 'HyperTextAccessibleWrap.h',
+]
+
+SOURCES += [
+ 'AccessibleWrap.mm',
+ 'DocAccessibleWrap.mm',
+ 'MacUtils.mm',
+ 'mozAccessible.mm',
+ 'mozActionElements.mm',
+ 'mozDocAccessible.mm',
+ 'mozHTMLAccessible.mm',
+ 'mozTableAccessible.mm',
+ 'mozTextAccessible.mm',
+ 'Platform.mm',
+ 'RootAccessibleWrap.mm',
+]
+
+LOCAL_INCLUDES += [
+ '/accessible/base',
+ '/accessible/generic',
+ '/accessible/html',
+ '/accessible/ipc',
+ '/accessible/ipc/other',
+ '/accessible/xul',
+ '/layout/generic',
+ '/layout/xul',
+ '/widget',
+ '/widget/cocoa',
+]
+
+FINAL_LIBRARY = 'xul'
+
+include('/ipc/chromium/chromium-config.mozbuild')
diff --git a/accessible/mac/mozAccessible.h b/accessible/mac/mozAccessible.h
new file mode 100644
index 0000000000..6d7db3fe98
--- /dev/null
+++ b/accessible/mac/mozAccessible.h
@@ -0,0 +1,181 @@
+/* -*- Mode: Objective-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 "AccessibleWrap.h"
+#include "ProxyAccessible.h"
+
+#import <Cocoa/Cocoa.h>
+
+#import "mozAccessibleProtocol.h"
+
+@class mozRootAccessible;
+
+/**
+ * All mozAccessibles are either abstract objects (that correspond to XUL
+ * widgets, HTML frames, etc) or are attached to a certain view; for example
+ * a document view. When we hand an object off to an AT, we always want
+ * to give it the represented view, in the latter case.
+ */
+
+namespace mozilla {
+namespace a11y {
+
+inline id <mozAccessible>
+GetObjectOrRepresentedView(id <mozAccessible> aObject)
+{
+ return [aObject hasRepresentedView] ? [aObject representedView] : aObject;
+}
+
+inline mozAccessible*
+GetNativeFromGeckoAccessible(Accessible* aAccessible)
+{
+ mozAccessible* native = nil;
+ aAccessible->GetNativeInterface((void**)&native);
+ return native;
+}
+
+inline mozAccessible*
+GetNativeFromProxy(const ProxyAccessible* aProxy)
+{
+ return reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
+}
+
+} // a11y
+} // mozilla
+
+// This is OR'd with the Accessible owner to indicate the wrap-ee is a proxy.
+static const uintptr_t IS_PROXY = 1;
+
+@interface mozAccessible : NSObject <mozAccessible>
+{
+ /**
+ * Weak reference; it owns us.
+ */
+ uintptr_t mGeckoAccessible;
+
+ /**
+ * Strong ref to array of children
+ */
+ NSMutableArray* mChildren;
+
+ /**
+ * Weak reference to the parent
+ */
+ mozAccessible* mParent;
+
+ /**
+ * The role of our gecko accessible.
+ */
+ mozilla::a11y::role mRole;
+}
+
+// return the Accessible for this mozAccessible if it exists.
+- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible;
+
+// return the ProxyAccessible for this mozAccessible if it exists.
+- (mozilla::a11y::ProxyAccessible*)getProxyAccessible;
+
+// inits with the gecko owner.
+- (id)initWithAccessible:(uintptr_t)aGeckoObj;
+
+// our accessible parent (AXParent)
+- (id <mozAccessible>)parent;
+
+// a lazy cache of our accessible children (AXChildren). updated
+- (NSArray*)children;
+
+// returns the size of this accessible.
+- (NSValue*)size;
+
+// returns the position, in cocoa coordinates.
+- (NSValue*)position;
+
+// can be overridden to report another role name.
+- (NSString*)role;
+
+// a subrole is a more specialized variant of the role. for example,
+// the role might be "textfield", while the subrole is "password textfield".
+- (NSString*)subrole;
+
+// Return the role description, as there are a few exceptions.
+- (NSString*)roleDescription;
+
+// returns the native window we're inside.
+- (NSWindow*)window;
+
+// the value of this element.
+- (id)value;
+
+// name that is associated with this accessible (for buttons, etc)
+- (NSString*)title;
+
+// the accessible description (help text) of this particular instance.
+- (NSString*)help;
+
+- (BOOL)isEnabled;
+
+// information about focus.
+- (BOOL)isFocused;
+- (BOOL)canBeFocused;
+
+// returns NO if for some reason we were unable to focus the element.
+- (BOOL)focus;
+
+// notifications sent out to listening accessible providers.
+- (void)didReceiveFocus;
+- (void)valueDidChange;
+- (void)selectedTextDidChange;
+- (void)documentLoadComplete;
+
+// internal method to retrieve a child at a given index.
+- (id)childAt:(uint32_t)i;
+
+#pragma mark -
+
+// invalidates and removes all our children from our cached array.
+- (void)invalidateChildren;
+
+/**
+ * Append a child if they are already cached.
+ */
+- (void)appendChild:(mozilla::a11y::Accessible*)aAccessible;
+
+// makes ourselves "expired". after this point, we might be around if someone
+// has retained us (e.g., a third-party), but we really contain no information.
+- (void)expire;
+- (BOOL)isExpired;
+
+#ifdef DEBUG
+- (void)printHierarchy;
+- (void)printHierarchyWithLevel:(unsigned)numSpaces;
+
+- (void)sanityCheckChildren;
+- (void)sanityCheckChildren:(NSArray*)theChildren;
+#endif
+
+// ---- NSAccessibility methods ---- //
+
+// whether to skip this element when traversing the accessibility
+// hierarchy.
+- (BOOL)accessibilityIsIgnored;
+
+// called by third-parties to determine the deepest child element under the mouse
+- (id)accessibilityHitTest:(NSPoint)point;
+
+// returns the deepest unignored focused accessible element
+- (id)accessibilityFocusedUIElement;
+
+// a mozAccessible needs to at least provide links to its parent and
+// children.
+- (NSArray*)accessibilityAttributeNames;
+
+// value for the specified attribute
+- (id)accessibilityAttributeValue:(NSString*)attribute;
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute;
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
+
+@end
+
diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm
new file mode 100644
index 0000000000..a02779ef25
--- /dev/null
+++ b/accessible/mac/mozAccessible.mm
@@ -0,0 +1,1197 @@
+/* -*- Mode: Objective-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/. */
+
+#import "mozAccessible.h"
+
+#import "MacUtils.h"
+#import "mozView.h"
+
+#include "Accessible-inl.h"
+#include "nsAccUtils.h"
+#include "nsIAccessibleRelation.h"
+#include "nsIAccessibleEditableText.h"
+#include "nsIPersistentProperties2.h"
+#include "Relation.h"
+#include "Role.h"
+#include "RootAccessible.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
+#include "mozilla/a11y/PDocAccessible.h"
+#include "OuterDocAccessible.h"
+
+#include "mozilla/Services.h"
+#include "nsRect.h"
+#include "nsCocoaUtils.h"
+#include "nsCoord.h"
+#include "nsObjCExceptions.h"
+#include "nsWhitespaceTokenizer.h"
+#include <prdtoa.h>
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
+#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
+#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
+#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
+#define NSAccessibilityMathBaseAttribute @"AXMathBase"
+#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript"
+#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript"
+#define NSAccessibilityMathUnderAttribute @"AXMathUnder"
+#define NSAccessibilityMathOverAttribute @"AXMathOver"
+#define NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness"
+// XXX WebKit also defines the following attributes.
+// See bugs 1176970 and 1176983.
+// - NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen"
+// - NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose"
+// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
+// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
+
+#pragma mark -
+
+@implementation mozAccessible
+
+- (id)initWithAccessible:(uintptr_t)aGeckoAccessible
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ((self = [super init])) {
+ mGeckoAccessible = aGeckoAccessible;
+ if (aGeckoAccessible & IS_PROXY)
+ mRole = [self getProxyAccessible]->Role();
+ else
+ mRole = [self getGeckoAccessible]->Role();
+ }
+
+ return self;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)dealloc
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mChildren release];
+ [super dealloc];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible
+{
+ // Check if mGeckoAccessible points at a proxy
+ if (mGeckoAccessible & IS_PROXY)
+ return nil;
+
+ return reinterpret_cast<AccessibleWrap*>(mGeckoAccessible);
+}
+
+- (mozilla::a11y::ProxyAccessible*)getProxyAccessible
+{
+ // Check if mGeckoAccessible points at a proxy
+ if (!(mGeckoAccessible & IS_PROXY))
+ return nil;
+
+ return reinterpret_cast<ProxyAccessible*>(mGeckoAccessible & ~IS_PROXY);
+}
+
+#pragma mark -
+
+- (BOOL)accessibilityIsIgnored
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ // unknown (either unimplemented, or irrelevant) elements are marked as ignored
+ // as well as expired elements.
+
+ bool noRole = [[self role] isEqualToString:NSAccessibilityUnknownRole];
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return (noRole && !(accWrap->InteractiveState() & states::FOCUSABLE));
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return (noRole && !(proxy->State() & states::FOCUSABLE));
+
+ return true;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ NSMutableArray* additional = [NSMutableArray array];
+ switch (mRole) {
+ case roles::MATHML_ROOT:
+ [additional addObject:NSAccessibilityMathRootIndexAttribute];
+ [additional addObject:NSAccessibilityMathRootRadicandAttribute];
+ break;
+ case roles::MATHML_SQUARE_ROOT:
+ [additional addObject:NSAccessibilityMathRootRadicandAttribute];
+ break;
+ case roles::MATHML_FRACTION:
+ [additional addObject:NSAccessibilityMathFractionNumeratorAttribute];
+ [additional addObject:NSAccessibilityMathFractionDenominatorAttribute];
+ [additional addObject:NSAccessibilityMathLineThicknessAttribute];
+ break;
+ case roles::MATHML_SUB:
+ case roles::MATHML_SUP:
+ case roles::MATHML_SUB_SUP:
+ [additional addObject:NSAccessibilityMathBaseAttribute];
+ [additional addObject:NSAccessibilityMathSubscriptAttribute];
+ [additional addObject:NSAccessibilityMathSuperscriptAttribute];
+ break;
+ case roles::MATHML_UNDER:
+ case roles::MATHML_OVER:
+ case roles::MATHML_UNDER_OVER:
+ [additional addObject:NSAccessibilityMathBaseAttribute];
+ [additional addObject:NSAccessibilityMathUnderAttribute];
+ [additional addObject:NSAccessibilityMathOverAttribute];
+ break;
+ // XXX bug 1176983
+ // roles::MATHML_MULTISCRIPTS should also have the following attributes:
+ // - NSAccessibilityMathPrescriptsAttribute
+ // - NSAccessibilityMathPostscriptsAttribute
+ // XXX bug 1176970
+ // roles::MATHML_FENCED should also have the following attributes:
+ // - NSAccessibilityMathFencedOpenAttribute
+ // - NSAccessibilityMathFencedCloseAttribute
+ default:
+ break;
+ }
+
+ return additional;
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // if we're expired, we don't support any attributes.
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return [NSArray array];
+
+ static NSArray* generalAttributes = nil;
+
+ if (!generalAttributes) {
+ // standard attributes that are shared and supported by all generic elements.
+ generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilitySubroleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityTitleUIElementAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+#if DEBUG
+ @"AXMozDescription",
+#endif
+ nil];
+ }
+
+ NSArray* objectAttributes = generalAttributes;
+
+ NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames];
+ if ([additionalAttributes count])
+ objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
+
+ return objectAttributes;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)childAt:(uint32_t)i
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ Accessible* child = accWrap->GetChildAt(i);
+ return child ? GetNativeFromGeckoAccessible(child) : nil;
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ ProxyAccessible* child = proxy->ChildAt(i);
+ return child ? GetNativeFromProxy(child) : nil;
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+#if DEBUG
+ if ([attribute isEqualToString:@"AXMozDescription"])
+ return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
+#endif
+
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
+ return [self children];
+ if ([attribute isEqualToString:NSAccessibilityParentAttribute])
+ return [self parent];
+
+#ifdef DEBUG_hakan
+ NSLog (@"(%@ responding to attr %@)", self, attribute);
+#endif
+
+ if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
+ return [self role];
+ if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
+ return [self position];
+ if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
+ return [self subrole];
+ if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
+ return [NSNumber numberWithBool:[self isEnabled]];
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute])
+ return [self value];
+ if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
+ return [self roleDescription];
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
+ return [NSNumber numberWithBool:[self isFocused]];
+ if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
+ return [self size];
+ if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
+ return [self window];
+ if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
+ return [self window];
+ if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
+ return [self title];
+ if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
+ if (accWrap) {
+ Relation rel = accWrap->RelationByType(RelationType::LABELLED_BY);
+ Accessible* tempAcc = rel.Next();
+ return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
+ }
+ nsTArray<ProxyAccessible*> rel = proxy->RelationByType(RelationType::LABELLED_BY);
+ ProxyAccessible* tempProxy = rel.SafeElementAt(0);
+ return tempProxy ? GetNativeFromProxy(tempProxy) : nil;
+ }
+ if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
+ return [self help];
+
+ switch (mRole) {
+ case roles::MATHML_ROOT:
+ if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_SQUARE_ROOT:
+ if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
+ return [self childAt:0];
+ break;
+ case roles::MATHML_FRACTION:
+ if ([attribute isEqualToString:NSAccessibilityMathFractionNumeratorAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathFractionDenominatorAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathLineThicknessAttribute]) {
+ // WebKit sets line thickness to some logical value parsed in the
+ // renderer object of the <mfrac> element. It's not clear whether the
+ // exact value is relevant to assistive technologies. From a semantic
+ // point of view, the only important point is to distinguish between
+ // <mfrac> elements that have a fraction bar and those that do not.
+ // Per the MathML 3 spec, the latter happens iff the linethickness
+ // attribute is of the form [zero-float][optional-unit]. In that case we
+ // set line thickness to zero and in the other cases we set it to one.
+ nsAutoString thickness;
+ if (accWrap) {
+ nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
+ nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
+ } else {
+ AutoTArray<Attribute, 10> attrs;
+ proxy->Attributes(&attrs);
+ for (size_t i = 0 ; i < attrs.Length() ; i++) {
+ if (attrs.ElementAt(i).Name() == "thickness") {
+ thickness = attrs.ElementAt(i).Value();
+ break;
+ }
+ }
+ }
+ double value = 1.0;
+ if (!thickness.IsEmpty())
+ value = PR_strtod(NS_LossyConvertUTF16toASCII(thickness).get(),
+ nullptr);
+ return [NSNumber numberWithInteger:(value ? 1 : 0)];
+ }
+ break;
+ case roles::MATHML_SUB:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return [self childAt:1];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return nil;
+#endif
+ break;
+ case roles::MATHML_SUP:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return nil;
+#endif
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_SUB_SUP:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return [self childAt:2];
+ break;
+ case roles::MATHML_UNDER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return [self childAt:1];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return nil;
+#endif
+ break;
+ case roles::MATHML_OVER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return nil;
+#endif
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_UNDER_OVER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return [self childAt:2];
+ break;
+ // XXX bug 1176983
+ // roles::MATHML_MULTISCRIPTS should also have the following attributes:
+ // - NSAccessibilityMathPrescriptsAttribute
+ // - NSAccessibilityMathPostscriptsAttribute
+ // XXX bug 1176970
+ // roles::MATHML_FENCED should also have the following attributes:
+ // - NSAccessibilityMathFencedOpenAttribute
+ // - NSAccessibilityMathFencedCloseAttribute
+ default:
+ break;
+ }
+
+#ifdef DEBUG
+ NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
+#endif
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
+ return [self canBeFocused];
+
+ return NO;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog (@"[%@] %@='%@'", self, attribute, value);
+#endif
+
+ // we only support focusing elements so far.
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
+ [self focus];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+ // Convert the given screen-global point in the cocoa coordinate system (with
+ // origin in the bottom-left corner of the screen) into point in the Gecko
+ // coordinate system (with origin in a top-left screen point).
+ NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
+ NSPoint tmpPoint = NSMakePoint(point.x,
+ [mainView frame].size.height - point.y);
+ LayoutDeviceIntPoint geckoPoint = nsCocoaUtils::
+ CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
+
+ mozAccessible* nativeChild = nil;
+ if (accWrap) {
+ Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+ Accessible::eDeepestChild);
+ if (child)
+ nativeChild = GetNativeFromGeckoAccessible(child);
+ } else if (proxy) {
+ ProxyAccessible* child = proxy->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+ Accessible::eDeepestChild);
+ if (child)
+ nativeChild = GetNativeFromProxy(child);
+ }
+
+ if (nativeChild)
+ return nativeChild;
+
+ // if we didn't find anything, return ourself or child view.
+ return GetObjectOrRepresentedView(self);
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ return nil;
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ // by default we return whatever the MacOS API know about.
+ // if you have custom actions, override.
+ return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+ mozAccessible* focusedChild = nil;
+ if (accWrap) {
+ Accessible* focusedGeckoChild = accWrap->FocusedChild();
+ if (focusedGeckoChild)
+ focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
+ } else if (proxy) {
+ ProxyAccessible* focusedGeckoChild = proxy->FocusedChild();
+ if (focusedGeckoChild)
+ focusedChild = GetNativeFromProxy(focusedGeckoChild);
+ }
+
+ if (focusedChild)
+ return GetObjectOrRepresentedView(focusedChild);
+
+ // return ourself if we can't get a native focused child.
+ return GetObjectOrRepresentedView(self);
+}
+
+#pragma mark -
+
+- (id <mozAccessible>)parent
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ id nativeParent = nil;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ Accessible* accessibleParent = accWrap->Parent();
+ if (accessibleParent)
+ nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
+ if (nativeParent)
+ return GetObjectOrRepresentedView(nativeParent);
+
+ // Return native of root accessible if we have no direct parent
+ nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible());
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if (ProxyAccessible* proxyParent = proxy->Parent()) {
+ nativeParent = GetNativeFromProxy(proxyParent);
+ }
+
+ if (nativeParent)
+ return GetObjectOrRepresentedView(nativeParent);
+
+ Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+ nativeParent = outerDoc ?
+ GetNativeFromGeckoAccessible(outerDoc) : nil;
+ } else {
+ return nil;
+ }
+
+ NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
+
+ return GetObjectOrRepresentedView(nativeParent);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)hasRepresentedView
+{
+ return NO;
+}
+
+- (id)representedView
+{
+ return nil;
+}
+
+- (BOOL)isRoot
+{
+ return NO;
+}
+
+// gets our native children lazily.
+// returns nil when there are no children.
+- (NSArray*)children
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (mChildren)
+ return mChildren;
+
+ // get the array of children.
+ mChildren = [[NSMutableArray alloc] init];
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ if (accWrap) {
+ uint32_t childCount = accWrap->ChildCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ mozAccessible* nativeChild = GetNativeFromGeckoAccessible(accWrap->GetChildAt(childIdx));
+ if (nativeChild)
+ [mChildren addObject:nativeChild];
+ }
+
+ // children from child if this is an outerdoc
+ OuterDocAccessible* docOwner = accWrap->AsOuterDoc();
+ if (docOwner) {
+ if (ProxyAccessible* proxyDoc = docOwner->RemoteChildDoc()) {
+ mozAccessible* nativeRemoteChild = GetNativeFromProxy(proxyDoc);
+ [mChildren insertObject:nativeRemoteChild atIndex:0];
+ NSAssert1 (nativeRemoteChild, @"%@ found a child remote doc missing a native\n", self);
+ }
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ uint32_t childCount = proxy->ChildrenCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ mozAccessible* nativeChild = GetNativeFromProxy(proxy->ChildAt(childIdx));
+ if (nativeChild)
+ [mChildren addObject:nativeChild];
+ }
+
+ }
+
+ return mChildren;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)position
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsIntRect rect;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ rect = accWrap->Bounds();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ rect = proxy->Bounds();
+ else
+ return nil;
+
+ NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
+ CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
+ NSPoint p = NSMakePoint(static_cast<CGFloat>(rect.x) / scaleFactor,
+ [mainView frame].size.height - static_cast<CGFloat>(rect.y + rect.height) / scaleFactor);
+
+ return [NSValue valueWithPoint:p];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)size
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsIntRect rect;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ rect = accWrap->Bounds();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ rect = proxy->Bounds();
+ else
+ return nil;
+
+ CGFloat scaleFactor =
+ nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]);
+ return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(rect.width) / scaleFactor,
+ static_cast<CGFloat>(rect.height) / scaleFactor)];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSString*)role
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ if (accWrap) {
+ #ifdef DEBUG_A11Y
+ NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
+ "Does not support Text when it should");
+ #endif
+ } else if (![self getProxyAccessible]) {
+ return nil;
+ }
+
+#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
+ case roles::geckoRole: \
+ return macRole;
+
+ switch (mRole) {
+#include "RoleMap.h"
+ default:
+ NS_NOTREACHED("Unknown role.");
+ return NSAccessibilityUnknownRole;
+ }
+
+#undef ROLE
+}
+
+- (NSString*)subrole
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+
+ // Deal with landmarks first
+ nsIAtom* landmark = nullptr;
+ if (accWrap)
+ landmark = accWrap->LandmarkRole();
+ else if (proxy)
+ landmark = proxy->LandmarkRole();
+
+ if (landmark) {
+ if (landmark == nsGkAtoms::application)
+ return @"AXLandmarkApplication";
+ if (landmark == nsGkAtoms::banner)
+ return @"AXLandmarkBanner";
+ if (landmark == nsGkAtoms::complementary)
+ return @"AXLandmarkComplementary";
+ if (landmark == nsGkAtoms::contentinfo)
+ return @"AXLandmarkContentInfo";
+ if (landmark == nsGkAtoms::form)
+ return @"AXLandmarkForm";
+ if (landmark == nsGkAtoms::main)
+ return @"AXLandmarkMain";
+ if (landmark == nsGkAtoms::navigation)
+ return @"AXLandmarkNavigation";
+ if (landmark == nsGkAtoms::search)
+ return @"AXLandmarkSearch";
+ if (landmark == nsGkAtoms::searchbox)
+ return @"AXSearchField";
+ }
+
+ // Now, deal with widget roles
+ nsIAtom* roleAtom = nullptr;
+ if (accWrap && accWrap->HasARIARole()) {
+ const nsRoleMapEntry* roleMap = accWrap->ARIARoleMap();
+ roleAtom = *roleMap->roleAtom;
+ }
+ if (proxy)
+ roleAtom = proxy->ARIARoleAtom();
+
+ if (roleAtom) {
+ if (roleAtom == nsGkAtoms::alert)
+ return @"AXApplicationAlert";
+ if (roleAtom == nsGkAtoms::alertdialog)
+ return @"AXApplicationAlertDialog";
+ if (roleAtom == nsGkAtoms::article)
+ return @"AXDocumentArticle";
+ if (roleAtom == nsGkAtoms::dialog)
+ return @"AXApplicationDialog";
+ if (roleAtom == nsGkAtoms::document)
+ return @"AXDocument";
+ if (roleAtom == nsGkAtoms::log_)
+ return @"AXApplicationLog";
+ if (roleAtom == nsGkAtoms::math)
+ return @"AXDocumentMath";
+ if (roleAtom == nsGkAtoms::note_)
+ return @"AXDocumentNote";
+ if (roleAtom == nsGkAtoms::region)
+ return @"AXDocumentRegion";
+ if (roleAtom == nsGkAtoms::status)
+ return @"AXApplicationStatus";
+ if (roleAtom == nsGkAtoms::tabpanel)
+ return @"AXTabPanel";
+ if (roleAtom == nsGkAtoms::timer)
+ return @"AXApplicationTimer";
+ if (roleAtom == nsGkAtoms::tooltip)
+ return @"AXUserInterfaceTooltip";
+ }
+
+ switch (mRole) {
+ case roles::LIST:
+ return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole;
+
+ case roles::ENTRY:
+ if ((accWrap && accWrap->IsSearchbox()) ||
+ (proxy && proxy->IsSearchbox()))
+ return @"AXSearchField";
+ break;
+
+ case roles::DEFINITION_LIST:
+ return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
+
+ case roles::TERM:
+ return @"AXTerm";
+
+ case roles::DEFINITION:
+ return @"AXDefinition";
+
+ case roles::MATHML_MATH:
+ return @"AXDocumentMath";
+
+ case roles::MATHML_FRACTION:
+ return @"AXMathFraction";
+
+ case roles::MATHML_FENCED:
+ // XXX bug 1176970
+ // This should be AXMathFence, but doing so without implementing the
+ // whole fence interface seems to make VoiceOver crash, so we present it
+ // as a row for now.
+ return @"AXMathRow";
+
+ case roles::MATHML_SUB:
+ case roles::MATHML_SUP:
+ case roles::MATHML_SUB_SUP:
+ return @"AXMathSubscriptSuperscript";
+
+ case roles::MATHML_ROW:
+ case roles::MATHML_STYLE:
+ case roles::MATHML_ERROR:
+ return @"AXMathRow";
+
+ case roles::MATHML_UNDER:
+ case roles::MATHML_OVER:
+ case roles::MATHML_UNDER_OVER:
+ return @"AXMathUnderOver";
+
+ case roles::MATHML_SQUARE_ROOT:
+ return @"AXMathSquareRoot";
+
+ case roles::MATHML_ROOT:
+ return @"AXMathRoot";
+
+ case roles::MATHML_TEXT:
+ return @"AXMathText";
+
+ case roles::MATHML_NUMBER:
+ return @"AXMathNumber";
+
+ case roles::MATHML_IDENTIFIER:
+ return @"AXMathIdentifier";
+
+ case roles::MATHML_TABLE:
+ return @"AXMathTable";
+
+ case roles::MATHML_TABLE_ROW:
+ return @"AXMathTableRow";
+
+ case roles::MATHML_CELL:
+ return @"AXMathTableCell";
+
+ // XXX: NSAccessibility also uses subroles AXMathSeparatorOperator and
+ // AXMathFenceOperator. We should use the NS_MATHML_OPERATOR_FENCE and
+ // NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they
+ // are only available from the MathML layout code. Hence we just fallback
+ // to subrole AXMathOperator for now.
+ // XXX bug 1175747 WebKit also creates anonymous operators for <mfenced>
+ // which have subroles AXMathSeparatorOperator and AXMathFenceOperator.
+ case roles::MATHML_OPERATOR:
+ return @"AXMathOperator";
+
+ case roles::MATHML_MULTISCRIPTS:
+ return @"AXMathMultiscript";
+
+ case roles::SWITCH:
+ return @"AXSwitch";
+
+ case roles::ALERT:
+ return @"AXApplicationAlert";
+
+ case roles::SEPARATOR:
+ return @"AXContentSeparator";
+
+ case roles::PROPERTYPAGE:
+ return @"AXTabPanel";
+
+ case roles::DETAILS:
+ return @"AXDetails";
+
+ case roles::SUMMARY:
+ return @"AXSummary";
+
+ default:
+ break;
+ }
+
+ return nil;
+}
+
+struct RoleDescrMap
+{
+ NSString* role;
+ const nsString description;
+};
+
+static const RoleDescrMap sRoleDescrMap[] = {
+ { @"AXApplicationAlert", NS_LITERAL_STRING("alert") },
+ { @"AXApplicationAlertDialog", NS_LITERAL_STRING("alertDialog") },
+ { @"AXApplicationLog", NS_LITERAL_STRING("log") },
+ { @"AXApplicationStatus", NS_LITERAL_STRING("status") },
+ { @"AXApplicationTimer", NS_LITERAL_STRING("timer") },
+ { @"AXContentSeparator", NS_LITERAL_STRING("separator") },
+ { @"AXDefinition", NS_LITERAL_STRING("definition") },
+ { @"AXDocument", NS_LITERAL_STRING("document") },
+ { @"AXDocumentArticle", NS_LITERAL_STRING("article") },
+ { @"AXDocumentMath", NS_LITERAL_STRING("math") },
+ { @"AXDocumentNote", NS_LITERAL_STRING("note") },
+ { @"AXDocumentRegion", NS_LITERAL_STRING("region") },
+ { @"AXLandmarkApplication", NS_LITERAL_STRING("application") },
+ { @"AXLandmarkBanner", NS_LITERAL_STRING("banner") },
+ { @"AXLandmarkComplementary", NS_LITERAL_STRING("complementary") },
+ { @"AXLandmarkContentInfo", NS_LITERAL_STRING("content") },
+ { @"AXLandmarkMain", NS_LITERAL_STRING("main") },
+ { @"AXLandmarkNavigation", NS_LITERAL_STRING("navigation") },
+ { @"AXLandmarkSearch", NS_LITERAL_STRING("search") },
+ { @"AXSearchField", NS_LITERAL_STRING("searchTextField") },
+ { @"AXTabPanel", NS_LITERAL_STRING("tabPanel") },
+ { @"AXTerm", NS_LITERAL_STRING("term") },
+ { @"AXUserInterfaceTooltip", NS_LITERAL_STRING("tooltip") }
+};
+
+struct RoleDescrComparator
+{
+ const NSString* mRole;
+ explicit RoleDescrComparator(const NSString* aRole) : mRole(aRole) {}
+ int operator()(const RoleDescrMap& aEntry) const {
+ return [mRole compare:aEntry.role];
+ }
+};
+
+- (NSString*)roleDescription
+{
+ if (mRole == roles::DOCUMENT)
+ return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
+
+ NSString* subrole = [self subrole];
+
+ if (subrole) {
+ size_t idx = 0;
+ if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
+ RoleDescrComparator(subrole), &idx)) {
+ return utils::LocalizedString(sRoleDescrMap[idx].description);
+ }
+ }
+
+ return NSAccessibilityRoleDescription([self role], subrole);
+}
+
+- (NSString*)title
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsAutoString title;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Name(title);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Name(title);
+
+ return nsCocoaUtils::ToNSString(title);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)value
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsAutoString value;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Value(value);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Value(value);
+
+ return nsCocoaUtils::ToNSString(value);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)valueDidChange
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog(@"%@'s value changed!", self);
+#endif
+ // sending out a notification is expensive, so we don't do it other than for really important objects,
+ // like mozTextAccessible.
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)selectedTextDidChange
+{
+ // Do nothing. mozTextAccessible will.
+}
+
+- (void)documentLoadComplete
+{
+ id realSelf = GetObjectOrRepresentedView(self);
+ NSAccessibilityPostNotification(realSelf, NSAccessibilityFocusedUIElementChangedNotification);
+ NSAccessibilityPostNotification(realSelf, @"AXLoadComplete");
+ NSAccessibilityPostNotification(realSelf, @"AXLayoutComplete");
+}
+
+- (NSString*)help
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // What needs to go here is actually the accDescription of an item.
+ // The MSAA acc_help method has nothing to do with this one.
+ nsAutoString helpText;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Description(helpText);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Description(helpText);
+
+ return nsCocoaUtils::ToNSString(helpText);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// objc-style description (from NSObject); not to be confused with the accessible description above.
+- (NSString*)description
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)isFocused
+{
+ return FocusMgr()->IsFocused([self getGeckoAccessible]);
+}
+
+- (BOOL)canBeFocused
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return accWrap->InteractiveState() & states::FOCUSABLE;
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return proxy->State() & states::FOCUSABLE;
+
+ return false;
+}
+
+- (BOOL)focus
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->TakeFocus();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->TakeFocus();
+ else
+ return NO;
+
+ return YES;
+}
+
+- (BOOL)isEnabled
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return ((accWrap->InteractiveState() & states::UNAVAILABLE) == 0);
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return ((proxy->State() & states::UNAVAILABLE) == 0);
+
+ return false;
+}
+
+// The root accessible calls this when the focused node was
+// changed to us.
+- (void)didReceiveFocus
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog (@"%@ received focus!", self);
+#endif
+ NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
+ NSAccessibilityFocusedUIElementChangedNotification);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (NSWindow*)window
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // Get a pointer to the native window (NSWindow) we reside in.
+ NSWindow *nativeWindow = nil;
+ DocAccessible* docAcc = nullptr;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ docAcc = accWrap->Document();
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+ if (outerDoc)
+ docAcc = outerDoc->Document();
+ }
+
+ if (docAcc)
+ nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
+
+ NSAssert1(nativeWindow, @"Could not get native window for %@", self);
+ return nativeWindow;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)invalidateChildren
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // make room for new children
+ [mChildren release];
+ mChildren = nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)appendChild:(Accessible*)aAccessible
+{
+ // if mChildren is nil, then we don't even need to bother
+ if (!mChildren)
+ return;
+
+ mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
+ if (curNative)
+ [mChildren addObject:curNative];
+}
+
+- (void)expire
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self invalidateChildren];
+
+ mGeckoAccessible = 0;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (BOOL)isExpired
+{
+ return ![self getGeckoAccessible] && ![self getProxyAccessible];
+}
+
+#pragma mark -
+#pragma mark Debug methods
+#pragma mark -
+
+#ifdef DEBUG
+
+// will check that our children actually reference us as their
+// parent.
+- (void)sanityCheckChildren:(NSArray *)children
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSEnumerator *iter = [children objectEnumerator];
+ mozAccessible *curObj = nil;
+
+ NSLog(@"sanity checking %@", self);
+
+ while ((curObj = [iter nextObject])) {
+ id realSelf = GetObjectOrRepresentedView(self);
+ NSLog(@"checking %@", realSelf);
+ NSAssert2([curObj parent] == realSelf,
+ @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)sanityCheckChildren
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self sanityCheckChildren:[self children]];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)printHierarchy
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self printHierarchyWithLevel:0];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)printHierarchyWithLevel:(unsigned)level
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
+
+ // print this node
+ NSMutableString *indent = [NSMutableString stringWithCapacity:level];
+ unsigned i=0;
+ for (;i<level;i++)
+ [indent appendString:@" "];
+
+ NSLog (@"%@(#%i) %@", indent, level, self);
+
+ // use |children| method to make sure our children are lazily fetched first.
+ NSArray *children = [self children];
+ if (!children)
+ return;
+
+ [self sanityCheckChildren];
+
+ NSEnumerator *iter = [children objectEnumerator];
+ mozAccessible *object = nil;
+
+ while (iter && (object = [iter nextObject]))
+ // print every child node's subtree, increasing the indenting
+ // by two for every level.
+ [object printHierarchyWithLevel:(level+1)];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+#endif /* DEBUG */
+
+@end
diff --git a/accessible/mac/mozAccessibleProtocol.h b/accessible/mac/mozAccessibleProtocol.h
new file mode 100644
index 0000000000..5f67b1dcf2
--- /dev/null
+++ b/accessible/mac/mozAccessibleProtocol.h
@@ -0,0 +1,69 @@
+/* -*- Mode: Objective-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/. */
+
+#import <Cocoa/Cocoa.h>
+
+#import "mozView.h"
+
+/* This protocol's primary use is so widget/cocoa can talk back to us
+ properly.
+
+ ChildView owns the topmost mozRootAccessible, and needs to take care of setting up
+ that parent/child relationship.
+
+ This protocol is thus used to make sure it knows it's talking to us, and not
+ just some random |id|.
+*/
+
+@protocol mozAccessible
+
+// returns whether this accessible is the root accessible. there is one
+// root accessible per window.
+- (BOOL)isRoot;
+
+// some mozAccessibles implement accessibility support in place of another object. for example,
+// ChildView gets its support from us.
+//
+// instead of returning a mozAccessible to the OS when it wants an object, we need to pass the view we represent, so the
+// OS doesn't get confused and think we return some random object.
+- (BOOL)hasRepresentedView;
+- (id)representedView;
+
+#ifdef DEBUG
+// debug utility that will print the native accessibility tree, starting
+// at this node.
+- (void)printHierarchy;
+#endif
+
+/*** general ***/
+
+// returns the accessible at the specified point.
+- (id)accessibilityHitTest:(NSPoint)point;
+
+// whether this element is flagged as ignored.
+- (BOOL)accessibilityIsIgnored;
+
+// currently focused UI element (possibly a child accessible)
+- (id)accessibilityFocusedUIElement;
+
+/*** attributes ***/
+
+// all supported attributes
+- (NSArray*)accessibilityAttributeNames;
+
+// value for given attribute.
+- (id)accessibilityAttributeValue:(NSString*)attribute;
+
+// whether a particular attribute can be modified
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute;
+
+/*** actions ***/
+
+- (NSArray*)accessibilityActionNames;
+- (NSString*)accessibilityActionDescription:(NSString*)action;
+- (void)accessibilityPerformAction:(NSString*)action;
+
+@end
+
diff --git a/accessible/mac/mozActionElements.h b/accessible/mac/mozActionElements.h
new file mode 100644
index 0000000000..a325921eb8
--- /dev/null
+++ b/accessible/mac/mozActionElements.h
@@ -0,0 +1,37 @@
+/* -*- Mode: Objective-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/. */
+
+#import <Cocoa/Cocoa.h>
+#import "mozAccessible.h"
+
+/* Simple subclasses for things like checkboxes, buttons, etc. */
+
+@interface mozButtonAccessible : mozAccessible
+ {
+ }
+- (BOOL)hasPopup;
+- (void)click;
+- (BOOL)isTab;
+@end
+
+@interface mozCheckboxAccessible : mozButtonAccessible
+// returns one of the constants defined in CheckboxValue
+- (int)isChecked;
+@end
+
+/* Class for tabs - not individual tabs */
+@interface mozTabsAccessible : mozAccessible
+{
+ NSMutableArray* mTabs;
+}
+-(id)tabs;
+@end
+
+/**
+ * Accessible for a PANE
+ */
+@interface mozPaneAccessible : mozAccessible
+
+@end
diff --git a/accessible/mac/mozActionElements.mm b/accessible/mac/mozActionElements.mm
new file mode 100644
index 0000000000..5decd6cccc
--- /dev/null
+++ b/accessible/mac/mozActionElements.mm
@@ -0,0 +1,340 @@
+/* -*- Mode: Objective-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/. */
+
+#import "mozActionElements.h"
+
+#import "MacUtils.h"
+#include "Accessible-inl.h"
+#include "DocAccessible.h"
+#include "XULTabAccessible.h"
+
+#include "nsDeckFrame.h"
+#include "nsObjCExceptions.h"
+
+using namespace mozilla::a11y;
+
+enum CheckboxValue {
+ // these constants correspond to the values in the OS
+ kUnchecked = 0,
+ kChecked = 1,
+ kMixed = 2
+};
+
+@implementation mozButtonAccessible
+
+- (NSArray*)accessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ static NSArray *attributes = nil;
+ if (!attributes) {
+ attributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
+ NSAccessibilityRoleAttribute, // required
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityPositionAttribute, // required
+ NSAccessibilitySizeAttribute, // required
+ NSAccessibilityWindowAttribute, // required
+ NSAccessibilityPositionAttribute, // required
+ NSAccessibilityTopLevelUIElementAttribute, // required
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityEnabledAttribute, // required
+ NSAccessibilityFocusedAttribute, // required
+ NSAccessibilityTitleAttribute, // required
+ NSAccessibilityChildrenAttribute,
+ NSAccessibilityDescriptionAttribute,
+#if DEBUG
+ @"AXMozDescription",
+#endif
+ nil];
+ }
+ return attributes;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ if ([self hasPopup])
+ return [self children];
+ return nil;
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
+ if ([self isTab])
+ return utils::LocalizedString(NS_LITERAL_STRING("tab"));
+
+ return NSAccessibilityRoleDescription([self role], nil);
+ }
+
+ return [super accessibilityAttributeValue:attribute];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)accessibilityIsIgnored
+{
+ return ![self getGeckoAccessible] && ![self getProxyAccessible];
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([self isEnabled]) {
+ if ([self hasPopup])
+ return [NSArray arrayWithObjects:NSAccessibilityPressAction,
+ NSAccessibilityShowMenuAction,
+ nil];
+ return [NSArray arrayWithObject:NSAccessibilityPressAction];
+ }
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ if ([self isTab])
+ return utils::LocalizedString(NS_LITERAL_STRING("switch"));
+
+ return @"press button"; // XXX: localize this later?
+ }
+
+ if ([self hasPopup]) {
+ if ([action isEqualToString:NSAccessibilityShowMenuAction])
+ return @"show menu";
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ if ([self isEnabled] && [action isEqualToString:NSAccessibilityPressAction]) {
+ // TODO: this should bring up the menu, but currently doesn't.
+ // once msaa and atk have merged better, they will implement
+ // the action needed to show the menu.
+ [self click];
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)click
+{
+ // both buttons and checkboxes have only one action. we should really stop using arbitrary
+ // arrays with actions, and define constants for these actions.
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->DoAction(0);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->DoAction(0);
+}
+
+- (BOOL)isTab
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return accWrap->Role() == roles::PAGETAB;
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return proxy->Role() == roles::PAGETAB;
+
+ return false;
+}
+
+- (BOOL)hasPopup
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return accWrap->NativeState() & states::HASPOPUP;
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return proxy->NativeState() & states::HASPOPUP;
+
+ return false;
+}
+
+@end
+
+@implementation mozCheckboxAccessible
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ if ([self isChecked] != kUnchecked)
+ return @"uncheck checkbox"; // XXX: localize this later?
+
+ return @"check checkbox"; // XXX: localize this later?
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (int)isChecked
+{
+ uint64_t state = 0;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ state = accWrap->NativeState();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ state = proxy->NativeState();
+
+ // check if we're checked or in a mixed state
+ if (state & states::CHECKED) {
+ return (state & states::MIXED) ? kMixed : kChecked;
+ }
+
+ return kUnchecked;
+}
+
+- (id)value
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ return [NSNumber numberWithInt:[self isChecked]];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+@end
+
+@implementation mozTabsAccessible
+
+- (void)dealloc
+{
+ [mTabs release];
+
+ [super dealloc];
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ // standard attributes that are shared and supported by root accessible (AXMain) elements.
+ static NSMutableArray* attributes = nil;
+
+ if (!attributes) {
+ attributes = [[super accessibilityAttributeNames] mutableCopy];
+ [attributes addObject:NSAccessibilityContentsAttribute];
+ [attributes addObject:NSAccessibilityTabsAttribute];
+ }
+
+ return attributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ if ([attribute isEqualToString:NSAccessibilityContentsAttribute])
+ return [super children];
+ if ([attribute isEqualToString:NSAccessibilityTabsAttribute])
+ return [self tabs];
+
+ return [super accessibilityAttributeValue:attribute];
+}
+
+/**
+ * Returns the selected tab (the mozAccessible)
+ */
+- (id)value
+{
+ mozAccessible* nativeAcc = nil;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ if (Accessible* accTab = accWrap->GetSelectedItem(0)) {
+ accTab->GetNativeInterface((void**)&nativeAcc);
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if (ProxyAccessible* proxyTab = proxy->GetSelectedItem(0)) {
+ nativeAcc = GetNativeFromProxy(proxyTab);
+ }
+ }
+
+ return nativeAcc;
+}
+
+/**
+ * Return the mozAccessibles that are the tabs.
+ */
+- (id)tabs
+{
+ if (mTabs)
+ return mTabs;
+
+ NSArray* children = [self children];
+ NSEnumerator* enumerator = [children objectEnumerator];
+ mTabs = [[NSMutableArray alloc] init];
+
+ id obj;
+ while ((obj = [enumerator nextObject]))
+ if ([obj isTab])
+ [mTabs addObject:obj];
+
+ return mTabs;
+}
+
+- (void)invalidateChildren
+{
+ [super invalidateChildren];
+
+ [mTabs release];
+ mTabs = nil;
+}
+
+@end
+
+@implementation mozPaneAccessible
+
+- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return 0;
+
+ // By default this calls -[[mozAccessible children] count].
+ // Since we don't cache mChildren. This is faster.
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
+ if (accWrap)
+ return accWrap->ChildCount() ? 1 : 0;
+
+ return proxy->ChildrenCount() ? 1 : 0;
+ }
+
+ return [super accessibilityArrayAttributeCount:attribute];
+}
+
+- (NSArray*)children
+{
+ if (![self getGeckoAccessible])
+ return nil;
+
+ nsDeckFrame* deckFrame = do_QueryFrame([self getGeckoAccessible]->GetFrame());
+ nsIFrame* selectedFrame = deckFrame ? deckFrame->GetSelectedBox() : nullptr;
+
+ Accessible* selectedAcc = nullptr;
+ if (selectedFrame) {
+ nsINode* node = selectedFrame->GetContent();
+ selectedAcc = [self getGeckoAccessible]->Document()->GetAccessible(node);
+ }
+
+ if (selectedAcc) {
+ mozAccessible *curNative = GetNativeFromGeckoAccessible(selectedAcc);
+ if (curNative)
+ return [NSArray arrayWithObjects:GetObjectOrRepresentedView(curNative), nil];
+ }
+
+ return nil;
+}
+
+@end
diff --git a/accessible/mac/mozDocAccessible.h b/accessible/mac/mozDocAccessible.h
new file mode 100644
index 0000000000..c381773110
--- /dev/null
+++ b/accessible/mac/mozDocAccessible.h
@@ -0,0 +1,31 @@
+/* -*- Mode: Objective-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/. */
+
+#import <Cocoa/Cocoa.h>
+#import "mozAccessible.h"
+
+// our protocol that we implement (so cocoa widgets can talk to us)
+#import "mozAccessibleProtocol.h"
+
+/*
+ The root accessible. There is one per window.
+ Created by the RootAccessibleWrap.
+*/
+@interface mozRootAccessible : mozAccessible
+{
+ // the mozView that we're representing.
+ // all outside communication goes through the mozView.
+ // in reality, it's just piping all calls to us, and we're
+ // doing its dirty work!
+ //
+ // whenever someone asks who we are (e.g., a child asking
+ // for its parent, or our parent asking for its child), we'll
+ // respond the mozView. it is absolutely necessary for third-
+ // party tools that we do this!
+ //
+ // /hwaara
+ id <mozView, mozAccessible> mParallelView; // weak ref
+}
+@end
diff --git a/accessible/mac/mozDocAccessible.mm b/accessible/mac/mozDocAccessible.mm
new file mode 100644
index 0000000000..4bae81f01c
--- /dev/null
+++ b/accessible/mac/mozDocAccessible.mm
@@ -0,0 +1,111 @@
+/* -*- Mode: Objective-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 "RootAccessibleWrap.h"
+
+#import "mozDocAccessible.h"
+
+#import "mozView.h"
+
+// This must be included last:
+#include "nsObjCExceptions.h"
+
+using namespace mozilla::a11y;
+
+static id <mozAccessible, mozView>
+getNativeViewFromRootAccessible(Accessible* aAccessible)
+{
+ RootAccessibleWrap* root =
+ static_cast<RootAccessibleWrap*>(aAccessible->AsRoot());
+ id <mozAccessible, mozView> nativeView = nil;
+ root->GetNativeWidget ((void**)&nativeView);
+ return nativeView;
+}
+
+#pragma mark -
+
+@implementation mozRootAccessible
+
+- (NSArray*)accessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // if we're expired, we don't support any attributes.
+ if (![self getGeckoAccessible])
+ return [NSArray array];
+
+ // standard attributes that are shared and supported by root accessible (AXMain) elements.
+ static NSMutableArray* attributes = nil;
+
+ if (!attributes) {
+ attributes = [[super accessibilityAttributeNames] mutableCopy];
+ [attributes addObject:NSAccessibilityMainAttribute];
+ [attributes addObject:NSAccessibilityMinimizedAttribute];
+ }
+
+ return attributes;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([attribute isEqualToString:NSAccessibilityMainAttribute])
+ return [NSNumber numberWithBool:[[self window] isMainWindow]];
+ if ([attribute isEqualToString:NSAccessibilityMinimizedAttribute])
+ return [NSNumber numberWithBool:[[self window] isMiniaturized]];
+
+ return [super accessibilityAttributeValue:attribute];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+
+// return the AXParent that our parallell NSView tells us about.
+- (id)parent
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (!mParallelView)
+ mParallelView = (id<mozView, mozAccessible>)[self representedView];
+
+ if (mParallelView)
+ return [mParallelView accessibilityAttributeValue:NSAccessibilityParentAttribute];
+
+ NSAssert(mParallelView, @"we're a root accessible w/o native view?");
+ return [super parent];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)hasRepresentedView
+{
+ return YES;
+}
+
+// this will return our parallell NSView. see mozDocAccessible.h
+- (id)representedView
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (mParallelView)
+ return (id)mParallelView;
+
+ mParallelView = getNativeViewFromRootAccessible ([self getGeckoAccessible]);
+
+ NSAssert(mParallelView, @"can't return root accessible's native parallel view.");
+ return mParallelView;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)isRoot
+{
+ return YES;
+}
+
+@end
diff --git a/accessible/mac/mozHTMLAccessible.h b/accessible/mac/mozHTMLAccessible.h
new file mode 100644
index 0000000000..c70a3c2a25
--- /dev/null
+++ b/accessible/mac/mozHTMLAccessible.h
@@ -0,0 +1,16 @@
+/* -*- Mode: Objective-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/. */
+
+#import "mozAccessible.h"
+
+@interface mozHeadingAccessible : mozAccessible
+
+@end
+
+@interface mozLinkAccessible : mozAccessible
+
+@end
diff --git a/accessible/mac/mozHTMLAccessible.mm b/accessible/mac/mozHTMLAccessible.mm
new file mode 100644
index 0000000000..2079a4aa6b
--- /dev/null
+++ b/accessible/mac/mozHTMLAccessible.mm
@@ -0,0 +1,141 @@
+/* -*- Mode: Objective-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/. */
+
+#import "mozHTMLAccessible.h"
+
+#import "Accessible-inl.h"
+#import "HyperTextAccessible.h"
+
+#import "nsCocoaUtils.h"
+
+using namespace mozilla::a11y;
+
+@implementation mozHeadingAccessible
+
+- (NSString*)title
+{
+ nsAutoString title;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ mozilla::ErrorResult rv;
+ // XXX use the flattening API when there are available
+ // see bug 768298
+ accWrap->GetContent()->GetTextContent(title, rv);
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ proxy->Title(title);
+ }
+
+ return nsCocoaUtils::ToNSString(title);
+}
+
+- (id)value
+{
+ uint32_t level = 0;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ level = accWrap->GetLevelInternal();
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ level = proxy->GetLevelInternal();
+ }
+
+ return [NSNumber numberWithInt:level];
+}
+
+@end
+
+@interface mozLinkAccessible ()
+-(NSURL*)url;
+@end
+
+@implementation mozLinkAccessible
+
+- (NSArray*)accessibilityAttributeNames
+{
+ // if we're expired, we don't support any attributes.
+ if (![self getGeckoAccessible] && ![self getProxyAccessible])
+ return [NSArray array];
+
+ static NSMutableArray* attributes = nil;
+
+ if (!attributes) {
+ attributes = [[super accessibilityAttributeNames] mutableCopy];
+ [attributes addObject:NSAccessibilityURLAttribute];
+ }
+
+ return attributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString *)attribute
+{
+ if ([attribute isEqualToString:NSAccessibilityURLAttribute])
+ return [self url];
+
+ return [super accessibilityAttributeValue:attribute];
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ // if we're expired, we don't support any attributes.
+ if (![self getGeckoAccessible] && ![self getProxyAccessible])
+ return [NSArray array];
+
+ static NSArray* actionNames = nil;
+
+ if (!actionNames) {
+ actionNames = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction,
+ nil];
+ }
+
+ return actionNames;
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy) {
+ return;
+ }
+
+ if ([action isEqualToString:NSAccessibilityPressAction]) {
+ if (accWrap) {
+ accWrap->DoAction(0);
+ } else if (proxy) {
+ proxy->DoAction(0);
+ }
+ return;
+ }
+
+ [super accessibilityPerformAction:action];
+
+}
+
+- (NSString*)customDescription
+{
+ return @"";
+}
+
+- (NSString*)value
+{
+ return @"";
+}
+
+- (NSURL*)url
+{
+ nsAutoString value;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ accWrap->Value(value);
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ proxy->Value(value);
+ }
+
+ NSString* urlString = value.IsEmpty() ? nil : nsCocoaUtils::ToNSString(value);
+ if (!urlString)
+ return nil;
+
+ return [NSURL URLWithString:urlString];
+}
+
+@end
diff --git a/accessible/mac/mozTableAccessible.h b/accessible/mac/mozTableAccessible.h
new file mode 100644
index 0000000000..435b5adc57
--- /dev/null
+++ b/accessible/mac/mozTableAccessible.h
@@ -0,0 +1,28 @@
+/* -*- Mode: Objective-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/. */
+
+#import "mozAccessible.h"
+
+@interface mozTablePartAccessible : mozAccessible
+- (BOOL)isLayoutTablePart;
+- (NSString*)role;
+@end
+
+@interface mozTableAccessible : mozTablePartAccessible
+- (NSArray*)additionalAccessibilityAttributeNames;
+- (id)accessibilityAttributeValue:(NSString*)attribute;
+@end
+
+@interface mozTableRowAccessible : mozTablePartAccessible
+- (NSArray*)additionalAccessibilityAttributeNames;
+- (id)accessibilityAttributeValue:(NSString*)attribute;
+@end
+
+@interface mozTableCellAccessible : mozTablePartAccessible
+- (NSArray*)additionalAccessibilityAttributeNames;
+- (id)accessibilityAttributeValue:(NSString*)attribute;
+@end
diff --git a/accessible/mac/mozTableAccessible.mm b/accessible/mac/mozTableAccessible.mm
new file mode 100644
index 0000000000..6ad157b9f0
--- /dev/null
+++ b/accessible/mac/mozTableAccessible.mm
@@ -0,0 +1,281 @@
+/* -*- Mode: Objective-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/. */
+
+#import "Accessible-inl.h"
+#import "mozTableAccessible.h"
+#import "TableAccessible.h"
+#import "TableCellAccessible.h"
+#import "nsCocoaUtils.h"
+
+using namespace mozilla::a11y;
+
+// convert an array of Gecko accessibles to an NSArray of native accessibles
+static inline NSMutableArray*
+ConvertToNSArray(nsTArray<Accessible*>& aArray)
+{
+ NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+
+ // iterate through the list, and get each native accessible.
+ size_t totalCount = aArray.Length();
+ for (size_t i = 0; i < totalCount; i++) {
+ Accessible* curAccessible = aArray.ElementAt(i);
+ mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible);
+ if (curNative)
+ [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+ }
+
+ return nativeArray;
+}
+
+// convert an array of Gecko proxy accessibles to an NSArray of native accessibles
+static inline NSMutableArray*
+ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
+{
+ NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+
+ // iterate through the list, and get each native accessible.
+ size_t totalCount = aArray.Length();
+ for (size_t i = 0; i < totalCount; i++) {
+ ProxyAccessible* curAccessible = aArray.ElementAt(i);
+ mozAccessible* curNative = GetNativeFromProxy(curAccessible);
+ if (curNative)
+ [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+ }
+
+ return nativeArray;
+}
+
+@implementation mozTablePartAccessible
+- (BOOL)isLayoutTablePart;
+{
+ if (Accessible* accWrap = [self getGeckoAccessible]) {
+ while (accWrap) {
+ if (accWrap->IsTable()) {
+ return accWrap->AsTable()->IsProbablyLayoutTable();
+ }
+ accWrap = accWrap->Parent();
+ }
+ return false;
+ }
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ while (proxy) {
+ if (proxy->IsTable()) {
+ return proxy->TableIsProbablyForLayout();
+ }
+ proxy = proxy->Parent();
+ }
+ }
+
+ return false;
+}
+
+- (NSString*)role
+{
+ return [self isLayoutTablePart] ? NSAccessibilityGroupRole : [super role];
+}
+@end
+
+@implementation mozTableAccessible
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
+ if ([self isLayoutTablePart]) {
+ return additionalAttributes;
+ }
+
+ static NSArray* tableAttrs = nil;
+ if (!tableAttrs) {
+ NSMutableArray* tempArray = [NSMutableArray new];
+ [tempArray addObject:NSAccessibilityRowCountAttribute];
+ [tempArray addObject:NSAccessibilityColumnCountAttribute];
+ [tempArray addObject:NSAccessibilityRowsAttribute];
+ tableAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ return [additionalAttributes arrayByAddingObjectsFromArray:tableAttrs];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ TableAccessible* table = accWrap->AsTable();
+ if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
+ return @(table->RowCount());
+ if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
+ return @(table->ColCount());
+ if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
+ // Create a new array with the list of table rows.
+ NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+ uint32_t totalCount = accWrap->ChildCount();
+ for (uint32_t i = 0; i < totalCount; i++) {
+ if (accWrap->GetChildAt(i)->IsTableRow()) {
+ mozAccessible* curNative =
+ GetNativeFromGeckoAccessible(accWrap->GetChildAt(i));
+ if (curNative)
+ [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+ }
+ }
+ return nativeArray;
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
+ return @(proxy->TableRowCount());
+ if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
+ return @(proxy->TableColumnCount());
+ if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
+ // Create a new array with the list of table rows.
+ NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
+ uint32_t totalCount = proxy->ChildrenCount();
+ for (uint32_t i = 0; i < totalCount; i++) {
+ if (proxy->ChildAt(i)->IsTableRow()) {
+ mozAccessible* curNative =
+ GetNativeFromProxy(proxy->ChildAt(i));
+ if (curNative)
+ [nativeArray addObject:GetObjectOrRepresentedView(curNative)];
+ }
+ }
+ return nativeArray;
+ }
+ }
+
+ return [super accessibilityAttributeValue:attribute];
+}
+@end
+
+@implementation mozTableRowAccessible
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
+ if ([self isLayoutTablePart]) {
+ return additionalAttributes;
+ }
+
+ static NSArray* tableRowAttrs = nil;
+ if (!tableRowAttrs) {
+ NSMutableArray* tempArray = [NSMutableArray new];
+ [tempArray addObject:NSAccessibilityIndexAttribute];
+ tableRowAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ return [additionalAttributes arrayByAddingObjectsFromArray:tableRowAttrs];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
+ // Count the number of rows before that one to obtain the row index.
+ uint32_t index = 0;
+ Accessible* parent = accWrap->Parent();
+ if (parent) {
+ for (int32_t i = accWrap->IndexInParent() - 1; i >= 0; i--) {
+ if (parent->GetChildAt(i)->IsTableRow()) {
+ index++;
+ }
+ }
+ }
+ return [NSNumber numberWithUnsignedInteger:index];
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
+ // Count the number of rows before that one to obtain the row index.
+ uint32_t index = 0;
+ ProxyAccessible* parent = proxy->Parent();
+ if (parent) {
+ for (int32_t i = proxy->IndexInParent() - 1; i >= 0; i--) {
+ if (parent->ChildAt(i)->IsTableRow()) {
+ index++;
+ }
+ }
+ }
+ return [NSNumber numberWithUnsignedInteger:index];
+ }
+ }
+
+ return [super accessibilityAttributeValue:attribute];
+}
+@end
+
+@implementation mozTableCellAccessible
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
+ if ([self isLayoutTablePart]) {
+ return additionalAttributes;
+ }
+
+ static NSArray* tableCellAttrs = nil;
+ if (!tableCellAttrs) {
+ NSMutableArray* tempArray = [NSMutableArray new];
+ [tempArray addObject:NSAccessibilityRowIndexRangeAttribute];
+ [tempArray addObject:NSAccessibilityColumnIndexRangeAttribute];
+ [tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
+ [tempArray addObject:NSAccessibilityColumnHeaderUIElementsAttribute];
+ tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
+ [tempArray release];
+ }
+
+ return [additionalAttributes arrayByAddingObjectsFromArray:tableCellAttrs];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ TableCellAccessible* cell = accWrap->AsTableCell();
+ if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
+ return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(),
+ cell->RowExtent())];
+ if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
+ return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(),
+ cell->ColExtent())];
+ if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
+ AutoTArray<Accessible*, 10> headerCells;
+ cell->RowHeaderCells(&headerCells);
+ return ConvertToNSArray(headerCells);
+ }
+ if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
+ AutoTArray<Accessible*, 10> headerCells;
+ cell->ColHeaderCells(&headerCells);
+ return ConvertToNSArray(headerCells);
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
+ return [NSValue valueWithRange:NSMakeRange(proxy->RowIdx(),
+ proxy->RowExtent())];
+ if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
+ return [NSValue valueWithRange:NSMakeRange(proxy->ColIdx(),
+ proxy->ColExtent())];
+ if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
+ nsTArray<ProxyAccessible*> headerCells;
+ proxy->RowHeaderCells(&headerCells);
+ return ConvertToNSArray(headerCells);
+ }
+ if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
+ nsTArray<ProxyAccessible*> headerCells;
+ proxy->ColHeaderCells(&headerCells);
+ return ConvertToNSArray(headerCells);
+ }
+ }
+
+ return [super accessibilityAttributeValue:attribute];
+}
+@end
diff --git a/accessible/mac/mozTextAccessible.h b/accessible/mac/mozTextAccessible.h
new file mode 100644
index 0000000000..8bc23ae8d5
--- /dev/null
+++ b/accessible/mac/mozTextAccessible.h
@@ -0,0 +1,17 @@
+/* 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/. */
+
+#import "mozAccessible.h"
+
+#import "HyperTextAccessible.h"
+
+@interface mozTextAccessible : mozAccessible
+{
+}
+@end
+
+@interface mozTextLeafAccessible : mozAccessible
+{
+}
+@end
diff --git a/accessible/mac/mozTextAccessible.mm b/accessible/mac/mozTextAccessible.mm
new file mode 100644
index 0000000000..1f433b802e
--- /dev/null
+++ b/accessible/mac/mozTextAccessible.mm
@@ -0,0 +1,627 @@
+/* -*- Mode: Objective-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 "Accessible-inl.h"
+#include "HyperTextAccessible-inl.h"
+#include "TextLeafAccessible.h"
+
+#include "nsCocoaUtils.h"
+#include "nsObjCExceptions.h"
+
+#import "mozTextAccessible.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+inline bool
+ToNSRange(id aValue, NSRange* aRange)
+{
+ NS_PRECONDITION(aRange, "aRange is nil");
+
+ if ([aValue isKindOfClass:[NSValue class]] &&
+ strcmp([(NSValue*)aValue objCType], @encode(NSRange)) == 0) {
+ *aRange = [aValue rangeValue];
+ return true;
+ }
+
+ return false;
+}
+
+inline NSString*
+ToNSString(id aValue)
+{
+ if ([aValue isKindOfClass:[NSString class]]) {
+ return aValue;
+ }
+
+ return nil;
+}
+
+@interface mozTextAccessible ()
+- (NSString*)subrole;
+- (NSString*)selectedText;
+- (NSValue*)selectedTextRange;
+- (NSValue*)visibleCharacterRange;
+- (long)textLength;
+- (BOOL)isReadOnly;
+- (NSNumber*)caretLineNumber;
+- (void)setText:(NSString*)newText;
+- (NSString*)text;
+- (NSString*)stringFromRange:(NSRange*)range;
+@end
+
+@implementation mozTextAccessible
+
+- (BOOL)accessibilityIsIgnored
+{
+ return ![self getGeckoAccessible] && ![self getProxyAccessible];
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ static NSMutableArray* supportedAttributes = nil;
+ if (!supportedAttributes) {
+ // text-specific attributes to supplement the standard one
+ supportedAttributes = [[NSMutableArray alloc] initWithObjects:
+ NSAccessibilitySelectedTextAttribute, // required
+ NSAccessibilitySelectedTextRangeAttribute, // required
+ NSAccessibilityNumberOfCharactersAttribute, // required
+ NSAccessibilityVisibleCharacterRangeAttribute, // required
+ NSAccessibilityInsertionPointLineNumberAttribute,
+ @"AXRequired",
+ @"AXInvalid",
+ nil
+ ];
+ [supportedAttributes addObjectsFromArray:[super accessibilityAttributeNames]];
+ }
+ return supportedAttributes;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
+ return [NSNumber numberWithInt:[self textLength]];
+
+ if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute])
+ return [self caretLineNumber];
+
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
+ return [self selectedTextRange];
+
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
+ return [self selectedText];
+
+ if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
+ return @"";
+
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+ // Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
+ // object's AXSelectedText attribute. See bug 674612 for details.
+ // Also if there is no selected text, we return the full text.
+ // See bug 369710 for details.
+ if ([[self role] isEqualToString:NSAccessibilityStaticTextRole]) {
+ NSString* selectedText = [self selectedText];
+ return (selectedText && [selectedText length]) ? selectedText : [self text];
+ }
+
+ return [self text];
+ }
+
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ if ([attribute isEqualToString:@"AXRequired"]) {
+ return [NSNumber numberWithBool:!!(accWrap->State() & states::REQUIRED)];
+ }
+
+ if ([attribute isEqualToString:@"AXInvalid"]) {
+ return [NSNumber numberWithBool:!!(accWrap->State() & states::INVALID)];
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if ([attribute isEqualToString:@"AXRequired"]) {
+ return [NSNumber numberWithBool:!!(proxy->State() & states::REQUIRED)];
+ }
+
+ if ([attribute isEqualToString:@"AXInvalid"]) {
+ return [NSNumber numberWithBool:!!(proxy->State() & states::INVALID)];
+ }
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
+ return [self visibleCharacterRange];
+
+ // let mozAccessible handle all other attributes
+ return [super accessibilityAttributeValue:attribute];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSArray*)accessibilityParameterizedAttributeNames
+{
+ static NSArray* supportedParametrizedAttributes = nil;
+ // text specific parametrized attributes
+ if (!supportedParametrizedAttributes) {
+ supportedParametrizedAttributes = [[NSArray alloc] initWithObjects:
+ NSAccessibilityStringForRangeParameterizedAttribute,
+ NSAccessibilityLineForIndexParameterizedAttribute,
+ NSAccessibilityRangeForLineParameterizedAttribute,
+ NSAccessibilityAttributedStringForRangeParameterizedAttribute,
+ NSAccessibilityBoundsForRangeParameterizedAttribute,
+#if DEBUG
+ NSAccessibilityRangeForPositionParameterizedAttribute,
+ NSAccessibilityRangeForIndexParameterizedAttribute,
+ NSAccessibilityRTFForRangeParameterizedAttribute,
+ NSAccessibilityStyleRangeForIndexParameterizedAttribute,
+#endif
+ nil
+ ];
+ }
+ return supportedParametrizedAttributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return nil;
+
+ if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
+ NSRange range;
+ if (!ToNSRange(parameter, &range)) {
+#if DEBUG
+ NSLog(@"%@: range not set", attribute);
+#endif
+ return @"";
+ }
+
+ return [self stringFromRange:&range];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute]) {
+ // XXX: actually get the integer value for the line #
+ return [NSValue valueWithRange:NSMakeRange(0, [self textLength])];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
+ NSRange range;
+ if (!ToNSRange(parameter, &range)) {
+#if DEBUG
+ NSLog(@"%@: range not set", attribute);
+#endif
+ return @"";
+ }
+
+ return [[[NSAttributedString alloc] initWithString:[self stringFromRange:&range]] autorelease];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute]) {
+ // XXX: actually return the line #
+ return [NSNumber numberWithInt:0];
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
+ NSRange range;
+ if (!ToNSRange(parameter, &range)) {
+#if DEBUG
+ NSLog(@"%@:no range", attribute);
+#endif
+ return nil;
+ }
+
+ int32_t start = range.location;
+ int32_t end = start + range.length;
+ DesktopIntRect bounds;
+ if (textAcc) {
+ bounds =
+ DesktopIntRect::FromUnknownRect(textAcc->TextBounds(start, end));
+ } else if (proxy) {
+ bounds =
+ DesktopIntRect::FromUnknownRect(proxy->TextBounds(start, end));
+ }
+
+ return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)];
+ }
+
+#if DEBUG
+ NSLog(@"unhandled attribute:%@ forParameter:%@", attribute, parameter);
+#endif
+
+ return nil;
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute])
+ return ![self isReadOnly];
+
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] ||
+ [attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] ||
+ [attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
+ return YES;
+
+ return [super accessibilityIsAttributeSettable:attribute];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return;
+
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
+ [self setText:ToNSString(value)];
+
+ return;
+ }
+
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
+ NSString* stringValue = ToNSString(value);
+ if (!stringValue)
+ return;
+
+ int32_t start = 0, end = 0;
+ nsString text;
+ if (textAcc) {
+ textAcc->SelectionBoundsAt(0, &start, &end);
+ textAcc->DeleteText(start, end - start);
+ nsCocoaUtils::GetStringForNSString(stringValue, text);
+ textAcc->InsertText(text, start);
+ } else if (proxy) {
+ nsString data;
+ proxy->SelectionBoundsAt(0, data, &start, &end);
+ proxy->DeleteText(start, end - start);
+ nsCocoaUtils::GetStringForNSString(stringValue, text);
+ proxy->InsertText(text, start);
+ }
+ }
+
+ if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
+ NSRange range;
+ if (!ToNSRange(value, &range))
+ return;
+
+ if (textAcc) {
+ textAcc->SetSelectionBoundsAt(0, range.location,
+ range.location + range.length);
+ } else if (proxy) {
+ proxy->SetSelectionBoundsAt(0, range.location,
+ range.location + range.length);
+ }
+ return;
+ }
+
+ if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
+ NSRange range;
+ if (!ToNSRange(value, &range))
+ return;
+
+ if (textAcc) {
+ textAcc->ScrollSubstringTo(range.location, range.location + range.length,
+ nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
+ } else if (proxy) {
+ proxy->ScrollSubstringTo(range.location, range.location + range.length,
+ nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
+ }
+ return;
+ }
+
+ [super accessibilitySetValue:value forAttribute:attribute];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (NSString*)subrole
+{
+ if(mRole == roles::PASSWORD_TEXT)
+ return NSAccessibilitySecureTextFieldSubrole;
+
+ return nil;
+}
+
+#pragma mark -
+
+- (BOOL)isReadOnly
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ([[self role] isEqualToString:NSAccessibilityStaticTextRole])
+ return YES;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (textAcc)
+ return (accWrap->State() & states::READONLY) == 0;
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return (proxy->State() & states::READONLY) == 0;
+
+ return NO;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (NSNumber*)caretLineNumber
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+
+ int32_t lineNumber = -1;
+ if (textAcc) {
+ lineNumber = textAcc->CaretLineNumber() - 1;
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ lineNumber = proxy->CaretLineNumber() - 1;
+ }
+
+ return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil;
+}
+
+- (void)setText:(NSString*)aNewString
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+
+ nsString text;
+ nsCocoaUtils::GetStringForNSString(aNewString, text);
+ if (textAcc) {
+ textAcc->ReplaceText(text);
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ proxy->ReplaceText(text);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (NSString*)text
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return nil;
+
+ // A password text field returns an empty value
+ if (mRole == roles::PASSWORD_TEXT)
+ return @"";
+
+ nsAutoString text;
+ if (textAcc) {
+ textAcc->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
+ } else if (proxy) {
+ proxy->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
+ }
+
+ return nsCocoaUtils::ToNSString(text);
+}
+
+- (long)textLength
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return 0;
+
+ return textAcc ? textAcc->CharacterCount() : proxy->CharacterCount();
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
+}
+
+- (long)selectedTextLength
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return 0;
+
+ int32_t start = 0, end = 0;
+ if (textAcc) {
+ textAcc->SelectionBoundsAt(0, &start, &end);
+ } else if (proxy) {
+ nsString data;
+ proxy->SelectionBoundsAt(0, data, &start, &end);
+ }
+ return (end - start);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
+}
+
+- (NSString*)selectedText
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return nil;
+
+ int32_t start = 0, end = 0;
+ nsAutoString selText;
+ if (textAcc) {
+ textAcc->SelectionBoundsAt(0, &start, &end);
+ if (start != end) {
+ textAcc->TextSubstring(start, end, selText);
+ }
+ } else if (proxy) {
+ proxy->SelectionBoundsAt(0, selText, &start, &end);
+ }
+
+ return nsCocoaUtils::ToNSString(selText);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)selectedTextRange
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+
+ int32_t start = 0;
+ int32_t end = 0;
+ int32_t count = 0;
+ if (textAcc) {
+ count = textAcc->SelectionCount();
+ if (count) {
+ textAcc->SelectionBoundsAt(0, &start, &end);
+ return [NSValue valueWithRange:NSMakeRange(start, end - start)];
+ }
+
+ start = textAcc->CaretOffset();
+ return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
+ }
+
+ if (proxy) {
+ count = proxy->SelectionCount();
+ if (count) {
+ nsString data;
+ proxy->SelectionBoundsAt(0, data, &start, &end);
+ return [NSValue valueWithRange:NSMakeRange(start, end - start)];
+ }
+
+ start = proxy->CaretOffset();
+ return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
+ }
+
+ return [NSValue valueWithRange:NSMakeRange(0, 0)];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)visibleCharacterRange
+{
+ // XXX this won't work with Textarea and such as we actually don't give
+ // the visible character range.
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return 0;
+
+ return [NSValue valueWithRange:
+ NSMakeRange(0, textAcc ?
+ textAcc->CharacterCount() : proxy->CharacterCount())];
+}
+
+- (void)valueDidChange
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
+ NSAccessibilityValueChangedNotification);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)selectedTextDidChange
+{
+ NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
+ NSAccessibilitySelectedTextChangedNotification);
+}
+
+- (NSString*)stringFromRange:(NSRange*)range
+{
+ NS_PRECONDITION(range, "no range");
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
+ if (!textAcc && !proxy)
+ return nil;
+
+ nsAutoString text;
+ if (textAcc) {
+ textAcc->TextSubstring(range->location,
+ range->location + range->length, text);
+ } else if (proxy) {
+ proxy->TextSubstring(range->location,
+ range->location + range->length, text);
+ }
+
+ return nsCocoaUtils::ToNSString(text);
+}
+
+@end
+
+@implementation mozTextLeafAccessible
+
+- (NSArray*)accessibilityAttributeNames
+{
+ static NSMutableArray* supportedAttributes = nil;
+ if (!supportedAttributes) {
+ supportedAttributes = [[super accessibilityAttributeNames] mutableCopy];
+ [supportedAttributes removeObject:NSAccessibilityChildrenAttribute];
+ }
+
+ return supportedAttributes;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
+ return @"";
+
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute])
+ return [self text];
+
+ return [super accessibilityAttributeValue:attribute];
+}
+
+- (NSString*)text
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ return nsCocoaUtils::ToNSString(accWrap->AsTextLeaf()->Text());
+ }
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ nsString text;
+ proxy->Text(&text);
+ return nsCocoaUtils::ToNSString(text);
+ }
+
+ return nil;
+}
+
+- (long)textLength
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ return accWrap->AsTextLeaf()->Text().Length();
+ }
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ nsString text;
+ proxy->Text(&text);
+ return text.Length();
+ }
+
+ return 0;
+}
+
+@end
diff --git a/accessible/moz.build b/accessible/moz.build
index edfd88f504..aa1cccb982 100644
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -9,6 +9,8 @@ if 'gtk' in toolkit:
DIRS += ['atk']
elif toolkit == 'windows':
DIRS += ['windows']
+elif toolkit == 'cocoa':
+ DIRS += ['mac']
else:
DIRS += ['other']
diff --git a/accessible/xpcom/moz.build b/accessible/xpcom/moz.build
index 5d1ad16884..d43efd06d0 100644
--- a/accessible/xpcom/moz.build
+++ b/accessible/xpcom/moz.build
@@ -42,6 +42,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'/accessible/windows/msaa',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/xul/moz.build b/accessible/xul/moz.build
index b04c35dc40..54182c0975 100644
--- a/accessible/xul/moz.build
+++ b/accessible/xul/moz.build
@@ -37,6 +37,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'/accessible/windows/ia2',
'/accessible/windows/msaa',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',