blob: cb95cb24940ca45ea11835c610f8f6e816e29526 [file] [log] [blame]
/*
* Copyright (C) 2020 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "AXLogger.h"
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
#include "AXIsolatedObject.h"
#endif
#include "AXObjectCache.h"
#include "AXSearchManager.h"
#include "AXTextRun.h"
#include "DocumentInlines.h"
#include "LocalFrameView.h"
#include "LogInitialization.h"
#include "Logging.h"
#include <algorithm>
#include <wtf/NeverDestroyed.h>
#include <wtf/OptionSet.h>
#include <wtf/text/MakeString.h>
#include <wtf/text/TextStream.h>
namespace WebCore {
#if !LOG_DISABLED
bool AXLogger::shouldLog()
{
// Modify the initializer list below to choose what thread you want to log messages from.
static constexpr OptionSet<AXLoggingOptions> loggingOptions { AXLoggingOptions::MainThread, AXLoggingOptions::OffMainThread };
// Add strings to the Vector below to just log from instances whose m_methodName includes any of the strings.
// For instance, if you want to just log from the wrapper and the AXIsolatedTree class:
// static NeverDestroyed nameFilter = Vector<String> { "WebAccessibilityObjectWrapper"_s, "AXIsolatedTree"_s };
// The default string "log nothing", prevents any output. An empty Vector or an empty string in the Vector will log everything.
static NeverDestroyed nameFilter = Vector<String> { "log nothing"_s };
if (!nameFilter->isEmpty()) {
auto it = std::find_if(nameFilter->begin(), nameFilter->end(), [this] (const auto& name) {
return m_methodName.contains(name);
});
if (it == nameFilter->end())
return false;
}
if (isMainThread())
return loggingOptions.contains(AXLoggingOptions::MainThread);
return loggingOptions.contains(AXLoggingOptions::OffMainThread);
}
AXLogger::AXLogger(const String& methodName)
: m_methodName(methodName)
{
auto initializeAXChannel = [] () {
static std::atomic<bool> initialized = false;
if (initialized)
return;
if (auto* channel = getLogChannel("Accessibility"_s)) {
LogAccessibility.state = WTFLogChannelState::On;
channel->level = WTFLogLevel::Debug;
initialized = true;
}
};
initializeAXChannel();
if (shouldLog()) {
if (!m_methodName.isEmpty())
LOG_WITH_STREAM(Accessibility, stream << m_methodName << " {");
}
m_startTime = MonotonicTime::now();
}
AXLogger::~AXLogger()
{
static const Seconds ExecutionTimeThreshold { 1_s };
auto elapsedTime = MonotonicTime::now() - m_startTime;
if (shouldLog() && !m_methodName.isEmpty()) {
if (elapsedTime > ExecutionTimeThreshold)
LOG_WITH_STREAM(Accessibility, stream << "} " << m_methodName << " exceeded ExecutionTimeThreshold " << elapsedTime);
else
LOG_WITH_STREAM(Accessibility, stream << "} " << m_methodName << " took " << elapsedTime);
}
}
void AXLogger::log(const String& message)
{
if (shouldLog())
LOG(Accessibility, "%s", message.utf8().data());
}
void AXLogger::log(const char* message)
{
if (shouldLog())
LOG(Accessibility, "%s", message);
}
void AXLogger::log(const AXCoreObject& object)
{
log(const_cast<AXCoreObject*>(&object));
}
void AXLogger::log(RefPtr<AXCoreObject> object)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
if (object)
stream << *object;
else
stream << "null";
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
void AXLogger::log(const Vector<Ref<AXCoreObject>>& objects)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
stream << "[";
for (auto object : objects)
stream << object.get();
stream << "]";
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
void AXLogger::log(const std::pair<Ref<AccessibilityObject>, AXNotification>& notification)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
stream << "Notification " << notification.second << " for object ";
stream << notification.first.get();
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
void AXLogger::log(const std::pair<RefPtr<AXCoreObject>, AXNotification>& notification)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
stream << "Notification " << notification.second << " for object ";
if (notification.first)
stream << *notification.first;
else
stream << "null";
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
void AXLogger::log(const AccessibilitySearchCriteria& criteria)
{
if (!shouldLog())
return;
TextStream stream(TextStream::LineMode::MultipleLine);
stream << criteria;
LOG(Accessibility, "%s", stream.release().utf8().data());
}
void AXLogger::log(AccessibilityObjectInclusion inclusion)
{
if (!shouldLog())
return;
TextStream stream(TextStream::LineMode::SingleLine);
stream.dumpProperty("ObjectInclusion"_s, inclusion);
LOG(Accessibility, "%s", stream.release().utf8().data());
}
void AXLogger::log(AXRelation relation)
{
if (!shouldLog())
return;
TextStream stream(TextStream::LineMode::SingleLine);
stream.dumpProperty("RelationType"_s, relation);
LOG(Accessibility, "%s", stream.release().utf8().data());
}
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
void AXLogger::log(AXIsolatedTree& tree)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
stream << tree;
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
#endif
void AXLogger::log(AXObjectCache& axObjectCache)
{
if (shouldLog()) {
TextStream stream(TextStream::LineMode::MultipleLine);
stream << axObjectCache;
LOG(Accessibility, "%s", stream.release().utf8().data());
}
}
void AXLogger::log(const String& collectionName, const AXObjectCache::DeferredCollection& collection)
{
unsigned size = 0;
WTF::switchOn(collection,
[&size] (const HashMap<Element*, String>& typedCollection) { size = typedCollection.size(); },
[&size] (const HashSet<AXID>& typedCollection) { size = typedCollection.size(); },
[&size] (const ListHashSet<Node*>& typedCollection) { size = typedCollection.size(); },
[&size] (const ListHashSet<Ref<AccessibilityObject>>& typedCollection) { size = typedCollection.size(); },
[&size] (const Vector<AXObjectCache::AttributeChange>& typedCollection) { size = typedCollection.size(); },
[&size] (const Vector<std::pair<Node*, Node*>>& typedCollection) { size = typedCollection.size(); },
[&size] (const WeakHashSet<Element, WeakPtrImplWithEventTargetData>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakHashSet<HTMLTableElement, WeakPtrImplWithEventTargetData>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakHashSet<AccessibilityObject>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakHashSet<AccessibilityTable>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakHashSet<AccessibilityTableCell>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakListHashSet<Node, WeakPtrImplWithEventTargetData>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakListHashSet<Element, WeakPtrImplWithEventTargetData>& typedCollection) { size = typedCollection.computeSize(); },
[&size] (const WeakHashMap<Element, String, WeakPtrImplWithEventTargetData>& typedCollection) { size = typedCollection.computeSize(); },
[] (auto&) {
ASSERT_NOT_REACHED();
return;
});
if (size)
log(makeString(collectionName, " size: "_s, size));
}
#endif // !LOG_DISABLED
TextStream& operator<<(TextStream& stream, AccessibilityRole role)
{
stream << accessibilityRoleToString(role);
return stream;
}
TextStream& operator<<(TextStream& stream, AccessibilitySearchDirection direction)
{
switch (direction) {
case AccessibilitySearchDirection::Next:
stream << "Next";
break;
case AccessibilitySearchDirection::Previous:
stream << "Previous";
break;
};
return stream;
}
TextStream& operator<<(TextStream& stream, AccessibilitySearchKey searchKey)
{
switch (searchKey) {
case AccessibilitySearchKey::AnyType:
stream << "AnyType";
break;
case AccessibilitySearchKey::Article:
stream << "Article";
break;
case AccessibilitySearchKey::BlockquoteSameLevel:
stream << "BlockquoteSameLevel";
break;
case AccessibilitySearchKey::Blockquote:
stream << "Blockquote";
break;
case AccessibilitySearchKey::BoldFont:
stream << "BoldFont";
break;
case AccessibilitySearchKey::Button:
stream << "Button";
break;
case AccessibilitySearchKey::Checkbox:
stream << "Checkbox";
break;
case AccessibilitySearchKey::Control:
stream << "Control";
break;
case AccessibilitySearchKey::DifferentType:
stream << "DifferentType";
break;
case AccessibilitySearchKey::FontChange:
stream << "FontChange";
break;
case AccessibilitySearchKey::FontColorChange:
stream << "FontColorChange";
break;
case AccessibilitySearchKey::Frame:
stream << "Frame";
break;
case AccessibilitySearchKey::Graphic:
stream << "Graphic";
break;
case AccessibilitySearchKey::HeadingLevel1:
stream << "HeadingLevel1";
break;
case AccessibilitySearchKey::HeadingLevel2:
stream << "HeadingLevel2";
break;
case AccessibilitySearchKey::HeadingLevel3:
stream << "HeadingLevel3";
break;
case AccessibilitySearchKey::HeadingLevel4:
stream << "HeadingLevel4";
break;
case AccessibilitySearchKey::HeadingLevel5:
stream << "HeadingLevel5";
break;
case AccessibilitySearchKey::HeadingLevel6:
stream << "HeadingLevel6";
break;
case AccessibilitySearchKey::HeadingSameLevel:
stream << "HeadingSameLevel";
break;
case AccessibilitySearchKey::Heading:
stream << "Heading";
break;
case AccessibilitySearchKey::Highlighted:
stream << "Highlighted";
break;
case AccessibilitySearchKey::ItalicFont:
stream << "ItalicFont";
break;
case AccessibilitySearchKey::KeyboardFocusable:
stream << "KeyboardFocusable";
break;
case AccessibilitySearchKey::Landmark:
stream << "Landmark";
break;
case AccessibilitySearchKey::Link:
stream << "Link";
break;
case AccessibilitySearchKey::List:
stream << "List";
break;
case AccessibilitySearchKey::LiveRegion:
stream << "LiveRegion";
break;
case AccessibilitySearchKey::MisspelledWord:
stream << "MisspelledWord";
break;
case AccessibilitySearchKey::Outline:
stream << "Outline";
break;
case AccessibilitySearchKey::PlainText:
stream << "PlainText";
break;
case AccessibilitySearchKey::RadioGroup:
stream << "RadioGroup";
break;
case AccessibilitySearchKey::SameType:
stream << "SameType";
break;
case AccessibilitySearchKey::StaticText:
stream << "StaticText";
break;
case AccessibilitySearchKey::StyleChange:
stream << "StyleChange";
break;
case AccessibilitySearchKey::TableSameLevel:
stream << "TableSameLevel";
break;
case AccessibilitySearchKey::Table:
stream << "Table";
break;
case AccessibilitySearchKey::TextField:
stream << "TextField";
break;
case AccessibilitySearchKey::Underline:
stream << "Underline";
break;
case AccessibilitySearchKey::UnvisitedLink:
stream << "UnvisitedLink";
break;
case AccessibilitySearchKey::VisitedLink:
stream << "VisitedLink";
break;
};
return stream;
}
TextStream& operator<<(TextStream& stream, const AccessibilitySearchCriteria& criteria)
{
TextStream::GroupScope groupScope(stream);
auto streamCriteriaObject = [&stream] (ASCIILiteral objectLabel, auto* axObject) {
stream.startGroup();
stream << objectLabel.characters() << " " << axObject << ", ID " << (axObject ? axObject->objectID().toUInt64() : 0);
stream.endGroup();
};
stream << "SearchCriteria " << &criteria;
streamCriteriaObject("anchorObject"_s, criteria.anchorObject);
streamCriteriaObject("startObject"_s, criteria.startObject);
stream.dumpProperty("searchDirection"_s, criteria.searchDirection);
stream.nextLine();
stream << "(searchKeys [";
for (auto searchKey : criteria.searchKeys)
stream << searchKey << ", ";
stream << "])";
stream.dumpProperty("searchText"_s, criteria.searchText);
stream.dumpProperty("resultsLimit"_s, criteria.resultsLimit);
stream.dumpProperty("visibleOnly"_s, criteria.visibleOnly);
stream.dumpProperty("immediateDescendantsOnly"_s, criteria.immediateDescendantsOnly);
return stream;
}
TextStream& operator<<(TextStream& stream, AccessibilityText text)
{
stream << text.textSource << ": " << text.text;
return stream;
}
TextStream& operator<<(TextStream& stream, AccessibilityTextSource source)
{
switch (source) {
case AccessibilityTextSource::Alternative:
stream << "Alternative";
break;
case AccessibilityTextSource::Children:
stream << "Children";
break;
case AccessibilityTextSource::Summary:
stream << "Summary";
break;
case AccessibilityTextSource::Help:
stream << "Help";
break;
case AccessibilityTextSource::Visible:
stream << "Visible";
break;
case AccessibilityTextSource::TitleTag:
stream << "TitleTag";
break;
case AccessibilityTextSource::Placeholder:
stream << "Placeholder";
break;
case AccessibilityTextSource::LabelByElement:
stream << "LabelByElement";
break;
case AccessibilityTextSource::Title:
stream << "Title";
break;
case AccessibilityTextSource::Subtitle:
stream << "Subtitle";
break;
case AccessibilityTextSource::Action:
stream << "Action";
break;
case AccessibilityTextSource::Heading:
stream << "Heading";
break;
}
return stream;
}
TextStream& operator<<(TextStream& stream, AccessibilityObjectInclusion inclusion)
{
switch (inclusion) {
case AccessibilityObjectInclusion::IncludeObject:
stream << "IncludeObject";
break;
case AccessibilityObjectInclusion::IgnoreObject:
stream << "IgnoreObject";
break;
case AccessibilityObjectInclusion::DefaultBehavior:
stream << "DefaultBehavior";
break;
}
return stream;
}
TextStream& operator<<(TextStream& stream, AXRelation relation)
{
switch (relation) {
case AXRelation::None:
stream << "None";
break;
case AXRelation::ActiveDescendant:
stream << "ActiveDescendant";
break;
case AXRelation::ActiveDescendantOf:
stream << "ActiveDescendantOf";
break;
case AXRelation::ControlledBy:
stream << "ControlledBy";
break;
case AXRelation::ControllerFor:
stream << "ControllerFor";
break;
case AXRelation::DescribedBy:
stream << "DescribedBy";
break;
case AXRelation::DescriptionFor:
stream << "DescriptionFor";
break;
case AXRelation::Details:
stream << "Details";
break;
case AXRelation::DetailsFor:
stream << "DetailsFor";
break;
case AXRelation::ErrorMessage:
stream << "ErrorMessage";
break;
case AXRelation::ErrorMessageFor:
stream << "ErrorMessageFor";
break;
case AXRelation::FlowsFrom:
stream << "FlowsFrom";
break;
case AXRelation::FlowsTo:
stream << "FlowsTo";
break;
case AXRelation::Headers:
stream << "Headers";
break;
case AXRelation::HeaderFor:
stream << "HeaderFor";
break;
case AXRelation::LabeledBy:
stream << "LabeledBy";
break;
case AXRelation::LabelFor:
stream << "LabelFor";
break;
case AXRelation::OwnedBy:
stream << "OwnedBy";
break;
case AXRelation::OwnerFor:
stream << "OwnerFor";
break;
}
return stream;
}
TextStream& operator<<(WTF::TextStream& stream, const TextUnderElementMode& mode)
{
String childrenInclusion;
switch (mode.childrenInclusion) {
case TextUnderElementMode::Children::SkipIgnoredChildren:
childrenInclusion = "SkipIgnoredChildren"_s;
break;
case TextUnderElementMode::Children::IncludeAllChildren:
childrenInclusion = "IncludeAllChildren"_s;
break;
case TextUnderElementMode::Children::IncludeNameFromContentsChildren:
childrenInclusion = "IncludeNameFromContentsChildren"_s;
break;
default:
ASSERT_NOT_REACHED();
break;
}
stream << childrenInclusion;
// Only log non-default values to avoid noise.
if (mode.includeFocusableContent)
stream << ", includeFocusableContent: 1";
if (mode.inHiddenSubtree)
stream << ", inHiddenSubtree: 1";
if (!mode.considerHiddenState)
stream << ", considerHiddenState: 0";
if (mode.ignoredChildNode)
stream << ", ignoredChildNode: " << mode.ignoredChildNode;
if (mode.trimWhitespace == TrimWhitespace::No)
stream << ", trimWhitespace: 0";
return stream;
}
TextStream& operator<<(TextStream& stream, AXNotification notification)
{
switch (notification) {
#define WEBCORE_LOG_AXNOTIFICATION(name) \
case AXNotification::name: \
stream << #name; \
break;
WEBCORE_AXNOTIFICATION_KEYS(WEBCORE_LOG_AXNOTIFICATION)
#undef WEBCORE_LOG_AXNOTIFICATION
}
return stream;
}
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
WTF::TextStream& operator<<(WTF::TextStream& stream, const AXPropertyVector& properties)
{
stream << "{"_s;
for (size_t i = 0; i < properties.size(); i++) {
if (i)
stream << ", ";
stream << properties[i].first;
}
stream << "}"_s;
return stream;
}
TextStream& operator<<(WTF::TextStream& stream, AXProperty property)
{
switch (property) {
#if !ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::AttributedText:
stream << "AttributedText";
break;
#endif // !ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::AXColumnCount:
stream << "AXColumnCount";
break;
case AXProperty::AXColumnIndex:
stream << "AXColumnIndex";
break;
case AXProperty::AXRowCount:
stream << "AXRowCount";
break;
case AXProperty::AXRowIndex:
stream << "AXRowIndex";
break;
case AXProperty::AccessKey:
stream << "AccessKey";
break;
case AXProperty::AccessibilityText:
stream << "AccessibilityText";
break;
case AXProperty::ActionVerb:
stream << "ActionVerb";
break;
case AXProperty::ARIARoleDescription:
stream << "ARIARoleDescription";
break;
case AXProperty::ARIALevel:
stream << "ARIALevel";
break;
case AXProperty::BackgroundColor:
stream << "BackgroundColor";
break;
case AXProperty::BrailleLabel:
stream << "BrailleLabel";
break;
case AXProperty::BrailleRoleDescription:
stream << "BrailleRoleDescription";
break;
case AXProperty::ButtonState:
stream << "ButtonState";
break;
case AXProperty::CanBeMultilineTextField:
stream << "CanBeMultilineTextField";
break;
case AXProperty::CanSetFocusAttribute:
stream << "CanSetFocusAttribute";
break;
case AXProperty::CanSetSelectedAttribute:
stream << "CanSetSelectedAttribute";
break;
case AXProperty::CanSetValueAttribute:
stream << "CanSetValueAttribute";
break;
#if PLATFORM(MAC)
case AXProperty::CaretBrowsingEnabled:
stream << "CaretBrowsingEnabled";
break;
#endif
case AXProperty::Cells:
stream << "Cells";
break;
case AXProperty::CellScope:
stream << "CellScope";
break;
case AXProperty::CellSlots:
stream << "CellSlots";
break;
case AXProperty::ColorValue:
stream << "ColorValue";
break;
case AXProperty::Columns:
stream << "Columns";
break;
case AXProperty::ColumnIndex:
stream << "ColumnIndex";
break;
case AXProperty::ColumnIndexRange:
stream << "ColumnIndexRange";
break;
case AXProperty::CurrentState:
stream << "CurrentState";
break;
case AXProperty::DateTimeComponentsType:
stream << "DateTimeComponentsType";
break;
case AXProperty::DateTimeValue:
stream << "DateTimeValue";
break;
case AXProperty::DatetimeAttributeValue:
stream << "DatetimeAttributeValue";
break;
case AXProperty::DecrementButton:
stream << "DecrementButton";
break;
case AXProperty::Description:
stream << "Description";
break;
case AXProperty::DisclosedByRow:
stream << "DisclosedByRow";
break;
case AXProperty::DisclosedRows:
stream << "DisclosedRows";
break;
case AXProperty::DocumentEncoding:
stream << "DocumentEncoding";
break;
case AXProperty::DocumentLinks:
stream << "DocumentLinks";
break;
case AXProperty::DocumentURI:
stream << "DocumentURI";
break;
case AXProperty::ElementName:
stream << "ElementName";
break;
case AXProperty::EmbeddedImageDescription:
stream << "EmbeddedImageDescription";
break;
case AXProperty::TextEmissionBehavior:
stream << "TextEmissionBehavior";
break;
case AXProperty::ExpandedTextValue:
stream << "ExpandedTextValue";
break;
case AXProperty::ExplicitAutoCompleteValue:
stream << "ExplicitAutoCompleteValue";
break;
case AXProperty::ExplicitInvalidStatus:
stream << "ExplicitInvalidStatus";
break;
case AXProperty::ExplicitLiveRegionRelevant:
stream << "ExplicitLiveRegionRelevant";
break;
case AXProperty::ExplicitLiveRegionStatus:
stream << "ExplicitLiveRegionStatus";
break;
case AXProperty::ExplicitOrientation:
stream << "ExplicitOrientation";
break;
case AXProperty::ExplicitPopupValue:
stream << "ExplicitPopupValue";
break;
case AXProperty::ExtendedDescription:
stream << "ExtendedDescription";
break;
#if PLATFORM(COCOA)
case AXProperty::Font:
stream << "Font";
break;
case AXProperty::FontOrientation:
stream << "FontOrientation";
break;
#endif // PLATFORM(COCOA)
case AXProperty::TextColor:
stream << "TextColor";
break;
case AXProperty::HasApplePDFAnnotationAttribute:
stream << "HasApplePDFAnnotationAttribute";
break;
case AXProperty::HasBoldFont:
stream << "HasBoldFont";
break;
case AXProperty::HasClickHandler:
stream << "HasClickHandler";
break;
case AXProperty::HasItalicFont:
stream << "HasItalicFont";
break;
case AXProperty::HasLinethrough:
stream << "HasLinethrough";
break;
case AXProperty::HasPlainText:
stream << "HasPlainText";
break;
case AXProperty::HasRemoteFrameChild:
stream << "HasRemoteFrameChild";
break;
case AXProperty::IsEditableWebArea:
stream << "IsEditableWebArea";
break;
case AXProperty::IsSubscript:
stream << "IsSubscript";
break;
case AXProperty::IsSuperscript:
stream << "IsSuperscript";
break;
case AXProperty::HasTextShadow:
stream << "HasTextShadow";
break;
case AXProperty::HasUnderline:
stream << "HasUnderline";
break;
case AXProperty::HorizontalScrollBar:
stream << "HorizontalScrollBar";
break;
case AXProperty::IdentifierAttribute:
stream << "IdentifierAttribute";
break;
case AXProperty::IncrementButton:
stream << "IncrementButton";
break;
case AXProperty::InitialFrameRect:
stream << "InitialFrameRect";
break;
case AXProperty::InnerHTML:
stream << "InnerHTML";
break;
case AXProperty::InputType:
stream << "InputType";
break;
case AXProperty::InternalLinkElement:
stream << "InternalLinkElement";
break;
case AXProperty::IsGrabbed:
stream << "IsGrabbed";
break;
case AXProperty::IsARIAGridRow:
stream << "IsARIAGridRow";
break;
case AXProperty::IsARIATreeGridRow:
stream << "IsARIATreeGridRow";
break;
case AXProperty::IsAnonymousMathOperator:
stream << "IsAnonymousMathOperator";
break;
case AXProperty::IsAttachment:
stream << "IsAttachment";
break;
case AXProperty::IsBusy:
stream << "IsBusy";
break;
case AXProperty::IsChecked:
stream << "IsChecked";
break;
case AXProperty::IsColumnHeader:
stream << "IsColumnHeader";
break;
case AXProperty::IsEnabled:
stream << "IsEnabled";
break;
case AXProperty::IsExpanded:
stream << "IsExpanded";
break;
case AXProperty::IsExposable:
stream << "IsExposable";
break;
case AXProperty::IsExposedTableCell:
stream << "IsExposedTableCell";
break;
case AXProperty::IsFieldset:
stream << "IsFieldset";
break;
case AXProperty::IsIgnored:
stream << "IsIgnored";
break;
case AXProperty::IsIndeterminate:
stream << "IsIndeterminate";
break;
case AXProperty::IsInlineText:
stream << "IsInlineText";
break;
case AXProperty::IsKeyboardFocusable:
stream << "IsKeyboardFocusable";
break;
case AXProperty::IsMathElement:
stream << "IsMathElement";
break;
case AXProperty::IsMathFraction:
stream << "IsMathFraction";
break;
case AXProperty::IsMathFenced:
stream << "IsMathFenced";
break;
case AXProperty::IsMathSubscriptSuperscript:
stream << "IsMathSubscriptSuperscript";
break;
case AXProperty::IsMathRow:
stream << "IsMathRow";
break;
case AXProperty::IsMathUnderOver:
stream << "IsMathUnderOver";
break;
case AXProperty::IsMathRoot:
stream << "IsMathRoot";
break;
case AXProperty::IsMathSquareRoot:
stream << "IsMathSquareRoot";
break;
case AXProperty::IsMathTable:
stream << "IsMathTable";
break;
case AXProperty::IsMathTableRow:
stream << "IsMathTableRow";
break;
case AXProperty::IsMathTableCell:
stream << "IsMathTableCell";
break;
case AXProperty::IsMathMultiscript:
stream << "IsMathMultiscript";
break;
case AXProperty::IsMathToken:
stream << "IsMathToken";
break;
case AXProperty::IsMultiSelectable:
stream << "IsMultiSelectable";
break;
case AXProperty::IsNonLayerSVGObject:
stream << "IsNonLayerSVGObject";
break;
case AXProperty::IsPlugin:
stream << "IsPlugin";
break;
case AXProperty::IsPressed:
stream << "IsPressed";
break;
case AXProperty::IsRequired:
stream << "IsRequired";
break;
case AXProperty::IsRowHeader:
stream << "IsRowHeader";
break;
case AXProperty::IsSecureField:
stream << "IsSecureField";
break;
case AXProperty::IsSelected:
stream << "IsSelected";
break;
case AXProperty::IsSelectedOptionActive:
stream << "IsSelectedOptionActive";
break;
case AXProperty::IsTable:
stream << "IsTable";
break;
case AXProperty::IsTableRow:
stream << "IsTableRow";
break;
case AXProperty::IsTree:
stream << "IsTree";
break;
case AXProperty::IsTreeItem:
stream << "IsTreeItem";
break;
case AXProperty::IsValueAutofillAvailable:
stream << "IsValueAutofillAvailable";
break;
case AXProperty::IsVisible:
stream << "IsVisible";
break;
case AXProperty::IsVisited:
stream << "IsVisited";
break;
case AXProperty::IsWidget:
stream << "IsWidget";
break;
case AXProperty::KeyShortcuts:
stream << "KeyShortcuts";
break;
case AXProperty::Language:
stream << "Language";
break;
case AXProperty::LinethroughColor:
stream << "LinethroughColor";
break;
#if ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::ListMarkerLineID:
stream << "ListMarkerLineID";
break;
case AXProperty::ListMarkerText:
stream << "ListMarkerText";
break;
#endif // ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::LiveRegionAtomic:
stream << "LiveRegionAtomic";
break;
case AXProperty::LocalizedActionVerb:
stream << "LocalizedActionVerb";
break;
case AXProperty::MathFencedOpenString:
stream << "MathFencedOpenString";
break;
case AXProperty::MathFencedCloseString:
stream << "MathFencedCloseString";
break;
case AXProperty::MathLineThickness:
stream << "MathLineThickness";
break;
case AXProperty::MathPrescripts:
stream << "MathPrescripts";
break;
case AXProperty::MathPostscripts:
stream << "MathPostscripts";
break;
case AXProperty::MathRadicand:
stream << "MathRadicand";
break;
case AXProperty::MathRootIndexObject:
stream << "MathRootIndexObject";
break;
case AXProperty::MathUnderObject:
stream << "MathUnderObject";
break;
case AXProperty::MathOverObject:
stream << "MathOverObject";
break;
case AXProperty::MathNumeratorObject:
stream << "MathNumeratorObject";
break;
case AXProperty::MathDenominatorObject:
stream << "MathDenominatorObject";
break;
case AXProperty::MathBaseObject:
stream << "MathBaseObject";
break;
case AXProperty::MathSubscriptObject:
stream << "MathSubscriptObject";
break;
case AXProperty::MathSuperscriptObject:
stream << "MathSuperscriptObject";
break;
case AXProperty::MaxValueForRange:
stream << "MaxValueForRange";
break;
case AXProperty::MinValueForRange:
stream << "MinValueForRange";
break;
case AXProperty::NameAttribute:
stream << "NameAttribute";
break;
case AXProperty::OuterHTML:
stream << "OuterHTML";
break;
case AXProperty::Path:
stream << "Path";
break;
case AXProperty::PlaceholderValue:
stream << "PlaceholderValue";
break;
#if PLATFORM(COCOA)
case AXProperty::PlatformWidget:
stream << "PlatformWidget";
break;
#endif
case AXProperty::PosInSet:
stream << "PosInSet";
break;
case AXProperty::PreventKeyboardDOMEventDispatch:
stream << "PreventKeyboardDOMEventDispatch";
break;
case AXProperty::RadioButtonGroup:
stream << "RadioButtonGroup";
break;
case AXProperty::RelativeFrame:
stream << "RelativeFrame";
break;
case AXProperty::RemoteFrameOffset:
stream << "RemoteFrameOffset";
break;
case AXProperty::RemoteFramePlatformElement:
stream << "RemoteFramePlatformElement";
break;
#if PLATFORM(COCOA)
case AXProperty::RemoteParent:
stream << "RemoteParent";
break;
#endif
case AXProperty::RolePlatformString:
stream << "RolePlatformString";
break;
case AXProperty::Rows:
stream << "Rows";
break;
case AXProperty::RowHeaders:
stream << "RowHeaders";
break;
case AXProperty::RowIndex:
stream << "RowIndex";
break;
case AXProperty::RowIndexRange:
stream << "RowIndexRange";
break;
case AXProperty::ScreenRelativePosition:
stream << "ScreenRelativePosition";
break;
case AXProperty::SelectedTextRange:
stream << "SelectedTextRange";
break;
case AXProperty::SetSize:
stream << "SetSize";
break;
case AXProperty::SortDirection:
stream << "SortDirection";
break;
case AXProperty::SpeakAs:
stream << "SpeakAs";
break;
case AXProperty::StringValue:
stream << "StringValue";
break;
case AXProperty::SubrolePlatformString:
stream << "SubrolePlatformString";
break;
case AXProperty::SupportsDragging:
stream << "SupportsDragging";
break;
case AXProperty::SupportsDropping:
stream << "SupportsDropping";
break;
case AXProperty::SupportsARIAOwns:
stream << "SupportsARIAOwns";
break;
case AXProperty::SupportsCheckedState:
stream << "SupportsCheckedState";
break;
case AXProperty::SupportsCurrent:
stream << "SupportsCurrent";
break;
case AXProperty::SupportsDatetimeAttribute:
stream << "SupportsDatetimeAttribute";
break;
case AXProperty::SupportsExpanded:
stream << "SupportsExpanded";
break;
case AXProperty::SupportsExpandedTextValue:
stream << "SupportsExpandedTextValue";
break;
case AXProperty::SupportsKeyShortcuts:
stream << "SupportsKeyShortcuts";
break;
case AXProperty::SupportsPath:
stream << "SupportsPath";
break;
case AXProperty::SupportsPosInSet:
stream << "SupportsPosInSet";
break;
case AXProperty::SupportsSetSize:
stream << "SupportsSetSize";
break;
case AXProperty::TextContentPrefixFromListMarker:
stream << "TextContentPrefixFromListMarker";
break;
#if !ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::TextContent:
stream << "TextContent";
break;
#endif // !ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::TextInputMarkedTextMarkerRange:
stream << "TextInputMarkedTextMarkerRange";
break;
#if ENABLE(AX_THREAD_TEXT_APIS)
case AXProperty::TextRuns:
stream << "TextRuns";
break;
#endif
case AXProperty::Title:
stream << "Title";
break;
case AXProperty::TitleAttributeValue:
stream << "TitleAttributeValue";
break;
case AXProperty::URL:
stream << "URL";
break;
case AXProperty::UnderlineColor:
stream << "UnderlineColor";
break;
case AXProperty::ValueAutofillButtonType:
stream << "ValueAutofillButtonType";
break;
case AXProperty::ValueDescription:
stream << "ValueDescription";
break;
case AXProperty::ValueForRange:
stream << "ValueForRange";
break;
case AXProperty::VerticalScrollBar:
stream << "VerticalScrollBar";
break;
case AXProperty::VisibleChildren:
stream << "VisibleChildren";
break;
case AXProperty::VisibleRows:
stream << "VisibleRows";
break;
}
return stream;
}
#endif // ENABLE(ACCESSIBILITY_ISOLATED_TREE)
TextStream& operator<<(TextStream& stream, const AXCoreObject& object)
{
constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
streamAXCoreObject(stream, object, options);
return stream;
}
#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
TextStream& operator<<(TextStream& stream, AXIsolatedTree& tree)
{
ASSERT(!isMainThread());
TextStream::GroupScope groupScope(stream);
stream << "treeID " << tree.treeID();
stream.dumpProperty("rootNodeID"_s, tree.rootNode()->objectID());
stream.dumpProperty("focusedNodeID"_s, tree.m_focusedNodeID);
constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
if (RefPtr root = tree.rootNode())
streamSubtree(stream, root.releaseNonNull(), options);
return stream;
}
void streamIsolatedSubtreeOnMainThread(TextStream& stream, const AXIsolatedTree& tree, AXID objectID, const OptionSet<AXStreamOptions>& options)
{
ASSERT(isMainThread());
stream.increaseIndent();
TextStream::GroupScope groupScope(stream);
if (options & AXStreamOptions::ObjectID)
stream << "objectID " << objectID;
auto ids = tree.m_nodeMap.get(objectID);
if (options & AXStreamOptions::ParentID)
stream.dumpProperty("parentObject"_s, ids.parentID);
for (auto& childID : ids.childrenIDs)
streamIsolatedSubtreeOnMainThread(stream, tree, childID, options);
stream.decreaseIndent();
}
#endif
TextStream& operator<<(TextStream& stream, AXObjectCache& axObjectCache)
{
TextStream::GroupScope groupScope(stream);
stream << "AXObjectCache " << &axObjectCache;
RefPtr document = axObjectCache.document();
if (!document)
stream << "No document!";
else if (RefPtr root = axObjectCache.get(document->view())) {
constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
streamSubtree(stream, root.releaseNonNull(), options);
} else
stream << "No root!";
return stream;
}
#if ENABLE(AX_THREAD_TEXT_APIS)
static void streamTextRuns(TextStream& stream, const AXTextRuns& runs)
{
stream.dumpProperty("textRuns"_s, makeString(interleave(runs.runs, [](auto& builder, auto& run) {
builder.append(run.lineIndex, ":|"_s, run.text, "|(len: "_s, run.text.length(), ')');
}, ", "_s)));
}
#endif // ENABLE(AX_THREAD_TEXT_APIS)
void streamAXCoreObject(TextStream& stream, const AXCoreObject& object, const OptionSet<AXStreamOptions>& options)
{
if (options & AXStreamOptions::ObjectID)
stream << "objectID " << object.objectID();
if (options & AXStreamOptions::Role)
stream.dumpProperty("role"_s, object.role());
auto* axObject = dynamicDowncast<AccessibilityObject>(object);
if (axObject) {
if (auto* renderer = axObject->renderer())
stream.dumpProperty("renderer"_s, renderer->debugDescription());
else if (auto* node = axObject->node())
stream.dumpProperty("node"_s, node->debugDescription());
}
if (options & AXStreamOptions::ParentID) {
auto* parent = object.parentObjectUnignored();
stream.dumpProperty("parentID"_s, parent ? parent->objectID().toUInt64() : 0);
}
auto id = options & AXStreamOptions::IdentifierAttribute ? object.identifierAttribute() : emptyString();
if (!id.isEmpty())
stream.dumpProperty("identifier"_s, WTFMove(id));
if (options & AXStreamOptions::OuterHTML) {
auto role = object.role();
auto* objectWithInterestingHTML = role == AccessibilityRole::Button ? // Add here other roles of interest.
&object : nullptr;
auto* parent = object.parentObjectUnignored();
if (role == AccessibilityRole::StaticText && parent)
objectWithInterestingHTML = parent;
if (objectWithInterestingHTML)
stream.dumpProperty("outerHTML"_s, objectWithInterestingHTML->outerHTML().left(150));
}
#if ENABLE(AX_THREAD_TEXT_APIS)
if (options & AXStreamOptions::TextRuns) {
if (auto* isolatedObject = dynamicDowncast<AXIsolatedObject>(object)) {
if (auto* runs = isolatedObject->textRuns(); runs && runs->size())
streamTextRuns(stream, *runs);
} else if (axObject) {
if (auto runs = const_cast<AccessibilityObject*>(axObject)->textRuns(); runs.size())
streamTextRuns(stream, runs);
}
}
#endif // ENABLE(AX_THREAD_TEXT_APIS)
if (options & AXStreamOptions::DisplayContents) {
if (axObject && axObject->hasDisplayContents())
stream.dumpProperty("hasDisplayContents"_s, true);
}
if (options & AXStreamOptions::Address) {
stream.dumpProperty("address"_s, &object);
stream.dumpProperty("wrapper"_s, object.wrapper());
}
}
void streamSubtree(TextStream& stream, const Ref<AXCoreObject>& object, const OptionSet<AXStreamOptions>& options)
{
stream.increaseIndent();
TextStream::GroupScope groupScope(stream);
streamAXCoreObject(stream, object, options);
for (auto& child : object->unignoredChildren(/* updateChildrenIfNeeded */ false))
streamSubtree(stream, child, options);
stream.decreaseIndent();
}
} // namespace WebCore