| // Copyright 2022 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef CHROME_RENDERER_ACCESSIBILITY_READ_ANYTHING_APP_CONTROLLER_H_ |
| #define CHROME_RENDERER_ACCESSIBILITY_READ_ANYTHING_APP_CONTROLLER_H_ |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "chrome/common/accessibility/read_anything.mojom.h" |
| #include "gin/wrappable.h" |
| #include "mojo/public/cpp/bindings/receiver.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "ui/accessibility/ax_node_id_forward.h" |
| #include "ui/accessibility/ax_tree_observer.h" |
| #include "ui/accessibility/ax_tree_update_forward.h" |
| |
| namespace content { |
| class RenderFrame; |
| } // namespace content |
| |
| namespace ui { |
| class AXNode; |
| class AXSerializableTree; |
| class AXTree; |
| } // namespace ui |
| |
| class AXTreeDistiller; |
| class ReadAnythingAppControllerTest; |
| |
| /////////////////////////////////////////////////////////////////////////////// |
| // ReadAnythingAppController |
| // |
| // A class that controls the Read Anything WebUI app. It serves two purposes: |
| // 1. Communicate with ReadAnythingPageHandler (written in c++) via mojom. |
| // 2. Communicate with ReadAnythingApp (written in ts) via gin bindings. |
| // The ReadAnythingAppController unserializes the AXTreeUpdate and exposes |
| // methods on it to the ts resource for accessing information about the AXTree. |
| // This class is owned by the ChromeRenderFrameObserver and has the same |
| // lifetime as the render frame. |
| // |
| // This class is responsible for identifying the nodes to be displayed by the |
| // webapp and providing attributes about them when queried. Nodes are selected |
| // from the provided AXTreeUpdate and content nodes. There are two rendering |
| // algorithms: |
| // 1. If the AXTreeUpdate has a selection, display a subtree containing all of |
| // the nodes between the selection start and end. |
| // 2. If the AXTreeUpdate has no selection, display a subtree containing all of |
| // the content nodes, their descendants, and their ancestors. |
| // |
| class ReadAnythingAppController |
| : public gin::Wrappable<ReadAnythingAppController>, |
| public read_anything::mojom::Page, |
| public ui::AXTreeObserver { |
| public: |
| static gin::WrapperInfo kWrapperInfo; |
| |
| ReadAnythingAppController(const ReadAnythingAppController&) = delete; |
| ReadAnythingAppController& operator=(const ReadAnythingAppController&) = |
| delete; |
| |
| // Installs v8 context for Read Anything and adds chrome.readAnything binding |
| // to page. |
| static ReadAnythingAppController* Install(content::RenderFrame* render_frame); |
| |
| private: |
| friend ReadAnythingAppControllerTest; |
| |
| explicit ReadAnythingAppController(content::RenderFrame* render_frame); |
| ~ReadAnythingAppController() override; |
| |
| // gin::WrappableBase: |
| gin::ObjectTemplateBuilder GetObjectTemplateBuilder( |
| v8::Isolate* isolate) override; |
| |
| // read_anything::mojom::Page: |
| void AccessibilityEventReceived( |
| const ui::AXTreeID& tree_id, |
| const std::vector<ui::AXTreeUpdate>& updates, |
| const std::vector<ui::AXEvent>& events) override; |
| void OnActiveAXTreeIDChanged(const ui::AXTreeID& tree_id) override; |
| void OnAXTreeDestroyed(const ui::AXTreeID& tree_id) override; |
| void OnThemeChanged( |
| read_anything::mojom::ReadAnythingThemePtr new_theme) override; |
| |
| // ui::AXTreeObserver: |
| void OnAtomicUpdateFinished(ui::AXTree* tree, |
| bool root_changed, |
| const std::vector<Change>& changes) override; |
| // TODO(crbug.com/1266555): Implement OnNodeWillBeDeleted to capture the |
| // deletion of child trees. |
| |
| // gin templates: |
| ui::AXNodeID RootId() const; |
| SkColor BackgroundColor() const; |
| std::string FontName() const; |
| float FontSize() const; |
| SkColor ForegroundColor() const; |
| float LetterSpacing() const; |
| float LineSpacing() const; |
| std::vector<ui::AXNodeID> GetChildren(ui::AXNodeID ax_node_id) const; |
| std::string GetHtmlTag(ui::AXNodeID ax_node_id) const; |
| std::string GetLanguage(ui::AXNodeID ax_node_id) const; |
| std::string GetTextContent(ui::AXNodeID ax_node_id) const; |
| std::string GetTextDirection(ui::AXNodeID ax_node_id) const; |
| std::string GetUrl(ui::AXNodeID ax_node_id) const; |
| void OnConnected(); |
| void OnLinkClicked(ui::AXNodeID ax_node_id) const; |
| |
| void Distill(); |
| void Draw(); |
| |
| void UnserializeUpdates(std::vector<ui::AXTreeUpdate> updates, |
| const ui::AXTreeID& tree_id); |
| |
| // Called when distillation has completed. |
| void OnAXTreeDistilled(const std::vector<ui::AXNodeID>& content_node_ids); |
| |
| // Helper functions for the rendering algorithm. Post-process the AXTree and |
| // cache values before sending an `updateContent` notification to the Read |
| // Anything app.ts. These functions: |
| // 1. Save state related to selection (start_node_, end_node_, start_offset_, |
| // end_offset_). |
| // 2. Save the display_node_ids_, which is a set of all nodes to be displayed |
| // in Read Anything app.ts. |
| void PostProcessAXTreeWithSelection(); |
| void PostProcessDistillableAXTree(); |
| |
| // The following methods are used for testing ReadAnythingAppTest. |
| // Snapshot_lite is a data structure which resembles an AXTreeUpdate. E.g.: |
| // const axTree = { |
| // root_id: 1, |
| // nodes: [ |
| // { |
| // id: 1, |
| // role: 'rootWebArea', |
| // child_ids: [2], |
| // }, |
| // { |
| // id: 2, |
| // role: 'staticText', |
| // name: 'Some text.', |
| // }, |
| // ], |
| // }; |
| void SetContentForTesting(v8::Local<v8::Value> v8_snapshot_lite, |
| std::vector<ui::AXNodeID> content_node_ids); |
| void SetThemeForTesting(const std::string& font_name, |
| float font_size, |
| SkColor foreground_color, |
| SkColor background_color, |
| int line_spacing, |
| int letter_spacing); |
| AXTreeDistiller* SetDistillerForTesting( |
| std::unique_ptr<AXTreeDistiller> distiller); |
| |
| double GetLetterSpacingValue( |
| read_anything::mojom::Spacing letter_spacing) const; |
| double GetLineSpacingValue(read_anything::mojom::Spacing line_spacing) const; |
| |
| ui::AXNode* GetAXNode(ui::AXNodeID ax_node_id) const; |
| |
| bool NodeIsContentNode(ui::AXNodeID ax_node_id) const; |
| |
| content::RenderFrame* render_frame_; |
| std::unique_ptr<AXTreeDistiller> distiller_; |
| mojo::Remote<read_anything::mojom::PageHandlerFactory> page_handler_factory_; |
| mojo::Remote<read_anything::mojom::PageHandler> page_handler_; |
| mojo::Receiver<read_anything::mojom::Page> receiver_{this}; |
| |
| // State: |
| |
| // AXTrees of all of the web contents in the browser’s tab strip. |
| std::map<ui::AXTreeID, std::unique_ptr<ui::AXSerializableTree>> trees_; |
| |
| // The AXTreeID of the currently active web contents. |
| ui::AXTreeID active_tree_id_ = ui::AXTreeIDUnknown(); |
| |
| // Distillation is slow and happens out-of-process when Screen2x is running. |
| // This boolean marks when distillation is in progress to avoid sending |
| // new distillation requests during that time. |
| bool distillation_in_progress_ = false; |
| |
| // A queue of pending updates on the active AXTree, which will be |
| // unserialized once distillation completes. |
| std::vector<ui::AXTreeUpdate> pending_updates_; |
| |
| // The node IDs identified as main by the distiller. These are static text |
| // nodes when generated by Screen2x. When generated by the rules-based |
| // distiller, these are heading or paragraph subtrees. |
| std::vector<ui::AXNodeID> content_node_ids_; |
| |
| // The node IDs that are displayed in the Read Anything app. This contains |
| // all ancestors and descendants of each content node. Or, if no content |
| // nodes were identified, this contains all nodes between the start and end |
| // nodes of the selection. |
| std::set<ui::AXNodeID> display_node_ids_; |
| |
| // Selection information. |
| bool has_selection_ = false; |
| ui::AXNode* start_node_ = nullptr; |
| ui::AXNode* end_node_ = nullptr; |
| int32_t start_offset_ = -1; |
| int32_t end_offset_ = -1; |
| |
| // Theme information. |
| SkColor background_color_; |
| std::string font_name_; |
| float font_size_; |
| SkColor foreground_color_; |
| float letter_spacing_; |
| float line_spacing_; |
| |
| base::WeakPtrFactory<ReadAnythingAppController> weak_ptr_factory_{this}; |
| }; |
| |
| #endif // CHROME_RENDERER_ACCESSIBILITY_READ_ANYTHING_APP_CONTROLLER_H_ |