⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 284675 in webkit


Ignore:
Timestamp:
Oct 22, 2021, 1:51:03 AM (5 years ago)
Author:
Carlos Garcia Campos
Message:

[GTK][a11y] Add implementation of text interface when building with ATSPI
https://bugs.webkit.org/show_bug.cgi?id=230258

Reviewed by Adrian Perez de Castro.

Source/WebCore:

  • SourcesGTK.txt:
  • accessibility/AXObjectCache.cpp:

(WebCore::AXObjectCache::postTextStateChangeNotification):
(WebCore::AXObjectCache::postTextReplacementNotification):
(WebCore::AXObjectCache::postTextReplacementNotificationForTextControl):
(WebCore::AXObjectCache::enqueuePasswordValueChangeNotification):

  • accessibility/AXObjectCache.h:
  • accessibility/AccessibilityObject.cpp:

(WebCore::AccessibilityObject::textIteratorBehaviorForTextRange const):

  • accessibility/AccessibilityObject.h:
  • accessibility/AccessibilityRenderObject.cpp:

(WebCore::AccessibilityRenderObject::indexForVisiblePosition const):

  • accessibility/atspi/AXObjectCacheAtspi.cpp:

(WebCore::AXObjectCache::postTextStateChangePlatformNotification):
(WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl):
(WebCore::AXObjectCache::postTextReplacementPlatformNotification):
(WebCore::AXObjectCache::nodeTextChangePlatformNotification): Deleted.

  • accessibility/atspi/AccessibilityAtspi.cpp:

(WebCore::AccessibilityAtspi::textChanged):
(WebCore::AccessibilityAtspi::textAttributesChanged):
(WebCore::AccessibilityAtspi::textCaretMoved):
(WebCore::AccessibilityAtspi::textSelectionChanged):

  • accessibility/atspi/AccessibilityAtspi.h:
  • accessibility/atspi/AccessibilityAtspiEnums.h:
  • accessibility/atspi/AccessibilityObjectAtspi.cpp:

(WebCore::roleIsTextType):
(WebCore::AccessibilityObjectAtspi::interfacesForObject):
(WebCore::AccessibilityObjectAtspi::path):
(WebCore::AccessibilityObjectAtspi::buildInterfaces const):

  • accessibility/atspi/AccessibilityObjectAtspi.h:
  • accessibility/atspi/AccessibilityObjectTextAtspi.cpp: Added.

(WebCore::AccessibilityObjectAtspi::atspiBoundaryToTextGranularity):
(WebCore::AccessibilityObjectAtspi::atspiGranularityToTextGranularity):
(WebCore::offsetMapping):
(WebCore::UTF16OffsetToUTF8):
(WebCore::UTF8OffsetToUTF16):
(WebCore::AccessibilityObjectAtspi::text const):
(WebCore::AccessibilityObject::getLengthForTextRange const):
(WebCore::AccessibilityObject::allowsTextRanges const):
(WebCore::AccessibilityObjectAtspi::textInserted):
(WebCore::AccessibilityObjectAtspi::textDeleted):
(WebCore::AccessibilityObjectAtspi::boundaryOffset const):
(WebCore::AccessibilityObjectAtspi::textAtOffset const):
(WebCore::AccessibilityObjectAtspi::characterAtOffset const):
(WebCore::AccessibilityObjectAtspi::boundsForRange const):
(WebCore::AccessibilityObjectAtspi::textExtents const):
(WebCore::AccessibilityObjectAtspi::offsetAtPoint const):
(WebCore::AccessibilityObjectAtspi::boundsForSelection const):
(WebCore::AccessibilityObjectAtspi::selectedRange const):
(WebCore::AccessibilityObjectAtspi::selectionBounds const):
(WebCore::AccessibilityObjectAtspi::setSelectedRange):
(WebCore::AccessibilityObjectAtspi::selectRange):
(WebCore::AccessibilityObjectAtspi::selectionChanged):
(WebCore::AccessibilityObjectAtspi::textAttributes const):
(WebCore::AccessibilityObjectAtspi::textAttributesWithUTF8Offset const):
(WebCore::AccessibilityObjectAtspi::textAttributesChanged):
(WebCore::AccessibilityObjectAtspi::scrollToMakeVisible const):
(WebCore::AccessibilityObjectAtspi::scrollToPoint const):

  • editing/atspi/FrameSelectionAtspi.cpp:

(WebCore::FrameSelection::notifyAccessibilityForSelectionChange):

  • html/HTMLTextFormControlElement.cpp:

(WebCore::HTMLTextFormControlElement::setInnerTextValue):

Tools:

Add unit tests for the text interface.

  • TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp:

(AtspiTextRangeDeleter::operator() const):
(AccessibilityTest::startEventMonitor):
(AccessibilityTest::stopEventMonitor):
(AccessibilityTest::findEvent):
(testTextBasic):
(testTextSurrogatePair):
(testTextIterator):
(testTextExtents):
(testTextSelections):
(testTextAttributes):
(testTextStateChanged):
(beforeAll):

Location:
trunk
Files:
1 added
17 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r284674 r284675  
     12021-10-22  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GTK][a11y] Add implementation of text interface when building with ATSPI
     4        https://bugs.webkit.org/show_bug.cgi?id=230258
     5
     6        Reviewed by Adrian Perez de Castro.
     7
     8        * SourcesGTK.txt:
     9        * accessibility/AXObjectCache.cpp:
     10        (WebCore::AXObjectCache::postTextStateChangeNotification):
     11        (WebCore::AXObjectCache::postTextReplacementNotification):
     12        (WebCore::AXObjectCache::postTextReplacementNotificationForTextControl):
     13        (WebCore::AXObjectCache::enqueuePasswordValueChangeNotification):
     14        * accessibility/AXObjectCache.h:
     15        * accessibility/AccessibilityObject.cpp:
     16        (WebCore::AccessibilityObject::textIteratorBehaviorForTextRange const):
     17        * accessibility/AccessibilityObject.h:
     18        * accessibility/AccessibilityRenderObject.cpp:
     19        (WebCore::AccessibilityRenderObject::indexForVisiblePosition const):
     20        * accessibility/atspi/AXObjectCacheAtspi.cpp:
     21        (WebCore::AXObjectCache::postTextStateChangePlatformNotification):
     22        (WebCore::AXObjectCache::postTextReplacementPlatformNotificationForTextControl):
     23        (WebCore::AXObjectCache::postTextReplacementPlatformNotification):
     24        (WebCore::AXObjectCache::nodeTextChangePlatformNotification): Deleted.
     25        * accessibility/atspi/AccessibilityAtspi.cpp:
     26        (WebCore::AccessibilityAtspi::textChanged):
     27        (WebCore::AccessibilityAtspi::textAttributesChanged):
     28        (WebCore::AccessibilityAtspi::textCaretMoved):
     29        (WebCore::AccessibilityAtspi::textSelectionChanged):
     30        * accessibility/atspi/AccessibilityAtspi.h:
     31        * accessibility/atspi/AccessibilityAtspiEnums.h:
     32        * accessibility/atspi/AccessibilityObjectAtspi.cpp:
     33        (WebCore::roleIsTextType):
     34        (WebCore::AccessibilityObjectAtspi::interfacesForObject):
     35        (WebCore::AccessibilityObjectAtspi::path):
     36        (WebCore::AccessibilityObjectAtspi::buildInterfaces const):
     37        * accessibility/atspi/AccessibilityObjectAtspi.h:
     38        * accessibility/atspi/AccessibilityObjectTextAtspi.cpp: Added.
     39        (WebCore::AccessibilityObjectAtspi::atspiBoundaryToTextGranularity):
     40        (WebCore::AccessibilityObjectAtspi::atspiGranularityToTextGranularity):
     41        (WebCore::offsetMapping):
     42        (WebCore::UTF16OffsetToUTF8):
     43        (WebCore::UTF8OffsetToUTF16):
     44        (WebCore::AccessibilityObjectAtspi::text const):
     45        (WebCore::AccessibilityObject::getLengthForTextRange const):
     46        (WebCore::AccessibilityObject::allowsTextRanges const):
     47        (WebCore::AccessibilityObjectAtspi::textInserted):
     48        (WebCore::AccessibilityObjectAtspi::textDeleted):
     49        (WebCore::AccessibilityObjectAtspi::boundaryOffset const):
     50        (WebCore::AccessibilityObjectAtspi::textAtOffset const):
     51        (WebCore::AccessibilityObjectAtspi::characterAtOffset const):
     52        (WebCore::AccessibilityObjectAtspi::boundsForRange const):
     53        (WebCore::AccessibilityObjectAtspi::textExtents const):
     54        (WebCore::AccessibilityObjectAtspi::offsetAtPoint const):
     55        (WebCore::AccessibilityObjectAtspi::boundsForSelection const):
     56        (WebCore::AccessibilityObjectAtspi::selectedRange const):
     57        (WebCore::AccessibilityObjectAtspi::selectionBounds const):
     58        (WebCore::AccessibilityObjectAtspi::setSelectedRange):
     59        (WebCore::AccessibilityObjectAtspi::selectRange):
     60        (WebCore::AccessibilityObjectAtspi::selectionChanged):
     61        (WebCore::AccessibilityObjectAtspi::textAttributes const):
     62        (WebCore::AccessibilityObjectAtspi::textAttributesWithUTF8Offset const):
     63        (WebCore::AccessibilityObjectAtspi::textAttributesChanged):
     64        (WebCore::AccessibilityObjectAtspi::scrollToMakeVisible const):
     65        (WebCore::AccessibilityObjectAtspi::scrollToPoint const):
     66        * editing/atspi/FrameSelectionAtspi.cpp:
     67        (WebCore::FrameSelection::notifyAccessibilityForSelectionChange):
     68        * html/HTMLTextFormControlElement.cpp:
     69        (WebCore::HTMLTextFormControlElement::setInnerTextValue):
     70
    1712021-10-22  Youenn Fablet  <youenn@apple.com>
    272
  • trunk/Source/WebCore/SourcesGTK.txt

    r284367 r284675  
    4343accessibility/atspi/AccessibilityObjectAtspi.cpp
    4444accessibility/atspi/AccessibilityObjectComponentAtspi.cpp
     45accessibility/atspi/AccessibilityObjectTextAtspi.cpp
    4546accessibility/atspi/AccessibilityRootAtspi.cpp
    4647accessibility/atspi/AXObjectCacheAtspi.cpp
  • trunk/Source/WebCore/accessibility/AXObjectCache.cpp

    r284142 r284675  
    14101410        return;
    14111411
    1412 #if PLATFORM(COCOA)
     1412#if PLATFORM(COCOA) || USE(ATSPI)
    14131413    stopCachingComputedObjectAttributes();
    14141414
     
    14291429    stopCachingComputedObjectAttributes();
    14301430
    1431 #if PLATFORM(COCOA)
     1431#if PLATFORM(COCOA) || USE(ATSPI)
    14321432    AccessibilityObject* object = getOrCreate(node);
    14331433    if (object && object->accessibilityIsIgnored()) {
     1434#if PLATFORM(COCOA)
    14341435        if (position.atLastEditingPositionForNode()) {
    14351436            if (AccessibilityObject* nextSibling = object->nextSiblingUnignored(1))
     
    14391440                object = previousSibling;
    14401441        }
     1442#elif USE(ATSPI)
     1443        // ATSPI doesn't expose text nodes, so we need the parent
     1444        // object which is the one implementing the text interface.
     1445        auto* parent = object->parentObjectUnignored();
     1446        if (is<AccessibilityObject>(parent))
     1447            object = downcast<AccessibilityObject>(parent);
     1448#endif
    14411449    }
    14421450
     
    14521460    stopCachingComputedObjectAttributes();
    14531461
    1454 #if PLATFORM(COCOA)
     1462#if PLATFORM(COCOA) || USE(ATSPI)
    14551463    if (object) {
    14561464        if (isPasswordFieldOrContainedByPasswordField(object))
     
    14911499
    14921500    AccessibilityObject* object = getOrCreate(node);
    1493 #if PLATFORM(COCOA)
     1501#if PLATFORM(COCOA) || USE(ATSPI)
    14941502    if (object) {
    14951503        if (enqueuePasswordValueChangeNotification(object))
     
    15261534
    15271535    AccessibilityObject* object = getOrCreate(node);
    1528 #if PLATFORM(COCOA)
     1536#if PLATFORM(COCOA) || USE(ATSPI)
    15291537    if (object) {
    15301538        if (enqueuePasswordValueChangeNotification(object))
     
    15451553
    15461554    AccessibilityObject* object = getOrCreate(&textControl);
    1547 #if PLATFORM(COCOA)
     1555#if PLATFORM(COCOA) || USE(ATSPI)
    15481556    if (object) {
    15491557        if (enqueuePasswordValueChangeNotification(object))
     
    15611569bool AXObjectCache::enqueuePasswordValueChangeNotification(AccessibilityObject* object)
    15621570{
     1571#if PLATFORM(COCOA)
    15631572    if (!isPasswordFieldOrContainedByPasswordField(object))
    15641573        return false;
     
    15761585
    15771586    return true;
     1587#else
     1588    UNUSED_PARAM(object);
     1589    return false;
     1590#endif
    15781591}
    15791592
     
    34643477}
    34653478
    3466 #if !PLATFORM(COCOA)
     3479#if !PLATFORM(COCOA) && !USE(ATSPI)
    34673480AXTextChange AXObjectCache::textChangeForEditType(AXTextEditType type)
    34683481{
  • trunk/Source/WebCore/accessibility/AXObjectCache.h

    r284080 r284675  
    399399    void platformPerformDeferredCacheUpdate();
    400400
    401 #if PLATFORM(COCOA)
     401#if PLATFORM(COCOA) || USE(ATSPI)
    402402    void postTextStateChangePlatformNotification(AXCoreObject*, const AXTextStateChangeIntent&, const VisibleSelection&);
    403403    void postTextStateChangePlatformNotification(AccessibilityObject*, AXTextEditType, const String&, const VisiblePosition&);
  • trunk/Source/WebCore/accessibility/AccessibilityObject.cpp

    r284606 r284675  
    32743274{
    32753275    TextIteratorBehaviors behaviors { TextIteratorBehavior::IgnoresStyleVisibility };
    3276    
    3277 #if USE(ATK)
     3276
     3277#if USE(ATK) || USE(ATSPI)
    32783278    // We need to emit replaced elements for GTK, and present
    32793279    // them with the 'object replacement character' (0xFFFC).
  • trunk/Source/WebCore/accessibility/AccessibilityObject.h

    r284529 r284675  
    834834#endif
    835835
    836 #if !(ENABLE(ACCESSIBILITY) && USE(ATK))
     836#if !(ENABLE(ACCESSIBILITY) && (USE(ATK) || USE(ATSPI)))
    837837inline bool AccessibilityObject::allowsTextRanges() const { return true; }
    838838inline unsigned AccessibilityObject::getLengthForTextRange() const { return text().length(); }
  • trunk/Source/WebCore/accessibility/AccessibilityRenderObject.cpp

    r284612 r284675  
    21612161        return 0;
    21622162
    2163 #if USE(ATK)
     2163#if USE(ATK) || USE(ATSPI)
    21642164    // We need to consider replaced elements for GTK, as they will be
    21652165    // presented with the 'object replacement character' (0xFFFC).
  • trunk/Source/WebCore/accessibility/atspi/AXObjectCacheAtspi.cpp

    r283361 r284675  
    2222
    2323#if ENABLE(ACCESSIBILITY) && USE(ATSPI)
     24#include "AXTextStateChangeIntent.h"
    2425#include "AccessibilityObject.h"
    2526#include "AccessibilityObjectAtspi.h"
     
    186187}
    187188
    188 void AXObjectCache::nodeTextChangePlatformNotification(AccessibilityObject* object, AXTextChange textChange, unsigned offset, const String& text)
    189 {
     189void AXObjectCache::postTextStateChangePlatformNotification(AXCoreObject* coreObject, const AXTextStateChangeIntent&, const VisibleSelection& selection)
     190{
     191    RELEASE_ASSERT(isMainThread());
     192    if (!coreObject)
     193        coreObject = rootWebArea();
     194
     195    if (!coreObject)
     196        return;
     197
     198    auto* wrapper = coreObject->wrapper();
     199    if (!wrapper)
     200        return;
     201
     202    wrapper->selectionChanged(selection);
     203}
     204
     205void AXObjectCache::postTextStateChangePlatformNotification(AccessibilityObject* coreObject, AXTextEditType editType, const String& text, const VisiblePosition& position)
     206{
     207    RELEASE_ASSERT(isMainThread());
     208    if (text.isEmpty())
     209        return;
     210
     211    auto* wrapper = coreObject->wrapper();
     212    if (!wrapper)
     213        return;
     214
     215    switch (editType) {
     216    case AXTextEditTypeDelete:
     217    case AXTextEditTypeCut:
     218        wrapper->textDeleted(text, position);
     219        break;
     220    case AXTextEditTypeInsert:
     221    case AXTextEditTypeTyping:
     222    case AXTextEditTypeDictation:
     223    case AXTextEditTypePaste:
     224        wrapper->textInserted(text, position);
     225        break;
     226    case AXTextEditTypeAttributesChange:
     227        wrapper->textAttributesChanged();
     228        break;
     229    case AXTextEditTypeUnknown:
     230        break;
     231    }
     232}
     233
     234void AXObjectCache::postTextReplacementPlatformNotificationForTextControl(AXCoreObject* coreObject, const String& deletedText, const String& insertedText, HTMLTextFormControlElement&)
     235{
     236    RELEASE_ASSERT(isMainThread());
     237    if (!coreObject)
     238        coreObject = rootWebArea();
     239
     240    if (!coreObject)
     241        return;
     242
     243    if (deletedText.isEmpty() && insertedText.isEmpty())
     244        return;
     245
     246    auto* wrapper = coreObject->wrapper();
     247    if (!wrapper)
     248        return;
     249
     250    if (!deletedText.isEmpty())
     251        wrapper->textDeleted(deletedText, coreObject->visiblePositionForIndex(0));
     252    if (!insertedText.isEmpty())
     253        wrapper->textInserted(insertedText, coreObject->visiblePositionForIndex(insertedText.length()));
     254}
     255
     256void AXObjectCache::postTextReplacementPlatformNotification(AXCoreObject* coreObject, AXTextEditType, const String& deletedText, AXTextEditType, const String& insertedText, const VisiblePosition& position)
     257{
     258    RELEASE_ASSERT(isMainThread());
     259    if (!coreObject)
     260        coreObject = rootWebArea();
     261
     262    if (!coreObject)
     263        return;
     264
     265    if (deletedText.isEmpty() && insertedText.isEmpty())
     266        return;
     267
     268    auto* wrapper = coreObject->wrapper();
     269    if (!wrapper)
     270        return;
     271
     272    if (!deletedText.isEmpty())
     273        wrapper->textDeleted(deletedText, position);
     274    if (!insertedText.isEmpty())
     275        wrapper->textInserted(insertedText, position);
    190276}
    191277
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.cpp

    r283361 r284675  
    165165        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "StateChanged",
    166166            g_variant_new("(siiva{sv})", name.data(), value, 0, g_variant_new_string("0"), nullptr), nullptr);
     167    });
     168}
     169
     170void AccessibilityAtspi::textChanged(AccessibilityObjectAtspi& atspiObject, const char* changeType, CString&& text, unsigned offset, unsigned length)
     171{
     172    RELEASE_ASSERT(isMainThread());
     173    m_queue->dispatch([this, atspiObject = Ref { atspiObject }, changeType = CString(changeType), text = WTFMove(text), offset, length] {
     174        if (!m_connection)
     175            return;
     176
     177        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextChanged",
     178            g_variant_new("(siiva{sv})", changeType.data(), offset, length, g_variant_new_string(text.data()), nullptr), nullptr);
     179    });
     180}
     181
     182void AccessibilityAtspi::textAttributesChanged(AccessibilityObjectAtspi& atspiObject)
     183{
     184    RELEASE_ASSERT(isMainThread());
     185    m_queue->dispatch([this, atspiObject = Ref { atspiObject }] {
     186        if (!m_connection)
     187            return;
     188
     189        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextAttributesChanged",
     190            g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
     191    });
     192}
     193
     194void AccessibilityAtspi::textCaretMoved(AccessibilityObjectAtspi& atspiObject, unsigned caretOffset)
     195{
     196    RELEASE_ASSERT(isMainThread());
     197    m_queue->dispatch([this, atspiObject = Ref { atspiObject }, caretOffset] {
     198        if (!m_connection)
     199            return;
     200
     201        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextCaretMoved",
     202            g_variant_new("(siiva{sv})", "", caretOffset, 0, g_variant_new_string(""), nullptr), nullptr);
     203    });
     204}
     205
     206void AccessibilityAtspi::textSelectionChanged(AccessibilityObjectAtspi& atspiObject)
     207{
     208    RELEASE_ASSERT(isMainThread());
     209    m_queue->dispatch([this, atspiObject = Ref { atspiObject }] {
     210        if (!m_connection)
     211            return;
     212
     213        g_dbus_connection_emit_signal(m_connection.get(), nullptr, atspiObject->path().utf8().data(), "org.a11y.atspi.Event.Object", "TextSelectionChanged",
     214            g_variant_new("(siiva{sv})", "", 0, 0, g_variant_new_string(""), nullptr), nullptr);
    167215    });
    168216}
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspi.h

    r283361 r284675  
    5757    void stateChanged(AccessibilityObjectAtspi&, const char*, bool);
    5858
     59    void textChanged(AccessibilityObjectAtspi&, const char*, CString&&, unsigned, unsigned);
     60    void textAttributesChanged(AccessibilityObjectAtspi&);
     61    void textCaretMoved(AccessibilityObjectAtspi&, unsigned);
     62    void textSelectionChanged(AccessibilityObjectAtspi&);
     63
    5964    static const char* localizedRoleName(AccessibilityRole);
    6065
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityAtspiEnums.h

    r284367 r284675  
    259259};
    260260
     261enum TextBoundaryType {
     262    CharBoundary,
     263    WordStartBoundary,
     264    WordEndBoundary,
     265    SentenceStartBoundary,
     266    SentenceEndBoundary,
     267    LineStartBoundary,
     268    LineEndBoundary
     269};
     270
     271enum TextGranularityType {
     272    CharGranularity,
     273    WordGranularity,
     274    SentenceGranularity,
     275    LineGranularity,
     276    ParagraphGranularity
     277};
     278
    261279} // namespace Atspi
    262280} // namespace WebCore
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.cpp

    r284367 r284675  
    2222
    2323#if ENABLE(ACCESSIBILITY) && USE(ATSPI)
     24#include "AXIsolatedObject.h"
    2425#include "AccessibilityAtspiEnums.h"
    2526#include "AccessibilityObjectInterface.h"
     
    2930#include "RenderAncestorIterator.h"
    3031#include "RenderBlock.h"
     32#include "RenderObject.h"
    3133#include <glib/gi18n-lib.h>
    3234#include <wtf/MainThread.h>
     
    4042}
    4143
     44static inline bool roleIsTextType(AccessibilityRole role)
     45{
     46    return role == AccessibilityRole::Paragraph
     47        || role == AccessibilityRole::Heading
     48        || role == AccessibilityRole::Div
     49        || role == AccessibilityRole::Cell
     50        || role == AccessibilityRole::Link
     51        || role == AccessibilityRole::WebCoreLink
     52        || role == AccessibilityRole::ListItem
     53        || role == AccessibilityRole::Pre
     54        || role == AccessibilityRole::GridCell
     55        || role == AccessibilityRole::TextGroup
     56        || role == AccessibilityRole::ApplicationTextGroup
     57        || role == AccessibilityRole::ApplicationGroup;
     58}
     59
    4260OptionSet<AccessibilityObjectAtspi::Interface> AccessibilityObjectAtspi::interfacesForObject(AXCoreObject& coreObject)
    4361{
    4462    OptionSet<Interface> interfaces = { Interface::Accessible, Interface::Component };
     63
     64    RenderObject* renderer = coreObject.isAccessibilityRenderObject() ? coreObject.renderer() : nullptr;
     65    if (coreObject.roleValue() == AccessibilityRole::StaticText || coreObject.roleValue() == AccessibilityRole::ColorWell)
     66        interfaces.add(Interface::Text);
     67    else if (coreObject.isTextControl() || coreObject.isNonNativeTextControl())
     68        interfaces.add(Interface::Text);
     69    else if (!coreObject.isWebArea()) {
     70        if (coreObject.roleValue() != AccessibilityRole::Table) {
     71            if ((renderer && renderer->childrenInline()) || roleIsTextType(coreObject.roleValue()) || coreObject.isMathToken())
     72                interfaces.add(Interface::Text);
     73        }
     74
     75        // Add the Text interface for list items whose first accessible child has a text renderer.
     76        if (coreObject.roleValue() == AccessibilityRole::ListItem) {
     77            const auto& children = coreObject.children();
     78            if (!children.isEmpty()) {
     79                auto childInterfaces = interfacesForObject(*children[0]);
     80                if (childInterfaces.contains(Interface::Text))
     81                    interfaces.add(Interface::Text);
     82            }
     83        }
     84    }
    4585
    4686    return interfaces;
     
    428468        if (m_interfaces.contains(Interface::Component))
    429469            interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_component_interface), &s_componentFunctions });
     470        if (m_interfaces.contains(Interface::Text))
     471            interfaces.append({ const_cast<GDBusInterfaceInfo*>(&webkit_text_interface), &s_textFunctions });
    430472        m_path = atspiRoot->atspi().registerObject(*this, WTFMove(interfaces));
    431473    }
     
    10411083    if (m_interfaces.contains(Interface::Component))
    10421084        g_variant_builder_add(builder, "s", webkit_component_interface.name);
     1085    if (m_interfaces.contains(Interface::Text))
     1086        g_variant_builder_add(builder, "s", webkit_text_interface.name);
    10431087}
    10441088
  • trunk/Source/WebCore/accessibility/atspi/AccessibilityObjectAtspi.h

    r284367 r284675  
    4545    enum class Interface : uint8_t {
    4646        Accessible = 1 << 0,
    47         Component = 1 << 1
     47        Component = 1 << 1,
     48        Text = 1 << 2
    4849    };
    4950    const OptionSet<Interface>& interfaces() const { return m_interfaces; }
     
    8384    void scrollToPoint(const IntPoint&, uint32_t) const;
    8485
     86    String text() const;
     87    enum class TextGranularity {
     88        Character,
     89        WordStart,
     90        WordEnd,
     91        SentenceStart,
     92        SentenceEnd,
     93        LineStart,
     94        LineEnd,
     95        Paragraph
     96    };
     97    IntPoint boundaryOffset(unsigned, TextGranularity) const;
     98    IntRect boundsForRange(unsigned, unsigned, uint32_t) const;
     99    struct TextAttributes {
     100        HashMap<String, String> attributes;
     101        int startOffset;
     102        int endOffset;
     103    };
     104    TextAttributes textAttributes(std::optional<unsigned> = std::nullopt, bool = false) const;
     105    IntPoint selectedRange() const;
     106    void setSelectedRange(unsigned, unsigned);
     107    void textInserted(const String&, const VisiblePosition&);
     108    void textDeleted(const String&, const VisiblePosition&);
     109    void textAttributesChanged();
     110    void selectionChanged(const VisibleSelection&);
     111
    85112private:
    86113    explicit AccessibilityObjectAtspi(AXCoreObject*);
     
    100127    float opacity() const;
    101128
     129    static TextGranularity atspiBoundaryToTextGranularity(uint32_t);
     130    static TextGranularity atspiGranularityToTextGranularity(uint32_t);
     131    CString text(int, int) const;
     132    CString textAtOffset(int, TextGranularity, int&, int&) const;
     133    int characterAtOffset(int) const;
     134    IntRect textExtents(int, int, uint32_t) const;
     135    int offsetAtPoint(const IntPoint&, uint32_t) const;
     136    IntPoint boundsForSelection(const VisibleSelection&) const;
     137    bool selectionBounds(int&, int&) const;
     138    bool selectRange(int, int);
     139    TextAttributes textAttributesWithUTF8Offset(std::optional<int> = std::nullopt, bool = false) const;
     140    bool scrollToMakeVisible(int, int, uint32_t) const;
     141    bool scrollToPoint(int, int, uint32_t, int, int) const;
     142
    102143    static OptionSet<Interface> interfacesForObject(AXCoreObject&);
    103144
    104145    static GDBusInterfaceVTable s_accessibleFunctions;
    105146    static GDBusInterfaceVTable s_componentFunctions;
     147    static GDBusInterfaceVTable s_textFunctions;
    106148
    107149    AXCoreObject* m_axObject { nullptr };
  • trunk/Source/WebCore/editing/atspi/FrameSelectionAtspi.cpp

    r282643 r284675  
    2424
    2525#include "AXObjectCache.h"
     26#include "DocumentInlines.h"
    2627
    2728namespace WebCore {
    2829
    29 void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent&)
     30void FrameSelection::notifyAccessibilityForSelectionChange(const AXTextStateChangeIntent& intent)
    3031{
    3132    if (!AXObjectCache::accessibilityEnabled())
    3233        return;
     34
     35    if (!m_selection.start().isNotNull() || !m_selection.end().isNotNull())
     36        return;
     37
     38    if (AXObjectCache* cache = m_document->existingAXObjectCache())
     39        cache->postTextStateChangeNotification(m_selection.start(), intent, m_selection);
    3340}
    3441
  • trunk/Source/WebCore/html/HTMLTextFormControlElement.cpp

    r284080 r284675  
    604604        }
    605605
    606 #if ENABLE(ACCESSIBILITY) && PLATFORM(COCOA)
     606#if ENABLE(ACCESSIBILITY) && (PLATFORM(COCOA) || USE(ATSPI))
    607607        if (textIsChanged && renderer()) {
    608608            if (AXObjectCache* cache = document().existingAXObjectCache())
  • trunk/Tools/ChangeLog

    r284669 r284675  
     12021-10-22  Carlos Garcia Campos  <cgarcia@igalia.com>
     2
     3        [GTK][a11y] Add implementation of text interface when building with ATSPI
     4        https://bugs.webkit.org/show_bug.cgi?id=230258
     5
     6        Reviewed by Adrian Perez de Castro.
     7
     8        Add unit tests for the text interface.
     9
     10        * TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp:
     11        (AtspiTextRangeDeleter::operator() const):
     12        (AccessibilityTest::startEventMonitor):
     13        (AccessibilityTest::stopEventMonitor):
     14        (AccessibilityTest::findEvent):
     15        (testTextBasic):
     16        (testTextSurrogatePair):
     17        (testTextIterator):
     18        (testTextExtents):
     19        (testTextSelections):
     20        (testTextAttributes):
     21        (testTextStateChanged):
     22        (beforeAll):
     23
    1242021-10-22  Kimmo Kinnunen  <kkinnunen@apple.com>
    225
  • trunk/Tools/TestWebKitAPI/Tests/WebKitGtk/TestWebKitAccessibility.cpp

    r284592 r284675  
    3535};
    3636
     37struct AtspiTextRangeDeleter {
     38    void operator()(AtspiTextRange* range) const
     39    {
     40        g_boxed_free(ATSPI_TYPE_TEXT_RANGE, range);
     41    }
     42};
     43
    3744using UniqueAtspiEvent = std::unique_ptr<AtspiEvent, AtspiEventDeleter>;
     45using UniqueAtspiTextRange = std::unique_ptr<AtspiTextRange, AtspiTextRangeDeleter>;
    3846
    3947class AccessibilityTest : public WebViewTest {
     
    99107    }
    100108
    101     void startEventMonitor(AtspiAccessible* source, const Vector<const char*>& events)
     109    void startEventMonitor(AtspiAccessible* source, Vector<CString>&& events)
    102110    {
    103111        m_eventMonitor.source = source;
     112        m_eventMonitor.eventTypes = WTFMove(events);
    104113        m_eventMonitor.listener = adoptGRef(atspi_event_listener_new([](AtspiEvent* event, gpointer userData) {
    105114            auto* test = static_cast<AccessibilityTest*>(userData);
     
    108117        }, this, nullptr));
    109118
    110         for (const auto* event : events)
    111             atspi_event_listener_register(m_eventMonitor.listener.get(), event, nullptr);
     119        for (const auto& event : m_eventMonitor.eventTypes)
     120            atspi_event_listener_register(m_eventMonitor.listener.get(), event.data(), nullptr);
    112121    }
    113122
     
    120129
    121130        auto events = WTFMove(m_eventMonitor.events);
    122         m_eventMonitor = { nullptr, { }, nullptr };
     131        for (const auto& event : m_eventMonitor.eventTypes)
     132            atspi_event_listener_deregister(m_eventMonitor.listener.get(), event.data(), nullptr);
     133        m_eventMonitor = { nullptr, { }, { }, nullptr };
    123134        return events;
     135    }
     136
     137    static AtspiEvent* findEvent(const Vector<UniqueAtspiEvent>& events, const char* eventType)
     138    {
     139        for (const auto& event : events) {
     140            if (!g_strcmp0(event->type, eventType))
     141                return event.get();
     142        }
     143
     144        return nullptr;
    124145    }
    125146
     
    137158    struct {
    138159        GRefPtr<AtspiEventListener> listener;
     160        Vector<CString> eventTypes;
    139161        Vector<UniqueAtspiEvent> events;
    140162        AtspiAccessible* source { nullptr };
     
    806828#endif
    807829
     830static void testTextBasic(AccessibilityTest* test, gconstpointer)
     831{
     832    test->showInWindow();
     833    test->loadHtml(
     834        "<html>"
     835        "  <body>"
     836        "    <p>This is a line of text</p>"
     837        "  </body>"
     838        "</html>",
     839        nullptr);
     840    test->waitUntilLoadFinished();
     841
     842    auto testApp = test->findTestApplication();
     843    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     844
     845    auto documentWeb = test->findDocumentWeb(testApp.get());
     846    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     847    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     848
     849    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     850    g_assert_true(ATSPI_IS_TEXT(p.get()));
     851    auto length = atspi_text_get_character_count(ATSPI_TEXT(p.get()), nullptr);
     852    g_assert_cmpint(length, ==, 22);
     853    GUniquePtr<char> text(atspi_text_get_text(ATSPI_TEXT(p.get()), 0, length, nullptr));
     854    g_assert_cmpstr(text.get(), ==, "This is a line of text");
     855    text.reset(atspi_text_get_text(ATSPI_TEXT(p.get()), 0, -1, nullptr));
     856    g_assert_cmpstr(text.get(), ==, "This is a line of text");
     857    text.reset(atspi_text_get_text(ATSPI_TEXT(p.get()), 5, 7, nullptr));
     858    g_assert_cmpstr(text.get(), ==, "is");
     859    text.reset(atspi_text_get_text(ATSPI_TEXT(p.get()), 0, -2, nullptr));
     860    g_assert_cmpstr(text.get(), ==, "");
     861    text.reset(atspi_text_get_text(ATSPI_TEXT(p.get()), -5, 23, nullptr));
     862    g_assert_cmpstr(text.get(), ==, "");
     863}
     864
     865static void testTextSurrogatePair(AccessibilityTest* test, gconstpointer)
     866{
     867#if USE(ATK)
     868    g_test_skip("Surrogate pairs are not correctly handled by WebKit when using ATK");
     869    return;
     870#endif
     871
     872    test->showInWindow();
     873    test->loadHtml(
     874        "<html>"
     875        "  <body>"
     876        "    <p>This contains a &#x1D306; symbol</p>"
     877        "    <input value='This contains a &#x1D306; symbol'/>"
     878        "  </body>"
     879        "</html>",
     880        nullptr);
     881    test->waitUntilLoadFinished();
     882
     883    auto testApp = test->findTestApplication();
     884    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     885
     886    auto documentWeb = test->findDocumentWeb(testApp.get());
     887    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     888    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 2);
     889
     890    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     891    g_assert_true(ATSPI_IS_TEXT(p.get()));
     892    auto length = atspi_text_get_character_count(ATSPI_TEXT(p.get()), nullptr);
     893    g_assert_cmpint(length, ==, 24);
     894    GUniquePtr<char> text(atspi_text_get_text(ATSPI_TEXT(p.get()), 0, length, nullptr));
     895    g_assert_cmpstr(text.get(), ==, "This contains a 𝌆 symbol");
     896    text.reset(atspi_text_get_text(ATSPI_TEXT(p.get()), 16, 17, nullptr));
     897    g_assert_cmpstr(text.get(), ==, "𝌆");
     898
     899    auto section = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 1, nullptr));
     900    g_assert_true(ATSPI_IS_ACCESSIBLE(section.get()));
     901    g_assert_cmpint(atspi_accessible_get_child_count(section.get(), nullptr), ==, 1);
     902    auto input = adoptGRef(atspi_accessible_get_child_at_index(section.get(), 0, nullptr));
     903    g_assert_true(ATSPI_IS_TEXT(input.get()));
     904    length = atspi_text_get_character_count(ATSPI_TEXT(input.get()), nullptr);
     905    g_assert_cmpint(length, ==, 24);
     906    text.reset(atspi_text_get_text(ATSPI_TEXT(input.get()), 0, length, nullptr));
     907    g_assert_cmpstr(text.get(), ==, "This contains a 𝌆 symbol");
     908    text.reset(atspi_text_get_text(ATSPI_TEXT(input.get()), 16, 17, nullptr));
     909    g_assert_cmpstr(text.get(), ==, "𝌆");
     910}
     911
     912static void testTextIterator(AccessibilityTest* test, gconstpointer)
     913{
     914    test->showInWindow(800, 600);
     915    test->loadHtml(
     916        "<html>"
     917        "  <body>"
     918        "    <p>Text of first sentence.   This is the second sentence.<br>And this is the next paragraph.</p>"
     919        "  </body>"
     920        "</html>",
     921        nullptr);
     922    test->waitUntilLoadFinished();
     923
     924    auto testApp = test->findTestApplication();
     925    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     926
     927    auto documentWeb = test->findDocumentWeb(testApp.get());
     928    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     929    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     930
     931    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     932    g_assert_true(ATSPI_IS_TEXT(p.get()));
     933    auto length = atspi_text_get_character_count(ATSPI_TEXT(p.get()), nullptr);
     934    g_assert_cmpint(length, ==, 84);
     935    GUniquePtr<char> text(atspi_text_get_text(ATSPI_TEXT(p.get()), 0, length, nullptr));
     936    g_assert_cmpstr(text.get(), ==, "Text of first sentence. This is the second sentence.\nAnd this is the next paragraph.");
     937
     938    // Character granularity.
     939    UniqueAtspiTextRange range(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 0, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     940    g_assert_cmpstr(range->content, ==, "T");
     941    g_assert_cmpint(range->start_offset, ==, 0);
     942    g_assert_cmpint(range->end_offset, ==, 1);
     943    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 83, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     944    g_assert_cmpstr(range->content, ==, ".");
     945    g_assert_cmpint(range->start_offset, ==, 83);
     946    g_assert_cmpint(range->end_offset, ==, 84);
     947    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 84, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     948    g_assert_cmpstr(range->content, ==, "");
     949    g_assert_cmpint(range->start_offset, ==, 84);
     950    g_assert_cmpint(range->end_offset, ==, 84);
     951    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 85, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     952    g_assert_cmpstr(range->content, ==, "");
     953#if USE(ATSPI)
     954    g_assert_cmpint(range->start_offset, ==, 0);
     955    g_assert_cmpint(range->end_offset, ==, 0);
     956#else
     957    // FIXME: ATK returns wrong offsets in this case.
     958    g_assert_cmpint(range->start_offset, ==, 84);
     959    g_assert_cmpint(range->end_offset, ==, 84);
     960#endif
     961    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), -1, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     962    g_assert_cmpstr(range->content, ==, "");
     963    g_assert_cmpint(range->start_offset, ==, 0);
     964    g_assert_cmpint(range->end_offset, ==, 0);
     965
     966    // Word granularity.
     967    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 0, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     968    g_assert_cmpstr(range->content, ==, "Text ");
     969    g_assert_cmpint(range->start_offset, ==, 0);
     970    g_assert_cmpint(range->end_offset, ==, 5);
     971    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 4, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     972    g_assert_cmpstr(range->content, ==, "Text ");
     973    g_assert_cmpint(range->start_offset, ==, 0);
     974    g_assert_cmpint(range->end_offset, ==, 5);
     975    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 5, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     976    g_assert_cmpstr(range->content, ==, "of ");
     977    g_assert_cmpint(range->start_offset, ==, 5);
     978    g_assert_cmpint(range->end_offset, ==, 8);
     979    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 40, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     980    g_assert_cmpstr(range->content, ==, "second ");
     981    g_assert_cmpint(range->start_offset, ==, 36);
     982    g_assert_cmpint(range->end_offset, ==, 43);
     983    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 16, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     984    g_assert_cmpstr(range->content, ==, "sentence. ");
     985    g_assert_cmpint(range->start_offset, ==, 14);
     986    g_assert_cmpint(range->end_offset, ==, 24);
     987    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 74, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     988    g_assert_cmpstr(range->content, ==, "paragraph.");
     989    g_assert_cmpint(range->start_offset, ==, 74);
     990    g_assert_cmpint(range->end_offset, ==, 84);
     991    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 83, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     992    g_assert_cmpstr(range->content, ==, "paragraph.");
     993    g_assert_cmpint(range->start_offset, ==, 74);
     994    g_assert_cmpint(range->end_offset, ==, 84);
     995    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 80, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     996    g_assert_cmpstr(range->content, ==, "paragraph.");
     997    g_assert_cmpint(range->start_offset, ==, 74);
     998    g_assert_cmpint(range->end_offset, ==, 84);
     999
     1000    // Sentence granularity.
     1001    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 0, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1002    g_assert_cmpstr(range->content, ==, "Text of first sentence. ");
     1003    g_assert_cmpint(range->start_offset, ==, 0);
     1004    g_assert_cmpint(range->end_offset, ==, 24);
     1005    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 23, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1006    g_assert_cmpstr(range->content, ==, "Text of first sentence. ");
     1007    g_assert_cmpint(range->start_offset, ==, 0);
     1008    g_assert_cmpint(range->end_offset, ==, 24);
     1009    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 24, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1010    g_assert_cmpstr(range->content, ==, "This is the second sentence.\n");
     1011    g_assert_cmpint(range->start_offset, ==, 24);
     1012    g_assert_cmpint(range->end_offset, ==, 53);
     1013    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 40, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1014    g_assert_cmpstr(range->content, ==, "This is the second sentence.\n");
     1015    g_assert_cmpint(range->start_offset, ==, 24);
     1016    g_assert_cmpint(range->end_offset, ==, 53);
     1017    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 53, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1018    g_assert_cmpstr(range->content, ==, "And this is the next paragraph.");
     1019    g_assert_cmpint(range->start_offset, ==, 53);
     1020    g_assert_cmpint(range->end_offset, ==, 84);
     1021    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 83, ATSPI_TEXT_GRANULARITY_SENTENCE, nullptr));
     1022    g_assert_cmpstr(range->content, ==, "And this is the next paragraph.");
     1023    g_assert_cmpint(range->start_offset, ==, 53);
     1024    g_assert_cmpint(range->end_offset, ==, 84);
     1025
     1026    // Line granularity.
     1027    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 0, ATSPI_TEXT_GRANULARITY_LINE, nullptr));
     1028    g_assert_cmpstr(range->content, ==, "Text of first sentence. This is the second sentence.\n");
     1029    g_assert_cmpint(range->start_offset, ==, 0);
     1030    g_assert_cmpint(range->end_offset, ==, 53);
     1031    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 83, ATSPI_TEXT_GRANULARITY_LINE, nullptr));
     1032    g_assert_cmpstr(range->content, ==, "And this is the next paragraph.");
     1033    g_assert_cmpint(range->start_offset, ==, 53);
     1034    g_assert_cmpint(range->end_offset, ==, 84);
     1035
     1036#if USE(ATSPI) // ATK doesn't implement paragraph granularity.
     1037    // Paragraph granularity.
     1038    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 0, ATSPI_TEXT_GRANULARITY_PARAGRAPH, nullptr));
     1039    g_assert_cmpstr(range->content, ==, "Text of first sentence. This is the second sentence.");
     1040    g_assert_cmpint(range->start_offset, ==, 0);
     1041    g_assert_cmpint(range->end_offset, ==, 52);
     1042    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(p.get()), 83, ATSPI_TEXT_GRANULARITY_PARAGRAPH, nullptr));
     1043    g_assert_cmpstr(range->content, ==, "And this is the next paragraph.");
     1044    g_assert_cmpint(range->start_offset, ==, 53);
     1045    g_assert_cmpint(range->end_offset, ==, 84);
     1046#endif
     1047
     1048    // Using a text control now.
     1049    test->loadHtml(
     1050        "<html>"
     1051        "  <body>"
     1052        "    <input value='Text of input field'/>"
     1053        "  </body>"
     1054        "</html>",
     1055        nullptr);
     1056    test->waitUntilLoadFinished();
     1057
     1058    documentWeb = test->findDocumentWeb(testApp.get());
     1059    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1060    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     1061
     1062    auto section = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1063    g_assert_true(ATSPI_IS_ACCESSIBLE(section.get()));
     1064    g_assert_cmpint(atspi_accessible_get_child_count(section.get(), nullptr), ==, 1);
     1065    auto input = adoptGRef(atspi_accessible_get_child_at_index(section.get(), 0, nullptr));
     1066    g_assert_true(ATSPI_IS_TEXT(input.get()));
     1067    length = atspi_text_get_character_count(ATSPI_TEXT(input.get()), nullptr);
     1068    g_assert_cmpint(length, ==, 19);
     1069    text.reset(atspi_text_get_text(ATSPI_TEXT(input.get()), 0, length, nullptr));
     1070    g_assert_cmpstr(text.get(), ==, "Text of input field");
     1071
     1072    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(input.get()), 0, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     1073    g_assert_cmpstr(range->content, ==, "T");
     1074    g_assert_cmpint(range->start_offset, ==, 0);
     1075    g_assert_cmpint(range->end_offset, ==, 1);
     1076    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(input.get()), 18, ATSPI_TEXT_GRANULARITY_CHAR, nullptr));
     1077    g_assert_cmpstr(range->content, ==, "d");
     1078    g_assert_cmpint(range->start_offset, ==, 18);
     1079    g_assert_cmpint(range->end_offset, ==, 19);
     1080
     1081    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(input.get()), 0, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     1082    g_assert_cmpstr(range->content, ==, "Text ");
     1083    g_assert_cmpint(range->start_offset, ==, 0);
     1084    g_assert_cmpint(range->end_offset, ==, 5);
     1085    range.reset(atspi_text_get_string_at_offset(ATSPI_TEXT(input.get()), 16, ATSPI_TEXT_GRANULARITY_WORD, nullptr));
     1086    g_assert_cmpstr(range->content, ==, "field");
     1087    g_assert_cmpint(range->start_offset, ==, 14);
     1088    g_assert_cmpint(range->end_offset, ==, 19);
     1089}
     1090
     1091static void testTextExtents(AccessibilityTest* test, gconstpointer)
     1092{
     1093    test->showInWindow();
     1094    test->loadHtml(
     1095        "<html>"
     1096        "  <body>"
     1097        "    <p>Text</p>"
     1098        "  </body>"
     1099        "</html>",
     1100        nullptr);
     1101    test->waitUntilLoadFinished();
     1102
     1103    auto testApp = test->findTestApplication();
     1104    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     1105
     1106    auto documentWeb = test->findDocumentWeb(testApp.get());
     1107    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1108    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     1109
     1110    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1111    g_assert_true(ATSPI_IS_TEXT(p.get()));
     1112    GUniquePtr<AtspiRect> firstCharRect(atspi_text_get_character_extents(ATSPI_TEXT(p.get()), 0, ATSPI_COORD_TYPE_WINDOW, nullptr));
     1113    g_assert_nonnull(firstCharRect.get());
     1114    g_assert_cmpuint(firstCharRect->x, >, 0);
     1115    g_assert_cmpuint(firstCharRect->y, >, 0);
     1116    g_assert_cmpuint(firstCharRect->width, >, 0);
     1117    g_assert_cmpuint(firstCharRect->height, >, 0);
     1118    GUniquePtr<AtspiRect> lastCharRect(atspi_text_get_character_extents(ATSPI_TEXT(p.get()), 3, ATSPI_COORD_TYPE_WINDOW, nullptr));
     1119    g_assert_nonnull(lastCharRect.get());
     1120    g_assert_cmpuint(lastCharRect->x, >, firstCharRect->x);
     1121    g_assert_cmpuint(lastCharRect->y, ==, firstCharRect->y);
     1122    g_assert_cmpuint(lastCharRect->width, >, 0);
     1123    g_assert_cmpuint(lastCharRect->height, >, 0);
     1124    GUniquePtr<AtspiRect> rangeRect(atspi_text_get_range_extents(ATSPI_TEXT(p.get()), 0, 4, ATSPI_COORD_TYPE_WINDOW, nullptr));
     1125    g_assert_nonnull(rangeRect.get());
     1126    g_assert_cmpuint(rangeRect->x, ==, firstCharRect->x);
     1127    g_assert_cmpuint(rangeRect->y, ==, firstCharRect->y);
     1128    g_assert_cmpuint(rangeRect->width, >, 0);
     1129    g_assert_cmpuint(rangeRect->height, >, 0);
     1130#if USE(ATSPI) // Offset at point is broken with ATK.
     1131    auto offset = atspi_text_get_offset_at_point(ATSPI_TEXT(p.get()), firstCharRect->x, firstCharRect->y, ATSPI_COORD_TYPE_WINDOW, nullptr);
     1132    g_assert_cmpint(offset, ==, 0);
     1133    offset = atspi_text_get_offset_at_point(ATSPI_TEXT(p.get()), firstCharRect->x + firstCharRect->width, firstCharRect->y, ATSPI_COORD_TYPE_WINDOW, nullptr);
     1134    g_assert_cmpint(offset, ==, 1);
     1135    offset = atspi_text_get_offset_at_point(ATSPI_TEXT(p.get()), lastCharRect->x, lastCharRect->y, ATSPI_COORD_TYPE_WINDOW, nullptr);
     1136    g_assert_cmpint(offset, ==, 3);
     1137    offset = atspi_text_get_offset_at_point(ATSPI_TEXT(p.get()), lastCharRect->x + lastCharRect->width, lastCharRect->y, ATSPI_COORD_TYPE_WINDOW, nullptr);
     1138    g_assert_cmpint(offset, ==, 4);
     1139#endif
     1140}
     1141
     1142static void testTextSelections(AccessibilityTest* test, gconstpointer)
     1143{
     1144    test->showInWindow(800, 600);
     1145    test->loadHtml(
     1146        "<html>"
     1147        "  <body>"
     1148        "    <p>This is a line of text</p>"
     1149        "    <input value='This is text input value'/>"
     1150        "  </body>"
     1151        "</html>",
     1152        nullptr);
     1153    test->waitUntilLoadFinished();
     1154
     1155    auto testApp = test->findTestApplication();
     1156    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     1157
     1158    auto documentWeb = test->findDocumentWeb(testApp.get());
     1159    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1160    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 2);
     1161
     1162    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1163    g_assert_true(ATSPI_IS_TEXT(p.get()));
     1164
     1165    auto selectionCount = atspi_text_get_n_selections(ATSPI_TEXT(p.get()), nullptr);
     1166    g_assert_cmpuint(selectionCount, ==, 0);
     1167    auto caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(p.get()), nullptr);
     1168#if USE(ATSPI)
     1169    g_assert_cmpint(caretOffset, ==, -1);
     1170#else
     1171    g_assert_cmpint(caretOffset, ==, 0);
     1172#endif
     1173
     1174    GUniquePtr<AtspiRange> selection(atspi_text_get_selection(ATSPI_TEXT(p.get()), 0, nullptr));
     1175    g_assert_nonnull(selection.get());
     1176    g_assert_cmpint(selection->start_offset, ==, 0);
     1177    g_assert_cmpint(selection->end_offset, ==, 0);
     1178
     1179    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(p.get()), 0, 5, 14, nullptr));
     1180    selectionCount = atspi_text_get_n_selections(ATSPI_TEXT(p.get()), nullptr);
     1181    g_assert_cmpuint(selectionCount, ==, 1);
     1182    caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(p.get()), nullptr);
     1183    g_assert_cmpint(caretOffset, ==, 14);
     1184    selection.reset(atspi_text_get_selection(ATSPI_TEXT(p.get()), 0, nullptr));
     1185    g_assert_nonnull(selection.get());
     1186    g_assert_cmpint(selection->start_offset, ==, 5);
     1187    g_assert_cmpint(selection->end_offset, ==, 14);
     1188    GUniquePtr<char> text(atspi_text_get_text(ATSPI_TEXT(p.get()), selection->start_offset, selection->end_offset, nullptr));
     1189    g_assert_cmpstr(text.get(), ==, "is a line");
     1190
     1191    g_assert_true(atspi_text_remove_selection(ATSPI_TEXT(p.get()), 0, nullptr));
     1192    selectionCount = atspi_text_get_n_selections(ATSPI_TEXT(p.get()), nullptr);
     1193    g_assert_cmpuint(selectionCount, ==, 0);
     1194    caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(p.get()), nullptr);
     1195    g_assert_cmpint(caretOffset, ==, 14);
     1196    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(p.get()), 0, nullptr));
     1197    caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(p.get()), nullptr);
     1198    g_assert_cmpint(caretOffset, ==, 0);
     1199
     1200    auto section = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 1, nullptr));
     1201    g_assert_true(ATSPI_IS_ACCESSIBLE(section.get()));
     1202    g_assert_cmpint(atspi_accessible_get_child_count(section.get(), nullptr), ==, 1);
     1203    auto input = adoptGRef(atspi_accessible_get_child_at_index(section.get(), 0, nullptr));
     1204    g_assert_true(ATSPI_IS_TEXT(input.get()));
     1205    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(input.get()), 5, nullptr));
     1206    caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(input.get()), nullptr);
     1207    g_assert_cmpint(caretOffset, ==, 5);
     1208    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(input.get()), 0, 5, 12, nullptr));
     1209    selectionCount = atspi_text_get_n_selections(ATSPI_TEXT(input.get()), nullptr);
     1210    g_assert_cmpuint(selectionCount, ==, 1);
     1211    caretOffset = atspi_text_get_caret_offset(ATSPI_TEXT(input.get()), nullptr);
     1212    g_assert_cmpint(caretOffset, ==, 12);
     1213    selection.reset(atspi_text_get_selection(ATSPI_TEXT(input.get()), 0, nullptr));
     1214    g_assert_nonnull(selection.get());
     1215    g_assert_cmpint(selection->start_offset, ==, 5);
     1216    g_assert_cmpint(selection->end_offset, ==, 12);
     1217    text.reset(atspi_text_get_text(ATSPI_TEXT(input.get()), selection->start_offset, selection->end_offset, nullptr));
     1218    g_assert_cmpstr(text.get(), ==, "is text");
     1219}
     1220
     1221static void testTextAttributes(AccessibilityTest* test, gconstpointer)
     1222{
     1223    test->showInWindow(800, 600);
     1224    test->loadHtml(
     1225        "<html>"
     1226        "  <body>"
     1227        "    <p>This is a <b>line</b> of text</p>"
     1228        "  </body>"
     1229        "</html>",
     1230        nullptr);
     1231    test->waitUntilLoadFinished();
     1232
     1233    auto testApp = test->findTestApplication();
     1234    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     1235
     1236    auto documentWeb = test->findDocumentWeb(testApp.get());
     1237    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1238    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     1239
     1240    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1241    g_assert_true(ATSPI_IS_TEXT(p.get()));
     1242
     1243    // Including default attributes.
     1244    int startOffset, endOffset;
     1245    GRefPtr<GHashTable> attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 0, TRUE, &startOffset, &endOffset, nullptr));
     1246    g_assert_nonnull(attributes.get());
     1247    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1248    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "400");
     1249    g_assert_cmpint(startOffset, ==, 0);
     1250    g_assert_cmpint(endOffset, ==, 10);
     1251
     1252    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 12, TRUE, &startOffset, &endOffset, nullptr));
     1253    g_assert_nonnull(attributes.get());
     1254    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1255    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "700");
     1256    GUniquePtr<char> value(atspi_text_get_attribute_value(ATSPI_TEXT(p.get()), 11, const_cast<char*>("weight"), nullptr));
     1257    g_assert_cmpstr(value.get(), ==, "700");
     1258    g_assert_cmpint(startOffset, ==, 10);
     1259    g_assert_cmpint(endOffset, ==, 14);
     1260
     1261    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 16, TRUE, &startOffset, &endOffset, nullptr));
     1262    g_assert_nonnull(attributes.get());
     1263    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1264    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "400");
     1265    g_assert_cmpint(startOffset, ==, 14);
     1266    g_assert_cmpint(endOffset, ==, 22);
     1267
     1268    // Without default attributes.
     1269    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 0, FALSE, &startOffset, &endOffset, nullptr));
     1270    g_assert_nonnull(attributes.get());
     1271    g_assert_cmpuint(g_hash_table_size(attributes.get()), ==, 0);
     1272    g_assert_cmpint(startOffset, ==, 0);
     1273    g_assert_cmpint(endOffset, ==, 10);
     1274
     1275    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 12, FALSE, &startOffset, &endOffset, nullptr));
     1276    g_assert_nonnull(attributes.get());
     1277    g_assert_cmpuint(g_hash_table_size(attributes.get()), ==, 1);
     1278    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "700");
     1279    g_assert_cmpint(startOffset, ==, 10);
     1280    g_assert_cmpint(endOffset, ==, 14);
     1281
     1282    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 16, FALSE, &startOffset, &endOffset, nullptr));
     1283    g_assert_nonnull(attributes.get());
     1284    g_assert_cmpuint(g_hash_table_size(attributes.get()), ==, 0);
     1285    g_assert_cmpint(startOffset, ==, 14);
     1286    g_assert_cmpint(endOffset, ==, 22);
     1287
     1288    // Only default attributes.
     1289    attributes = adoptGRef(atspi_text_get_default_attributes(ATSPI_TEXT(p.get()), nullptr));
     1290    g_assert_nonnull(attributes.get());
     1291    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1292    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "400");
     1293
     1294#if USE(ATSPI)
     1295    // Atspi implementation handles ranges with the same attributes as one run.
     1296    test->loadHtml(
     1297        "<html>"
     1298        "  <body>"
     1299        "    <p>This is a <b>li</b><b>ne</b> of text</p>"
     1300        "  </body>"
     1301        "</html>",
     1302        nullptr);
     1303    test->waitUntilLoadFinished();
     1304
     1305    documentWeb = test->findDocumentWeb(testApp.get());
     1306    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1307    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 1);
     1308
     1309    p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1310    g_assert_true(ATSPI_IS_TEXT(p.get()));
     1311
     1312    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 0, TRUE, &startOffset, &endOffset, nullptr));
     1313    g_assert_nonnull(attributes.get());
     1314    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1315    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "400");
     1316    g_assert_cmpint(startOffset, ==, 0);
     1317    g_assert_cmpint(endOffset, ==, 10);
     1318
     1319    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 12, TRUE, &startOffset, &endOffset, nullptr));
     1320    g_assert_nonnull(attributes.get());
     1321    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1322    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "700");
     1323    g_assert_cmpint(startOffset, ==, 10);
     1324    g_assert_cmpint(endOffset, ==, 14);
     1325
     1326    attributes = adoptGRef(atspi_text_get_attribute_run(ATSPI_TEXT(p.get()), 16, TRUE, &startOffset, &endOffset, nullptr));
     1327    g_assert_nonnull(attributes.get());
     1328    g_assert_cmpuint(g_hash_table_size(attributes.get()), >, 1);
     1329    g_assert_cmpstr(static_cast<const char*>(g_hash_table_lookup(attributes.get(), "weight")), ==, "400");
     1330    g_assert_cmpint(startOffset, ==, 14);
     1331    g_assert_cmpint(endOffset, ==, 22);
     1332
     1333#endif
     1334}
     1335
     1336static void testTextStateChanged(AccessibilityTest* test, gconstpointer)
     1337{
     1338    test->showInWindow(800, 600);
     1339    test->loadHtml(
     1340        "<html>"
     1341        "  <body>"
     1342        "    <p>This is a line of text</p>"
     1343        "    <input value='This is a text field'/>"
     1344        "    <div contenteditable=true>This is content editable</div>"
     1345        "  </body>"
     1346        "</html>",
     1347        nullptr);
     1348    test->waitUntilLoadFinished();
     1349
     1350    auto testApp = test->findTestApplication();
     1351    g_assert_true(ATSPI_IS_ACCESSIBLE(testApp.get()));
     1352
     1353    auto documentWeb = test->findDocumentWeb(testApp.get());
     1354    g_assert_true(ATSPI_IS_ACCESSIBLE(documentWeb.get()));
     1355    g_assert_cmpint(atspi_accessible_get_child_count(documentWeb.get(), nullptr), ==, 3);
     1356
     1357    // Text caret moved.
     1358    auto p = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 0, nullptr));
     1359    g_assert_true(ATSPI_IS_TEXT(p.get()));
     1360    test->startEventMonitor(p.get(), { "object:text-caret-moved" });
     1361    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(p.get()), 10, nullptr));
     1362    auto events = test->stopEventMonitor(1);
     1363    g_assert_cmpuint(events.size(), ==, 1);
     1364    g_assert_cmpstr(events[0]->type, ==, "object:text-caret-moved");
     1365    g_assert_cmpuint(events[0]->detail1, ==, 10);
     1366
     1367    auto section = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 1, nullptr));
     1368    g_assert_true(ATSPI_IS_ACCESSIBLE(section.get()));
     1369    g_assert_cmpint(atspi_accessible_get_child_count(section.get(), nullptr), ==, 1);
     1370    auto input = adoptGRef(atspi_accessible_get_child_at_index(section.get(), 0, nullptr));
     1371    g_assert_true(ATSPI_IS_TEXT(input.get()));
     1372    test->startEventMonitor(input.get(), { "object:text-caret-moved" });
     1373    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(input.get()), 5, nullptr));
     1374    events = test->stopEventMonitor(1);
     1375    g_assert_cmpuint(events.size(), ==, 1);
     1376    g_assert_cmpstr(events[0]->type, ==, "object:text-caret-moved");
     1377    g_assert_cmpuint(events[0]->detail1, ==, 5);
     1378
     1379    auto div = adoptGRef(atspi_accessible_get_child_at_index(documentWeb.get(), 2, nullptr));
     1380    g_assert_true(ATSPI_IS_TEXT(div.get()));
     1381    test->startEventMonitor(div.get(), { "object:text-caret-moved" });
     1382    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(div.get()), 15, nullptr));
     1383    events = test->stopEventMonitor(1);
     1384    g_assert_cmpuint(events.size(), ==, 1);
     1385    g_assert_cmpstr(events[0]->type, ==, "object:text-caret-moved");
     1386    g_assert_cmpuint(events[0]->detail1, ==, 15);
     1387
     1388#if USE(ATSPI) // Selection changed seems to be broken with ATK.
     1389    // Selection changed.
     1390    test->startEventMonitor(p.get(), { "object:text-selection-changed", "object:text-caret-moved" });
     1391    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(p.get()), 0, 10, 15, nullptr));
     1392    events = test->stopEventMonitor(2);
     1393    g_assert_cmpuint(events.size(), ==, 2);
     1394    auto* event = AccessibilityTest::findEvent(events, "object:text-caret-moved");
     1395    g_assert_nonnull(event);
     1396    g_assert_cmpuint(event->detail1, ==, 15);
     1397    g_assert_nonnull(AccessibilityTest::findEvent(events, "object:text-selection-changed"));
     1398
     1399    test->startEventMonitor(input.get(), { "object:text-selection-changed", "object:text-caret-moved" });
     1400    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(input.get()), 0, 5, 10, nullptr));
     1401    events = test->stopEventMonitor(2);
     1402    g_assert_cmpuint(events.size(), ==, 2);
     1403    event = AccessibilityTest::findEvent(events, "object:text-caret-moved");
     1404    g_assert_nonnull(event);
     1405    g_assert_cmpuint(event->detail1, ==, 10);
     1406    g_assert_nonnull(AccessibilityTest::findEvent(events, "object:text-selection-changed"));
     1407
     1408    test->startEventMonitor(div.get(), { "object:text-selection-changed", "object:text-caret-moved" });
     1409    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(div.get()), 0, 15, 18, nullptr));
     1410    events = test->stopEventMonitor(2);
     1411    g_assert_cmpuint(events.size(), ==, 2);
     1412    event = AccessibilityTest::findEvent(events, "object:text-caret-moved");
     1413    g_assert_nonnull(event);
     1414    g_assert_cmpuint(event->detail1, ==, 18);
     1415    g_assert_nonnull(AccessibilityTest::findEvent(events, "object:text-selection-changed"));
     1416#endif
     1417
     1418    // Text changed.
     1419    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(input.get()), 5, nullptr));
     1420    test->startEventMonitor(input.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1421    test->keyStroke(GDK_KEY_a);
     1422    events = test->stopEventMonitor(1);
     1423    g_assert_cmpuint(events.size(), ==, 1);
     1424    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:insert");
     1425#if USE(ATSPI)
     1426    g_assert_cmpuint(events[0]->detail1, ==, 5);
     1427#endif
     1428    g_assert_cmpuint(events[0]->detail2, ==, 1);
     1429    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1430    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "a");
     1431    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(input.get()), 0, nullptr));
     1432    test->startEventMonitor(input.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1433    test->keyStroke(GDK_KEY_Delete);
     1434    events = test->stopEventMonitor(1);
     1435    g_assert_cmpuint(events.size(), ==, 1);
     1436    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:delete");
     1437    g_assert_cmpuint(events[0]->detail1, ==, 0);
     1438    g_assert_cmpuint(events[0]->detail2, ==, 1);
     1439    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1440    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "T");
     1441
     1442    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(div.get()), 10, nullptr));
     1443    test->startEventMonitor(div.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1444    test->keyStroke(GDK_KEY_b);
     1445    events = test->stopEventMonitor(1);
     1446    g_assert_cmpuint(events.size(), ==, 1);
     1447    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:insert");
     1448#if USE(ATSPI)
     1449    g_assert_cmpuint(events[0]->detail1, ==, 10);
     1450#endif
     1451    g_assert_cmpuint(events[0]->detail2, ==, 1);
     1452    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1453    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "b");
     1454    g_assert_true(atspi_text_set_caret_offset(ATSPI_TEXT(div.get()), 15, nullptr));
     1455    test->startEventMonitor(div.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1456    test->keyStroke(GDK_KEY_BackSpace);
     1457    events = test->stopEventMonitor(1);
     1458    g_assert_cmpuint(events.size(), ==, 1);
     1459    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:delete");
     1460#if USE(ATSPI)
     1461    g_assert_cmpuint(events[0]->detail1, ==, 14);
     1462#endif
     1463    g_assert_cmpuint(events[0]->detail2, ==, 1);
     1464    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1465    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "n");
     1466
     1467    // Text replaced.
     1468    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(input.get()), 0, 5, 10, nullptr));
     1469    test->startEventMonitor(input.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1470    test->keyStroke(GDK_KEY_c);
     1471    events = test->stopEventMonitor(2);
     1472    g_assert_cmpuint(events.size(), ==, 2);
     1473    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:delete");
     1474    g_assert_cmpuint(events[0]->detail1, ==, 6);
     1475    g_assert_cmpuint(events[0]->detail2, ==, 5);
     1476    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1477    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "is a ");
     1478    g_assert_cmpstr(events[1]->type, ==, "object:text-changed:insert");
     1479#if USE(ATSPI)
     1480    g_assert_cmpuint(events[1]->detail1, ==, 5);
     1481#endif
     1482    g_assert_cmpuint(events[1]->detail2, ==, 1);
     1483    g_assert_true(G_VALUE_HOLDS_STRING(&events[1]->any_data));
     1484    g_assert_cmpstr(g_value_get_string(&events[1]->any_data), ==, "c");
     1485
     1486    g_assert_true(atspi_text_set_selection(ATSPI_TEXT(div.get()), 0, 10, 18, nullptr));
     1487    test->startEventMonitor(div.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1488    test->keyStroke(GDK_KEY_Delete);
     1489    events = test->stopEventMonitor(1);
     1490    g_assert_cmpuint(events.size(), ==, 1);
     1491    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:delete");
     1492#if USE(ATSPI)
     1493    g_assert_cmpuint(events[0]->detail1, ==, 10);
     1494#endif
     1495    g_assert_cmpuint(events[0]->detail2, ==, 8);
     1496    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1497    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "bntet ed");
     1498
     1499#if USE(ATSPI)
     1500    // Text input value changed.
     1501    test->startEventMonitor(input.get(), { "object:text-changed:insert", "object:text-changed:delete" });
     1502    test->runJavaScriptAndWaitUntilFinished("document.getElementsByTagName('input')[0].value = 'foo';", nullptr);
     1503    events = test->stopEventMonitor(2);
     1504    g_assert_cmpuint(events.size(), ==, 2);
     1505    g_assert_cmpstr(events[0]->type, ==, "object:text-changed:delete");
     1506    g_assert_cmpuint(events[0]->detail1, ==, 0);
     1507    g_assert_cmpuint(events[0]->detail2, ==, 16);
     1508    g_assert_true(G_VALUE_HOLDS_STRING(&events[0]->any_data));
     1509    g_assert_cmpstr(g_value_get_string(&events[0]->any_data), ==, "his actext field");
     1510    g_assert_cmpstr(events[1]->type, ==, "object:text-changed:insert");
     1511    g_assert_cmpuint(events[1]->detail1, ==, 0);
     1512    g_assert_cmpuint(events[1]->detail2, ==, 3);
     1513    g_assert_true(G_VALUE_HOLDS_STRING(&events[1]->any_data));
     1514    g_assert_cmpstr(g_value_get_string(&events[1]->any_data), ==, "foo");
     1515#endif
     1516}
     1517
    8081518void beforeAll()
    8091519{
     
    8181528    AccessibilityTest::add("WebKitAccessibility", "component/scroll-to", testComponentScrollTo);
    8191529#endif
     1530    AccessibilityTest::add("WebKitAccessibility", "text/basic", testTextBasic);
     1531    AccessibilityTest::add("WebKitAccessibility", "text/surrogate-pair", testTextSurrogatePair);
     1532    AccessibilityTest::add("WebKitAccessibility", "text/iterator", testTextIterator);
     1533    AccessibilityTest::add("WebKitAccessibility", "text/extents", testTextExtents);
     1534    AccessibilityTest::add("WebKitAccessibility", "text/selections", testTextSelections);
     1535    AccessibilityTest::add("WebKitAccessibility", "text/attributes", testTextAttributes);
     1536    AccessibilityTest::add("WebKitAccessibility", "text/state-changed", testTextStateChanged);
    8201537}
    8211538
Note: See TracChangeset for help on using the changeset viewer.