/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebEngine module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE.Chromium file.

#include "browser_accessibility_qt.h"

#if QT_CONFIG(accessibility)

#include "ui/accessibility/ax_enums.mojom.h"

#include "browser_accessibility_manager_qt.h"
#include "qtwebenginecoreglobal_p.h"
#include "type_conversion.h"

using namespace blink;
using QtWebEngineCore::toQt;

namespace content {

const BrowserAccessibilityQt *ToBrowserAccessibilityQt(const BrowserAccessibility *obj)
{
    return static_cast<const BrowserAccessibilityQt *>(obj);
}

QAccessibleInterface *toQAccessibleInterface(BrowserAccessibility *obj)
{
    return static_cast<BrowserAccessibilityQt *>(obj);
}

BrowserAccessibilityQt::BrowserAccessibilityQt()
{
    QAccessible::registerAccessibleInterface(this);
}

bool BrowserAccessibilityQt::isValid() const
{
    auto managerQt = static_cast<BrowserAccessibilityManagerQt *>(manager_);
    return managerQt && managerQt->isValid();
}

QObject *BrowserAccessibilityQt::object() const
{
    return nullptr;
}

QAccessibleInterface *BrowserAccessibilityQt::childAt(int x, int y) const
{
    for (int i = 0; i < childCount(); ++i) {
        QAccessibleInterface *childIface = child(i);
        Q_ASSERT(childIface);
        if (childIface->rect().contains(x,y))
            return childIface;
    }
    return nullptr;
}

void *BrowserAccessibilityQt::interface_cast(QAccessible::InterfaceType type)
{
    switch (type) {
    case QAccessible::ActionInterface:
        if (!actionNames().isEmpty())
            return static_cast<QAccessibleActionInterface*>(this);
        break;
    case QAccessible::TextInterface:
        if (HasState(ax::mojom::State::kEditable))
            return static_cast<QAccessibleTextInterface*>(this);
        break;
    case QAccessible::ValueInterface: {
        QAccessible::Role r = role();
        if (r == QAccessible::ProgressBar ||
                r == QAccessible::Slider ||
                r == QAccessible::ScrollBar ||
                r == QAccessible::SpinBox)
            return static_cast<QAccessibleValueInterface*>(this);
        break;
    }
    case QAccessible::TableInterface: {
        QAccessible::Role r = role();
        if (r == QAccessible::Table ||
            r == QAccessible::List ||
            r == QAccessible::Tree)
            return static_cast<QAccessibleTableInterface*>(this);
        break;
    }
    case QAccessible::TableCellInterface: {
        QAccessible::Role r = role();
        if (r == QAccessible::Cell ||
            r == QAccessible::ListItem ||
            r == QAccessible::TreeItem)
            return static_cast<QAccessibleTableCellInterface*>(this);
        break;
    }
    default:
        break;
    }
    return nullptr;
}

QAccessibleInterface *BrowserAccessibilityQt::parent() const
{
    BrowserAccessibility *p = PlatformGetParent();
    if (p)
        return static_cast<BrowserAccessibilityQt*>(p);
    return static_cast<BrowserAccessibilityManagerQt*>(manager())->rootParentAccessible();
}

QAccessibleInterface *BrowserAccessibilityQt::child(int index) const
{
    return static_cast<BrowserAccessibilityQt*>(BrowserAccessibility::PlatformGetChild(index));
}

QAccessibleInterface *BrowserAccessibilityQt::focusChild() const
{
    if (state().focused)
        return const_cast<BrowserAccessibilityQt *>(this);

    for (int i = 0; i < childCount(); ++i) {
        if (QAccessibleInterface *iface = child(i)->focusChild())
            return iface;
    }

    return nullptr;
}

int BrowserAccessibilityQt::childCount() const
{
    return PlatformChildCount();
}

int BrowserAccessibilityQt::indexOfChild(const QAccessibleInterface *iface) const
{

    const BrowserAccessibilityQt *child = static_cast<const BrowserAccessibilityQt*>(iface);
    return const_cast<BrowserAccessibilityQt *>(child)->GetIndexInParent();
}

QString BrowserAccessibilityQt::text(QAccessible::Text t) const
{
    switch (t) {
    case QAccessible::Name:
        return toQt(GetStringAttribute(ax::mojom::StringAttribute::kName));
    case QAccessible::Description:
        return toQt(GetStringAttribute(ax::mojom::StringAttribute::kDescription));
    case QAccessible::Value:
        return toQt(GetStringAttribute(ax::mojom::StringAttribute::kValue));
    case QAccessible::Accelerator:
        return toQt(GetStringAttribute(ax::mojom::StringAttribute::kKeyShortcuts));
    default:
        break;
    }
    return QString();
}

void BrowserAccessibilityQt::setText(QAccessible::Text t, const QString &text)
{
}

QRect BrowserAccessibilityQt::rect() const
{
    if (!manager()) // needed implicitly by GetScreenBoundsRect()
        return QRect();
    gfx::Rect bounds = GetUnclippedScreenBoundsRect();
    return QRect(bounds.x(), bounds.y(), bounds.width(), bounds.height());
}

QAccessible::Role BrowserAccessibilityQt::role() const
{
    switch (GetRole()) {
    case ax::mojom::Role::kNone:
    case ax::mojom::Role::kUnknown:
        return QAccessible::NoRole;

    // Internal roles (matching auralinux and win)
    case ax::mojom::Role::kKeyboard:
    case ax::mojom::Role::kIgnored:
    case ax::mojom::Role::kImeCandidate:
    case ax::mojom::Role::kPresentational:
        return QAccessible::NoRole;

    // Used by Chromium to distinguish between the root of the tree
    // for this page, and a web area for a frame within this page.
    case ax::mojom::Role::kWebArea:
    case ax::mojom::Role::kWebView:
    case ax::mojom::Role::kRootWebArea: // not sure if we need to make a diff here, but this seems common
        return QAccessible::WebDocument;

    // These roles all directly correspond to blink accessibility roles,
    // keep these alphabetical.
    case ax::mojom::Role::kAbbr:
        return QAccessible::StaticText;
    case ax::mojom::Role::kAlert:
    case ax::mojom::Role::kAlertDialog:
        return QAccessible::AlertMessage;
    case ax::mojom::Role::kAnchor:
        return QAccessible::Link;
    case ax::mojom::Role::kApplication:
        return QAccessible::Document; // returning Application here makes Qt return the top level app object
    case ax::mojom::Role::kArticle:
        return QAccessible::Section;
    case ax::mojom::Role::kAudio:
        return QAccessible::Sound;
    case ax::mojom::Role::kBanner:
        return QAccessible::Section;
    case ax::mojom::Role::kBlockquote:
        return QAccessible::Section;
    case ax::mojom::Role::kButton:
        return QAccessible::Button;
    case ax::mojom::Role::kCanvas:
        return QAccessible::Canvas;
    case ax::mojom::Role::kCaption:
        return QAccessible::Heading;
    case ax::mojom::Role::kCaret:
        return QAccessible::NoRole; // FIXME: https://codereview.chromium.org/2781613003
    case ax::mojom::Role::kCell:
        return QAccessible::Cell;
    case ax::mojom::Role::kCheckBox:
        return QAccessible::CheckBox;
    case ax::mojom::Role::kClient:
        return QAccessible::Client;
    case ax::mojom::Role::kCode:
        return QAccessible::StaticText;
    case ax::mojom::Role::kColorWell:
        return QAccessible::ColorChooser;
    case ax::mojom::Role::kColumn:
        return QAccessible::Column;
    case ax::mojom::Role::kColumnHeader:
        return QAccessible::ColumnHeader;
    case ax::mojom::Role::kComboBoxGrouping:
    case ax::mojom::Role::kComboBoxMenuButton:
    case ax::mojom::Role::kTextFieldWithComboBox:
        return QAccessible::ComboBox;
    case ax::mojom::Role::kComplementary:
        return QAccessible::ComplementaryContent;
    case ax::mojom::Role::kComment:
        return QAccessible::Section;
    case ax::mojom::Role::kContentDeletion:
    case ax::mojom::Role::kContentInsertion:
        return QAccessible::Grouping;
    case ax::mojom::Role::kContentInfo:
        return QAccessible::Section;
    case ax::mojom::Role::kDate:
    case ax::mojom::Role::kDateTime:
        return QAccessible::Clock;
    case ax::mojom::Role::kDefinition:
        return QAccessible::Paragraph;
    case ax::mojom::Role::kDescriptionList:
        return QAccessible::List;
    case ax::mojom::Role::kDescriptionListDetail:
        return QAccessible::Paragraph;
    case ax::mojom::Role::kDescriptionListTerm:
        return QAccessible::ListItem;
    case ax::mojom::Role::kDetails:
        return QAccessible::Grouping;
    case ax::mojom::Role::kDesktop:
        return QAccessible::Pane;
    case ax::mojom::Role::kDialog:
        return QAccessible::Dialog;
    case ax::mojom::Role::kDirectory:
        return QAccessible::List;
    case ax::mojom::Role::kDisclosureTriangle:
        return QAccessible::Button;
    case ax::mojom::Role::kGenericContainer:
        return QAccessible::Section;
    case ax::mojom::Role::kDocCover:
        return QAccessible::Graphic;
    case ax::mojom::Role::kDocBackLink:
    case ax::mojom::Role::kDocBiblioRef:
    case ax::mojom::Role::kDocGlossRef:
    case ax::mojom::Role::kDocNoteRef:
        return QAccessible::Link;
    case ax::mojom::Role::kDocBiblioEntry:
    case ax::mojom::Role::kDocEndnote:
    case ax::mojom::Role::kDocFootnote:
        return QAccessible::ListItem;
    case ax::mojom::Role::kDocPageBreak:
        return QAccessible::Separator;
    case ax::mojom::Role::kDocAbstract:
    case ax::mojom::Role::kDocAcknowledgments:
    case ax::mojom::Role::kDocAfterword:
    case ax::mojom::Role::kDocAppendix:
    case ax::mojom::Role::kDocBibliography:
    case ax::mojom::Role::kDocChapter:
    case ax::mojom::Role::kDocColophon:
    case ax::mojom::Role::kDocConclusion:
    case ax::mojom::Role::kDocCredit:
    case ax::mojom::Role::kDocCredits:
    case ax::mojom::Role::kDocDedication:
    case ax::mojom::Role::kDocEndnotes:
    case ax::mojom::Role::kDocEpigraph:
    case ax::mojom::Role::kDocEpilogue:
    case ax::mojom::Role::kDocErrata:
    case ax::mojom::Role::kDocExample:
    case ax::mojom::Role::kDocForeword:
    case ax::mojom::Role::kDocGlossary:
    case ax::mojom::Role::kDocIndex:
    case ax::mojom::Role::kDocIntroduction:
    case ax::mojom::Role::kDocNotice:
    case ax::mojom::Role::kDocPageList:
    case ax::mojom::Role::kDocPart:
    case ax::mojom::Role::kDocPreface:
    case ax::mojom::Role::kDocPrologue:
    case ax::mojom::Role::kDocPullquote:
    case ax::mojom::Role::kDocQna:
        return QAccessible::Section;
    case ax::mojom::Role::kDocSubtitle:
        return QAccessible::Heading;
    case ax::mojom::Role::kDocTip:
    case ax::mojom::Role::kDocToc:
        return QAccessible::Section;
    case ax::mojom::Role::kDocument:
        return QAccessible::Document;
    case ax::mojom::Role::kEmbeddedObject:
        return QAccessible::Grouping;
    case ax::mojom::Role::kEmphasis:
        return QAccessible::StaticText;
    case ax::mojom::Role::kFeed:
        return QAccessible::Section;
    case ax::mojom::Role::kFigcaption:
        return QAccessible::Heading;
    case ax::mojom::Role::kFigure:
        return QAccessible::Section;
    case ax::mojom::Role::kFooter:
        return QAccessible::Footer;
    case ax::mojom::Role::kFooterAsNonLandmark:
        return QAccessible::Section;
    case ax::mojom::Role::kForm:
        return QAccessible::Form;
    case ax::mojom::Role::kGraphicsDocument:
        return QAccessible::Document;
    case ax::mojom::Role::kGraphicsObject:
        return QAccessible::Pane;
    case ax::mojom::Role::kGraphicsSymbol:
        return QAccessible::Graphic;
    case ax::mojom::Role::kGrid:
        return QAccessible::Table;
    case ax::mojom::Role::kGroup:
        return QAccessible::Grouping;
    case ax::mojom::Role::kHeader:
    case ax::mojom::Role::kHeaderAsNonLandmark:
        return QAccessible::Section;
    case ax::mojom::Role::kHeading:
        return QAccessible::Heading;
    case ax::mojom::Role::kIframe:
        return QAccessible::WebDocument;
    case ax::mojom::Role::kIframePresentational:
        return QAccessible::Grouping;
    case ax::mojom::Role::kImage:
        return QAccessible::Graphic;
    case ax::mojom::Role::kImageMap:
        return QAccessible::Document;
    case ax::mojom::Role::kInlineTextBox:
        return QAccessible::StaticText;
    case ax::mojom::Role::kInputTime:
        return QAccessible::SpinBox;
    case ax::mojom::Role::kLabelText:
        return QAccessible::StaticText;
    case ax::mojom::Role::kLayoutTable:
    case ax::mojom::Role::kLayoutTableCell:
    case ax::mojom::Role::kLayoutTableRow:
        return QAccessible::Section;
    case ax::mojom::Role::kLegend:
        return QAccessible::StaticText;
    case ax::mojom::Role::kLineBreak:
        return QAccessible::Separator;
    case ax::mojom::Role::kLink:
        return QAccessible::Link;
    case ax::mojom::Role::kList:
        return QAccessible::List;
    case ax::mojom::Role::kListBox:
        return QAccessible::ComboBox;
    case ax::mojom::Role::kListBoxOption:
        return QAccessible::ListItem;
    case ax::mojom::Role::kListItem:
        return QAccessible::ListItem;
    case ax::mojom::Role::kListGrid:
        return  QAccessible::List;
    case ax::mojom::Role::kListMarker:
        return QAccessible::StaticText;
    case ax::mojom::Role::kLog:
        return QAccessible::Section;
    case ax::mojom::Role::kMain:
        return QAccessible::Grouping;
    case ax::mojom::Role::kMark:
        return QAccessible::StaticText;
    case ax::mojom::Role::kMarquee:
        return QAccessible::Section;
    case ax::mojom::Role::kMath:
        return QAccessible::Equation;
    case ax::mojom::Role::kMenu:
        return QAccessible::PopupMenu;
    case ax::mojom::Role::kMenuBar:
        return QAccessible::MenuBar;
    case ax::mojom::Role::kMenuItem:
        return QAccessible::MenuItem;
    case ax::mojom::Role::kMenuItemCheckBox:
        return QAccessible::CheckBox;
    case ax::mojom::Role::kMenuItemRadio:
        return QAccessible::RadioButton;
    case ax::mojom::Role::kMenuListOption:
        return QAccessible::MenuItem;
    case ax::mojom::Role::kMenuListPopup:
        return QAccessible::PopupMenu;
    case ax::mojom::Role::kMeter:
        return QAccessible::Chart;
    case ax::mojom::Role::kNavigation:
        return QAccessible::Section;
    case ax::mojom::Role::kNote:
        return QAccessible::Note;
    case ax::mojom::Role::kPane:
        return QAccessible::Pane;
    case ax::mojom::Role::kParagraph:
        return QAccessible::Paragraph;
    case ax::mojom::Role::kPdfActionableHighlight:
        return QAccessible::Button;
    case ax::mojom::Role::kPluginObject:
        return QAccessible::Grouping;
    case ax::mojom::Role::kPopUpButton:
        return QAccessible::ComboBox;
    case ax::mojom::Role::kPortal:
        return QAccessible::Button;
    case ax::mojom::Role::kPre:
        return QAccessible::Section;
    case ax::mojom::Role::kProgressIndicator:
        return QAccessible::ProgressBar;
    case ax::mojom::Role::kRadioButton:
        return QAccessible::RadioButton;
    case ax::mojom::Role::kRadioGroup:
        return QAccessible::Grouping;
    case ax::mojom::Role::kRegion:
        return QAccessible::Section;
    case ax::mojom::Role::kRow:
        return QAccessible::Row;
    case ax::mojom::Role::kRowGroup:
        return QAccessible::Section;
    case ax::mojom::Role::kRowHeader:
        return QAccessible::RowHeader;
    case ax::mojom::Role::kRuby:
        return QAccessible::StaticText;
    case ax::mojom::Role::kRubyAnnotation:
        return QAccessible::StaticText;
    case ax::mojom::Role::kScrollBar:
        return QAccessible::ScrollBar;
    case ax::mojom::Role::kScrollView:
        return QAccessible::Pane;
    case ax::mojom::Role::kSearch:
        return QAccessible::Section;
    case ax::mojom::Role::kSearchBox:
        return QAccessible::EditableText;
    case ax::mojom::Role::kSection:
        return QAccessible::Section;
    case ax::mojom::Role::kSlider:
    case ax::mojom::Role::kSliderThumb:
        return QAccessible::Slider;
    case ax::mojom::Role::kSpinButton:
        return QAccessible::SpinBox;
    case ax::mojom::Role::kSplitter:
        return QAccessible::Splitter;
    case ax::mojom::Role::kStaticText:
        return QAccessible::StaticText;
    case ax::mojom::Role::kStatus:
        return QAccessible::Indicator;
    case ax::mojom::Role::kStrong:
        return QAccessible::StaticText;
    case ax::mojom::Role::kSuggestion:
        return QAccessible::Section;
    case ax::mojom::Role::kSvgRoot:
        return QAccessible::Graphic;
    case ax::mojom::Role::kSwitch:
        return QAccessible::Button;
    case ax::mojom::Role::kTable:
        return QAccessible::Table;
    case ax::mojom::Role::kTableHeaderContainer:
        return QAccessible::Section;
    case ax::mojom::Role::kTab:
        return QAccessible::PageTab;
    case ax::mojom::Role::kTabList:
        return QAccessible::PageTabList;
    case ax::mojom::Role::kTabPanel:
        return QAccessible::Pane;
    case ax::mojom::Role::kTerm:
        return QAccessible::StaticText;
    case ax::mojom::Role::kTextField:
        return QAccessible::EditableText;
    case ax::mojom::Role::kTime:
    case ax::mojom::Role::kTimer:
        return QAccessible::Clock;
    case ax::mojom::Role::kTitleBar:
        return QAccessible::Document;
    case ax::mojom::Role::kToggleButton:
        return QAccessible::Button;
    case ax::mojom::Role::kToolbar:
        return QAccessible::ToolBar;
    case ax::mojom::Role::kTooltip:
        return QAccessible::ToolTip;
    case ax::mojom::Role::kTree:
        return QAccessible::Tree;
    case ax::mojom::Role::kTreeGrid:
        return QAccessible::Tree;
    case ax::mojom::Role::kTreeItem:
        return QAccessible::TreeItem;
    case ax::mojom::Role::kVideo:
        return QAccessible::Animation;
    case ax::mojom::Role::kWindow:
        return QAccessible::Window;
    }
    return QAccessible::NoRole;
}

QAccessible::State BrowserAccessibilityQt::state() const
{
    QAccessible::State state = QAccessible::State();
    if (HasState(ax::mojom::State::kCollapsed))
        state.collapsed = true;
    if (HasState(ax::mojom::State::kDefault))
        state.defaultButton = true;
    if (HasState(ax::mojom::State::kEditable))
        state.editable = true;
    if (HasState(ax::mojom::State::kExpanded))
        state.expanded = true;
    if (HasState(ax::mojom::State::kFocusable))
        state.focusable = true;
    if (HasState(ax::mojom::State::kHorizontal))
    {} // FIXME
    if (HasState(ax::mojom::State::kHovered))
        state.hotTracked = true;
    if (HasState(ax::mojom::State::kIgnored))
    {} // FIXME
    if (HasState(ax::mojom::State::kInvisible))
        state.invisible = true;
    if (HasState(ax::mojom::State::kLinked))
        state.linked = true;
    if (HasState(ax::mojom::State::kMultiline))
        state.multiLine = true;
    if (HasState(ax::mojom::State::kMultiselectable))
        state.multiSelectable = true;
    if (HasState(ax::mojom::State::kProtected))
        state.passwordEdit = true;
    if (HasState(ax::mojom::State::kRequired))
    {} // FIXME
    if (HasState(ax::mojom::State::kRichlyEditable))
    {} // FIXME
    if (HasState(ax::mojom::State::kVertical))
    {} // FIXME
    if (HasState(ax::mojom::State::kVisited))
        state.traversed = true;

    if (IsOffscreen())
        state.offscreen = true;
    if (manager()->GetFocus() == this)
        state.focused = true;
    if (GetBoolAttribute(ax::mojom::BoolAttribute::kBusy))
        state.busy = true;
    if (GetBoolAttribute(ax::mojom::BoolAttribute::kModal))
        state.modal = true;
    if (HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) {
        state.selectable = true;
        state.selected = GetBoolAttribute(ax::mojom::BoolAttribute::kSelected);
    }
    if (HasIntAttribute(ax::mojom::IntAttribute::kCheckedState)) {
        state.checkable = true;
        const ax::mojom::CheckedState checkedState =
                static_cast<ax::mojom::CheckedState>(GetIntAttribute(ax::mojom::IntAttribute::kCheckedState));
        switch (checkedState) {
        case ax::mojom::CheckedState::kTrue:
            if (GetRole() == ax::mojom::Role::kToggleButton)
                state.pressed = true;
            else
                state.checked = true;
            break;
        case ax::mojom::CheckedState::kMixed:
            state.checkStateMixed = true;
            break;
        case ax::mojom::CheckedState::kFalse:
        case ax::mojom::CheckedState::kNone:
            break;
        }
    }
    if (HasIntAttribute(ax::mojom::IntAttribute::kRestriction)) {
        const ax::mojom::Restriction restriction = static_cast<ax::mojom::Restriction>(GetIntAttribute(ax::mojom::IntAttribute::kRestriction));
        switch (restriction) {
        case ax::mojom::Restriction::kReadOnly:
            state.readOnly = true;
            break;
        case ax::mojom::Restriction::kDisabled:
            state.disabled = true;
            break;
        case ax::mojom::Restriction::kNone:
            break;
        }
    }
    if (HasIntAttribute(ax::mojom::IntAttribute::kHasPopup)) {
        const ax::mojom::HasPopup hasPopup = static_cast<ax::mojom::HasPopup>(GetIntAttribute(ax::mojom::IntAttribute::kHasPopup));
        switch (hasPopup) {
        case ax::mojom::HasPopup::kFalse:
            break;
        case ax::mojom::HasPopup::kTrue:
        case ax::mojom::HasPopup::kMenu:
        case ax::mojom::HasPopup::kListbox:
        case ax::mojom::HasPopup::kTree:
        case ax::mojom::HasPopup::kGrid:
        case ax::mojom::HasPopup::kDialog:
            state.hasPopup = true;
            break;
        }
    }
    return state;
}

void BrowserAccessibilityQt::Destroy()
{
    // delete this
    QAccessible::Id interfaceId = QAccessible::uniqueId(this);
    QAccessible::deleteAccessibleInterface(interfaceId);
}

QStringList BrowserAccessibilityQt::actionNames() const
{
    QStringList actions;
    if (HasState(ax::mojom::State::kFocusable))
        actions << QAccessibleActionInterface::setFocusAction();
    return actions;
}

void BrowserAccessibilityQt::doAction(const QString &actionName)
{
    if (actionName == QAccessibleActionInterface::setFocusAction())
        manager()->SetFocus(*this);
}

QStringList BrowserAccessibilityQt::keyBindingsForAction(const QString &actionName) const
{
    QT_NOT_YET_IMPLEMENTED
    return QStringList();
}

void BrowserAccessibilityQt::addSelection(int startOffset, int endOffset)
{
    manager()->SetSelection(AXPlatformRange(CreatePositionAt(startOffset), CreatePositionAt(endOffset)));
}

QString BrowserAccessibilityQt::attributes(int offset, int *startOffset, int *endOffset) const
{
    *startOffset = offset;
    *endOffset = offset;
    return QString();
}

int BrowserAccessibilityQt::cursorPosition() const
{
    int pos = 0;
    GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart, &pos);
    return pos;
}

QRect BrowserAccessibilityQt::characterRect(int /*offset*/) const
{
    QT_NOT_YET_IMPLEMENTED
    return QRect();
}

int BrowserAccessibilityQt::selectionCount() const
{
    int start = 0;
    int end = 0;
    GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart, &start);
    GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, &end);
    if (start != end)
        return 1;
    return 0;
}

int BrowserAccessibilityQt::offsetAtPoint(const QPoint &/*point*/) const
{
    QT_NOT_YET_IMPLEMENTED
    return 0;
}

void BrowserAccessibilityQt::selection(int selectionIndex, int *startOffset, int *endOffset) const
{
    Q_ASSERT(startOffset && endOffset);
    *startOffset = 0;
    *endOffset = 0;
    if (selectionIndex != 0)
        return;
    GetIntAttribute(ax::mojom::IntAttribute::kTextSelStart, startOffset);
    GetIntAttribute(ax::mojom::IntAttribute::kTextSelEnd, endOffset);
}

QString BrowserAccessibilityQt::text(int startOffset, int endOffset) const
{
    return text(QAccessible::Value).mid(startOffset, endOffset - startOffset);
}

void BrowserAccessibilityQt::removeSelection(int selectionIndex)
{
    manager()->SetSelection(AXPlatformRange(CreatePositionAt(0), CreatePositionAt(0)));
}

void BrowserAccessibilityQt::setCursorPosition(int position)
{
    manager()->SetSelection(AXPlatformRange(CreatePositionAt(position), CreatePositionAt(position)));
}

void BrowserAccessibilityQt::setSelection(int selectionIndex, int startOffset, int endOffset)
{
    if (selectionIndex != 0)
        return;
    manager()->SetSelection(AXPlatformRange(CreatePositionAt(startOffset), CreatePositionAt(endOffset)));
}

int BrowserAccessibilityQt::characterCount() const
{
    return text(QAccessible::Value).length();
}

void BrowserAccessibilityQt::scrollToSubstring(int startIndex, int endIndex)
{
    int count = characterCount();
    if (startIndex < endIndex && endIndex < count)
        manager()->ScrollToMakeVisible(*this,
                                       GetRootFrameHypertextRangeBoundsRect(
                                           startIndex,
                                           endIndex - startIndex,
                                           ui::AXClippingBehavior::kUnclipped));
}

QVariant BrowserAccessibilityQt::currentValue() const
{
    QVariant result;
    float value;
    if (GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange, &value)) {
        result = (double) value;
    }
    return result;
}

void BrowserAccessibilityQt::setCurrentValue(const QVariant &value)
{
    // not yet implemented anywhere in blink
    QT_NOT_YET_IMPLEMENTED
}

QVariant BrowserAccessibilityQt::maximumValue() const
{
    QVariant result;
    float value;
    if (GetFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange, &value)) {
        result = (double) value;
    }
    return result;
}

QVariant BrowserAccessibilityQt::minimumValue() const
{
    QVariant result;
    float value;
    if (GetFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange, &value)) {
        result = (double) value;
    }
    return result;
}

QVariant BrowserAccessibilityQt::minimumStepSize() const
{
    QVariant result;
    float value;
    if (GetFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange, &value)) {
        result = (double) value;
    }
    return result;
}

QAccessibleInterface *BrowserAccessibilityQt::cellAt(int row, int column) const
{
    int columns = 0;
    int rows = 0;
    if (!GetIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, &columns) ||
        !GetIntAttribute(ax::mojom::IntAttribute::kTableRowCount, &rows) ||
        columns <= 0 ||
        rows <= 0) {
      return 0;
    }

    if (row < 0 || row >= rows || column < 0 || column >= columns)
      return 0;

    base::Optional<int> cell_id = GetCellId(row, column);
    BrowserAccessibility* cell = cell_id ? manager()->GetFromID(*cell_id) : nullptr;
    if (cell) {
      QAccessibleInterface *iface = static_cast<BrowserAccessibilityQt*>(cell);
      return iface;
    }

    return nullptr;
}

QAccessibleInterface *BrowserAccessibilityQt::caption() const
{
    return nullptr;
}

QAccessibleInterface *BrowserAccessibilityQt::summary() const
{
    return nullptr;
}

QString BrowserAccessibilityQt::columnDescription(int column) const
{
    return QString();
}

QString BrowserAccessibilityQt::rowDescription(int row) const
{
    return QString();
}

int BrowserAccessibilityQt::columnCount() const
{
    int columns = 0;
    if (GetIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, &columns))
        return columns;

    return 0;
}

int BrowserAccessibilityQt::rowCount() const
{
    int rows = 0;
    if (GetIntAttribute(ax::mojom::IntAttribute::kTableRowCount, &rows))
      return rows;
    return 0;
}

int BrowserAccessibilityQt::selectedCellCount() const
{
    return 0;
}

int BrowserAccessibilityQt::selectedColumnCount() const
{
    return 0;
}

int BrowserAccessibilityQt::selectedRowCount() const
{
    return 0;
}

QList<QAccessibleInterface *> BrowserAccessibilityQt::selectedCells() const
{
    return QList<QAccessibleInterface *>();
}

QList<int> BrowserAccessibilityQt::selectedColumns() const
{
    return QList<int>();
}

QList<int> BrowserAccessibilityQt::selectedRows() const
{
    return QList<int>();
}

bool BrowserAccessibilityQt::isColumnSelected(int /*column*/) const
{
    return false;
}

bool BrowserAccessibilityQt::isRowSelected(int /*row*/) const
{
    return false;
}

bool BrowserAccessibilityQt::selectRow(int /*row*/)
{
    return false;
}

bool BrowserAccessibilityQt::selectColumn(int /*column*/)
{
    return false;
}

bool BrowserAccessibilityQt::unselectRow(int /*row*/)
{
    return false;
}

bool BrowserAccessibilityQt::unselectColumn(int /*column*/)
{
    return false;
}

int BrowserAccessibilityQt::columnExtent() const
{
    return 1;
}

QList<QAccessibleInterface *> BrowserAccessibilityQt::columnHeaderCells() const
{
    return QList<QAccessibleInterface*>();
}

int BrowserAccessibilityQt::columnIndex() const
{
    int column = 0;
    if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, &column))
      return column;
    return 0;
}

int BrowserAccessibilityQt::rowExtent() const
{
    return 1;
}

QList<QAccessibleInterface *> BrowserAccessibilityQt::rowHeaderCells() const
{
    return QList<QAccessibleInterface*>();
}

int BrowserAccessibilityQt::rowIndex() const
{
    int row = 0;
    if (GetIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, &row))
      return row;
    return 0;
}

bool BrowserAccessibilityQt::isSelected() const
{
    return false;
}

QAccessibleInterface *BrowserAccessibilityQt::table() const
{
    BrowserAccessibility* find_table = PlatformGetParent();
    while (find_table && find_table->GetRole() != ax::mojom::Role::kTable)
        find_table = find_table->PlatformGetParent();
    if (!find_table)
        return nullptr;
    return static_cast<BrowserAccessibilityQt*>(find_table);
}

void BrowserAccessibilityQt::modelChange(QAccessibleTableModelChangeEvent *)
{

}

} // namespace content

#endif // QT_CONFIG(accessibility)
