blob: eaeaeb8a3c0145a9ad74a0a4adafa58a87a74b0e [file] [log] [blame]
/*
* Copyright (C) 2021 Igalia S.L.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "AccessibilityRootAtspi.h"
#if USE(ATSPI)
#include "AXObjectCache.h"
#include "AccessibilityAtspiEnums.h"
#include "AccessibilityAtspiInterfaces.h"
#include "Document.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameView.h"
#include "Page.h"
#include <glib/gi18n-lib.h>
#include <locale.h>
namespace WebCore {
Ref<AccessibilityRootAtspi> AccessibilityRootAtspi::create(Page& page)
{
return adoptRef(*new AccessibilityRootAtspi(page));
}
AccessibilityRootAtspi::AccessibilityRootAtspi(Page& page)
: m_page(page)
{
}
GDBusInterfaceVTable AccessibilityRootAtspi::s_accessibleFunctions = {
// method_call
[](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData) {
auto& rootObject = *static_cast<AccessibilityRootAtspi*>(userData);
if (!g_strcmp0(methodName, "GetRole"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", Atspi::Role::Filler));
else if (!g_strcmp0(methodName, "GetRoleName"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", "filler"));
else if (!g_strcmp0(methodName, "GetLocalizedRoleName"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(s)", _("filler")));
else if (!g_strcmp0(methodName, "GetState")) {
GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("(au)"));
g_variant_builder_open(&builder, G_VARIANT_TYPE("au"));
g_variant_builder_add(&builder, "u", 0);
g_variant_builder_add(&builder, "u", 0);
g_variant_builder_close(&builder);
g_dbus_method_invocation_return_value(invocation, g_variant_builder_end(&builder));
} else if (!g_strcmp0(methodName, "GetAttributes")) {
GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("(a{ss})"));
g_variant_builder_open(&builder, G_VARIANT_TYPE("a{ss}"));
#if PLATFORM(GTK)
g_variant_builder_add(&builder, "{ss}", "toolkit", "WebKitGTK");
#elif PLATFORM(WPE)
g_variant_builder_add(&builder, "{ss}", "toolkit", "WPEWebKit");
#endif
g_variant_builder_close(&builder);
g_dbus_method_invocation_return_value(invocation, g_variant_builder_end(&builder));
} else if (!g_strcmp0(methodName, "GetApplication"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(@(so))", rootObject.applicationReference()));
else if (!g_strcmp0(methodName, "GetChildAtIndex")) {
int index;
g_variant_get(parameters, "(i)", &index);
if (!index) {
if (auto* child = rootObject.child()) {
g_dbus_method_invocation_return_value(invocation, g_variant_new("(@(so))", child->reference()));
return;
}
}
g_dbus_method_invocation_return_value(invocation, g_variant_new("(@(so))", AccessibilityAtspi::singleton().nullReference()));
} else if (!g_strcmp0(methodName, "GetChildren")) {
GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("a(so)"));
if (auto* child = rootObject.child())
g_variant_builder_add(&builder, "@(so)", child->reference());
g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(so))", &builder));
} else if (!g_strcmp0(methodName, "GetIndexInParent")) {
g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", 0));
} else if (!g_strcmp0(methodName, "GetRelationSet")) {
GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("a(ua(so))"));
g_dbus_method_invocation_return_value(invocation, g_variant_new("(a(ua(so)))", &builder));
} else if (!g_strcmp0(methodName, "GetInterfaces")) {
GVariantBuilder builder = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("as"));
g_variant_builder_add(&builder, "s", webkit_accessible_interface.name);
g_variant_builder_add(&builder, "s", webkit_component_interface.name);
g_dbus_method_invocation_return_value(invocation, g_variant_new("(as)", &builder));
}
},
// get_property
[](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* propertyName, GError** error, gpointer userData) -> GVariant* {
auto& rootObject = *static_cast<AccessibilityRootAtspi*>(userData);
if (!g_strcmp0(propertyName, "Name"))
return g_variant_new_string("");
if (!g_strcmp0(propertyName, "Description"))
return g_variant_new_string("");
if (!g_strcmp0(propertyName, "Locale"))
return g_variant_new_string(setlocale(LC_MESSAGES, nullptr));
if (!g_strcmp0(propertyName, "AccessibleId"))
return g_variant_new_string("");
if (!g_strcmp0(propertyName, "Parent"))
return rootObject.parentReference();
if (!g_strcmp0(propertyName, "ChildCount"))
return g_variant_new_int32(rootObject.child() ? 1 : 0);
g_set_error(error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Unknown property '%s'", propertyName);
return nullptr;
},
// set_property,
nullptr,
// padding
{ nullptr }
};
GDBusInterfaceVTable AccessibilityRootAtspi::s_socketFunctions = {
// method_call
[](GDBusConnection*, const gchar* sender, const gchar*, const gchar*, const gchar* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData) {
auto& rootObject = *static_cast<AccessibilityRootAtspi*>(userData);
if (!g_strcmp0(methodName, "Embedded")) {
const char* path;
g_variant_get(parameters, "(&s)", &path);
rootObject.embedded(sender, path);
g_dbus_method_invocation_return_value(invocation, nullptr);
}
},
// get_property
nullptr,
// set_property,
nullptr,
// padding
{ nullptr }
};
void AccessibilityRootAtspi::registerObject(CompletionHandler<void(const String&)>&& completionHandler)
{
if (m_page)
m_page->setAccessibilityRootObject(this);
Vector<std::pair<GDBusInterfaceInfo*, GDBusInterfaceVTable*>> interfaces;
interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_accessible_interface), &s_accessibleFunctions });
interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_socket_interface), &s_socketFunctions });
interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_component_interface), &s_componentFunctions });
AccessibilityAtspi::singleton().registerRoot(*this, WTFMove(interfaces), WTFMove(completionHandler));
}
void AccessibilityRootAtspi::unregisterObject()
{
AccessibilityAtspi::singleton().unregisterRoot(*this);
if (m_page)
m_page->setAccessibilityRootObject(nullptr);
}
void AccessibilityRootAtspi::setPath(String&& path)
{
m_path = WTFMove(path);
}
void AccessibilityRootAtspi::embedded(const char* parentUniqueName, const char* parentPath)
{
m_parentUniqueName = String::fromUTF8(parentUniqueName);
m_parentPath = String::fromUTF8(parentPath);
AccessibilityAtspi::singleton().parentChanged(*this);
}
GVariant* AccessibilityRootAtspi::applicationReference() const
{
if (m_parentUniqueName.isNull())
return AccessibilityAtspi::singleton().nullReference();
return g_variant_new("(so)", m_parentUniqueName.utf8().data(), "/org/a11y/atspi/accessible/root");
}
GVariant* AccessibilityRootAtspi::reference() const
{
return g_variant_new("(so)", AccessibilityAtspi::singleton().uniqueName(), m_path.utf8().data());
}
GVariant* AccessibilityRootAtspi::parentReference() const
{
if (m_parentUniqueName.isNull())
return AccessibilityAtspi::singleton().nullReference();
return g_variant_new("(so)", m_parentUniqueName.utf8().data(), m_parentPath.utf8().data());
}
AccessibilityObjectAtspi* AccessibilityRootAtspi::child() const
{
if (!m_page)
return nullptr;
Frame& frame = m_page->mainFrame();
if (!frame.document())
return nullptr;
AXObjectCache::enableAccessibility();
AXObjectCache* cache = frame.document()->axObjectCache();
if (!cache)
return nullptr;
AXCoreObject* rootObject = cache->rootObject();
return rootObject ? rootObject->wrapper() : nullptr;
}
void AccessibilityRootAtspi::childAdded(AccessibilityObjectAtspi& child)
{
AccessibilityAtspi::singleton().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Added);
}
void AccessibilityRootAtspi::childRemoved(AccessibilityObjectAtspi& child)
{
AccessibilityAtspi::singleton().childrenChanged(*this, child, AccessibilityAtspi::ChildrenChanged::Removed);
}
void AccessibilityRootAtspi::serialize(GVariantBuilder* builder) const
{
g_variant_builder_add(builder, "(so)", AccessibilityAtspi::singleton().uniqueName(), m_path.utf8().data());
g_variant_builder_add(builder, "@(so)", applicationReference());
g_variant_builder_add(builder, "@(so)", parentReference());
g_variant_builder_add(builder, "i", 0);
// Do not set the children count in cache, because child is handled by children-changed signals.
g_variant_builder_add(builder, "i", -1);
GVariantBuilder interfaces = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("as"));
g_variant_builder_add(&interfaces, "s", webkit_accessible_interface.name);
g_variant_builder_add(&interfaces, "s", webkit_component_interface.name);
g_variant_builder_add(builder, "@as", g_variant_new("as", &interfaces));
g_variant_builder_add(builder, "s", "");
g_variant_builder_add(builder, "u", Atspi::Role::Filler);
g_variant_builder_add(builder, "s", "");
GVariantBuilder states = G_VARIANT_BUILDER_INIT(G_VARIANT_TYPE("au"));
g_variant_builder_add(&states, "u", 0);
g_variant_builder_add(&states, "u", 0);
g_variant_builder_add(builder, "@au", g_variant_builder_end(&states));
}
GDBusInterfaceVTable AccessibilityRootAtspi::s_componentFunctions = {
// method_call
[](GDBusConnection*, const gchar*, const gchar*, const gchar*, const gchar* methodName, GVariant* parameters, GDBusMethodInvocation* invocation, gpointer userData) {
auto& rootObject = *static_cast<AccessibilityRootAtspi*>(userData);
if (!g_strcmp0(methodName, "Contains"))
g_dbus_method_invocation_return_error_literal(invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
else if (!g_strcmp0(methodName, "GetAccessibleAtPoint"))
g_dbus_method_invocation_return_error_literal(invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
else if (!g_strcmp0(methodName, "GetExtents")) {
uint32_t coordinateType;
g_variant_get(parameters, "(u)", &coordinateType);
auto rect = rootObject.frameRect(coordinateType);
g_dbus_method_invocation_return_value(invocation, g_variant_new("((iiii))", rect.x(), rect.y(), rect.width(), rect.height()));
} else if (!g_strcmp0(methodName, "GetPosition")) {
uint32_t coordinateType;
g_variant_get(parameters, "(u)", &coordinateType);
auto rect = rootObject.frameRect(coordinateType);
g_dbus_method_invocation_return_value(invocation, g_variant_new("((ii))", rect.x(), rect.y()));
} else if (!g_strcmp0(methodName, "GetSize")) {
auto rect = rootObject.frameRect(Atspi::CoordinateType::ParentCoordinates);
g_dbus_method_invocation_return_value(invocation, g_variant_new("((ii))", rect.width(), rect.height()));
} else if (!g_strcmp0(methodName, "GetLayer"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", Atspi::ComponentLayer::WidgetLayer));
else if (!g_strcmp0(methodName, "GetMDIZOrder"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(n)", 0));
else if (!g_strcmp0(methodName, "GrabFocus"))
g_dbus_method_invocation_return_error_literal(invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
else if (!g_strcmp0(methodName, "GetAlpha"))
g_dbus_method_invocation_return_value(invocation, g_variant_new("(d)", 1.0));
else if ((!g_strcmp0(methodName, "SetExtents")) || !g_strcmp0(methodName, "SetPosition") || !g_strcmp0(methodName, "SetSize") || !g_strcmp0(methodName, "ScrollTo") || !g_strcmp0(methodName, "ScrollToPoint"))
g_dbus_method_invocation_return_error_literal(invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "");
},
// get_property
nullptr,
// set_property,
nullptr,
// padding
{ nullptr }
};
IntRect AccessibilityRootAtspi::frameRect(uint32_t coordinateType) const
{
if (!m_page)
return { };
auto* frameView = m_page->mainFrame().view();
if (!frameView)
return { };
auto frameRect = frameView->frameRect();
switch (coordinateType) {
case Atspi::CoordinateType::ScreenCoordinates:
return frameView->contentsToScreen(frameRect);
case Atspi::CoordinateType::WindowCoordinates:
return frameView->contentsToWindow(frameRect);
case Atspi::CoordinateType::ParentCoordinates:
return frameRect;
}
RELEASE_ASSERT_NOT_REACHED();
}
} // namespace WebCore
#endif // USE(ATSPI)