Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@chunhtai
Copy link
Contributor

@chunhtai chunhtai commented Jan 7, 2021

Description

design doc
go/accessibility-bridge-detailed-design

Related Issues

flutter/flutter#23601

Tests

I added the following tests:

see files

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the contributor guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the C++, Objective-C, Java style guides for the engine.
  • I read the tree hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation.
  • All existing and new tests are passing.
  • I am willing to follow-up on review comments in a timely manner.

Reviewer Checklist

Breaking Change

Did any tests fail when you ran them? Please read handling breaking changes.

@google-cla google-cla bot added the cla: yes label Jan 7, 2021
@chunhtai chunhtai requested review from dnfield and gw280 January 7, 2021 20:15
Copy link
Contributor

@dnfield dnfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've mainly looked at the header files so far, not the implementation so much.

It might help to have a design doc for this laying out what the class heirarchy/relationships will be.

In general:

  • Please avoid using raw pointers in C++ code unless absolutely necessary.
  • Please clarify the relationship between FlutterAccessibility and the AccessibilityBridge class, and who owns what.


namespace ui { // namespace

constexpr int khasScrollingAction =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
constexpr int khasScrollingAction =
constexpr int kHasScrollingAction =

(and elsewhere)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also might want to define this in the header that defines FlutterSemanticsAction, or perhaps as a method in there with a comment to update it if we add anything to the semantics action enumeration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will be in the embedder.h, I think we should not add an API there if this is only matter for this use case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could export it from embedder.h and just define it from kScrollableSemanticsActions in semantics_node right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

embedder.h redefined FlutterSemanticsActions. I will add a constant in the embedder.h

Comment on lines 46 to 48
/// @brief Handle accessibility events generated due to accessibility
/// tree
/// changes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: formatting here and below. Looks like clang-format might have added line breaks wher eyou don't want them.

not a nit: these docs need to be improved. If this really is the one coming from Flutter to platform, then we should give some examples of what kind of events might get sent and handled here. We should talk about what a targetted event is - e.g. what can be a target, what kind of events there are.

Also, please avoid using raw pointers - I'm assuming this probably wants to be a std::shared_ptr<AccessibilityBridge>, but a weak or unique is fine too if that's what's appropriate.

AccessibilityBridge* bridge) = 0;

//------------------------------------------------------------------------------
/// @brief Dispatch accessibility action back to the Flutter framework
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document some examples, and how these are different from events.

//------------------------------------------------------------------------------
/// @brief Gets the accessibility bridge to which this accessibility node
/// belongs.
AccessibilityBridge* GetBridge() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this linkage?

We should come up with a working and as-simple-as-possible ownership model for this.

More specifically: we need to avoid a model where nodes have pointers to other nodes, and to bridges, and bridges also have pointers back to the nodes, and no one knows who owns what or how to properly free memory anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discuss offline, I will keep this as is


//------------------------------------------------------------------------------
/// @brief Gets the underlying ax node for this accessibility node.
AXNode* GetAXNode() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like we're leaking an abstraction.

Do we want to make this implement AXNode instead?

Do we need to publicly expose this?

And again, we should avoid raw pointer usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we discuss offline, I will keep this as is

Comment on lines 73 to 74
AXNode* ax_node_;
AccessibilityBridge* bridge_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid raw pointers.

I'm also still questioning why we'd need a pointer from this object to an a11y bridge, but I might be misunderstanding what this object does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this object wraps AXNode and uses it to implement AXPlatformNodeDelegate API, it needs reference so that it can have access to the parent and its sibling which is required to implement the AXPlatformNodeDelegate API. Alternatively, we can store all these information in this node, but we will have to keep updating them when the structure of AXTree changes.

@dnfield
Copy link
Contributor

dnfield commented Jan 7, 2021

Sorry, I see you linked a design doc here, I'll take a look there as well :)


namespace flutter { // namespace

constexpr int kHasScrollingAction =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to export this const int in embedder.h if it uses enum in the same file?
I tried

FLUTTER_EXPORT const int32_t kHasScrollingAction =
    kFlutterSemanticsActionScrollLeft |
    kFlutterSemanticsActionScrollRight |
    kFlutterSemanticsActionScrollUp |
    kFlutterSemanticsActionScrollDown;

but it kept complaining about

duplicate symbol '_kHasScrollingAction' in:
    obj/flutter/shell/platform/embedder/embedder.embedder_engine.o
    obj/flutter/shell/platform/embedder/embedder.platform_view_embedder.o

Do i miss something?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can only be defined in one place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I remove the definition in this file and put it in embedder.h only.

/// To flush the pending updates, call the CommitUpdates().
///
/// @param[in] node A pointer to the semantics node update.
void AddFlutterSemanticsNodeUpdate(const FlutterSemanticsNode* node);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I keep it as is, unless we want to create a separate memory copy in the embedding, we cannot use smart pointer here

/// @param[in] action A pointer to the custom semantics action
/// update.
void AddFlutterSemanticsCustomActionUpdate(
const FlutterSemanticsCustomAction* action);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the custom action here is the available custom action on a semantics node because the embedding sends the custom action information of a node separately from the node update. Not really sure how I should put in the doc here...

std::unique_ptr<AccessibilityBridgeDelegate> delegate_;

private:
// See FlutterSemanticsNode in embedder.h
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two reason i did this.

  1. the embedding sends update to a semantics node one by one, and the memory of the semantics node is destroy right before it sends the next one. That means I have to create a memory copy of the the FlutterSemanticsNode, which means i have to write a function to assign each attribute one by one anyway.
  2. continuing on 1, the FlutterSemanticsNode uses raw pointer to hold data array, which is make it a bit risky to manage the memory allocation. If I create a different struct to use std vector, it will be easier to manage the memory allocation.

/// constructor doesn't take any arguments because in the Windows
/// subclass we use a special function to construct a COM object.
/// Subclasses must call super.
virtual void Init(AccessibilityBridge* bridge, ui::AXNode* node);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept AccessibilityBridge as raw pointer, I have tried to use shared_ptr/weak_ptr but that forces the owner that create AccessibilityBridge to store it as shared_ptr. I found that quite confusing.

for example, the make embedding can only create AccessibilityBridge using this

std::shared_ptr< AccessibilityBridge> _bridge = std::make_shared<AccessibilityBridge>(delegate);

they cannot do the following

AccessibilityBridge _beidge(delegate);

or

std::unique_ptr< AccessibilityBridge> _bridge = std::make_unique<AccessibilityBridge>(delegate);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's actually less confusing because it clarifies up front that the embedding is creating something that it does not exclusively own.

If you want to stack allocate the bridge (and destroy it when the creator is collected), then we should be giving a weak pointer here.

@chunhtai
Copy link
Contributor Author

@dnfield This is ready for another look

//------------------------------------------------------------------------------
/// @brief Gets the accessibility bridge to which this accessibility node
/// belongs.
AccessibilityBridge* GetBridge() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be a raw pointer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed this getter because subclass should figure out their way to access the bridge in the embedding. but it still need to be stored in this class as a raw pointer, I couldn't figure out a way to use smart pointer in this case without forcing the owner of accessiblity bridge to hold it as shared_ptr.


//------------------------------------------------------------------------------
/// @brief Gets the underlying ax node for this accessibility node.
ui::AXNode* GetAXNode() const;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shouldn't be a raw pointer.

It also seems like it shouldn't be public. Why would an embedder ever need to access this?

Copy link
Contributor Author

@chunhtai chunhtai Jan 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the AXNode is owned by the axtree and cannot be a smart pointer here as well. I make it a protected method which should make it less risky

id_wrapper_map_;
ui::AXTree tree_;
ui::AXEventGenerator event_generator_;
std::unordered_map<int32_t, SemanticsNode> _pending_semantics_node_updates;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename this so the _ is at the end.

const ui::AXCoordinateSystem coordinate_system,
const ui::AXClippingBehavior clipping_behavior,
ui::AXOffscreenResult* offscreen_result) const {
// TODO(chunhtai): consider screen dpr.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should have a bug filed.

However, we should be handling this better. Have you tested this code at multiple DPR scales?

Also, we should document who has the responsibility of multiplying byt he DPR - whether it's the bridge or a node delegate.

@chunhtai chunhtai requested a review from dnfield January 20, 2021 21:13
@chunhtai
Copy link
Contributor Author

a friendly bump

Copy link
Contributor

@dnfield dnfield left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@chunhtai chunhtai added the waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land. label Jan 22, 2021
@fluttergithubbot fluttergithubbot merged commit 0118b54 into flutter:master Jan 22, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jan 22, 2021
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Jan 22, 2021
zanderso pushed a commit to flutter/flutter that referenced this pull request Jan 23, 2021
* 5b2defd Roll Fuchsia Mac SDK from MrtHftV0U... to 7nZajqutF... (flutter/engine#23827)

* c8ffb20 Roll Skia from 87a055b02027 to e0d023562bd9 (1 revision) (flutter/engine#23829)

* 296902b implemented GetMainContext() for opengl (flutter/engine#23634)

* 5cf3eae Roll Skia from e0d023562bd9 to 982127b7d57d (4 revisions) (flutter/engine#23831)

* fd9a079 [iOS] Fixes DisplayLinkManager leaks (flutter/engine#22194)

* b241537 Roll Fuchsia Linux Toolchain from git_revision:2c0536b76b35fa592ac7b4a0e4bb176eaf55af75 to IJxh_9dNS... (flutter/engine#23832)

* 5c2003f Roll Skia from 982127b7d57d to 6de1e52d0b12 (1 revision) (flutter/engine#23834)

* 5b9cd44 Automatically download Noto fonts as backup fonts in CanvasKit mode (flutter/engine#23728)

* 70a6824 Roll Dart SDK from 5e24a66b1bb8 to 704928da5702 (2 revisions) (flutter/engine#23838)

* b0c46d8 Roll Skia from 6de1e52d0b12 to 8a37fb2c605b (5 revisions) (flutter/engine#23836)

* d4132ea Use references when iterating over SkParagraph text boxes (flutter/engine#23837)

* 87960d8 Fix typo in embedder unit tests (flutter/engine#23783)

* 7f66714 iOS deeplink sends "path + query" instead of just path (flutter/engine#23562)

* 1474d08 Roll Skia from 8a37fb2c605b to 37d16f135265 (4 revisions) (flutter/engine#23839)

* 3da13fc Make android more lenient when it comes to out-of-order key event responses (flutter/engine#23604)

* 9223073 Fix background crash when FlutterView going appear while app goes background (flutter/engine#23175)

* 7c19824 Pass the filename directly to JNI for loading deferred component. (flutter/engine#23824)

* 5dc2469 Reland path vol tracker (flutter/engine#23840)

* e7e76f1 Roll Skia from 37d16f135265 to e89d8ea20b62 (2 revisions) (flutter/engine#23841)

* 07f4861 Roll Dart SDK from 704928da5702 to 1db2d4d95562 (1 revision) (flutter/engine#23846)

* 993ab78 Roll Skia from e89d8ea20b62 to c09761f57605 (1 revision) (flutter/engine#23843)

* a4836a6 Call Dart plugin registrant if available (flutter/engine#23813)

* 475a234 Roll Fuchsia Linux SDK from UGavhI1zv... to mODEe2CNk... (flutter/engine#23848)

* b51da31 Roll Skia from c09761f57605 to 450f8565c7f3 (5 revisions) (flutter/engine#23851)

* cb7106d Roll Skia from 450f8565c7f3 to 372791761157 (1 revision) (flutter/engine#23855)

* 69980e5 Roll Fuchsia Mac SDK from 7nZajqutF... to tuJCioUf3... (flutter/engine#23857)

* 20ff574 Roll Skia from 372791761157 to ce75036b3eaf (4 revisions) (flutter/engine#23858)

* 0118b54 Implements accessibility bridge in common library (flutter/engine#23491)

* ffc77f0 Search multiple paths when loading deferred component .so files. (flutter/engine#23849)

* 71d264d Revert "implemented GetMainContext() for opengl (#23634)" (flutter/engine#23859)

* fb48735 Roll Skia from ce75036b3eaf to cc6961b9ac5e (3 revisions) (flutter/engine#23860)

* fdddf87 Roll Dart SDK from 1db2d4d95562 to 82b4c77fb17f (2 revisions) (flutter/engine#23861)
hjfreyer pushed a commit to hjfreyer/engine that referenced this pull request Mar 22, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

accessibility affects: desktop cla: yes waiting for tree to go green This PR is approved and tested, but waiting for the tree to be green to land.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants