-
Notifications
You must be signed in to change notification settings - Fork 6k
Isolate the AccessibilityBridge from single views #36573
Changes from all commits
f766226
80a735a
65edba4
eda5c5d
186778d
dfc01b7
17e33db
8b4f689
9bb124f
6cd5c67
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,9 +20,9 @@ | |
namespace flutter { | ||
|
||
//------------------------------------------------------------------------------ | ||
/// Use this class to maintain an accessibility tree. This class consumes | ||
/// semantics updates from the embedder API and produces an accessibility tree | ||
/// in the native format. | ||
/// Use this class to maintain the accessibility tree for a view. This class | ||
/// consumes semantics updates from the embedder API and produces an | ||
/// accessibility tree in the native format. | ||
/// | ||
/// The bridge creates an AXTree to hold the semantics data that comes from | ||
/// Flutter semantics updates. The tree holds AXNode[s] which contain the | ||
|
@@ -63,6 +63,7 @@ class AccessibilityBridge | |
class AccessibilityBridgeDelegate { | ||
public: | ||
virtual ~AccessibilityBridgeDelegate() = default; | ||
|
||
//--------------------------------------------------------------------------- | ||
/// @brief Handle accessibility events generated due to accessibility | ||
/// tree changes. These events are generated in accessibility | ||
|
@@ -101,12 +102,19 @@ class AccessibilityBridge | |
CreateFlutterPlatformNodeDelegate() = 0; | ||
}; | ||
|
||
//----------------------------------------------------------------------------- | ||
/// @brief Creates a new instance of a accessibility bridge without a | ||
/// delegate. | ||
/// | ||
/// The bridge is not ready for use until a delegate has been | ||
/// assigned. See AccessibilityBridge::UpdateDelegate. | ||
AccessibilityBridge(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. From my understanding, this trick is necessary due to a circular dependency between the accessibility bridge and the delegate. The drawback is that it's now possible for the accessibility bridge to be in an invalid state. Would it be possible to avoid this circular dependency to avoid this drawback? For example, perhaps the accessibility bridge could provide its instance when it calls the delegate's methods? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're absolutely correct about the drawback. There's another approach: The bridge is initialized with the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another approach is to let AccessibilityBridge to be able to host multiple AXTrees, and I think that is what chromium does, too. The AXTreeData can set the id for different AXTree, and you can also derive axtree id from a AXNode. You can let the FlutterEngine to create a wrapper delegate that wires up action and event to different ViewController and view. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can, but is it making a big difference? In the current approach, the engine will manage a map of view controllers and bridges. In your approach, the engine will manage a map of view controllers and one bridge, and the bridge will manage a map of delegates and trees (which is likely another class). So you're basically moving what's in the engine into the bridge singleton, which isn't much code (just map addition and map removal). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually I'm wondering, why can't we merge the delegate and the bridge? We'll still use virtual methods to allow platform-specific code, and reassign views when the view is being swapped. The engine will hold a map of bridges, and there will be no circular reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My original thought about the multiple AXTree in one bridge vs Multiple bridges was on how easy to use the bridge in different platforms. I am not sure how the embedding code will be like, so I will leave the decision to you. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think that doing the same thing that chromium does will help us in the short term. We will definitely encounter mistakes in the short term as we add this new functionality. The more help we can get, the better. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
My initial thought was also the 2nd option, but I gave up because it's still very hard to initialize the bridge. There are two ways: If the outside code assign the bridge to the delegate manually, it'd be hard to get the delegate since it's been moved in, and has to use a dangerous raw pointer: void productionCode() {
auto delegate = new FlutterPlatformNodeDelegateMac();
// The delegate will be moved away before it can be assigned a bridge.
auto bridge = new AccessibilityBridge(std::unique_ptr<FlutterPlatformNodeDelegateMac>(delegate));
delegate->SetBridge(bridge);
} Or the bridge can also assign the delegate automatically, but then the bridge needs to access its own AccessibilityBridge::AccessibilityBridge(std::unique_ptr<AccessibilityBridgeDelegate> delegate) {
delegate_ = std::move(delegate);
delegate_->SetBridge(weak_from_this()); // crashes
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
What kind of help and from whom are we expecting? The code is going to evolve and who other than the Flutter contributors will read our code? I think the best kind of help is that we choose the best structure that suits us with the best maintainability and cleanest flow, instead of trying to fit our feet in others' shoes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
shared_from_this and weak_from_this requires the AccessibilityBridge to be created with shared_pointer. It should not crash though since the flutterengine in macos already holds it as shared_ptr, and it has already used shared_from_this in UpdateDelegate. Anyway, I don't think this is the right way though, not every platform's delegate needs the reference to bridge.
I am not sure what moved away means, can you use shared_ptr + weak_ptr? the unqiue ptr doesn't really make sense in this ownership model if multiple objects want references from it. it would also be good to draw out the ownership model, right now there seems to be a lot of moving parts, engine, bridge, delegate, viewcontroller. |
||
|
||
//----------------------------------------------------------------------------- | ||
/// @brief Creates a new instance of a accessibility bridge. | ||
/// | ||
/// @param[in] user_data A custom pointer to the data of your | ||
/// choice. This pointer can be retrieve later | ||
/// through GetUserData(). | ||
/// This is effectively identical to the default constructor | ||
/// followed by AccessibilityBridge::UpdateDelegate. | ||
explicit AccessibilityBridge( | ||
std::unique_ptr<AccessibilityBridgeDelegate> delegate); | ||
~AccessibilityBridge(); | ||
|
Uh oh!
There was an error while loading. Please reload this page.