AXLogger::streamAXCoreObject is missing a null check for AccessibilityObject dynamicD...
[webkit.git] / Source / WebCore / accessibility / AXLogger.cpp
blob07a81426a1025ce3664cae6021f68ff154de478b
1 /*
2 * Copyright (C) 2020 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
14 * its contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "config.h"
30 #include "AXLogger.h"
32 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
33 #include "AXIsolatedObject.h"
34 #endif
35 #include "AXObjectCache.h"
36 #include "FrameView.h"
37 #include "LogInitialization.h"
38 #include "Logging.h"
39 #include <wtf/OptionSet.h>
40 #include <wtf/text/TextStream.h>
42 namespace WebCore {
44 static bool shouldLog()
46 // Modify the initializer list below to choose what thread you want to log messages from.
47 static constexpr OptionSet<AXLoggingOptions> loggingOptions { AXLoggingOptions::MainThread, AXLoggingOptions::OffMainThread };
49 return (isMainThread() && loggingOptions & AXLoggingOptions::MainThread)
50 || (!isMainThread() && loggingOptions & AXLoggingOptions::OffMainThread);
53 #if !LOG_DISABLED
55 AXLogger::AXLogger(const String& methodName)
56 : m_methodName(methodName)
58 if (auto* channel = getLogChannel("Accessibility"_s))
59 channel->level = WTFLogLevel::Debug;
61 if (shouldLog()) {
62 if (!m_methodName.isEmpty())
63 LOG_WITH_STREAM(Accessibility, stream << m_methodName << " {");
67 AXLogger::~AXLogger()
69 if (shouldLog()) {
70 if (!m_methodName.isEmpty())
71 LOG_WITH_STREAM(Accessibility, stream << "} " << m_methodName);
75 void AXLogger::log(const String& message)
77 if (shouldLog())
78 LOG(Accessibility, "%s", message.utf8().data());
81 void AXLogger::log(const char* message)
83 if (shouldLog())
84 LOG(Accessibility, "%s", message);
87 void AXLogger::log(RefPtr<AXCoreObject> object)
89 if (shouldLog()) {
90 TextStream stream(TextStream::LineMode::MultipleLine);
92 if (object)
93 stream << *object;
94 else
95 stream << "null";
97 LOG(Accessibility, "%s", stream.release().utf8().data());
101 void AXLogger::log(const Vector<RefPtr<AXCoreObject>>& objects)
103 if (shouldLog()) {
104 TextStream stream(TextStream::LineMode::MultipleLine);
106 stream << "[";
107 for (auto object : objects) {
108 if (object)
109 stream << *object;
110 else
111 stream << "null";
113 stream << "]";
115 LOG(Accessibility, "%s", stream.release().utf8().data());
119 void AXLogger::log(const std::pair<RefPtr<AXCoreObject>, AXObjectCache::AXNotification>& notification)
121 if (shouldLog()) {
122 TextStream stream(TextStream::LineMode::MultipleLine);
123 stream << "Notification " << notification.second << " for object ";
124 if (notification.first)
125 stream << *notification.first;
126 else
127 stream << "null";
128 LOG(Accessibility, "%s", stream.release().utf8().data());
132 void AXLogger::log(const AccessibilitySearchCriteria& criteria)
134 TextStream stream(TextStream::LineMode::MultipleLine);
135 stream << criteria;
136 LOG(Accessibility, "%s", stream.release().utf8().data());
139 void AXLogger::log(AccessibilityObjectInclusion inclusion)
141 TextStream stream(TextStream::LineMode::SingleLine);
142 stream.dumpProperty("ObjectInclusion", inclusion);
143 LOG(Accessibility, "%s", stream.release().utf8().data());
146 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
147 void AXLogger::log(AXIsolatedTree& tree)
149 if (shouldLog()) {
150 TextStream stream(TextStream::LineMode::MultipleLine);
151 stream << tree;
152 LOG(Accessibility, "%s", stream.release().utf8().data());
155 #endif
157 void AXLogger::log(AXObjectCache& axObjectCache)
159 if (shouldLog()) {
160 TextStream stream(TextStream::LineMode::MultipleLine);
161 stream << axObjectCache;
162 LOG(Accessibility, "%s", stream.release().utf8().data());
166 #endif // !LOG_DISABLED
168 TextStream& operator<<(TextStream& stream, AccessibilityRole role)
170 stream << accessibilityRoleToString(role);
171 return stream;
174 TextStream& operator<<(TextStream& stream, AccessibilitySearchDirection direction)
176 switch (direction) {
177 case AccessibilitySearchDirection::Next:
178 stream << "Next";
179 break;
180 case AccessibilitySearchDirection::Previous:
181 stream << "Previous";
182 break;
185 return stream;
188 TextStream& operator<<(TextStream& stream, AccessibilitySearchKey searchKey)
190 switch (searchKey) {
191 case AccessibilitySearchKey::AnyType:
192 stream << "AnyType";
193 break;
194 case AccessibilitySearchKey::Article:
195 stream << "Article";
196 break;
197 case AccessibilitySearchKey::BlockquoteSameLevel:
198 stream << "BlockquoteSameLevel";
199 break;
200 case AccessibilitySearchKey::Blockquote:
201 stream << "Blockquote";
202 break;
203 case AccessibilitySearchKey::BoldFont:
204 stream << "BoldFont";
205 break;
206 case AccessibilitySearchKey::Button:
207 stream << "Button";
208 break;
209 case AccessibilitySearchKey::CheckBox:
210 stream << "CheckBox";
211 break;
212 case AccessibilitySearchKey::Control:
213 stream << "Control";
214 break;
215 case AccessibilitySearchKey::DifferentType:
216 stream << "DifferentType";
217 break;
218 case AccessibilitySearchKey::FontChange:
219 stream << "FontChange";
220 break;
221 case AccessibilitySearchKey::FontColorChange:
222 stream << "FontColorChange";
223 break;
224 case AccessibilitySearchKey::Frame:
225 stream << "Frame";
226 break;
227 case AccessibilitySearchKey::Graphic:
228 stream << "Graphic";
229 break;
230 case AccessibilitySearchKey::HeadingLevel1:
231 stream << "HeadingLevel1";
232 break;
233 case AccessibilitySearchKey::HeadingLevel2:
234 stream << "HeadingLevel2";
235 break;
236 case AccessibilitySearchKey::HeadingLevel3:
237 stream << "HeadingLevel3";
238 break;
239 case AccessibilitySearchKey::HeadingLevel4:
240 stream << "HeadingLevel4";
241 break;
242 case AccessibilitySearchKey::HeadingLevel5:
243 stream << "HeadingLevel5";
244 break;
245 case AccessibilitySearchKey::HeadingLevel6:
246 stream << "HeadingLevel6";
247 break;
248 case AccessibilitySearchKey::HeadingSameLevel:
249 stream << "HeadingSameLevel";
250 break;
251 case AccessibilitySearchKey::Heading:
252 stream << "Heading";
253 break;
254 case AccessibilitySearchKey::Highlighted:
255 stream << "Highlighted";
256 break;
257 case AccessibilitySearchKey::ItalicFont:
258 stream << "ItalicFont";
259 break;
260 case AccessibilitySearchKey::KeyboardFocusable:
261 stream << "KeyboardFocusable";
262 break;
263 case AccessibilitySearchKey::Landmark:
264 stream << "Landmark";
265 break;
266 case AccessibilitySearchKey::Link:
267 stream << "Link";
268 break;
269 case AccessibilitySearchKey::List:
270 stream << "List";
271 break;
272 case AccessibilitySearchKey::LiveRegion:
273 stream << "LiveRegion";
274 break;
275 case AccessibilitySearchKey::MisspelledWord:
276 stream << "MisspelledWord";
277 break;
278 case AccessibilitySearchKey::Outline:
279 stream << "Outline";
280 break;
281 case AccessibilitySearchKey::PlainText:
282 stream << "PlainText";
283 break;
284 case AccessibilitySearchKey::RadioGroup:
285 stream << "RadioGroup";
286 break;
287 case AccessibilitySearchKey::SameType:
288 stream << "SameType";
289 break;
290 case AccessibilitySearchKey::StaticText:
291 stream << "StaticText";
292 break;
293 case AccessibilitySearchKey::StyleChange:
294 stream << "StyleChange";
295 break;
296 case AccessibilitySearchKey::TableSameLevel:
297 stream << "TableSameLevel";
298 break;
299 case AccessibilitySearchKey::Table:
300 stream << "Table";
301 break;
302 case AccessibilitySearchKey::TextField:
303 stream << "TextField";
304 break;
305 case AccessibilitySearchKey::Underline:
306 stream << "Underline";
307 break;
308 case AccessibilitySearchKey::UnvisitedLink:
309 stream << "UnvisitedLink";
310 break;
311 case AccessibilitySearchKey::VisitedLink:
312 stream << "VisitedLink";
313 break;
316 return stream;
319 TextStream& operator<<(TextStream& stream, const AccessibilitySearchCriteria& criteria)
321 TextStream::GroupScope groupScope(stream);
322 auto streamCriteriaObject = [&stream] (ASCIILiteral objectLabel, auto* axObject) {
323 stream.startGroup();
324 stream << objectLabel.characters() << " " << axObject << ", ID " << (axObject ? axObject->objectID() : AXID());
325 stream.endGroup();
328 stream << "SearchCriteria " << &criteria;
329 streamCriteriaObject("anchorObject"_s, criteria.anchorObject);
330 streamCriteriaObject("startObject"_s, criteria.startObject);
331 stream.dumpProperty("searchDirection", criteria.searchDirection);
333 stream.nextLine();
334 stream << "(searchKeys [";
335 for (auto searchKey : criteria.searchKeys)
336 stream << searchKey << ", ";
337 stream << "])";
339 stream.dumpProperty("searchText", criteria.searchText);
340 stream.dumpProperty("resultsLimit", criteria.resultsLimit);
341 stream.dumpProperty("visibleOnly", criteria.visibleOnly);
342 stream.dumpProperty("immediateDescendantsOnly", criteria.immediateDescendantsOnly);
344 return stream;
347 TextStream& operator<<(TextStream& stream, AccessibilityObjectInclusion inclusion)
349 switch (inclusion) {
350 case AccessibilityObjectInclusion::IncludeObject:
351 stream << "IncludeObject";
352 break;
353 case AccessibilityObjectInclusion::IgnoreObject:
354 stream << "IgnoreObject";
355 break;
356 case AccessibilityObjectInclusion::DefaultBehavior:
357 stream << "DefaultBehavior";
358 break;
361 return stream;
364 TextStream& operator<<(TextStream& stream, AXObjectCache::AXNotification notification)
366 switch (notification) {
367 case AXObjectCache::AXNotification::AXActiveDescendantChanged:
368 stream << "AXActiveDescendantChanged";
369 break;
370 case AXObjectCache::AXNotification::AXAriaRoleChanged:
371 stream << "AXAriaRoleChanged";
372 break;
373 case AXObjectCache::AXNotification::AXAutocorrectionOccured:
374 stream << "AXAutocorrectionOccured";
375 break;
376 case AXObjectCache::AXNotification::AXCheckedStateChanged:
377 stream << "AXCheckedStateChanged";
378 break;
379 case AXObjectCache::AXNotification::AXChildrenChanged:
380 stream << "AXChildrenChanged";
381 break;
382 case AXObjectCache::AXNotification::AXCurrentStateChanged:
383 stream << "AXCurrentStateChanged";
384 break;
385 case AXObjectCache::AXNotification::AXDisabledStateChanged:
386 stream << "AXDisabledStateChanged";
387 break;
388 case AXObjectCache::AXNotification::AXFocusedUIElementChanged:
389 stream << "AXFocusedUIElementChanged";
390 break;
391 case AXObjectCache::AXNotification::AXFrameLoadComplete:
392 stream << "AXFrameLoadComplete";
393 break;
394 case AXObjectCache::AXNotification::AXIdAttributeChanged:
395 stream << "AXIdAttributeChanged";
396 break;
397 case AXObjectCache::AXNotification::AXImageOverlayChanged:
398 stream << "AXImageOverlayChanged";
399 break;
400 case AXObjectCache::AXNotification::AXLanguageChanged:
401 stream << "AXLanguageChanged";
402 break;
403 case AXObjectCache::AXNotification::AXLayoutComplete:
404 stream << "AXLayoutComplete";
405 break;
406 case AXObjectCache::AXNotification::AXLoadComplete:
407 stream << "AXLoadComplete";
408 break;
409 case AXObjectCache::AXNotification::AXNewDocumentLoadComplete:
410 stream << "AXNewDocumentLoadComplete";
411 break;
412 case AXObjectCache::AXNotification::AXPageScrolled:
413 stream << "AXPageScrolled";
414 break;
415 case AXObjectCache::AXNotification::AXSelectedChildrenChanged:
416 stream << "AXSelectedChildrenChanged";
417 break;
418 case AXObjectCache::AXNotification::AXSelectedStateChanged:
419 stream << "AXSelectedStateChanged";
420 break;
421 case AXObjectCache::AXNotification::AXSelectedTextChanged:
422 stream << "AXSelectedTextChanged";
423 break;
424 case AXObjectCache::AXNotification::AXValueChanged:
425 stream << "AXValueChanged";
426 break;
427 case AXObjectCache::AXNotification::AXScrolledToAnchor:
428 stream << "AXScrolledToAnchor";
429 break;
430 case AXObjectCache::AXNotification::AXLiveRegionCreated:
431 stream << "AXLiveRegionCreated";
432 break;
433 case AXObjectCache::AXNotification::AXLiveRegionChanged:
434 stream << "AXLiveRegionChanged";
435 break;
436 case AXObjectCache::AXNotification::AXMenuListItemSelected:
437 stream << "AXMenuListItemSelected";
438 break;
439 case AXObjectCache::AXNotification::AXMenuListValueChanged:
440 stream << "AXMenuListValueChanged";
441 break;
442 case AXObjectCache::AXNotification::AXMenuClosed:
443 stream << "AXMenuClosed";
444 break;
445 case AXObjectCache::AXNotification::AXMenuOpened:
446 stream << "AXMenuOpened";
447 break;
448 case AXObjectCache::AXNotification::AXRowCountChanged:
449 stream << "AXRowCountChanged";
450 break;
451 case AXObjectCache::AXNotification::AXRowCollapsed:
452 stream << "AXRowCollapsed";
453 break;
454 case AXObjectCache::AXNotification::AXRowExpanded:
455 stream << "AXRowExpanded";
456 break;
457 case AXObjectCache::AXNotification::AXExpandedChanged:
458 stream << "AXExpandedChanged";
459 break;
460 case AXObjectCache::AXNotification::AXInvalidStatusChanged:
461 stream << "AXInvalidStatusChanged";
462 break;
463 case AXObjectCache::AXNotification::AXPressDidSucceed:
464 stream << "AXPressDidSucceed";
465 break;
466 case AXObjectCache::AXNotification::AXPressDidFail:
467 stream << "AXPressDidFail";
468 break;
469 case AXObjectCache::AXNotification::AXPressedStateChanged:
470 stream << "AXPressedStateChanged";
471 break;
472 case AXObjectCache::AXNotification::AXReadOnlyStatusChanged:
473 stream << "AXReadOnlyStatusChanged";
474 break;
475 case AXObjectCache::AXNotification::AXRequiredStatusChanged:
476 stream << "AXRequiredStatusChanged";
477 break;
478 case AXObjectCache::AXNotification::AXSortDirectionChanged:
479 stream << "AXSortDirectionChanged";
480 break;
481 case AXObjectCache::AXNotification::AXTextChanged:
482 stream << "AXTextChanged";
483 break;
484 case AXObjectCache::AXNotification::AXElementBusyChanged:
485 stream << "AXElementBusyChanged";
486 break;
487 case AXObjectCache::AXNotification::AXDraggingStarted:
488 stream << "AXDraggingStarted";
489 break;
490 case AXObjectCache::AXNotification::AXDraggingEnded:
491 stream << "AXDraggingEnded";
492 break;
493 case AXObjectCache::AXNotification::AXDraggingEnteredDropZone:
494 stream << "AXDraggingEnteredDropZone";
495 break;
496 case AXObjectCache::AXNotification::AXDraggingDropped:
497 stream << "AXDraggingDropped";
498 break;
499 case AXObjectCache::AXNotification::AXDraggingExitedDropZone:
500 stream << "AXDraggingExitedDropZone";
501 break;
504 return stream;
507 TextStream& operator<<(TextStream& stream, const AXCoreObject& object)
509 constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
510 streamAXCoreObject(stream, object, options);
511 return stream;
514 #if ENABLE(ACCESSIBILITY_ISOLATED_TREE)
515 TextStream& operator<<(TextStream& stream, AXIsolatedTree& tree)
517 ASSERT(!isMainThread());
518 TextStream::GroupScope groupScope(stream);
519 stream << "treeID " << tree.treeID();
520 stream.dumpProperty("rootNodeID", tree.rootNode()->objectID());
521 stream.dumpProperty("focusedNodeID", tree.m_focusedNodeID);
522 constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
523 streamSubtree(stream, tree.rootNode(), options);
524 return stream;
527 void streamIsolatedSubtreeOnMainThread(TextStream& stream, const AXIsolatedTree& tree, AXID objectID, const OptionSet<AXStreamOptions>& options)
529 ASSERT(isMainThread());
531 if (!shouldLog())
532 return;
534 stream.increaseIndent();
535 TextStream::GroupScope groupScope(stream);
537 if (options & AXStreamOptions::ObjectID)
538 stream << "objectID " << objectID;
540 auto ids = tree.m_nodeMap.get(objectID);
541 if (options & AXStreamOptions::ParentID)
542 stream.dumpProperty("parentObject", ids.parentID);
544 for (auto& childID : ids.childrenIDs)
545 streamIsolatedSubtreeOnMainThread(stream, tree, childID, options);
547 stream.decreaseIndent();
549 #endif
551 TextStream& operator<<(TextStream& stream, AXObjectCache& axObjectCache)
553 TextStream::GroupScope groupScope(stream);
554 stream << "AXObjectCache " << &axObjectCache;
556 if (auto* root = axObjectCache.get(axObjectCache.document().view())) {
557 constexpr OptionSet<AXStreamOptions> options = { AXStreamOptions::ObjectID, AXStreamOptions::Role, AXStreamOptions::ParentID, AXStreamOptions::IdentifierAttribute, AXStreamOptions::OuterHTML, AXStreamOptions::DisplayContents, AXStreamOptions::Address };
558 streamSubtree(stream, root, options);
559 } else
560 stream << "No root!";
562 return stream;
565 void streamAXCoreObject(TextStream& stream, const AXCoreObject& object, const OptionSet<AXStreamOptions>& options)
567 if (options & AXStreamOptions::ObjectID)
568 stream << "objectID " << object.objectID();
570 if (options & AXStreamOptions::Role)
571 stream.dumpProperty("role", object.roleValue());
573 if (options & AXStreamOptions::ParentID) {
574 auto* parent = object.parentObjectUnignored();
575 stream.dumpProperty("parentObject", parent ? parent->objectID() : AXID());
578 if (options & AXStreamOptions::IdentifierAttribute)
579 stream.dumpProperty("identifierAttribute", object.identifierAttribute());
581 if (options & AXStreamOptions::OuterHTML) {
582 auto role = object.roleValue();
583 auto* objectWithInterestingHTML = role == AccessibilityRole::Button ? // Add here other roles of interest.
584 &object : nullptr;
586 auto* parent = object.parentObjectUnignored();
587 if (role == AccessibilityRole::StaticText && parent)
588 objectWithInterestingHTML = parent;
590 if (objectWithInterestingHTML)
591 stream.dumpProperty("outerHTML", objectWithInterestingHTML->outerHTML());
594 if (options & AXStreamOptions::DisplayContents) {
595 if (auto* axObject = dynamicDowncast<AccessibilityObject>(&object); axObject && axObject->hasDisplayContents())
596 stream.dumpProperty("hasDisplayContents", true);
599 if (options & AXStreamOptions::Address) {
600 stream.dumpProperty("address", &object);
601 stream.dumpProperty("wrapper", object.wrapper());
605 void streamSubtree(TextStream& stream, const RefPtr<AXCoreObject>& object, const OptionSet<AXStreamOptions>& options)
607 if (!object || !shouldLog())
608 return;
610 stream.increaseIndent();
612 TextStream::GroupScope groupScope(stream);
613 streamAXCoreObject(stream, *object, options);
614 for (auto& child : object->children(false))
615 streamSubtree(stream, child, options);
617 stream.decreaseIndent();
620 } // namespace WebCore