mirror of
https://github.com/aphrody-labs/cef.git
synced 2026-10-13 21:07:07 +00:00
osr: mac: cefclient: Fix accessibility object lifetime (fixes #3595)
Native accessibility objects now have explicit ownership, detach safely before node deletion, and are released during view teardown.
This commit is contained in:
1 parent
9270759d79
commit
49815fbdaa
17 files changed
+347
-73
No files matched your search
@@ -2261,6 +2261,7 @@ if (is_mac) {
|
||||
gypi_paths2.cefclient_sources_browser +
|
||||
gypi_paths2.cefclient_sources_common +
|
||||
gypi_paths2.shared_sources_osr_mac +
|
||||
gypi_paths2.shared_sources_osr_accessibility +
|
||||
gypi_paths2.cefclient_sources_mac
|
||||
deps = [
|
||||
":libcef_dll_wrapper",
|
||||
@@ -2483,6 +2484,7 @@ if (is_mac) {
|
||||
gypi_paths2.shared_sources_mac +
|
||||
gypi_paths2.ceftests_sources_common +
|
||||
gypi_paths2.shared_sources_osr_mac +
|
||||
gypi_paths2.shared_sources_osr_accessibility +
|
||||
gypi_paths2.ceftests_sources_mac
|
||||
deps = [
|
||||
":libcef_dll_wrapper",
|
||||
@@ -2569,6 +2571,7 @@ if (is_mac) {
|
||||
gypi_paths2.includes_wrapper_win +
|
||||
gypi_paths2.shared_sources_win +
|
||||
gypi_paths2.cefclient_sources_win +
|
||||
gypi_paths2.shared_sources_osr_accessibility +
|
||||
gypi_paths2.cefclient_sources_resources_win_rc
|
||||
|
||||
# Set /SUBSYSTEM:WINDOWS.
|
||||
@@ -2665,6 +2668,7 @@ if (is_mac) {
|
||||
gypi_paths2.cefclient_sources_common +
|
||||
gypi_paths2.cefclient_sources_renderer +
|
||||
gypi_paths2.cefclient_sources_win +
|
||||
gypi_paths2.shared_sources_osr_accessibility +
|
||||
gypi_paths2.cefclient_sources_resources_win_rc + [
|
||||
# Generated installer config resource (embedded in client DLL,
|
||||
# read by bootstrap.exe).
|
||||
|
||||
+8
-9
@@ -229,11 +229,18 @@
|
||||
'tests/shared/process_helper_mac.cc',
|
||||
],
|
||||
'shared_sources_osr_mac': [
|
||||
'tests/shared/browser/osr_accessibility_node_mac.mm',
|
||||
'tests/shared/browser/osr_begin_frame_timer_mac.h',
|
||||
'tests/shared/browser/osr_begin_frame_timer_mac.mm',
|
||||
'tests/shared/browser/osr_renderer_metal.h',
|
||||
'tests/shared/browser/osr_renderer_metal.mm',
|
||||
],
|
||||
'shared_sources_osr_accessibility': [
|
||||
'tests/shared/browser/osr_accessibility_helper.cc',
|
||||
'tests/shared/browser/osr_accessibility_helper.h',
|
||||
'tests/shared/browser/osr_accessibility_node.cc',
|
||||
'tests/shared/browser/osr_accessibility_node.h',
|
||||
],
|
||||
'shared_sources_win': [
|
||||
'tests/shared/browser/main_message_loop_external_pump_win.cc',
|
||||
'tests/shared/browser/resource_util_win.cc',
|
||||
@@ -379,10 +386,6 @@
|
||||
'tests/cefclient/browser/main_context_impl_win.cc',
|
||||
'tests/cefclient/browser/main_message_loop_multithreaded_win.cc',
|
||||
'tests/cefclient/browser/main_message_loop_multithreaded_win.h',
|
||||
'tests/cefclient/browser/osr_accessibility_helper.cc',
|
||||
'tests/cefclient/browser/osr_accessibility_helper.h',
|
||||
'tests/cefclient/browser/osr_accessibility_node.cc',
|
||||
'tests/cefclient/browser/osr_accessibility_node.h',
|
||||
'tests/cefclient/browser/osr_accessibility_node_win.cc',
|
||||
'tests/cefclient/browser/osr_dragdrop_win.cc',
|
||||
'tests/cefclient/browser/osr_dragdrop_win.h',
|
||||
@@ -421,11 +424,6 @@
|
||||
'tests/cefclient/browser/browser_window_std_mac.h',
|
||||
'tests/cefclient/browser/browser_window_std_mac.mm',
|
||||
'tests/cefclient/browser/main_context_impl_posix.cc',
|
||||
'tests/cefclient/browser/osr_accessibility_helper.cc',
|
||||
'tests/cefclient/browser/osr_accessibility_helper.h',
|
||||
'tests/cefclient/browser/osr_accessibility_node.cc',
|
||||
'tests/cefclient/browser/osr_accessibility_node.h',
|
||||
'tests/cefclient/browser/osr_accessibility_node_mac.mm',
|
||||
'tests/cefclient/browser/root_window_mac.h',
|
||||
'tests/cefclient/browser/root_window_mac.mm',
|
||||
'tests/cefclient/browser/temp_window_mac.h',
|
||||
@@ -691,6 +689,7 @@
|
||||
'tests/ceftests/win/ceftests.rc',
|
||||
],
|
||||
'ceftests_sources_mac': [
|
||||
'tests/ceftests/osr_accessibility_unittest_mac.mm',
|
||||
'tests/ceftests/osr_begin_frame_unittest_mac.mm',
|
||||
'tests/ceftests/osr_renderer_metal_unittest.mm',
|
||||
'tests/ceftests/os_rendering_unittest_mac.h',
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
'includes': [
|
||||
'shared_sources_mac',
|
||||
'shared_sources_osr_mac',
|
||||
'shared_sources_osr_accessibility',
|
||||
'cefclient_sources_mac',
|
||||
],
|
||||
}}
|
||||
@@ -72,6 +73,7 @@
|
||||
'set': 'CEFCLIENT_WINDOWS_SRCS',
|
||||
'includes': [
|
||||
'shared_sources_win',
|
||||
'shared_sources_osr_accessibility',
|
||||
'cefclient_sources_win',
|
||||
'cefclient_sources_resources_win_rc',
|
||||
],
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "tests/cefclient/browser/bytes_write_handler.h"
|
||||
#include "tests/cefclient/browser/main_context.h"
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/cefclient/browser/text_input_client_osr_mac.h"
|
||||
#include "tests/cefclient/browser/util_mac.h"
|
||||
#include "tests/shared/browser/geometry_util.h"
|
||||
#include "tests/shared/browser/main_message_loop.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/osr_begin_frame_timer_mac.h"
|
||||
#include "tests/shared/browser/osr_renderer_metal.h"
|
||||
|
||||
@@ -119,6 +119,7 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self detach];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
#if !__has_feature(objc_arc)
|
||||
if (text_input_context_osr_mac_) {
|
||||
@@ -132,6 +133,10 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
||||
- (void)detach {
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
display_pending_ = false;
|
||||
// Clear the view's pointer before destruction notifications can reenter it.
|
||||
auto* accessibility_helper = accessibility_helper_;
|
||||
accessibility_helper_ = nullptr;
|
||||
delete accessibility_helper;
|
||||
renderer_ = nullptr;
|
||||
browser_window_ = nullptr;
|
||||
if (text_input_client_) {
|
||||
@@ -1108,9 +1113,11 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
||||
client::OsrAXNode* node = accessibility_helper_->GetRootNode();
|
||||
// Add Root as first Kid
|
||||
NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1];
|
||||
NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
|
||||
node->GetNativeAccessibleObject(nullptr));
|
||||
[kids addObject:child];
|
||||
if (node) {
|
||||
NSObject* child = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
|
||||
node->GetNativeAccessibleObject(nullptr));
|
||||
[kids addObject:child];
|
||||
}
|
||||
return NSAccessibilityUnignoredChildren(kids);
|
||||
} else {
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
// data back and forth between MSAA clients and CefClient renderers.
|
||||
// Sample implementation based on ui\accessibility\ax_platform_node_win.h
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
@@ -234,9 +234,9 @@ struct CefIAccessible : public IAccessible {
|
||||
STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo) override;
|
||||
STDMETHODIMP GetTypeInfo(unsigned int iTInfo,
|
||||
LCID lcid,
|
||||
ITypeInfo FAR* FAR* ppTInfo) override;
|
||||
ITypeInfo FAR * FAR * ppTInfo) override;
|
||||
STDMETHODIMP GetIDsOfNames(REFIID riid,
|
||||
OLECHAR FAR* FAR* rgszNames,
|
||||
OLECHAR FAR * FAR * rgszNames,
|
||||
unsigned int cNames,
|
||||
LCID lcid,
|
||||
DISPID FAR* rgDispId) override;
|
||||
@@ -673,12 +673,12 @@ STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) {
|
||||
|
||||
STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo,
|
||||
LCID lcid,
|
||||
ITypeInfo FAR* FAR* ppTInfo) {
|
||||
ITypeInfo FAR * FAR * ppTInfo) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid,
|
||||
OLECHAR FAR* FAR* rgszNames,
|
||||
OLECHAR FAR * FAR * rgszNames,
|
||||
unsigned int cNames,
|
||||
LCID lcid,
|
||||
DISPID FAR* rgDispId) {
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
#include "include/base/cef_build.h"
|
||||
#include "include/views/cef_display.h"
|
||||
#include "tests/cefclient/browser/main_context.h"
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/cefclient/browser/osr_ime_handler_win.h"
|
||||
#include "tests/cefclient/browser/osr_render_handler_win_d3d11.h"
|
||||
#include "tests/cefclient/browser/osr_render_handler_win_gl.h"
|
||||
#include "tests/cefclient/browser/resource.h"
|
||||
#include "tests/shared/browser/geometry_util.h"
|
||||
#include "tests/shared/browser/main_message_loop.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/util_win.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "include/wrapper/cef_helpers.h"
|
||||
#include "tests/cefclient/browser/client_handler_osr.h"
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/cefclient/browser/osr_dragdrop_win.h"
|
||||
#include "tests/cefclient/browser/osr_render_handler_win.h"
|
||||
#include "tests/cefclient/browser/osr_renderer_settings.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
'shared_sources_linux:LINUX',
|
||||
'shared_sources_mac:MAC',
|
||||
'shared_sources_osr_mac:MAC',
|
||||
'shared_sources_osr_accessibility:MAC',
|
||||
'shared_sources_renderer:WINDOWS',
|
||||
'shared_sources_renderer:LINUX',
|
||||
'shared_sources_win:WINDOWS',
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
// Copyright (c) 2026 The Chromium Embedded Framework Authors. All rights
|
||||
// reserved. Use of this source code is governed by a BSD-style license that
|
||||
// can be found in the LICENSE file.
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "include/cef_parser.h"
|
||||
#include "tests/ceftests/thread_helper.h"
|
||||
#include "tests/gtest/include/gtest/gtest.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
// Exercise the legacy NSAccessibility entry points implemented by the sample.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
// Observe actual object deallocation without retaining the accessibility
|
||||
// object.
|
||||
@interface OsrAXLifetimeObserver : NSObject {
|
||||
@public
|
||||
int* destruction_count_;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation OsrAXLifetimeObserver
|
||||
- (void)dealloc {
|
||||
++*destruction_count_;
|
||||
#if !__has_feature(objc_arc)
|
||||
[super dealloc];
|
||||
#endif
|
||||
}
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
void ObserveLifetime(NSObject* object, int* count) {
|
||||
static char key;
|
||||
OsrAXLifetimeObserver* observer = [[OsrAXLifetimeObserver alloc] init];
|
||||
observer->destruction_count_ = count;
|
||||
objc_setAssociatedObject(object, &key, observer, OBJC_ASSOCIATION_RETAIN);
|
||||
#if !__has_feature(objc_arc)
|
||||
[observer release];
|
||||
#endif
|
||||
}
|
||||
|
||||
client::OsrAXNode* CreateNode(int id = 1) {
|
||||
auto value = CefDictionaryValue::Create();
|
||||
value->SetString("role", "button");
|
||||
auto attributes = CefDictionaryValue::Create();
|
||||
attributes->SetString("name", "Test button");
|
||||
value->SetDictionary("attributes", attributes);
|
||||
return client::OsrAXNode::CreateNode("test-tree", id, value, nullptr);
|
||||
}
|
||||
|
||||
NSObject* NativeObject(client::OsrAXNode* node,
|
||||
client::OsrAXNode* parent = nullptr) {
|
||||
return CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
|
||||
node->GetNativeAccessibleObject(parent));
|
||||
}
|
||||
|
||||
void NativeObjectSurvivesAutoreleasePoolsImpl() {
|
||||
int destroyed = 0;
|
||||
auto* node = CreateNode();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
@autoreleasepool {
|
||||
NSObject* object = NativeObject(node);
|
||||
if (i == 0) {
|
||||
ObserveLifetime(object, &destroyed);
|
||||
}
|
||||
EXPECT_TRUE([NSAccessibilityButtonRole
|
||||
isEqual:[object accessibilityAttributeValue:
|
||||
NSAccessibilityRoleAttribute]]);
|
||||
EXPECT_TRUE(
|
||||
[@"Test button" isEqual:[object accessibilityAttributeValue:
|
||||
NSAccessibilityTitleAttribute]]);
|
||||
}
|
||||
// Without ownership, the first pool drain frees the cached native object.
|
||||
ASSERT_EQ(0, destroyed);
|
||||
}
|
||||
@autoreleasepool {
|
||||
node->Destroy();
|
||||
}
|
||||
EXPECT_EQ(1, destroyed);
|
||||
}
|
||||
|
||||
void RetainedNativeObjectDetachesImpl() {
|
||||
int destroyed = 0;
|
||||
CFTypeRef retained;
|
||||
@autoreleasepool {
|
||||
auto* node = CreateNode();
|
||||
NSObject* object = NativeObject(node);
|
||||
ObserveLifetime(object, &destroyed);
|
||||
retained = CFRetain((__bridge CFTypeRef)object);
|
||||
node->Destroy();
|
||||
}
|
||||
EXPECT_EQ(0, destroyed);
|
||||
@autoreleasepool {
|
||||
NSObject* object = (__bridge NSObject*)retained;
|
||||
EXPECT_TRUE([object accessibilityIsIgnored]);
|
||||
for (NSString* attribute in [object accessibilityAttributeNames]) {
|
||||
EXPECT_EQ(nil, [object accessibilityAttributeValue:attribute]);
|
||||
}
|
||||
EXPECT_EQ(nil, [object accessibilityFocusedUIElement]);
|
||||
EXPECT_EQ(nil, [object accessibilityHitTest:NSZeroPoint]);
|
||||
EXPECT_EQ(0U, [[object accessibilityActionNames] count]);
|
||||
EXPECT_EQ(
|
||||
nil,
|
||||
[object accessibilityActionDescription:NSAccessibilityPressAction]);
|
||||
[object accessibilityPerformAction:NSAccessibilityPressAction];
|
||||
[object accessibilityPerformAction:NSAccessibilityShowMenuAction];
|
||||
CFRelease(retained);
|
||||
}
|
||||
EXPECT_EQ(1, destroyed);
|
||||
}
|
||||
|
||||
void NativeParentCanChangeImpl() {
|
||||
@autoreleasepool {
|
||||
auto* first = CreateNode(1);
|
||||
auto* second = CreateNode(2);
|
||||
auto* child = CreateNode(3);
|
||||
NSObject* first_object = NativeObject(first);
|
||||
NSObject* second_object = NativeObject(second);
|
||||
// A focus query can create the child before its parent is assigned.
|
||||
NSObject* child_object = NativeObject(child);
|
||||
EXPECT_EQ(child_object, NativeObject(child, first));
|
||||
EXPECT_EQ(first_object,
|
||||
[child_object
|
||||
accessibilityAttributeValue:NSAccessibilityParentAttribute]);
|
||||
EXPECT_EQ(child_object, NativeObject(child, second));
|
||||
first->Destroy();
|
||||
EXPECT_EQ(second_object,
|
||||
[child_object
|
||||
accessibilityAttributeValue:NSAccessibilityParentAttribute]);
|
||||
child->SetParent(nullptr);
|
||||
second->Destroy();
|
||||
EXPECT_EQ(nil,
|
||||
[child_object
|
||||
accessibilityAttributeValue:NSAccessibilityParentAttribute]);
|
||||
child->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
void HelperTeardownDetachesAllNodesImpl() {
|
||||
// No browser is needed to verify role queries and teardown of rootless nodes.
|
||||
auto tree = CefParseJSON(R"({"ax_tree_id":"test-tree","updates":[{
|
||||
"nodes":[{"id":1,"role":"button"},{"id":2,"role":"button"}]
|
||||
}]})",
|
||||
JSON_PARSER_RFC);
|
||||
auto helper = std::make_unique<client::OsrAccessibilityHelper>(tree, nullptr);
|
||||
auto* first = helper->GetNode("test-tree", 1);
|
||||
auto* second = helper->GetNode("test-tree", 2);
|
||||
ASSERT_NE(nullptr, first);
|
||||
ASSERT_NE(nullptr, second);
|
||||
int destroyed = 0;
|
||||
CFTypeRef retained;
|
||||
@autoreleasepool {
|
||||
// Construction does not query the browser/window; parent resolution is
|
||||
// lazy.
|
||||
NSObject* object = NativeObject(first);
|
||||
ObserveLifetime(object, &destroyed);
|
||||
retained = CFRetain((__bridge CFTypeRef)object);
|
||||
ObserveLifetime(NativeObject(second), &destroyed);
|
||||
helper.reset();
|
||||
}
|
||||
EXPECT_EQ(1, destroyed);
|
||||
@autoreleasepool {
|
||||
NSObject* object = (__bridge NSObject*)retained;
|
||||
EXPECT_TRUE([object accessibilityIsIgnored]);
|
||||
EXPECT_EQ(
|
||||
nil, [object accessibilityAttributeValue:NSAccessibilityRoleAttribute]);
|
||||
CFRelease(retained);
|
||||
}
|
||||
EXPECT_EQ(2, destroyed);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
UI_THREAD_TEST(OsrAccessibilityTest, NativeObjectSurvivesAutoreleasePools)
|
||||
UI_THREAD_TEST(OsrAccessibilityTest, RetainedNativeObjectDetaches)
|
||||
UI_THREAD_TEST(OsrAccessibilityTest, NativeParentCanChange)
|
||||
UI_THREAD_TEST(OsrAccessibilityTest, HelperTeardownDetachesAllNodes)
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
+19
-3
@@ -2,9 +2,9 @@
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
@@ -43,6 +43,10 @@ OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
|
||||
UpdateAccessibilityTree(value);
|
||||
}
|
||||
|
||||
OsrAccessibilityHelper::~OsrAccessibilityHelper() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
int OsrAccessibilityHelper::CastToInt(CefRefPtr<CefValue> value) {
|
||||
if (value->GetType() == VTYPE_STRING) {
|
||||
const std::string& str = value->GetString();
|
||||
@@ -226,10 +230,22 @@ void OsrAccessibilityHelper::UpdateFocusedNode(const CefString& treeId,
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::Reset() {
|
||||
accessibility_node_map_.clear();
|
||||
// Remove nodes from lookups before destruction notifications can reenter.
|
||||
decltype(accessibility_node_map_) trees;
|
||||
trees.swap(accessibility_node_map_);
|
||||
root_tree_id_ = "";
|
||||
focused_tree_id_ = "";
|
||||
focused_node_id_ = -1;
|
||||
for (auto& [tree_id, tree] : trees) {
|
||||
for (auto& [node_id, node] : tree.node_map_) {
|
||||
node->SetParent(nullptr);
|
||||
}
|
||||
}
|
||||
for (auto& [tree_id, tree] : trees) {
|
||||
for (auto& [node_id, node] : tree.node_map_) {
|
||||
node->Destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
|
||||
+8
-3
@@ -2,8 +2,8 @@
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
#ifndef CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
#define CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
@@ -26,6 +26,7 @@ class OsrAXTree {
|
||||
void SetRootNodeId(int nodeId) { root_node_id_ = nodeId; }
|
||||
|
||||
private:
|
||||
friend class OsrAccessibilityHelper;
|
||||
CefString parent_tree_id_;
|
||||
int root_node_id_ = -1;
|
||||
std::map<int, OsrAXNode*> node_map_;
|
||||
@@ -38,6 +39,10 @@ class OsrAccessibilityHelper {
|
||||
public:
|
||||
OsrAccessibilityHelper(CefRefPtr<CefValue> value,
|
||||
CefRefPtr<CefBrowser> browser);
|
||||
~OsrAccessibilityHelper();
|
||||
|
||||
OsrAccessibilityHelper(const OsrAccessibilityHelper&) = delete;
|
||||
OsrAccessibilityHelper& operator=(const OsrAccessibilityHelper&) = delete;
|
||||
|
||||
void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
|
||||
|
||||
@@ -78,4 +83,4 @@ class OsrAccessibilityHelper {
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
#endif // CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
||||
+2
-2
@@ -5,9 +5,9 @@
|
||||
// Base class implementation for CEF Acccessibility node. This is subclassed and
|
||||
// used by both IAccessible/NSAccessibility protocol implementation.
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
+3
-3
@@ -2,8 +2,8 @@
|
||||
// 2013 The Chromium Authors. All rights reserved. Use of this source code is
|
||||
// governed by a BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
#define CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
#ifndef CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
#define CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
@@ -120,4 +120,4 @@ class OsrAXNode {
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
#endif // CEF_TESTS_SHARED_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
||||
+73
-37
@@ -5,12 +5,12 @@
|
||||
// Sample implementation for the NSAccessibility protocol for interacting with
|
||||
// VoiceOver and other accessibility clients.
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
#include "tests/shared/browser/osr_accessibility_node.h"
|
||||
|
||||
#import <AppKit/NSAccessibility.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
#include "tests/shared/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -354,60 +354,64 @@ inline int MiddleY(const CefRect& rect) {
|
||||
@interface OsrAXNodeObject : NSObject {
|
||||
// OsrAXNode* proxy object
|
||||
client::OsrAXNode* node_;
|
||||
CefNativeAccessible* parent_;
|
||||
}
|
||||
|
||||
- (id)init:(client::OsrAXNode*)node;
|
||||
+ (OsrAXNodeObject*)elementWithNode:(client::OsrAXNode*)node;
|
||||
- (id)initWithNode:(client::OsrAXNode*)node;
|
||||
- (void)detach;
|
||||
@end
|
||||
|
||||
@implementation OsrAXNodeObject
|
||||
- (id)init:(client::OsrAXNode*)node {
|
||||
node_ = node;
|
||||
parent_ = node_->GetParentAccessibleObject();
|
||||
if (!parent_) {
|
||||
parent_ = node_->GetWindowHandle();
|
||||
- (id)initWithNode:(client::OsrAXNode*)node {
|
||||
if ((self = [super init])) {
|
||||
node_ = node;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (OsrAXNodeObject*)elementWithNode:(client::OsrAXNode*)node {
|
||||
// We manage the release ourself
|
||||
return [[OsrAXNodeObject alloc] init:node];
|
||||
- (void)detach {
|
||||
node_ = nullptr;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if ([object isKindOfClass:[OsrAXNodeObject self]]) {
|
||||
OsrAXNodeObject* other = object;
|
||||
return (node_ == other->node_);
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
// There is one native wrapper per node. Detached wrappers remain distinct.
|
||||
return self == object;
|
||||
}
|
||||
|
||||
// Utility methods to map AX information received from renderer
|
||||
// to platform properties
|
||||
- (NSString*)axRole {
|
||||
// Get the Role from CefAccessibilityHelper and Map to NSRole
|
||||
return AxRoleToNSAxRole(node_->AxRole());
|
||||
return node_ ? AxRoleToNSAxRole(node_->AxRole()) : nil;
|
||||
}
|
||||
|
||||
- (NSString*)axDescription {
|
||||
if (!node_) {
|
||||
return nil;
|
||||
}
|
||||
std::string desc = node_->AxDescription();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (NSString*)axName {
|
||||
if (!node_) {
|
||||
return nil;
|
||||
}
|
||||
std::string desc = node_->AxName();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (NSString*)axValue {
|
||||
if (!node_) {
|
||||
return nil;
|
||||
}
|
||||
std::string desc = node_->AxValue();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (void)doMouseClick:(cef_mouse_button_type_t)type {
|
||||
if (!node_) {
|
||||
return;
|
||||
}
|
||||
CefRefPtr<CefBrowser> browser = node_->GetBrowser();
|
||||
if (browser) {
|
||||
CefMouseEvent mouse_event;
|
||||
@@ -422,14 +426,18 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (NSMutableArray*)getKids {
|
||||
if (!node_) {
|
||||
return nil;
|
||||
}
|
||||
int numChilds = node_->GetChildCount();
|
||||
if (numChilds > 0) {
|
||||
NSMutableArray* kids = [NSMutableArray arrayWithCapacity:numChilds];
|
||||
for (int index = 0; index < numChilds; index++) {
|
||||
client::OsrAXNode* child = node_->ChildAtIndex(index);
|
||||
[kids addObject:child ? CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
|
||||
child->GetNativeAccessibleObject(node_))
|
||||
: nil];
|
||||
if (child) {
|
||||
[kids addObject:CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(
|
||||
child->GetNativeAccessibleObject(node_))];
|
||||
}
|
||||
}
|
||||
return kids;
|
||||
}
|
||||
@@ -437,6 +445,9 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (NSPoint)position {
|
||||
if (!node_) {
|
||||
return NSZeroPoint;
|
||||
}
|
||||
CefRect cef_rect = node_->AxLocation();
|
||||
NSPoint origin = NSMakePoint(cef_rect.x, cef_rect.y);
|
||||
NSSize size = NSMakeSize(cef_rect.width, cef_rect.height);
|
||||
@@ -454,6 +465,9 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (NSSize)size {
|
||||
if (!node_) {
|
||||
return NSZeroSize;
|
||||
}
|
||||
CefRect cef_rect = node_->AxLocation();
|
||||
NSRect rect =
|
||||
NSMakeRect(cef_rect.x, cef_rect.y, cef_rect.width, cef_rect.height);
|
||||
@@ -469,7 +483,7 @@ inline int MiddleY(const CefRect& rect) {
|
||||
// attributes
|
||||
|
||||
- (BOOL)accessibilityIsIgnored {
|
||||
return NO;
|
||||
return node_ == nullptr;
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames {
|
||||
@@ -493,10 +507,16 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute {
|
||||
NSObject* typed_parent = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(parent_);
|
||||
if (!node_) {
|
||||
return nil;
|
||||
}
|
||||
// Resolve the current parent only while attached. Do not cache an unowned
|
||||
// native parent pointer, which can outlive the parent or miss reparenting.
|
||||
CefNativeAccessible* parent = node_->GetParentAccessibleObject();
|
||||
if (!parent) {
|
||||
parent = node_->GetWindowHandle();
|
||||
}
|
||||
NSObject* typed_parent = CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(parent);
|
||||
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
||||
return [self axRole];
|
||||
} else if ([attribute
|
||||
@@ -535,15 +555,15 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (id)accessibilityHitTest:(NSPoint)point {
|
||||
return NSAccessibilityUnignoredAncestor(self);
|
||||
return node_ ? NSAccessibilityUnignoredAncestor(self) : nil;
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityActionNames {
|
||||
return [NSArray arrayWithObject:NSAccessibilityPressAction];
|
||||
return node_ ? @[ NSAccessibilityPressAction ] : @[];
|
||||
}
|
||||
|
||||
- (NSString*)accessibilityActionDescription:(NSString*)action {
|
||||
return NSAccessibilityActionDescription(action);
|
||||
return node_ ? NSAccessibilityActionDescription(action) : nil;
|
||||
}
|
||||
|
||||
- (void)accessibilityPerformAction:(NSString*)action {
|
||||
@@ -557,13 +577,11 @@ inline int MiddleY(const CefRect& rect) {
|
||||
}
|
||||
|
||||
- (id)accessibilityFocusedUIElement {
|
||||
return NSAccessibilityUnignoredAncestor(self);
|
||||
return node_ ? NSAccessibilityUnignoredAncestor(self) : nil;
|
||||
}
|
||||
|
||||
- (BOOL)accessibilityNotifiesWhenDestroyed {
|
||||
// Indicate that BrowserAccessibilityCocoa will post a notification when it's
|
||||
// destroyed (see -detach). This allows VoiceOver to do some internal things
|
||||
// more efficiently.
|
||||
// OsrAXNode::Destroy posts the notification after detaching this wrapper.
|
||||
return YES;
|
||||
}
|
||||
|
||||
@@ -590,9 +608,21 @@ void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
|
||||
|
||||
void OsrAXNode::Destroy() {
|
||||
if (platform_accessibility_) {
|
||||
// Transfer our ownership to a local before notifying clients, which may
|
||||
// reenter the wrapper or keep it alive after the C++ node is deleted.
|
||||
#if __has_feature(objc_arc)
|
||||
OsrAXNodeObject* object =
|
||||
(__bridge_transfer OsrAXNodeObject*)platform_accessibility_;
|
||||
#else
|
||||
OsrAXNodeObject* object = (OsrAXNodeObject*)platform_accessibility_;
|
||||
#endif
|
||||
platform_accessibility_ = nullptr;
|
||||
[object detach];
|
||||
NSAccessibilityPostNotification(
|
||||
CAST_CEF_NATIVE_ACCESSIBLE_TO_NSOBJECT(platform_accessibility_),
|
||||
NSAccessibilityUIElementDestroyedNotification);
|
||||
object, NSAccessibilityUIElementDestroyedNotification);
|
||||
#if !__has_feature(objc_arc)
|
||||
[object release];
|
||||
#endif
|
||||
}
|
||||
|
||||
delete this;
|
||||
@@ -601,11 +631,17 @@ void OsrAXNode::Destroy() {
|
||||
// Create and return NSAccessibility Implementation Object for Mac
|
||||
CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(
|
||||
client::OsrAXNode* parent) {
|
||||
if (!platform_accessibility_) {
|
||||
platform_accessibility_ = CAST_NSOBJECT_TO_CEF_NATIVE_ACCESSIBLE(
|
||||
[OsrAXNodeObject elementWithNode:this]);
|
||||
if (parent) {
|
||||
SetParent(parent);
|
||||
}
|
||||
if (!platform_accessibility_) {
|
||||
OsrAXNodeObject* object = [[OsrAXNodeObject alloc] initWithNode:this];
|
||||
#if __has_feature(objc_arc)
|
||||
platform_accessibility_ = (__bridge_retained CefNativeAccessible*)object;
|
||||
#else
|
||||
platform_accessibility_ = object;
|
||||
#endif
|
||||
}
|
||||
return platform_accessibility_;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,9 @@ cc_library(
|
||||
# when linking libraries.
|
||||
filegroup(
|
||||
name = "SrcsWin",
|
||||
srcs = srcs_common + srcs_browser + srcs_browser_win + srcs_renderer,
|
||||
srcs = srcs_common + srcs_browser + srcs_browser_win + srcs_renderer + [
|
||||
"//tests/shared:OsrAccessibilitySrcsWin",
|
||||
],
|
||||
target_compatible_with = [
|
||||
"@platforms//os:windows",
|
||||
],
|
||||
|
||||
@@ -34,6 +34,10 @@ srcs_osr_mac = [
|
||||
${shared_sources_osr_mac}
|
||||
]
|
||||
|
||||
srcs_osr_accessibility = [
|
||||
${shared_sources_osr_accessibility}
|
||||
]
|
||||
|
||||
srcs_browser_mac_ceftests = [
|
||||
${ceftests_sources_mac_browser_shared}
|
||||
]
|
||||
@@ -91,7 +95,7 @@ declare_cc_library(
|
||||
|
||||
declare_objc_library(
|
||||
name = "OsrLibMac",
|
||||
srcs = srcs_osr_mac,
|
||||
srcs = srcs_osr_mac + srcs_osr_accessibility,
|
||||
target_compatible_with = [
|
||||
"@platforms//os:macos",
|
||||
],
|
||||
@@ -133,6 +137,14 @@ declare_objc_library(
|
||||
# Windows targets.
|
||||
#
|
||||
|
||||
filegroup(
|
||||
name = "OsrAccessibilitySrcsWin",
|
||||
srcs = srcs_osr_accessibility,
|
||||
target_compatible_with = [
|
||||
"@platforms//os:windows",
|
||||
],
|
||||
)
|
||||
|
||||
# Include files directly in the declare_exe target. This simplifies the build
|
||||
# configuration and avoids issues with Windows discarding symbols (like WinMain)
|
||||
# when linking libraries.
|
||||
|
||||
@@ -1268,6 +1268,8 @@ def main(argv=None):
|
||||
# transfer shared files
|
||||
transfer_gypi_files(cef_dir, cef_paths2['shared_sources_win'], \
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
transfer_gypi_files(cef_dir, cef_paths2['shared_sources_osr_accessibility'], \
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
|
||||
# transfer cefclient files
|
||||
transfer_gypi_files(
|
||||
@@ -1441,6 +1443,8 @@ def main(argv=None):
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
transfer_gypi_files(cef_dir, cef_paths2['shared_sources_osr_mac'], \
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
transfer_gypi_files(cef_dir, cef_paths2['shared_sources_osr_accessibility'], \
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
transfer_gypi_files(cef_dir, cef_paths2['shared_sources_mac_helper'], \
|
||||
'tests/shared/', shared_dir, options.quiet)
|
||||
|
||||
|
||||
Reference in new issue
Block a user