mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-02-22 23:19:09 +01:00
Implement accessibility enhancements (issue #1217)
- Add new CefBrowserHost::SetAccessibilityState method for toggling accessibility state when readers are detected by the client. - Add new CefAccessibilityHandler interface for the delivery of accessibility notifications to windowless (OSR) clients. - Fix delivery of CefFocusHandler callbacks to windowless clients. - cefclient: Add example windowless accessibility implementation on Windows and macOS. - cefclient: Automatically detect screen readers on Windows and macOS.
This commit is contained in:
parent
ece935318d
commit
406867f293
9
BUILD.gn
9
BUILD.gn
@ -380,6 +380,8 @@ static_library("libcef_static") {
|
||||
"libcef/browser/origin_whitelist_impl.h",
|
||||
"libcef/browser/osr/browser_platform_delegate_osr.cc",
|
||||
"libcef/browser/osr/browser_platform_delegate_osr.h",
|
||||
"libcef/browser/osr/osr_accessibility_util.cc",
|
||||
"libcef/browser/osr/osr_accessibility_util.h",
|
||||
"libcef/browser/osr/osr_util.cc",
|
||||
"libcef/browser/osr/osr_util.h",
|
||||
"libcef/browser/osr/render_widget_host_view_osr.cc",
|
||||
@ -1738,11 +1740,12 @@ if (is_mac) {
|
||||
|
||||
libs = [
|
||||
"comctl32.lib",
|
||||
"shlwapi.lib",
|
||||
"rpcrt4.lib",
|
||||
"opengl32.lib",
|
||||
"glu32.lib",
|
||||
"imm32.lib",
|
||||
"oleacc.lib",
|
||||
"opengl32.lib",
|
||||
"rpcrt4.lib",
|
||||
"shlwapi.lib",
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@
|
||||
{
|
||||
'variables': {
|
||||
'autogen_cpp_includes': [
|
||||
'include/cef_accessibility_handler.h',
|
||||
'include/cef_app.h',
|
||||
'include/cef_auth_callback.h',
|
||||
'include/cef_browser.h',
|
||||
@ -100,6 +101,7 @@
|
||||
'include/views/cef_window_delegate.h',
|
||||
],
|
||||
'autogen_capi_includes': [
|
||||
'include/capi/cef_accessibility_handler_capi.h',
|
||||
'include/capi/cef_app_capi.h',
|
||||
'include/capi/cef_auth_callback_capi.h',
|
||||
'include/capi/cef_browser_capi.h',
|
||||
@ -188,6 +190,8 @@
|
||||
'include/capi/views/cef_window_delegate_capi.h',
|
||||
],
|
||||
'autogen_library_side': [
|
||||
'libcef_dll/ctocpp/accessibility_handler_ctocpp.cc',
|
||||
'libcef_dll/ctocpp/accessibility_handler_ctocpp.h',
|
||||
'libcef_dll/ctocpp/app_ctocpp.cc',
|
||||
'libcef_dll/ctocpp/app_ctocpp.h',
|
||||
'libcef_dll/cpptoc/auth_callback_cpptoc.cc',
|
||||
@ -460,6 +464,8 @@
|
||||
'libcef_dll/cpptoc/zip_reader_cpptoc.h',
|
||||
],
|
||||
'autogen_client_side': [
|
||||
'libcef_dll/cpptoc/accessibility_handler_cpptoc.cc',
|
||||
'libcef_dll/cpptoc/accessibility_handler_cpptoc.h',
|
||||
'libcef_dll/cpptoc/app_cpptoc.cc',
|
||||
'libcef_dll/cpptoc/app_cpptoc.h',
|
||||
'libcef_dll/ctocpp/auth_callback_ctocpp.cc',
|
||||
|
@ -286,6 +286,11 @@
|
||||
'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',
|
||||
'tests/cefclient/browser/osr_ime_handler_win.cc',
|
||||
@ -321,6 +326,11 @@
|
||||
'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',
|
||||
@ -399,6 +409,7 @@
|
||||
'tests/cefsimple/simple_handler_linux.cc',
|
||||
],
|
||||
'ceftests_sources_common': [
|
||||
'tests/ceftests/accessibility_unittest.cc',
|
||||
'tests/ceftests/browser_info_map_unittest.cc',
|
||||
'tests/ceftests/command_line_unittest.cc',
|
||||
'tests/ceftests/cookie_unittest.cc',
|
||||
|
79
include/capi/cef_accessibility_handler_capi.h
Normal file
79
include/capi/cef_accessibility_handler_capi.h
Normal file
@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||
// Framework nor the names of its contributors may be used to endorse
|
||||
// or promote products derived from this software without specific prior
|
||||
// written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This file was generated by the CEF translator tool and should not edited
|
||||
// by hand. See the translator.README.txt file in the tools directory for
|
||||
// more information.
|
||||
//
|
||||
|
||||
#ifndef CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
|
||||
#define CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
|
||||
#pragma once
|
||||
|
||||
#include "include/capi/cef_values_capi.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
///
|
||||
// Implement this structure to receive accessibility notification when
|
||||
// accessibility events have been registered. The functions of this structure
|
||||
// will be called on the UI thread.
|
||||
///
|
||||
typedef struct _cef_accessibility_handler_t {
|
||||
///
|
||||
// Base structure.
|
||||
///
|
||||
cef_base_ref_counted_t base;
|
||||
|
||||
///
|
||||
// Called after renderer process sends accessibility tree changes to the
|
||||
// browser process.
|
||||
///
|
||||
void (CEF_CALLBACK *on_accessibility_tree_change)(
|
||||
struct _cef_accessibility_handler_t* self, struct _cef_value_t* value);
|
||||
|
||||
///
|
||||
// Called after renderer process sends accessibility location changes to the
|
||||
// browser process.
|
||||
///
|
||||
void (CEF_CALLBACK *on_accessibility_location_change)(
|
||||
struct _cef_accessibility_handler_t* self, struct _cef_value_t* value);
|
||||
} cef_accessibility_handler_t;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // CEF_INCLUDE_CAPI_CEF_ACCESSIBILITY_HANDLER_CAPI_H_
|
@ -748,6 +748,34 @@ typedef struct _cef_browser_host_t {
|
||||
///
|
||||
struct _cef_navigation_entry_t* (CEF_CALLBACK *get_visible_navigation_entry)(
|
||||
struct _cef_browser_host_t* self);
|
||||
|
||||
///
|
||||
// Set accessibility state for all frames. |accessibility_state| may be
|
||||
// default, enabled or disabled. If |accessibility_state| is STATE_DEFAULT
|
||||
// then accessibility will be disabled by default and the state may be further
|
||||
// controlled with the "force-renderer-accessibility" and "disable-renderer-
|
||||
// accessibility" command-line switches. If |accessibility_state| is
|
||||
// STATE_ENABLED then accessibility will be enabled. If |accessibility_state|
|
||||
// is STATE_DISABLED then accessibility will be completely disabled.
|
||||
//
|
||||
// For windowed browsers accessibility will be enabled in Complete mode (which
|
||||
// corresponds to kAccessibilityModeComplete in Chromium). In this mode all
|
||||
// platform accessibility objects will be created and managed by Chromium's
|
||||
// internal implementation. The client needs only to detect the screen reader
|
||||
// and call this function appropriately. For example, on macOS the client can
|
||||
// handle the @"AXEnhancedUserStructure" accessibility attribute to detect
|
||||
// VoiceOver state changes and on Windows the client can handle WM_GETOBJECT
|
||||
// with OBJID_CLIENT to detect accessibility readers.
|
||||
//
|
||||
// For windowless browsers accessibility will be enabled in TreeOnly mode
|
||||
// (which corresponds to kAccessibilityModeWebContentsOnly in Chromium). In
|
||||
// this mode renderer accessibility is enabled, the full tree is computed, and
|
||||
// events are passed to CefAccessibiltyHandler, but platform accessibility
|
||||
// objects are not created. The client may implement platform accessibility
|
||||
// objects using CefAccessibiltyHandler callbacks if desired.
|
||||
///
|
||||
void (CEF_CALLBACK *set_accessibility_state)(struct _cef_browser_host_t* self,
|
||||
cef_state_t accessibility_state);
|
||||
} cef_browser_host_t;
|
||||
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#define CEF_INCLUDE_CAPI_CEF_RENDER_HANDLER_CAPI_H_
|
||||
#pragma once
|
||||
|
||||
#include "include/capi/cef_accessibility_handler_capi.h"
|
||||
#include "include/capi/cef_base_capi.h"
|
||||
#include "include/capi/cef_browser_capi.h"
|
||||
#include "include/capi/cef_drag_data_capi.h"
|
||||
@ -57,6 +58,14 @@ typedef struct _cef_render_handler_t {
|
||||
///
|
||||
cef_base_ref_counted_t base;
|
||||
|
||||
///
|
||||
// Return the handler for accessibility notifications. If no handler is
|
||||
// provided the default implementation will be used.
|
||||
///
|
||||
struct _cef_accessibility_handler_t* (
|
||||
CEF_CALLBACK *get_accessibility_handler)(
|
||||
struct _cef_render_handler_t* self);
|
||||
|
||||
///
|
||||
// Called to retrieve the root window rectangle in screen coordinates. Return
|
||||
// true (1) if the rectangle was provided.
|
||||
|
66
include/cef_accessibility_handler.h
Normal file
66
include/cef_accessibility_handler.h
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright (c) 2017 Marshall A. Greenblatt. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the name Chromium Embedded
|
||||
// Framework nor the names of its contributors may be used to endorse
|
||||
// or promote products derived from this software without specific prior
|
||||
// written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// The contents of this file must follow a specific format in order to
|
||||
// support the CEF translator tool. See the translator.README.txt file in the
|
||||
// tools directory for more information.
|
||||
//
|
||||
|
||||
#ifndef CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
|
||||
#define CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
|
||||
#pragma once
|
||||
|
||||
#include "include/cef_values.h"
|
||||
|
||||
///
|
||||
// Implement this interface to receive accessibility notification when
|
||||
// accessibility events have been registered. The methods of this class will
|
||||
// be called on the UI thread.
|
||||
///
|
||||
/*--cef(source=client)--*/
|
||||
class CefAccessibilityHandler : public virtual CefBaseRefCounted {
|
||||
public:
|
||||
///
|
||||
// Called after renderer process sends accessibility tree changes to the
|
||||
// browser process.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) =0;
|
||||
|
||||
///
|
||||
// Called after renderer process sends accessibility location changes to the
|
||||
// browser process.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) =0;
|
||||
};
|
||||
|
||||
#endif // CEF_INCLUDE_CEF_ACCESSIBILITY_HANDLER_H_
|
@ -365,7 +365,7 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
|
||||
// Retrieve the window handle of the browser that opened this browser. Will
|
||||
// return NULL for non-popup windows or if this browser is wrapped in a
|
||||
// CefBrowserView. This method can be used in combination with custom handling
|
||||
// of modal windows.
|
||||
// of modal windows.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual CefWindowHandle GetOpenerWindowHandle() =0;
|
||||
@ -805,6 +805,35 @@ class CefBrowserHost : public virtual CefBaseRefCounted {
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() =0;
|
||||
|
||||
///
|
||||
// Set accessibility state for all frames. |accessibility_state| may be
|
||||
// default, enabled or disabled. If |accessibility_state| is STATE_DEFAULT
|
||||
// then accessibility will be disabled by default and the state may be further
|
||||
// controlled with the "force-renderer-accessibility" and
|
||||
// "disable-renderer-accessibility" command-line switches. If
|
||||
// |accessibility_state| is STATE_ENABLED then accessibility will be enabled.
|
||||
// If |accessibility_state| is STATE_DISABLED then accessibility will be
|
||||
// completely disabled.
|
||||
//
|
||||
// For windowed browsers accessibility will be enabled in Complete mode (which
|
||||
// corresponds to kAccessibilityModeComplete in Chromium). In this mode all
|
||||
// platform accessibility objects will be created and managed by Chromium's
|
||||
// internal implementation. The client needs only to detect the screen reader
|
||||
// and call this method appropriately. For example, on macOS the client can
|
||||
// handle the @"AXEnhancedUserInterface" accessibility attribute to detect
|
||||
// VoiceOver state changes and on Windows the client can handle WM_GETOBJECT
|
||||
// with OBJID_CLIENT to detect accessibility readers.
|
||||
//
|
||||
// For windowless browsers accessibility will be enabled in TreeOnly mode
|
||||
// (which corresponds to kAccessibilityModeWebContentsOnly in Chromium). In
|
||||
// this mode renderer accessibility is enabled, the full tree is computed, and
|
||||
// events are passed to CefAccessibiltyHandler, but platform accessibility
|
||||
// objects are not created. The client may implement platform accessibility
|
||||
// objects using CefAccessibiltyHandler callbacks if desired.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual void SetAccessibilityState(cef_state_t accessibility_state) =0;
|
||||
};
|
||||
|
||||
#endif // CEF_INCLUDE_CEF_BROWSER_H_
|
||||
|
@ -40,6 +40,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "include/cef_accessibility_handler.h"
|
||||
#include "include/cef_base.h"
|
||||
#include "include/cef_browser.h"
|
||||
#include "include/cef_drag_data.h"
|
||||
@ -57,6 +58,15 @@ class CefRenderHandler : public virtual CefBaseRefCounted {
|
||||
typedef cef_paint_element_type_t PaintElementType;
|
||||
typedef std::vector<CefRect> RectList;
|
||||
|
||||
///
|
||||
// Return the handler for accessibility notifications. If no handler is
|
||||
// provided the default implementation will be used.
|
||||
///
|
||||
/*--cef()--*/
|
||||
virtual CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
///
|
||||
// Called to retrieve the root window rectangle in screen coordinates. Return
|
||||
// true if the rectangle was provided.
|
||||
|
@ -46,6 +46,7 @@
|
||||
#include "components/zoom/zoom_controller.h"
|
||||
#include "content/browser/renderer_host/render_view_host_impl.h"
|
||||
#include "content/browser/gpu/compositor_util.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/common/view_messages.h"
|
||||
#include "content/public/browser/desktop_media_id.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
@ -930,6 +931,39 @@ CefRefPtr<CefNavigationEntry> CefBrowserHostImpl::GetVisibleNavigationEntry() {
|
||||
return new CefNavigationEntryImpl(entry);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::SetAccessibilityState(
|
||||
cef_state_t accessibility_state) {
|
||||
// Do nothing if state is set to default. It'll be disabled by default and
|
||||
// controlled by the commmand-line flags "force-renderer-accessibility" and
|
||||
// "disable-renderer-accessibility".
|
||||
if (accessibility_state == STATE_DEFAULT)
|
||||
return;
|
||||
|
||||
if (!CEF_CURRENTLY_ON_UIT()) {
|
||||
CEF_POST_TASK(CEF_UIT,
|
||||
base::Bind(&CefBrowserHostImpl::SetAccessibilityState,
|
||||
this, accessibility_state));
|
||||
return;
|
||||
}
|
||||
|
||||
content::WebContentsImpl* web_contents_impl =
|
||||
static_cast<content::WebContentsImpl*>(web_contents());
|
||||
|
||||
if (!web_contents_impl)
|
||||
return;
|
||||
|
||||
content::AccessibilityMode accMode;
|
||||
// In windowless mode set accessibility to TreeOnly mode. Else native
|
||||
// accessibility APIs, specific to each platform, are also created.
|
||||
if (accessibility_state == STATE_ENABLED) {
|
||||
if (IsWindowless())
|
||||
accMode = content::kAccessibilityModeWebContentsOnly;
|
||||
else
|
||||
accMode = content::kAccessibilityModeComplete;
|
||||
}
|
||||
web_contents_impl->SetAccessibilityMode(accMode);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::SetMouseCursorChangeDisabled(bool disabled) {
|
||||
base::AutoLock lock_scope(state_lock_);
|
||||
mouse_cursor_change_disabled_ = disabled;
|
||||
@ -2690,6 +2724,28 @@ bool CefBrowserHostImpl::OnMessageReceived(const IPC::Message& message) {
|
||||
return handled;
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData) {
|
||||
// Only needed in windowless mode.
|
||||
if (IsWindowless()) {
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return;
|
||||
|
||||
platform_delegate_->AccessibilityEventReceived(eventData);
|
||||
}
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
|
||||
// Only needed in windowless mode.
|
||||
if (IsWindowless()) {
|
||||
if (!web_contents() || !platform_delegate_)
|
||||
return;
|
||||
|
||||
platform_delegate_->AccessibilityLocationChangesReceived(locData);
|
||||
}
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::OnWebContentsFocused() {
|
||||
if (client_.get()) {
|
||||
CefRefPtr<CefFocusHandler> handler = client_->GetFocusHandler();
|
||||
|
@ -229,6 +229,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
|
||||
void DragSourceSystemDragEnded() override;
|
||||
void DragSourceEndedAt(int x, int y, DragOperationsMask op) override;
|
||||
CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() override;
|
||||
void SetAccessibilityState(cef_state_t accessibility_state) override;
|
||||
|
||||
// CefBrowser methods.
|
||||
CefRefPtr<CefBrowserHost> GetHost() override;
|
||||
@ -471,6 +472,13 @@ class CefBrowserHostImpl : public CefBrowserHost,
|
||||
void DidUpdateFaviconURL(
|
||||
const std::vector<content::FaviconURL>& candidates) override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
void AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData)
|
||||
override;
|
||||
void AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData)
|
||||
override;
|
||||
|
||||
void OnWebContentsFocused() override;
|
||||
// Override to provide a thread safe implementation.
|
||||
bool Send(IPC::Message* message) override;
|
||||
|
@ -202,6 +202,16 @@ void CefBrowserPlatformDelegate::DragSourceSystemDragEnded() {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegate::AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
// static
|
||||
int CefBrowserPlatformDelegate::TranslateModifiers(uint32 cef_modifiers) {
|
||||
int webkit_modifiers = 0;
|
||||
|
@ -262,6 +262,10 @@ class CefBrowserPlatformDelegate {
|
||||
virtual void DragSourceEndedAt(int x, int y,
|
||||
cef_drag_operations_mask_t op);
|
||||
virtual void DragSourceSystemDragEnded();
|
||||
virtual void AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData);
|
||||
virtual void AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData);
|
||||
|
||||
protected:
|
||||
// Allow deletion via scoped_ptr only.
|
||||
|
@ -8,6 +8,7 @@
|
||||
|
||||
#include "libcef/browser/browser_host_impl.h"
|
||||
#include "libcef/browser/image_impl.h"
|
||||
#include "libcef/browser/osr/osr_accessibility_util.h"
|
||||
#include "libcef/browser/osr/render_widget_host_view_osr.h"
|
||||
#include "libcef/browser/osr/web_contents_view_osr.h"
|
||||
#include "libcef/common/drag_data_impl.h"
|
||||
@ -522,6 +523,34 @@ void CefBrowserPlatformDelegateOsr::DragSourceSystemDragEnded() {
|
||||
drag_start_rwh_ = nullptr;
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData) {
|
||||
CefRefPtr<CefRenderHandler> handler = browser_->client()->GetRenderHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefAccessibilityHandler> acchandler =
|
||||
handler->GetAccessibilityHandler();
|
||||
|
||||
if (acchandler.get()) {
|
||||
acchandler->OnAccessibilityTreeChange(
|
||||
osr_accessibility_util::ParseAccessibilityEventData(eventData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CefBrowserPlatformDelegateOsr::AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData) {
|
||||
CefRefPtr<CefRenderHandler> handler = browser_->client()->GetRenderHandler();
|
||||
if (handler.get()) {
|
||||
CefRefPtr<CefAccessibilityHandler> acchandler =
|
||||
handler->GetAccessibilityHandler();
|
||||
|
||||
if (acchandler.get()) {
|
||||
acchandler->OnAccessibilityLocationChange(
|
||||
osr_accessibility_util::ParseAccessibilityLocationData(locData));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CefWindowHandle CefBrowserPlatformDelegateOsr::GetParentWindowHandle() const {
|
||||
return GetHostWindowHandle();
|
||||
}
|
||||
|
@ -88,6 +88,12 @@ class CefBrowserPlatformDelegateOsr :
|
||||
void DragSourceEndedAt(int x, int y,
|
||||
cef_drag_operations_mask_t op) override;
|
||||
void DragSourceSystemDragEnded() override;
|
||||
void AccessibilityEventReceived(
|
||||
const std::vector<content::AXEventNotificationDetails>& eventData)
|
||||
override;
|
||||
void AccessibilityLocationChangesReceived(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& locData)
|
||||
override;
|
||||
|
||||
// CefBrowserPlatformDelegateNative::WindowlessHandler methods:
|
||||
CefWindowHandle GetParentWindowHandle() const override;
|
||||
|
457
libcef/browser/osr/osr_accessibility_util.cc
Normal file
457
libcef/browser/osr/osr_accessibility_util.cc
Normal file
@ -0,0 +1,457 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#include "libcef/browser/osr/osr_accessibility_util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "base/json/string_escape.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "content/public/browser/ax_event_notification_details.h"
|
||||
#include "ui/accessibility/ax_tree_update.h"
|
||||
#include "ui/accessibility/ax_text_utils.h"
|
||||
#include "ui/gfx/transform.h"
|
||||
|
||||
namespace {
|
||||
using ui::ToString;
|
||||
|
||||
template<typename T>
|
||||
CefRefPtr<CefListValue> ToCefValue(const std::vector<T>& vecData);
|
||||
|
||||
template<>
|
||||
CefRefPtr<CefListValue> ToCefValue<int>(const std::vector<int>& vecData) {
|
||||
CefRefPtr<CefListValue> value = CefListValue::Create();
|
||||
for (size_t i = 0; i <vecData.size(); i ++)
|
||||
value->SetInt(i, vecData[i]);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Helper function for AXNodeData::ToCefValue - Converts AXState attributes to
|
||||
// CefListValue.
|
||||
CefRefPtr<CefListValue> ToCefValue(uint32_t state) {
|
||||
CefRefPtr<CefListValue> value = CefListValue::Create();
|
||||
|
||||
static ui::AXState state_arr[] = {
|
||||
ui::AX_STATE_NONE,
|
||||
ui::AX_STATE_BUSY,
|
||||
ui::AX_STATE_CHECKED,
|
||||
ui::AX_STATE_COLLAPSED,
|
||||
ui::AX_STATE_DEFAULT,
|
||||
ui::AX_STATE_DISABLED,
|
||||
ui::AX_STATE_EDITABLE,
|
||||
ui::AX_STATE_EXPANDED,
|
||||
ui::AX_STATE_FOCUSABLE,
|
||||
ui::AX_STATE_HASPOPUP,
|
||||
ui::AX_STATE_HORIZONTAL,
|
||||
ui::AX_STATE_HOVERED,
|
||||
ui::AX_STATE_INVISIBLE,
|
||||
ui::AX_STATE_LINKED,
|
||||
ui::AX_STATE_MULTILINE,
|
||||
ui::AX_STATE_MULTISELECTABLE,
|
||||
ui::AX_STATE_OFFSCREEN,
|
||||
ui::AX_STATE_PRESSED,
|
||||
ui::AX_STATE_PROTECTED,
|
||||
ui::AX_STATE_READ_ONLY,
|
||||
ui::AX_STATE_REQUIRED,
|
||||
ui::AX_STATE_RICHLY_EDITABLE,
|
||||
ui::AX_STATE_SELECTABLE,
|
||||
ui::AX_STATE_SELECTED,
|
||||
ui::AX_STATE_VERTICAL,
|
||||
ui::AX_STATE_VISITED,
|
||||
ui::AX_STATE_LAST
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
// Iterate and find which states are set.
|
||||
for (unsigned i = 0; i < arraysize(state_arr); i++) {
|
||||
if (state & (1 << state_arr[i]))
|
||||
value->SetString(index++, ToString(state_arr[i]));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
// Helper function for AXNodeData::ToCefValue - converts GfxRect to
|
||||
// CefDictionaryValue.
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(const gfx::RectF& bounds) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
value->SetDouble("x", bounds.x());
|
||||
value->SetDouble("y", bounds.y());
|
||||
value->SetDouble("width", bounds.width());
|
||||
value->SetDouble("height", bounds.height());
|
||||
return value;
|
||||
}
|
||||
|
||||
// Helper Functor for adding AxNodeData::attributes to AXNodeData::ToCefValue.
|
||||
struct PopulateAxNodeAttributes {
|
||||
CefRefPtr<CefDictionaryValue> attributes;
|
||||
|
||||
explicit PopulateAxNodeAttributes(CefRefPtr<CefDictionaryValue> attrs) :
|
||||
attributes(attrs) {}
|
||||
|
||||
// Int Attributes
|
||||
void operator()(const std::pair<ui::AXIntAttribute, int32_t> attr) {
|
||||
if (attr.first == ui::AX_INT_ATTRIBUTE_NONE)
|
||||
return;
|
||||
|
||||
switch (attr.first) {
|
||||
case ui::AX_INT_ATTRIBUTE_NONE:
|
||||
break;
|
||||
case ui::AX_ATTR_SCROLL_X:
|
||||
case ui::AX_ATTR_SCROLL_X_MIN:
|
||||
case ui::AX_ATTR_SCROLL_X_MAX:
|
||||
case ui::AX_ATTR_SCROLL_Y:
|
||||
case ui::AX_ATTR_SCROLL_Y_MIN:
|
||||
case ui::AX_ATTR_SCROLL_Y_MAX:
|
||||
case ui::AX_ATTR_HIERARCHICAL_LEVEL:
|
||||
case ui::AX_ATTR_TEXT_SEL_START:
|
||||
case ui::AX_ATTR_TEXT_SEL_END:
|
||||
case ui::AX_ATTR_ARIA_COL_COUNT:
|
||||
case ui::AX_ATTR_ARIA_COL_INDEX:
|
||||
case ui::AX_ATTR_ARIA_ROW_COUNT:
|
||||
case ui::AX_ATTR_ARIA_ROW_INDEX:
|
||||
case ui::AX_ATTR_TABLE_ROW_COUNT:
|
||||
case ui::AX_ATTR_TABLE_COLUMN_COUNT:
|
||||
case ui::AX_ATTR_TABLE_CELL_COLUMN_INDEX:
|
||||
case ui::AX_ATTR_TABLE_CELL_ROW_INDEX:
|
||||
case ui::AX_ATTR_TABLE_CELL_COLUMN_SPAN:
|
||||
case ui::AX_ATTR_TABLE_CELL_ROW_SPAN:
|
||||
case ui::AX_ATTR_TABLE_COLUMN_HEADER_ID:
|
||||
case ui::AX_ATTR_TABLE_COLUMN_INDEX:
|
||||
case ui::AX_ATTR_TABLE_HEADER_ID:
|
||||
case ui::AX_ATTR_TABLE_ROW_HEADER_ID:
|
||||
case ui::AX_ATTR_TABLE_ROW_INDEX:
|
||||
case ui::AX_ATTR_ACTIVEDESCENDANT_ID:
|
||||
case ui::AX_ATTR_IN_PAGE_LINK_TARGET_ID:
|
||||
case ui::AX_ATTR_ERRORMESSAGE_ID:
|
||||
case ui::AX_ATTR_MEMBER_OF_ID:
|
||||
case ui::AX_ATTR_NEXT_ON_LINE_ID:
|
||||
case ui::AX_ATTR_PREVIOUS_ON_LINE_ID:
|
||||
case ui::AX_ATTR_CHILD_TREE_ID:
|
||||
case ui::AX_ATTR_SET_SIZE:
|
||||
case ui::AX_ATTR_POS_IN_SET:
|
||||
attributes->SetInt(ToString(attr.first), attr.second);
|
||||
break;
|
||||
case ui::AX_ATTR_ACTION:
|
||||
attributes->SetString(ToString(attr.first),
|
||||
ui::ActionToUnlocalizedString(
|
||||
static_cast<ui::AXSupportedAction>(attr.second)));
|
||||
break;
|
||||
case ui::AX_ATTR_INVALID_STATE:
|
||||
if (ui::AX_INVALID_STATE_NONE != attr.second) {
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXInvalidState>(attr.second)));
|
||||
}
|
||||
break;
|
||||
case ui::AX_ATTR_SORT_DIRECTION:
|
||||
if (ui::AX_SORT_DIRECTION_NONE != attr.second) {
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXSortDirection>(attr.second)));
|
||||
}
|
||||
break;
|
||||
case ui::AX_ATTR_NAME_FROM:
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXNameFrom>(attr.second)));
|
||||
break;
|
||||
case ui::AX_ATTR_COLOR_VALUE:
|
||||
case ui::AX_ATTR_BACKGROUND_COLOR:
|
||||
case ui::AX_ATTR_COLOR:
|
||||
attributes->SetString(ToString(attr.first),
|
||||
base::StringPrintf("0x%X", attr.second));
|
||||
break;
|
||||
case ui::AX_ATTR_DESCRIPTION_FROM:
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXDescriptionFrom>(attr.second)));
|
||||
break;
|
||||
case ui::AX_ATTR_ARIA_CURRENT_STATE:
|
||||
if (ui::AX_ARIA_CURRENT_STATE_NONE != attr.second) {
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXAriaCurrentState>(attr.second)));
|
||||
}
|
||||
break;
|
||||
case ui::AX_ATTR_TEXT_DIRECTION:
|
||||
if (ui::AX_TEXT_DIRECTION_NONE != attr.second) {
|
||||
attributes->SetString(ToString(attr.first), ToString(
|
||||
static_cast<ui::AXTextDirection>(attr.second)));
|
||||
}
|
||||
break;
|
||||
case ui::AX_ATTR_TEXT_STYLE: {
|
||||
auto text_style = static_cast<ui::AXTextStyle>(attr.second);
|
||||
if (text_style == ui::AX_TEXT_STYLE_NONE)
|
||||
break;
|
||||
|
||||
static ui::AXTextStyle textStyleArr[] = {
|
||||
ui::AX_TEXT_STYLE_BOLD,
|
||||
ui::AX_TEXT_STYLE_ITALIC,
|
||||
ui::AX_TEXT_STYLE_UNDERLINE,
|
||||
ui::AX_TEXT_STYLE_LINE_THROUGH
|
||||
};
|
||||
|
||||
CefRefPtr<CefListValue> list = CefListValue::Create();
|
||||
int index = 0;
|
||||
// Iterate and find which states are set.
|
||||
for (unsigned i = 0; i < arraysize(textStyleArr); i++) {
|
||||
if (text_style & textStyleArr[i])
|
||||
list->SetString(index++, ToString(textStyleArr[i]));
|
||||
}
|
||||
attributes->SetList(ToString(attr.first), list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set Bool Attributes.
|
||||
void operator()(const std::pair<ui::AXBoolAttribute, bool> attr) {
|
||||
if (attr.first != ui::AX_BOOL_ATTRIBUTE_NONE)
|
||||
attributes->SetBool(ToString(attr.first), attr.second);
|
||||
}
|
||||
// Set String Attributes.
|
||||
void operator()(const std::pair<ui::AXStringAttribute, std::string>& attr) {
|
||||
if (attr.first != ui::AX_STRING_ATTRIBUTE_NONE)
|
||||
attributes->SetString(ToString(attr.first), attr.second);
|
||||
}
|
||||
// Set Float attributes.
|
||||
void operator()(const std::pair<ui::AXFloatAttribute, float>& attr) {
|
||||
if (attr.first != ui::AX_FLOAT_ATTRIBUTE_NONE)
|
||||
attributes->SetDouble(ToString(attr.first), attr.second);
|
||||
}
|
||||
|
||||
// Set Int list attributes.
|
||||
void operator()(const std::pair<ui::AXIntListAttribute,
|
||||
std::vector<int32_t>>& attr) {
|
||||
if (attr.first != ui::AX_INT_LIST_ATTRIBUTE_NONE) {
|
||||
CefRefPtr<CefListValue> list;
|
||||
|
||||
if (ui::AX_ATTR_MARKER_TYPES == attr.first) {
|
||||
list = CefListValue::Create();
|
||||
int index = 0;
|
||||
for (size_t i = 0; i < attr.second.size(); ++i) {
|
||||
auto type = static_cast<ui::AXMarkerType>(attr.second[i]);
|
||||
|
||||
if (type == ui::AX_MARKER_TYPE_NONE)
|
||||
continue;
|
||||
|
||||
static ui::AXMarkerType marktypeArr[] = {
|
||||
ui::AX_MARKER_TYPE_SPELLING,
|
||||
ui::AX_MARKER_TYPE_GRAMMAR,
|
||||
ui::AX_MARKER_TYPE_TEXT_MATCH
|
||||
};
|
||||
|
||||
// Iterate and find which markers are set.
|
||||
for (unsigned j = 0; j < arraysize(marktypeArr); j++) {
|
||||
if (type & marktypeArr[j])
|
||||
list->SetString(index++, ToString(marktypeArr[j]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list = ToCefValue(attr.second);
|
||||
}
|
||||
attributes->SetList(ToString(attr.first), list);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Converts AXNodeData to CefDictionaryValue(like AXNodeData::ToString).
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXNodeData& node) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (node.id != -1)
|
||||
value->SetInt("id", node.id);
|
||||
|
||||
value->SetString("role", ToString(node.role));
|
||||
value->SetList("state", ToCefValue(node.state));
|
||||
|
||||
if (node.offset_container_id != -1)
|
||||
value->SetInt("offset_container_id", node.offset_container_id);
|
||||
|
||||
value->SetDictionary("location", ToCefValue(node.location));
|
||||
|
||||
// Transform matrix is private, so we set the string that Clients can parse
|
||||
// and use if needed.
|
||||
if (node.transform && !node.transform->IsIdentity())
|
||||
value->SetString("transform", node.transform->ToString());
|
||||
|
||||
if (!node.child_ids.empty())
|
||||
value->SetList("child_ids", ToCefValue(node.child_ids));
|
||||
|
||||
CefRefPtr<CefDictionaryValue> attributes = CefDictionaryValue::Create();
|
||||
PopulateAxNodeAttributes func(attributes);
|
||||
|
||||
// Poupulate Int Attributes.
|
||||
std::for_each(node.int_attributes.begin(), node.int_attributes.end(), func);
|
||||
|
||||
// Poupulate String Attributes.
|
||||
std::for_each(node.string_attributes.begin(),
|
||||
node.string_attributes.end(), func);
|
||||
|
||||
// Poupulate Float Attributes.
|
||||
std::for_each(node.float_attributes.begin(),
|
||||
node.float_attributes.end(), func);
|
||||
|
||||
// Poupulate Bool Attributes.
|
||||
std::for_each(node.bool_attributes.begin(), node.bool_attributes.end(), func);
|
||||
|
||||
// Populate int list attributes.
|
||||
std::for_each(node.intlist_attributes.begin(),
|
||||
node.intlist_attributes.end(), func);
|
||||
|
||||
value->SetDictionary("attributes", attributes);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Converts AXTreeData to CefDictionaryValue(like AXTreeData::ToString).
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXTreeData& treeData) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (treeData.tree_id != -1)
|
||||
value->SetInt("tree_id", treeData.tree_id);
|
||||
|
||||
if (treeData.parent_tree_id != -1)
|
||||
value->SetInt("parent_tree_id", treeData.parent_tree_id);
|
||||
|
||||
if (treeData.focused_tree_id != -1)
|
||||
value->SetInt("focused_tree_id", treeData.focused_tree_id);
|
||||
|
||||
if (!treeData.doctype.empty())
|
||||
value->SetString("doctype", treeData.doctype);
|
||||
|
||||
value->SetBool("loaded", treeData.loaded);
|
||||
|
||||
if (treeData.loading_progress != 0.0)
|
||||
value->SetDouble("loading_progress", treeData.loading_progress);
|
||||
|
||||
if (!treeData.mimetype.empty())
|
||||
value->SetString("mimetype", treeData.mimetype);
|
||||
if (!treeData.url.empty())
|
||||
value->SetString("url", treeData.url);
|
||||
if (!treeData.title.empty())
|
||||
value->SetString("title", treeData.title);
|
||||
|
||||
if (treeData.sel_anchor_object_id != -1) {
|
||||
value->SetInt("sel_anchor_object_id", treeData.sel_anchor_object_id);
|
||||
value->SetInt("sel_anchor_offset", treeData.sel_anchor_offset);
|
||||
value->SetString("sel_anchor_affinity",
|
||||
ToString(treeData.sel_anchor_affinity));
|
||||
}
|
||||
if (treeData.sel_focus_object_id != -1) {
|
||||
value->SetInt("sel_focus_object_id", treeData.sel_anchor_object_id);
|
||||
value->SetInt("sel_focus_offset", treeData.sel_anchor_offset);
|
||||
value->SetString("sel_focus_affinity",
|
||||
ToString(treeData.sel_anchor_affinity));
|
||||
}
|
||||
|
||||
if (treeData.focus_id != -1)
|
||||
value->SetInt("focus_id", treeData.focus_id);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Converts AXTreeUpdate to CefDictionaryValue(like AXTreeUpdate::ToString).
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXTreeUpdate& update) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (update.has_tree_data) {
|
||||
value->SetBool("has_tree_data", true);
|
||||
value->SetDictionary("tree_data", ToCefValue(update.tree_data));
|
||||
}
|
||||
|
||||
if (update.node_id_to_clear != 0)
|
||||
value->SetInt("node_id_to_clear", update.node_id_to_clear);
|
||||
|
||||
if (update.root_id != 0)
|
||||
value->SetInt("root_id", update.root_id);
|
||||
|
||||
value->SetList("nodes", ToCefValue(update.nodes));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Convert AXEventNotificationDetails to CefDictionaryValue.
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(
|
||||
const content::AXEventNotificationDetails& eventData) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (eventData.id != -1)
|
||||
value->SetInt("id", eventData.id);
|
||||
|
||||
if (eventData.ax_tree_id != -1)
|
||||
value->SetInt("ax_tree_id", eventData.ax_tree_id);
|
||||
|
||||
if (eventData.event_type != ui::AX_EVENT_NONE)
|
||||
value->SetString("event_type", ToString(eventData.event_type));
|
||||
|
||||
if (eventData.event_from != ui::AX_EVENT_FROM_NONE)
|
||||
value->SetString("event_from", ToString(eventData.event_from));
|
||||
|
||||
value->SetDictionary("update", ToCefValue(eventData.update));
|
||||
return value;
|
||||
}
|
||||
|
||||
// Convert AXRelativeBounds to CefDictionaryValue. Similar to
|
||||
// AXRelativeBounds::ToString. See that for more details
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(const ui::AXRelativeBounds& location) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (location.offset_container_id != -1)
|
||||
value->SetInt("offset_container_id", location.offset_container_id);
|
||||
|
||||
value->SetDictionary("bounds", ToCefValue(location.bounds));
|
||||
|
||||
// Transform matrix is private, so we set the string that Clients can parse
|
||||
// and use if needed.
|
||||
if (location.transform && !location.transform->IsIdentity())
|
||||
value->SetString("transform", location.transform->ToString());
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Convert AXEventNotificationDetails to CefDictionaryValue.
|
||||
CefRefPtr<CefDictionaryValue> ToCefValue(
|
||||
const content::AXLocationChangeNotificationDetails& locData) {
|
||||
CefRefPtr<CefDictionaryValue> value = CefDictionaryValue::Create();
|
||||
|
||||
if (locData.id != -1)
|
||||
value->SetInt("id", locData.id);
|
||||
|
||||
if (locData.ax_tree_id != -1)
|
||||
value->SetInt("ax_tree_id", locData.ax_tree_id);
|
||||
|
||||
value->SetDictionary("new_location", ToCefValue(locData.new_location));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
CefRefPtr<CefListValue> ToCefValue(const std::vector<T>& vecData) {
|
||||
CefRefPtr<CefListValue> value = CefListValue::Create();
|
||||
|
||||
for (size_t i = 0; i <vecData.size(); i ++)
|
||||
value->SetDictionary(i, ToCefValue(vecData[i]));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace osr_accessibility_util {
|
||||
|
||||
CefRefPtr<CefValue> ParseAccessibilityEventData(
|
||||
const std::vector<content::AXEventNotificationDetails>& data) {
|
||||
CefRefPtr<CefValue> value = CefValue::Create();
|
||||
value->SetList(ToCefValue(data));
|
||||
return value;
|
||||
}
|
||||
|
||||
CefRefPtr<CefValue> ParseAccessibilityLocationData(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& data) {
|
||||
CefRefPtr<CefValue> value = CefValue::Create();
|
||||
value->SetList(ToCefValue(data));
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace osr_accessibility_util
|
29
libcef/browser/osr/osr_accessibility_util.h
Normal file
29
libcef/browser/osr/osr_accessibility_util.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2017 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.
|
||||
|
||||
#ifndef CEF_LIBCEF_BROWSER_OSR_ACCESSIBILITY_UTIL_H_
|
||||
#define CEF_LIBCEF_BROWSER_OSR_ACCESSIBILITY_UTIL_H_
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "include/cef_values.h"
|
||||
|
||||
namespace content {
|
||||
struct AXEventNotificationDetails;
|
||||
struct AXLocationChangeNotificationDetails;
|
||||
}
|
||||
|
||||
namespace osr_accessibility_util {
|
||||
|
||||
// Convert Accessibility Event and location updates to CefValue, which may be
|
||||
// consumed or serialized with CefJSONWrite.
|
||||
CefRefPtr<CefValue> ParseAccessibilityEventData(
|
||||
const std::vector<content::AXEventNotificationDetails>& data);
|
||||
|
||||
CefRefPtr<CefValue> ParseAccessibilityLocationData(
|
||||
const std::vector<content::AXLocationChangeNotificationDetails>& data);
|
||||
|
||||
} // namespace osr_accessibility_util
|
||||
|
||||
#endif // CEF_LIBCEF_BROWSER_ACCESSIBILITY_UTIL_H_
|
@ -71,6 +71,20 @@ void CefWebContentsViewOSR::StoreFocus() {
|
||||
void CefWebContentsViewOSR::RestoreFocus() {
|
||||
}
|
||||
|
||||
void CefWebContentsViewOSR::GotFocus() {
|
||||
if (web_contents_) {
|
||||
content::WebContentsImpl* web_contents_impl =
|
||||
static_cast<content::WebContentsImpl*>(web_contents_);
|
||||
if (web_contents_impl)
|
||||
web_contents_impl->NotifyWebContentsFocused();
|
||||
}
|
||||
}
|
||||
|
||||
void CefWebContentsViewOSR::TakeFocus(bool reverse) {
|
||||
if (web_contents_->GetDelegate())
|
||||
web_contents_->GetDelegate()->TakeFocus(web_contents_, reverse);
|
||||
}
|
||||
|
||||
content::DropData* CefWebContentsViewOSR::GetDropData() const {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -70,6 +70,8 @@ class CefWebContentsViewOSR : public content::WebContentsView,
|
||||
const content::DragEventSourceInfo& event_info,
|
||||
content::RenderWidgetHostImpl* source_rwh) override;
|
||||
void UpdateDragCursor(blink::WebDragOperation operation) override;
|
||||
virtual void GotFocus() override;
|
||||
virtual void TakeFocus(bool reverse) override;
|
||||
|
||||
private:
|
||||
CefRenderWidgetHostViewOSR* GetView() const;
|
||||
|
81
libcef_dll/cpptoc/accessibility_handler_cpptoc.cc
Normal file
81
libcef_dll/cpptoc/accessibility_handler_cpptoc.cc
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright (c) 2017 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.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This file was generated by the CEF translator tool. If making changes by
|
||||
// hand only do so within the body of existing method and function
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/accessibility_handler_cpptoc.h"
|
||||
#include "libcef_dll/ctocpp/value_ctocpp.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
// MEMBER FUNCTIONS - Body may be edited by hand.
|
||||
|
||||
void CEF_CALLBACK accessibility_handler_on_accessibility_tree_change(
|
||||
struct _cef_accessibility_handler_t* self, struct _cef_value_t* value) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
// Verify param: value; type: refptr_diff
|
||||
DCHECK(value);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefAccessibilityHandlerCppToC::Get(self)->OnAccessibilityTreeChange(
|
||||
CefValueCToCpp::Wrap(value));
|
||||
}
|
||||
|
||||
void CEF_CALLBACK accessibility_handler_on_accessibility_location_change(
|
||||
struct _cef_accessibility_handler_t* self, struct _cef_value_t* value) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
// Verify param: value; type: refptr_diff
|
||||
DCHECK(value);
|
||||
if (!value)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefAccessibilityHandlerCppToC::Get(self)->OnAccessibilityLocationChange(
|
||||
CefValueCToCpp::Wrap(value));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
// CONSTRUCTOR - Do not edit by hand.
|
||||
|
||||
CefAccessibilityHandlerCppToC::CefAccessibilityHandlerCppToC() {
|
||||
GetStruct()->on_accessibility_tree_change =
|
||||
accessibility_handler_on_accessibility_tree_change;
|
||||
GetStruct()->on_accessibility_location_change =
|
||||
accessibility_handler_on_accessibility_location_change;
|
||||
}
|
||||
|
||||
template<> CefRefPtr<CefAccessibilityHandler> CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::UnwrapDerived(
|
||||
CefWrapperType type, cef_accessibility_handler_t* s) {
|
||||
NOTREACHED() << "Unexpected class type: " << type;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
template<> base::AtomicRefCount CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::DebugObjCt = 0;
|
||||
#endif
|
||||
|
||||
template<> CefWrapperType CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::kWrapperType =
|
||||
WT_ACCESSIBILITY_HANDLER;
|
34
libcef_dll/cpptoc/accessibility_handler_cpptoc.h
Normal file
34
libcef_dll/cpptoc/accessibility_handler_cpptoc.h
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2017 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.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This file was generated by the CEF translator tool. If making changes by
|
||||
// hand only do so within the body of existing method and function
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
|
||||
#ifndef CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
|
||||
#define CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
|
||||
#pragma once
|
||||
|
||||
#if !defined(WRAPPING_CEF_SHARED)
|
||||
#error This file can be included wrapper-side only
|
||||
#endif
|
||||
|
||||
#include "include/cef_accessibility_handler.h"
|
||||
#include "include/capi/cef_accessibility_handler_capi.h"
|
||||
#include "libcef_dll/cpptoc/cpptoc_ref_counted.h"
|
||||
|
||||
// Wrap a C++ class with a C structure.
|
||||
// This class may be instantiated and accessed wrapper-side only.
|
||||
class CefAccessibilityHandlerCppToC
|
||||
: public CefCppToCRefCounted<CefAccessibilityHandlerCppToC,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t> {
|
||||
public:
|
||||
CefAccessibilityHandlerCppToC();
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_DLL_CPPTOC_ACCESSIBILITY_HANDLER_CPPTOC_H_
|
@ -1005,6 +1005,19 @@ struct _cef_navigation_entry_t* CEF_CALLBACK browser_host_get_visible_navigation
|
||||
return CefNavigationEntryCppToC::Wrap(_retval);
|
||||
}
|
||||
|
||||
void CEF_CALLBACK browser_host_set_accessibility_state(
|
||||
struct _cef_browser_host_t* self, cef_state_t accessibility_state) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return;
|
||||
|
||||
// Execute
|
||||
CefBrowserHostCppToC::Get(self)->SetAccessibilityState(
|
||||
accessibility_state);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@ -1072,6 +1085,7 @@ CefBrowserHostCppToC::CefBrowserHostCppToC() {
|
||||
browser_host_drag_source_system_drag_ended;
|
||||
GetStruct()->get_visible_navigation_entry =
|
||||
browser_host_get_visible_navigation_entry;
|
||||
GetStruct()->set_accessibility_state = browser_host_set_accessibility_state;
|
||||
}
|
||||
|
||||
template<> CefRefPtr<CefBrowserHost> CefCppToCRefCounted<CefBrowserHostCppToC,
|
||||
|
@ -10,6 +10,7 @@
|
||||
// for more information.
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/accessibility_handler_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/render_handler_cpptoc.h"
|
||||
#include "libcef_dll/ctocpp/browser_ctocpp.h"
|
||||
#include "libcef_dll/ctocpp/drag_data_ctocpp.h"
|
||||
@ -19,6 +20,22 @@ namespace {
|
||||
|
||||
// MEMBER FUNCTIONS - Body may be edited by hand.
|
||||
|
||||
cef_accessibility_handler_t* CEF_CALLBACK render_handler_get_accessibility_handler(
|
||||
struct _cef_render_handler_t* self) {
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
DCHECK(self);
|
||||
if (!self)
|
||||
return NULL;
|
||||
|
||||
// Execute
|
||||
CefRefPtr<CefAccessibilityHandler> _retval = CefRenderHandlerCppToC::Get(
|
||||
self)->GetAccessibilityHandler();
|
||||
|
||||
// Return type: refptr_same
|
||||
return CefAccessibilityHandlerCppToC::Wrap(_retval);
|
||||
}
|
||||
|
||||
int CEF_CALLBACK render_handler_get_root_screen_rect(
|
||||
struct _cef_render_handler_t* self, cef_browser_t* browser,
|
||||
cef_rect_t* rect) {
|
||||
@ -396,6 +413,8 @@ void CEF_CALLBACK render_handler_on_ime_composition_range_changed(
|
||||
// CONSTRUCTOR - Do not edit by hand.
|
||||
|
||||
CefRenderHandlerCppToC::CefRenderHandlerCppToC() {
|
||||
GetStruct()->get_accessibility_handler =
|
||||
render_handler_get_accessibility_handler;
|
||||
GetStruct()->get_root_screen_rect = render_handler_get_root_screen_rect;
|
||||
GetStruct()->get_view_rect = render_handler_get_view_rect;
|
||||
GetStruct()->get_screen_point = render_handler_get_screen_point;
|
||||
|
75
libcef_dll/ctocpp/accessibility_handler_ctocpp.cc
Normal file
75
libcef_dll/ctocpp/accessibility_handler_ctocpp.cc
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright (c) 2017 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.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This file was generated by the CEF translator tool. If making changes by
|
||||
// hand only do so within the body of existing method and function
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
|
||||
#include "libcef_dll/cpptoc/value_cpptoc.h"
|
||||
#include "libcef_dll/ctocpp/accessibility_handler_ctocpp.h"
|
||||
|
||||
|
||||
// VIRTUAL METHODS - Body may be edited by hand.
|
||||
|
||||
void CefAccessibilityHandlerCToCpp::OnAccessibilityTreeChange(
|
||||
CefRefPtr<CefValue> value) {
|
||||
cef_accessibility_handler_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, on_accessibility_tree_change))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Verify param: value; type: refptr_diff
|
||||
DCHECK(value.get());
|
||||
if (!value.get())
|
||||
return;
|
||||
|
||||
// Execute
|
||||
_struct->on_accessibility_tree_change(_struct,
|
||||
CefValueCppToC::Wrap(value));
|
||||
}
|
||||
|
||||
void CefAccessibilityHandlerCToCpp::OnAccessibilityLocationChange(
|
||||
CefRefPtr<CefValue> value) {
|
||||
cef_accessibility_handler_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, on_accessibility_location_change))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Verify param: value; type: refptr_diff
|
||||
DCHECK(value.get());
|
||||
if (!value.get())
|
||||
return;
|
||||
|
||||
// Execute
|
||||
_struct->on_accessibility_location_change(_struct,
|
||||
CefValueCppToC::Wrap(value));
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR - Do not edit by hand.
|
||||
|
||||
CefAccessibilityHandlerCToCpp::CefAccessibilityHandlerCToCpp() {
|
||||
}
|
||||
|
||||
template<> cef_accessibility_handler_t* CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::UnwrapDerived(
|
||||
CefWrapperType type, CefAccessibilityHandler* c) {
|
||||
NOTREACHED() << "Unexpected class type: " << type;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
template<> base::AtomicRefCount CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::DebugObjCt = 0;
|
||||
#endif
|
||||
|
||||
template<> CefWrapperType CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t>::kWrapperType =
|
||||
WT_ACCESSIBILITY_HANDLER;
|
38
libcef_dll/ctocpp/accessibility_handler_ctocpp.h
Normal file
38
libcef_dll/ctocpp/accessibility_handler_ctocpp.h
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2017 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.
|
||||
//
|
||||
// ---------------------------------------------------------------------------
|
||||
//
|
||||
// This file was generated by the CEF translator tool. If making changes by
|
||||
// hand only do so within the body of existing method and function
|
||||
// implementations. See the translator.README.txt file in the tools directory
|
||||
// for more information.
|
||||
//
|
||||
|
||||
#ifndef CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
|
||||
#define CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
|
||||
#pragma once
|
||||
|
||||
#if !defined(BUILDING_CEF_SHARED)
|
||||
#error This file can be included DLL-side only
|
||||
#endif
|
||||
|
||||
#include "include/cef_accessibility_handler.h"
|
||||
#include "include/capi/cef_accessibility_handler_capi.h"
|
||||
#include "libcef_dll/ctocpp/ctocpp_ref_counted.h"
|
||||
|
||||
// Wrap a C structure with a C++ class.
|
||||
// This class may be instantiated and accessed DLL-side only.
|
||||
class CefAccessibilityHandlerCToCpp
|
||||
: public CefCToCppRefCounted<CefAccessibilityHandlerCToCpp,
|
||||
CefAccessibilityHandler, cef_accessibility_handler_t> {
|
||||
public:
|
||||
CefAccessibilityHandlerCToCpp();
|
||||
|
||||
// CefAccessibilityHandler methods.
|
||||
void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) override;
|
||||
void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) override;
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_DLL_CTOCPP_ACCESSIBILITY_HANDLER_CTOCPP_H_
|
@ -843,6 +843,19 @@ CefRefPtr<CefNavigationEntry> CefBrowserHostCToCpp::GetVisibleNavigationEntry(
|
||||
return CefNavigationEntryCToCpp::Wrap(_retval);
|
||||
}
|
||||
|
||||
void CefBrowserHostCToCpp::SetAccessibilityState(
|
||||
cef_state_t accessibility_state) {
|
||||
cef_browser_host_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, set_accessibility_state))
|
||||
return;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
_struct->set_accessibility_state(_struct,
|
||||
accessibility_state);
|
||||
}
|
||||
|
||||
|
||||
// CONSTRUCTOR - Do not edit by hand.
|
||||
|
||||
|
@ -103,6 +103,7 @@ class CefBrowserHostCToCpp
|
||||
void DragSourceEndedAt(int x, int y, DragOperationsMask op) OVERRIDE;
|
||||
void DragSourceSystemDragEnded() OVERRIDE;
|
||||
CefRefPtr<CefNavigationEntry> GetVisibleNavigationEntry() OVERRIDE;
|
||||
void SetAccessibilityState(cef_state_t accessibility_state) OVERRIDE;
|
||||
};
|
||||
|
||||
#endif // CEF_LIBCEF_DLL_CTOCPP_BROWSER_HOST_CTOCPP_H_
|
||||
|
@ -12,11 +12,28 @@
|
||||
|
||||
#include "libcef_dll/cpptoc/browser_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/drag_data_cpptoc.h"
|
||||
#include "libcef_dll/ctocpp/accessibility_handler_ctocpp.h"
|
||||
#include "libcef_dll/ctocpp/render_handler_ctocpp.h"
|
||||
|
||||
|
||||
// VIRTUAL METHODS - Body may be edited by hand.
|
||||
|
||||
CefRefPtr<CefAccessibilityHandler> CefRenderHandlerCToCpp::GetAccessibilityHandler(
|
||||
) {
|
||||
cef_render_handler_t* _struct = GetStruct();
|
||||
if (CEF_MEMBER_MISSING(_struct, get_accessibility_handler))
|
||||
return NULL;
|
||||
|
||||
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
|
||||
|
||||
// Execute
|
||||
cef_accessibility_handler_t* _retval = _struct->get_accessibility_handler(
|
||||
_struct);
|
||||
|
||||
// Return type: refptr_same
|
||||
return CefAccessibilityHandlerCToCpp::Wrap(_retval);
|
||||
}
|
||||
|
||||
bool CefRenderHandlerCToCpp::GetRootScreenRect(CefRefPtr<CefBrowser> browser,
|
||||
CefRect& rect) {
|
||||
cef_render_handler_t* _struct = GetStruct();
|
||||
|
@ -31,6 +31,7 @@ class CefRenderHandlerCToCpp
|
||||
CefRenderHandlerCToCpp();
|
||||
|
||||
// CefRenderHandler methods.
|
||||
CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() override;
|
||||
bool GetRootScreenRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override;
|
||||
bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) override;
|
||||
bool GetScreenPoint(CefRefPtr<CefBrowser> browser, int viewX, int viewY,
|
||||
|
@ -107,6 +107,7 @@
|
||||
#include "libcef_dll/cpptoc/x509certificate_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/xml_reader_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/zip_reader_cpptoc.h"
|
||||
#include "libcef_dll/ctocpp/accessibility_handler_ctocpp.h"
|
||||
#include "libcef_dll/ctocpp/app_ctocpp.h"
|
||||
#include "libcef_dll/ctocpp/browser_process_handler_ctocpp.h"
|
||||
#include "libcef_dll/ctocpp/views/browser_view_delegate_ctocpp.h"
|
||||
@ -237,6 +238,8 @@ CEF_EXPORT void cef_shutdown() {
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// Check that all wrapper objects have been destroyed
|
||||
DCHECK(base::AtomicRefCountIsZero(
|
||||
&CefAccessibilityHandlerCToCpp::DebugObjCt));
|
||||
DCHECK(base::AtomicRefCountIsZero(&CefAuthCallbackCppToC::DebugObjCt));
|
||||
DCHECK(base::AtomicRefCountIsZero(
|
||||
&CefBeforeDownloadCallbackCppToC::DebugObjCt));
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "include/cef_web_plugin.h"
|
||||
#include "include/capi/cef_web_plugin_capi.h"
|
||||
#include "include/cef_version.h"
|
||||
#include "libcef_dll/cpptoc/accessibility_handler_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/app_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/browser_process_handler_cpptoc.h"
|
||||
#include "libcef_dll/cpptoc/views/browser_view_delegate_cpptoc.h"
|
||||
@ -229,6 +230,8 @@ CEF_GLOBAL void CefShutdown() {
|
||||
|
||||
#if DCHECK_IS_ON()
|
||||
// Check that all wrapper objects have been destroyed
|
||||
DCHECK(base::AtomicRefCountIsZero(
|
||||
&CefAccessibilityHandlerCppToC::DebugObjCt));
|
||||
DCHECK(base::AtomicRefCountIsZero(&CefAuthCallbackCToCpp::DebugObjCt));
|
||||
DCHECK(base::AtomicRefCountIsZero(
|
||||
&CefBeforeDownloadCallbackCToCpp::DebugObjCt));
|
||||
|
@ -17,6 +17,7 @@
|
||||
enum CefWrapperType {
|
||||
WT_BASE_REF_COUNTED = 1,
|
||||
WT_BASE_SCOPED,
|
||||
WT_ACCESSIBILITY_HANDLER,
|
||||
WT_APP,
|
||||
WT_AUTH_CALLBACK,
|
||||
WT_BEFORE_DOWNLOAD_CALLBACK,
|
||||
|
@ -1279,6 +1279,10 @@ void BrowserWindowOsrGtk::OnImeCompositionRangeChanged(
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
}
|
||||
|
||||
void BrowserWindowOsrGtk::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
}
|
||||
|
||||
void BrowserWindowOsrGtk::Create(ClientWindowHandle parent_handle) {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
DCHECK(!glarea_);
|
||||
|
@ -80,6 +80,7 @@ class BrowserWindowOsrGtk : public BrowserWindow,
|
||||
CefRefPtr<CefBrowser> browser,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
|
||||
|
||||
private:
|
||||
~BrowserWindowOsrGtk();
|
||||
|
@ -83,6 +83,8 @@ class BrowserWindowOsrMac : public BrowserWindow,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
void UpdateAccessibilityTree(CefRefPtr<CefValue> value) OVERRIDE;
|
||||
|
||||
private:
|
||||
// Create the NSView.
|
||||
void Create(ClientWindowHandle parent_handle, const CefRect& rect);
|
||||
|
@ -13,10 +13,14 @@
|
||||
#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/shared/browser/geometry_util.h"
|
||||
#include "tests/shared/browser/main_message_loop.h"
|
||||
|
||||
#import <AppKit/NSAccessibility.h>
|
||||
|
||||
namespace {
|
||||
|
||||
CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
@ -29,7 +33,7 @@ CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
} // namespace
|
||||
|
||||
@interface BrowserOpenGLView
|
||||
: NSOpenGLView <NSDraggingSource, NSDraggingDestination> {
|
||||
: NSOpenGLView <NSDraggingSource, NSDraggingDestination, NSAccessibility> {
|
||||
@private
|
||||
NSTrackingArea* tracking_area_;
|
||||
client::BrowserWindowOsrMac* browser_window_;
|
||||
@ -52,6 +56,9 @@ CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
// For intreacting with IME.
|
||||
NSTextInputContext* text_input_context_osr_mac_;
|
||||
|
||||
// Manages Accessibility Tree
|
||||
client::OsrAccessibilityHelper* accessibility_helper_;
|
||||
|
||||
// Event monitor for scroll wheel end event.
|
||||
id endWheelMonitor_;
|
||||
}
|
||||
@ -99,6 +106,7 @@ CefTextInputClientOSRMac* GetInputClientFromContext(
|
||||
- (NSRect)convertRectToBackingInternal:(NSRect)aRect;
|
||||
- (void)ChangeCompositionRange:(CefRange)range
|
||||
character_bounds:(const CefRenderHandler::RectList&) character_bounds;
|
||||
- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value;
|
||||
@end
|
||||
|
||||
|
||||
@ -948,6 +956,50 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
||||
}
|
||||
}
|
||||
|
||||
// NSAccessibility Protocol implementation.
|
||||
- (BOOL)accessibilityIsIgnored {
|
||||
if(!accessibility_helper_)
|
||||
return YES;
|
||||
else
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
||||
if(!accessibility_helper_)
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
||||
return NSAccessibilityGroupRole;
|
||||
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
||||
client::OsrAXNode* node = accessibility_helper_->GetRootNode();
|
||||
std::string desc = node ? node->AxDescription(): "";
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
|
||||
client::OsrAXNode* node = accessibility_helper_->GetRootNode();
|
||||
std::string desc = node ? node->AxValue(): "";
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
} else if ([attribute isEqualToString:
|
||||
NSAccessibilityRoleDescriptionAttribute]) {
|
||||
return NSAccessibilityRoleDescriptionForUIElement(self);
|
||||
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
||||
client::OsrAXNode* node = accessibility_helper_->GetRootNode();
|
||||
// Add Root as first Kid
|
||||
NSMutableArray* kids = [NSMutableArray arrayWithCapacity:1];
|
||||
NSObject* child = node->GetNativeAccessibleObject(NULL);
|
||||
[kids addObject: child];
|
||||
return NSAccessibilityUnignoredChildren(kids);
|
||||
} else {
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)accessibilityFocusedUIElement {
|
||||
if (accessibility_helper_) {
|
||||
client::OsrAXNode* node = accessibility_helper_->GetFocusedNode();
|
||||
return node ? node->GetNativeAccessibleObject(NULL) : nil;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Utility methods.
|
||||
- (void)resetDragDrop {
|
||||
current_drag_op_ = NSDragOperationNone;
|
||||
@ -1142,6 +1194,21 @@ NSPoint ConvertPointFromWindowToScreen(NSWindow* window, NSPoint point) {
|
||||
if (client)
|
||||
[client ChangeCompositionRange: range character_bounds:bounds];
|
||||
}
|
||||
|
||||
- (void)UpdateAccessibilityTree:(CefRefPtr<CefValue>)value {
|
||||
if (!accessibility_helper_) {
|
||||
accessibility_helper_ = new client::OsrAccessibilityHelper(value,
|
||||
[self getBrowser]);
|
||||
} else {
|
||||
accessibility_helper_->UpdateAccessibilityTree(value);
|
||||
}
|
||||
|
||||
if (accessibility_helper_) {
|
||||
NSAccessibilityPostNotification(self,
|
||||
NSAccessibilityValueChangedNotification);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@ -1493,6 +1560,14 @@ void BrowserWindowOsrMac::OnImeCompositionRangeChanged(
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserWindowOsrMac::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
if (nsview_) {
|
||||
[GLView(nsview_) UpdateAccessibilityTree:value];
|
||||
}
|
||||
}
|
||||
|
||||
void BrowserWindowOsrMac::Create(ClientWindowHandle parent_handle,
|
||||
const CefRect& rect) {
|
||||
REQUIRE_MAIN_THREAD();
|
||||
|
@ -146,4 +146,11 @@ void ClientHandlerOsr::OnImeCompositionRangeChanged(
|
||||
character_bounds);
|
||||
}
|
||||
|
||||
void ClientHandlerOsr::OnAccessibilityTreeChange(CefRefPtr<CefValue> value) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
if (!osr_delegate_)
|
||||
return;
|
||||
osr_delegate_->UpdateAccessibilityTree(value);
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
@ -13,6 +13,7 @@ namespace client {
|
||||
// Client handler implementation for windowless browsers. There will only ever
|
||||
// be one browser per handler instance.
|
||||
class ClientHandlerOsr : public ClientHandler,
|
||||
public CefAccessibilityHandler,
|
||||
public CefRenderHandler {
|
||||
public:
|
||||
// Implement this interface to receive notification of ClientHandlerOsr
|
||||
@ -61,6 +62,8 @@ class ClientHandlerOsr : public ClientHandler,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) = 0;
|
||||
|
||||
virtual void UpdateAccessibilityTree(CefRefPtr<CefValue> value) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~OsrDelegate() {}
|
||||
};
|
||||
@ -77,6 +80,9 @@ class ClientHandlerOsr : public ClientHandler,
|
||||
CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE {
|
||||
return this;
|
||||
}
|
||||
CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() OVERRIDE {
|
||||
return this;
|
||||
}
|
||||
|
||||
// CefLifeSpanHandler methods.
|
||||
void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
|
||||
@ -118,6 +124,10 @@ class ClientHandlerOsr : public ClientHandler,
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
// CefAccessibilityHandler methods.
|
||||
void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) OVERRIDE;
|
||||
void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) OVERRIDE {}
|
||||
|
||||
private:
|
||||
// Only accessed on the UI thread.
|
||||
OsrDelegate* osr_delegate_;
|
||||
|
145
tests/cefclient/browser/osr_accessibility_helper.cc
Normal file
145
tests/cefclient/browser/osr_accessibility_helper.cc
Normal file
@ -0,0 +1,145 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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/cefclient/browser/osr_accessibility_node.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
OsrAccessibilityHelper::OsrAccessibilityHelper(CefRefPtr<CefValue> value,
|
||||
CefRefPtr<CefBrowser> browser)
|
||||
: root_node_id_(-1),
|
||||
focused_node_id_(-1),
|
||||
browser_(browser) {
|
||||
UpdateAccessibilityTree(value);
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::UpdateAccessibilityTree(
|
||||
CefRefPtr<CefValue> value) {
|
||||
if (value && value->GetType() == VTYPE_LIST) {
|
||||
CefRefPtr<CefListValue > list = value->GetList();
|
||||
size_t numEvents = list->GetSize();
|
||||
if (numEvents > 0) {
|
||||
for (size_t i = 0; i < numEvents; i++) {
|
||||
CefRefPtr<CefDictionaryValue> event = list->GetDictionary(i);
|
||||
if (event && event->HasKey("event_type") && event->HasKey("update")) {
|
||||
std::string event_type = event->GetString("event_type");
|
||||
|
||||
CefRefPtr<CefDictionaryValue> update = event->GetDictionary("update");
|
||||
if (event_type == "layoutComplete")
|
||||
UpdateLayout(update);
|
||||
if (event_type == "focus" && event->HasKey("id")) {
|
||||
// Update focused node id
|
||||
focused_node_id_ = event->GetInt("id");
|
||||
UpdateFocusedNode(update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::UpdateLayout(
|
||||
CefRefPtr<CefDictionaryValue> update) {
|
||||
if (update) {
|
||||
CefRefPtr<CefDictionaryValue> tree_data;
|
||||
// get tree data
|
||||
if (update->HasKey("has_tree_data") && update->GetBool("has_tree_data"))
|
||||
tree_data = update->GetDictionary("tree_data");
|
||||
|
||||
// If a node is to be cleared
|
||||
if (update->HasKey("node_id_to_clear")) {
|
||||
int node_id_to_clear = update->GetInt("node_id_to_clear");
|
||||
|
||||
// reset root node if that is to be cleared
|
||||
if (node_id_to_clear == root_node_id_)
|
||||
root_node_id_ = -1;
|
||||
OsrAXNode *node = GetNode(node_id_to_clear);
|
||||
DestroyNode(node);
|
||||
}
|
||||
|
||||
if (update->HasKey("root_id"))
|
||||
root_node_id_ = update->GetInt("root_id");
|
||||
|
||||
if (tree_data && tree_data->HasKey("focus_id"))
|
||||
focused_node_id_ = tree_data->GetInt("focus_id");
|
||||
// Now initialize/update the node data.
|
||||
if (update->HasKey("nodes")) {
|
||||
CefRefPtr<CefListValue> nodes = update->GetList("nodes");
|
||||
|
||||
for (size_t index = 0; index < nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node) {
|
||||
int node_id = node->GetInt("id");
|
||||
OsrAXNode* axNode = GetNode(node_id);
|
||||
// Create if it is a new one
|
||||
if (axNode) {
|
||||
axNode->UpdateValue(node);
|
||||
} else {
|
||||
axNode = OsrAXNode::CreateNode(node, this);
|
||||
accessibility_node_map_[node_id] = axNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::UpdateFocusedNode(
|
||||
CefRefPtr<CefDictionaryValue> update) {
|
||||
if (update && update->HasKey("nodes")) {
|
||||
CefRefPtr<CefListValue> nodes = update->GetList("nodes");
|
||||
|
||||
for (size_t index = 0; index < nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node) {
|
||||
int node_id = node->GetInt("id");
|
||||
OsrAXNode* axNode = GetNode(node_id);
|
||||
// Create if it is a new one
|
||||
if (axNode) {
|
||||
axNode->UpdateValue(node);
|
||||
} else {
|
||||
axNode = OsrAXNode::CreateNode(node, this);
|
||||
accessibility_node_map_[node_id] = axNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Now Notify Screen Reader
|
||||
OsrAXNode* axNode = GetFocusedNode();
|
||||
// Fallback to Root
|
||||
if (!axNode)
|
||||
axNode = GetRootNode();
|
||||
|
||||
axNode->NotifyAccessibilityEvent("focus");
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::Reset() {
|
||||
accessibility_node_map_.clear();
|
||||
root_node_id_ = focused_node_id_ = -1;
|
||||
}
|
||||
|
||||
void OsrAccessibilityHelper::DestroyNode(OsrAXNode* node) {
|
||||
if (node) {
|
||||
int numChilds = node->GetChildCount();
|
||||
if (numChilds > 0) {
|
||||
for (int i = 0; i < numChilds; i++) {
|
||||
DestroyNode(node->ChildAtIndex(i));
|
||||
}
|
||||
}
|
||||
accessibility_node_map_.erase(node->OsrAXNodeId());
|
||||
node->Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
OsrAXNode* OsrAccessibilityHelper::GetNode(int nodeId) const {
|
||||
if (nodeId != -1 &&
|
||||
accessibility_node_map_.find(nodeId) != accessibility_node_map_.end()) {
|
||||
return accessibility_node_map_.at(nodeId);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace client
|
63
tests/cefclient/browser/osr_accessibility_helper.h
Normal file
63
tests/cefclient/browser/osr_accessibility_helper.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "include/cef_browser.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
class OsrAXNode;
|
||||
|
||||
// Helper class that abstracts Renderer Accessibility tree and provides a
|
||||
// uniform interface to be consumed by IAccessible interface on Windows and
|
||||
// NSAccessibility implementation on Mac in CefClient.
|
||||
class OsrAccessibilityHelper {
|
||||
public:
|
||||
OsrAccessibilityHelper(CefRefPtr<CefValue> value,
|
||||
CefRefPtr<CefBrowser> browser);
|
||||
|
||||
void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
|
||||
|
||||
OsrAXNode* GetRootNode() const {
|
||||
return GetNode(root_node_id_);
|
||||
}
|
||||
|
||||
OsrAXNode* GetFocusedNode() const {
|
||||
return GetNode(focused_node_id_);
|
||||
}
|
||||
|
||||
CefWindowHandle GetWindowHandle() const {
|
||||
return browser_->GetHost()->GetWindowHandle();
|
||||
}
|
||||
|
||||
CefRefPtr<CefBrowser> GetBrowser() const {
|
||||
return browser_;
|
||||
};
|
||||
|
||||
OsrAXNode* GetNode(int nodeId) const;
|
||||
|
||||
private:
|
||||
OsrAXNode* CreateNode(OsrAXNode* parent, CefRefPtr<CefDictionaryValue> value);
|
||||
|
||||
void Reset();
|
||||
|
||||
void UpdateLayout(CefRefPtr<CefDictionaryValue> update);
|
||||
|
||||
void UpdateFocusedNode(CefRefPtr<CefDictionaryValue> update);
|
||||
|
||||
// Destroy the node and remove from Map
|
||||
void DestroyNode(OsrAXNode* node);
|
||||
int root_node_id_;
|
||||
int focused_node_id_;
|
||||
CefRefPtr<CefBrowser> browser_;
|
||||
std::map<int, OsrAXNode*> accessibility_node_map_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_HELPER_H_
|
102
tests/cefclient/browser/osr_accessibility_node.cc
Normal file
102
tests/cefclient/browser/osr_accessibility_node.cc
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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.
|
||||
|
||||
// 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/cefclient/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
OsrAXNode::OsrAXNode(CefRefPtr<CefDictionaryValue> value,
|
||||
OsrAccessibilityHelper* helper)
|
||||
: node_id_(-1), platform_accessibility_(NULL), parent_(NULL),
|
||||
accessibility_helper_(helper) {
|
||||
UpdateValue(value);
|
||||
}
|
||||
|
||||
void OsrAXNode::UpdateValue(CefRefPtr<CefDictionaryValue> value) {
|
||||
if (value && value->HasKey("id")) {
|
||||
node_id_ = value->GetInt("id");
|
||||
|
||||
if (value->HasKey("role"))
|
||||
role_ = value->GetString("role");
|
||||
|
||||
if (value->HasKey("child_ids")) {
|
||||
CefRefPtr<CefListValue> childs = value->GetList("child_ids");
|
||||
// Reset child Ids
|
||||
child_ids_.clear();
|
||||
for(size_t idx = 0; idx < childs->GetSize(); idx++)
|
||||
child_ids_.push_back(childs->GetInt(idx));
|
||||
}
|
||||
// Update Location
|
||||
if (value->HasKey("location")) {
|
||||
CefRefPtr<CefDictionaryValue> loc = value->GetDictionary("location");
|
||||
if (loc) {
|
||||
location_ = CefRect(loc->GetDouble("x"), loc->GetDouble("y"),
|
||||
loc->GetDouble("width"), loc->GetDouble("height"));
|
||||
}
|
||||
}
|
||||
// Update offsets
|
||||
if (value->HasKey("offset_container_id")) {
|
||||
offset_container_id_ = value->GetInt("offset_container_id");
|
||||
}
|
||||
// Update attributes
|
||||
if (value->HasKey("attributes")) {
|
||||
attributes_ = value->GetDictionary("attributes");
|
||||
|
||||
if (attributes_ && attributes_->HasKey("name"))
|
||||
name_ = attributes_->GetString("name");
|
||||
if (attributes_ && attributes_->HasKey("value"))
|
||||
value_ = attributes_->GetString("value");
|
||||
if (attributes_ && attributes_->HasKey("description"))
|
||||
description_ = attributes_->GetString("description");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CefWindowHandle OsrAXNode::GetWindowHandle() const {
|
||||
if (accessibility_helper_)
|
||||
return accessibility_helper_->GetWindowHandle();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CefRefPtr<CefBrowser> OsrAXNode::GetBrowser() const {
|
||||
if (accessibility_helper_)
|
||||
return accessibility_helper_->GetBrowser();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void OsrAXNode::SetParent(OsrAXNode* parent) {
|
||||
parent_ = parent;
|
||||
}
|
||||
|
||||
CefRect OsrAXNode::AxLocation() const {
|
||||
CefRect loc = location_;
|
||||
OsrAXNode* offsetNode = accessibility_helper_->GetNode(offset_container_id_);
|
||||
// Add offset from parent Lcoation
|
||||
if (offsetNode) {
|
||||
CefRect offset = offsetNode->AxLocation();
|
||||
loc.x += offset.x;
|
||||
loc.y += offset.y;
|
||||
}
|
||||
return loc;
|
||||
}
|
||||
|
||||
OsrAXNode* OsrAXNode::ChildAtIndex(int index) const {
|
||||
if (index < GetChildCount())
|
||||
return accessibility_helper_->GetNode(child_ids_[index]);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create and return the platform specific OsrAXNode Object
|
||||
OsrAXNode* OsrAXNode::CreateNode(CefRefPtr<CefDictionaryValue> value,
|
||||
OsrAccessibilityHelper* helper) {
|
||||
return new OsrAXNode(value, helper);
|
||||
}
|
||||
|
||||
} // namespace client
|
116
tests/cefclient/browser/osr_accessibility_node.h
Normal file
116
tests/cefclient/browser/osr_accessibility_node.h
Normal file
@ -0,0 +1,116 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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_
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "include/cef_browser.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#ifdef __OBJC__
|
||||
@class NSObject;
|
||||
#else
|
||||
class NSObject;
|
||||
#endif
|
||||
typedef NSObject CefNativeAccessible;
|
||||
#elif defined(OS_WIN)
|
||||
struct IAccessible;
|
||||
typedef IAccessible CefNativeAccessible;
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
namespace client {
|
||||
|
||||
class OsrAccessibilityHelper;
|
||||
|
||||
// OsrAXNode is the base class for implementation for the NSAccessibility
|
||||
// protocol for interacting with VoiceOver and other accessibility clients.
|
||||
class OsrAXNode {
|
||||
public:
|
||||
// Create and return the platform specific OsrAXNode Object.
|
||||
static OsrAXNode* CreateNode(CefRefPtr<CefDictionaryValue> value,
|
||||
OsrAccessibilityHelper* helper);
|
||||
|
||||
// Update Value.
|
||||
void UpdateValue(CefRefPtr<CefDictionaryValue> value);
|
||||
|
||||
// Fire a platform-specific notification that an event has occurred on
|
||||
// this object.
|
||||
void NotifyAccessibilityEvent(std::string event_type) const;
|
||||
|
||||
// Call Destroy rather than deleting this, because the subclass may
|
||||
// use reference counting.
|
||||
void Destroy();
|
||||
|
||||
// Return NSAccessibility Object for Mac/ IAccessible for Windows
|
||||
CefNativeAccessible* GetNativeAccessibleObject(OsrAXNode* parent);
|
||||
|
||||
CefNativeAccessible* GetParentAccessibleObject() const {
|
||||
return parent_? parent_->platform_accessibility_ : NULL;
|
||||
}
|
||||
|
||||
OsrAccessibilityHelper* GetAccessibilityHelper() const {
|
||||
return accessibility_helper_;
|
||||
};
|
||||
|
||||
int GetChildCount() const {
|
||||
return static_cast<int>(child_ids_.size());
|
||||
}
|
||||
|
||||
// Return the Child at the specified index
|
||||
OsrAXNode* ChildAtIndex(int index) const;
|
||||
|
||||
const CefString& AxRole() const {
|
||||
return role_;
|
||||
}
|
||||
|
||||
int OsrAXNodeId() const {
|
||||
return node_id_;
|
||||
}
|
||||
|
||||
const CefString& AxValue() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
const CefString& AxName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
const CefString& AxDescription() const {
|
||||
return description_;
|
||||
}
|
||||
|
||||
CefRect AxLocation() const;
|
||||
|
||||
CefWindowHandle GetWindowHandle() const;
|
||||
|
||||
CefRefPtr<CefBrowser> GetBrowser() const;
|
||||
|
||||
void SetParent(OsrAXNode* parent);
|
||||
|
||||
protected:
|
||||
OsrAXNode(CefRefPtr<CefDictionaryValue> value,
|
||||
OsrAccessibilityHelper* helper);
|
||||
|
||||
int node_id_;
|
||||
CefString role_;
|
||||
CefString value_;
|
||||
CefString name_;
|
||||
CefString description_;
|
||||
CefRect location_;
|
||||
std::vector<int> child_ids_;
|
||||
CefNativeAccessible* platform_accessibility_;
|
||||
OsrAXNode* parent_;
|
||||
int offset_container_id_;
|
||||
OsrAccessibilityHelper* accessibility_helper_;
|
||||
CefRefPtr<CefDictionaryValue> attributes_;
|
||||
};
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // CEF_TESTS_CEFCLIENT_BROWSER_OSR_ACCESSIBILITY_NODE_H_
|
498
tests/cefclient/browser/osr_accessibility_node_mac.mm
Normal file
498
tests/cefclient/browser/osr_accessibility_node_mac.mm
Normal file
@ -0,0 +1,498 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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.
|
||||
|
||||
// Sample implementation for the NSAccessibility protocol for interacting with
|
||||
// VoiceOver and other accessibility clients.
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_node.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <AppKit/NSAccessibility.h>
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace {
|
||||
|
||||
NSString* AxRoleToNSAxRole(const std::string& role_string) {
|
||||
if (role_string == "abbr")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "alertDialog")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "alert")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "annotation")
|
||||
return NSAccessibilityUnknownRole;
|
||||
if (role_string == "application")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "article")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "audio")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "banner")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "blockquote")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "busyIndicator")
|
||||
return NSAccessibilityBusyIndicatorRole;
|
||||
if (role_string == "button")
|
||||
return NSAccessibilityButtonRole;
|
||||
if (role_string == "buttonDropDown")
|
||||
return NSAccessibilityButtonRole;
|
||||
if (role_string == "canvas")
|
||||
return NSAccessibilityImageRole;
|
||||
if (role_string == "caption")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "checkBox")
|
||||
return NSAccessibilityCheckBoxRole;
|
||||
if (role_string == "colorWell")
|
||||
return NSAccessibilityColorWellRole;
|
||||
if (role_string == "column")
|
||||
return NSAccessibilityColumnRole;
|
||||
if (role_string == "comboBox")
|
||||
return NSAccessibilityComboBoxRole;
|
||||
if (role_string == "complementary")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "contentInfo")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "definition")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "descriptionListDetail")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "descriptionList")
|
||||
return NSAccessibilityListRole;
|
||||
if (role_string == "descriptionListTerm")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "details")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "dialog")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "directory")
|
||||
return NSAccessibilityListRole;
|
||||
if (role_string == "disclosureTriangle")
|
||||
return NSAccessibilityDisclosureTriangleRole;
|
||||
if (role_string == "div")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "document")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "embeddedObject")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "figcaption")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "figure")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "footer")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "form")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "grid")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "group")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "iframe")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "iframePresentational")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "ignored")
|
||||
return NSAccessibilityUnknownRole;
|
||||
if (role_string == "imageMapLink")
|
||||
return NSAccessibilityLinkRole;
|
||||
if (role_string == "imageMap")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "image")
|
||||
return NSAccessibilityImageRole;
|
||||
if (role_string == "labelText")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "legend")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "link")
|
||||
return NSAccessibilityLinkRole;
|
||||
if (role_string == "listBoxOption")
|
||||
return NSAccessibilityStaticTextRole;
|
||||
if (role_string == "listBox")
|
||||
return NSAccessibilityListRole;
|
||||
if (role_string == "listItem")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "list")
|
||||
return NSAccessibilityListRole;
|
||||
if (role_string == "log")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "main")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "mark")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "marquee")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "math")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "menu")
|
||||
return NSAccessibilityMenuRole;
|
||||
if (role_string == "menuBar")
|
||||
return NSAccessibilityMenuBarRole;
|
||||
if (role_string == "menuButton")
|
||||
return NSAccessibilityButtonRole;
|
||||
if (role_string == "menuItem")
|
||||
return NSAccessibilityMenuItemRole;
|
||||
if (role_string == "menuItemCheckBox")
|
||||
return NSAccessibilityMenuItemRole;
|
||||
if (role_string == "menuItemRadio")
|
||||
return NSAccessibilityMenuItemRole;
|
||||
if (role_string == "menuListOption")
|
||||
return NSAccessibilityMenuItemRole;
|
||||
if (role_string == "menuListPopup")
|
||||
return NSAccessibilityUnknownRole;
|
||||
if (role_string == "meter")
|
||||
return NSAccessibilityProgressIndicatorRole;
|
||||
if (role_string == "navigation")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "note")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "outline")
|
||||
return NSAccessibilityOutlineRole;
|
||||
if (role_string == "paragraph")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "popUpButton")
|
||||
return NSAccessibilityPopUpButtonRole;
|
||||
if (role_string == "pre")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "presentational")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "progressIndicator")
|
||||
return NSAccessibilityProgressIndicatorRole;
|
||||
if (role_string == "radioButton")
|
||||
return NSAccessibilityRadioButtonRole;
|
||||
if (role_string == "radioGroup")
|
||||
return NSAccessibilityRadioGroupRole;
|
||||
if (role_string == "region")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "row")
|
||||
return NSAccessibilityRowRole;
|
||||
if (role_string == "ruler")
|
||||
return NSAccessibilityRulerRole;
|
||||
if (role_string == "scrollBar")
|
||||
return NSAccessibilityScrollBarRole;
|
||||
if (role_string == "search")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "searchBox")
|
||||
return NSAccessibilityTextFieldRole;
|
||||
if (role_string == "slider")
|
||||
return NSAccessibilitySliderRole;
|
||||
if (role_string == "sliderThumb")
|
||||
return NSAccessibilityValueIndicatorRole;
|
||||
if (role_string == "spinButton")
|
||||
return NSAccessibilityIncrementorRole;
|
||||
if (role_string == "splitter")
|
||||
return NSAccessibilitySplitterRole;
|
||||
if (role_string == "staticText")
|
||||
return NSAccessibilityStaticTextRole;
|
||||
if (role_string == "status")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "svgRoot")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "switch")
|
||||
return NSAccessibilityCheckBoxRole;
|
||||
if (role_string == "tabGroup")
|
||||
return NSAccessibilityTabGroupRole;
|
||||
if (role_string == "tabList")
|
||||
return NSAccessibilityTabGroupRole;
|
||||
if (role_string == "tabPanel")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "tab")
|
||||
return NSAccessibilityRadioButtonRole;
|
||||
if (role_string == "tableHeaderContainer")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "table")
|
||||
return NSAccessibilityTableRole;
|
||||
if (role_string == "textField")
|
||||
return NSAccessibilityTextFieldRole;
|
||||
if (role_string == "time")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "timer")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "toggleButton")
|
||||
return NSAccessibilityCheckBoxRole;
|
||||
if (role_string == "toolbar")
|
||||
return NSAccessibilityToolbarRole;
|
||||
if (role_string == "treeGrid")
|
||||
return NSAccessibilityTableRole;
|
||||
if (role_string == "treeItem")
|
||||
return NSAccessibilityRowRole;
|
||||
if (role_string == "tree")
|
||||
return NSAccessibilityOutlineRole;
|
||||
if (role_string == "unknown")
|
||||
return NSAccessibilityUnknownRole;
|
||||
if (role_string == "tooltip")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "video")
|
||||
return NSAccessibilityGroupRole;
|
||||
if (role_string == "window")
|
||||
return NSAccessibilityWindowRole;
|
||||
return [NSString stringWithUTF8String:role_string.c_str()];
|
||||
}
|
||||
|
||||
inline int MiddleX(const CefRect& rect) {
|
||||
return rect.x + rect.width / 2;
|
||||
}
|
||||
|
||||
inline int MiddleY(const CefRect& rect) {
|
||||
return rect.y + rect.height / 2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// OsrAXNodeObject is sample implementation for the NSAccessibility protocol
|
||||
// for interacting with VoiceOver and other accessibility clients.
|
||||
@interface OsrAXNodeObject : NSObject {
|
||||
// OsrAXNode* proxy object
|
||||
client::OsrAXNode* node_;
|
||||
CefNativeAccessible* parent_;
|
||||
}
|
||||
|
||||
- (id) init:(client::OsrAXNode*) node;
|
||||
+ (OsrAXNodeObject *) elementWithNode:(client::OsrAXNode*) node;
|
||||
@end
|
||||
|
||||
|
||||
@implementation OsrAXNodeObject
|
||||
- (id)init:(client::OsrAXNode*)node {
|
||||
node_ = node;
|
||||
parent_ = node_->GetParentAccessibleObject();
|
||||
if (!parent_) {
|
||||
parent_ = node_->GetWindowHandle();
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (OsrAXNodeObject *)elementWithNode:(client::OsrAXNode*)node {
|
||||
// We manage the release ourself
|
||||
return [[OsrAXNodeObject alloc] init:node];
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(id)object {
|
||||
if ([object isKindOfClass:[OsrAXNodeObject self]]) {
|
||||
OsrAXNodeObject* other = object;
|
||||
return (node_ == other->node_);
|
||||
} else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
|
||||
- (NSString*) axDescription {
|
||||
std::string desc = node_->AxDescription();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (NSString*) axName {
|
||||
std::string desc = node_->AxName();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (NSString*) axValue {
|
||||
std::string desc = node_->AxValue();
|
||||
return [NSString stringWithUTF8String:desc.c_str()];
|
||||
}
|
||||
|
||||
- (void)doMouseClick: (cef_mouse_button_type_t)type {
|
||||
CefRefPtr<CefBrowser> browser = node_->GetBrowser();
|
||||
if (browser) {
|
||||
CefMouseEvent mouse_event;
|
||||
const CefRect& rect = node_->AxLocation();
|
||||
mouse_event.x = MiddleX(rect);
|
||||
mouse_event.y = MiddleY(rect);
|
||||
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, type, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, type, true, 1);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSMutableArray *) getKids {
|
||||
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 ? child->GetNativeAccessibleObject(node_) : nil];
|
||||
}
|
||||
return kids;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSPoint) position {
|
||||
CefRect cef_rect = node_->AxLocation();
|
||||
NSPoint origin = NSMakePoint(cef_rect.x, cef_rect.y);
|
||||
NSSize size = NSMakeSize(cef_rect.width, cef_rect.height);
|
||||
|
||||
NSView* view = node_->GetWindowHandle();
|
||||
origin.y = NSHeight([view bounds]) - origin.y;
|
||||
NSPoint originInWindow = [view convertPoint:origin toView:nil];
|
||||
|
||||
NSRect point_rect = NSMakeRect(originInWindow.x, originInWindow.y, 0, 0);
|
||||
NSPoint originInScreen = [[view window]
|
||||
convertRectToScreen:point_rect].origin;
|
||||
|
||||
originInScreen.y = originInScreen.y - size.height;
|
||||
return originInScreen;
|
||||
}
|
||||
|
||||
- (NSSize) size {
|
||||
CefRect cef_rect = node_->AxLocation();
|
||||
NSRect rect = NSMakeRect(cef_rect.x, cef_rect.y,
|
||||
cef_rect.width, cef_rect.height);
|
||||
NSView* view = node_->GetWindowHandle();
|
||||
rect = [[view window]convertRectToScreen: rect];
|
||||
return rect.size;
|
||||
}
|
||||
|
||||
//
|
||||
// accessibility protocol
|
||||
//
|
||||
|
||||
// attributes
|
||||
|
||||
- (BOOL)accessibilityIsIgnored {
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray *)accessibilityAttributeNames {
|
||||
static NSArray* attributes = nil;
|
||||
if (attributes == nil) {
|
||||
attributes = [[NSArray alloc] initWithObjects:
|
||||
NSAccessibilityRoleAttribute,
|
||||
NSAccessibilityRoleDescriptionAttribute,
|
||||
NSAccessibilityChildrenAttribute,
|
||||
NSAccessibilityValueAttribute,
|
||||
NSAccessibilityTitleAttribute,
|
||||
NSAccessibilityDescriptionAttribute,
|
||||
NSAccessibilityFocusedAttribute,
|
||||
NSAccessibilityParentAttribute,
|
||||
NSAccessibilityWindowAttribute,
|
||||
NSAccessibilityTopLevelUIElementAttribute,
|
||||
NSAccessibilityPositionAttribute,
|
||||
NSAccessibilitySizeAttribute,
|
||||
nil];
|
||||
}
|
||||
return attributes;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
||||
if (!node_)
|
||||
return nil;
|
||||
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
||||
return [self axRole];
|
||||
} else if ([attribute isEqualToString:
|
||||
NSAccessibilityRoleDescriptionAttribute]) {
|
||||
return NSAccessibilityRoleDescription([self axRole], nil);
|
||||
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
||||
// Just check if the app thinks we're focused.
|
||||
id focusedElement = [NSApp accessibilityAttributeValue:
|
||||
NSAccessibilityFocusedUIElementAttribute];
|
||||
return [NSNumber numberWithBool:[focusedElement isEqual:self]];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
||||
return NSAccessibilityUnignoredAncestor(parent_);
|
||||
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
||||
return NSAccessibilityUnignoredChildren([self getKids]);
|
||||
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
||||
// We're in the same window as our parent.
|
||||
return [parent_
|
||||
accessibilityAttributeValue:NSAccessibilityWindowAttribute];
|
||||
} else if ([attribute isEqualToString:
|
||||
NSAccessibilityTopLevelUIElementAttribute]) {
|
||||
// We're in the same top level element as our parent.
|
||||
return [parent_ accessibilityAttributeValue:
|
||||
NSAccessibilityTopLevelUIElementAttribute];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
||||
return [NSValue valueWithPoint:[self position]];
|
||||
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
||||
return [NSValue valueWithSize:[self size]];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
||||
return [self axDescription];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
|
||||
return [self axValue];
|
||||
} else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
|
||||
return [self axName];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (id)accessibilityHitTest:(NSPoint)point {
|
||||
return NSAccessibilityUnignoredAncestor(self);
|
||||
}
|
||||
|
||||
- (NSArray *)accessibilityActionNames {
|
||||
return [NSArray arrayWithObject:NSAccessibilityPressAction];
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityActionDescription:(NSString *)action {
|
||||
return NSAccessibilityActionDescription(action);
|
||||
}
|
||||
|
||||
- (void)accessibilityPerformAction:(NSString *)action {
|
||||
if ([action isEqualToString:NSAccessibilityPressAction]) {
|
||||
// Do Click on Default action
|
||||
[self doMouseClick:MBT_LEFT];
|
||||
} else if ([action isEqualToString:NSAccessibilityShowMenuAction]) {
|
||||
// Right click for Context Menu
|
||||
[self doMouseClick:MBT_RIGHT];
|
||||
}
|
||||
}
|
||||
|
||||
- (id)accessibilityFocusedUIElement {
|
||||
return NSAccessibilityUnignoredAncestor(self);
|
||||
}
|
||||
|
||||
- (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.
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace client {
|
||||
|
||||
void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
|
||||
if (event_type == "focus") {
|
||||
NSAccessibilityPostNotification(GetWindowHandle(),
|
||||
NSAccessibilityFocusedUIElementChangedNotification);
|
||||
} else if (event_type == "textChanged") {
|
||||
NSAccessibilityPostNotification(GetWindowHandle(),
|
||||
NSAccessibilityTitleChangedNotification);
|
||||
} else if (event_type == "valueChanged"){
|
||||
NSAccessibilityPostNotification(GetWindowHandle(),
|
||||
NSAccessibilityValueChangedNotification);
|
||||
} else if (event_type == "textSelectionChanged") {
|
||||
NSAccessibilityPostNotification(GetWindowHandle(),
|
||||
NSAccessibilityValueChangedNotification);
|
||||
}
|
||||
}
|
||||
|
||||
void OsrAXNode::Destroy() {
|
||||
if (platform_accessibility_) {
|
||||
NSAccessibilityPostNotification(platform_accessibility_,
|
||||
NSAccessibilityUIElementDestroyedNotification);
|
||||
}
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
// Create and return NSAccessibility Implementation Object for Mac
|
||||
CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(
|
||||
client::OsrAXNode* parent) {
|
||||
if (!platform_accessibility_) {
|
||||
platform_accessibility_ = [OsrAXNodeObject elementWithNode:this];
|
||||
SetParent(parent);
|
||||
}
|
||||
return platform_accessibility_;
|
||||
}
|
||||
|
||||
} // namespace client
|
674
tests/cefclient/browser/osr_accessibility_node_win.cc
Normal file
674
tests/cefclient/browser/osr_accessibility_node_win.cc
Normal file
@ -0,0 +1,674 @@
|
||||
// Copyright 2017 The Chromium Embedded Framework Authors. Portions copyright
|
||||
// 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.
|
||||
|
||||
// This class implements our accessible proxy object that handles moving
|
||||
// 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"
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
|
||||
#include <string>
|
||||
#include <atlbase.h>
|
||||
#include <oleacc.h>
|
||||
|
||||
#include "tests/cefclient/browser/osr_accessibility_helper.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
// Return CO_E_OBJNOTCONNECTED for accessible objects thar still exists but the
|
||||
// window and/or object it references has been destroyed.
|
||||
#define DATACHECK(node) (node) ? S_OK : CO_E_OBJNOTCONNECTED
|
||||
#define VALID_CHILDID(varChild) ((varChild.vt == VT_I4))
|
||||
|
||||
namespace {
|
||||
|
||||
// Helper function to convert a rectangle from client coordinates to screen
|
||||
// coordinates.
|
||||
void ClientToScreen(HWND hwnd, LPRECT lpRect) {
|
||||
if (lpRect) {
|
||||
POINT ptTL = { lpRect->left, lpRect->top };
|
||||
POINT ptBR = { lpRect->right, lpRect->bottom };
|
||||
// Win32 API only provides the call for a point.
|
||||
ClientToScreen(hwnd, &ptTL);
|
||||
ClientToScreen(hwnd, &ptBR);
|
||||
SetRect(lpRect, ptTL.x, ptTL.y, ptBR.x, ptBR.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to convert to MSAARole
|
||||
int AxRoleToMSAARole(const std::string& role_string) {
|
||||
if (role_string == "alert")
|
||||
return ROLE_SYSTEM_ALERT;
|
||||
if (role_string == "application")
|
||||
return ROLE_SYSTEM_APPLICATION;
|
||||
if (role_string == "buttonDropDown")
|
||||
return ROLE_SYSTEM_BUTTONDROPDOWN;
|
||||
if (role_string == "popUpButton")
|
||||
return ROLE_SYSTEM_BUTTONMENU;
|
||||
if (role_string == "checkBox")
|
||||
return ROLE_SYSTEM_CHECKBUTTON;
|
||||
if (role_string == "comboBox")
|
||||
return ROLE_SYSTEM_COMBOBOX;
|
||||
if (role_string == "dialog")
|
||||
return ROLE_SYSTEM_DIALOG;
|
||||
if (role_string == "group")
|
||||
return ROLE_SYSTEM_GROUPING;
|
||||
if (role_string == "image")
|
||||
return ROLE_SYSTEM_GRAPHIC;
|
||||
if (role_string == "link")
|
||||
return ROLE_SYSTEM_LINK;
|
||||
if (role_string == "locationBar")
|
||||
return ROLE_SYSTEM_GROUPING;
|
||||
if (role_string == "menuBar")
|
||||
return ROLE_SYSTEM_MENUBAR;
|
||||
if (role_string == "menuItem")
|
||||
return ROLE_SYSTEM_MENUITEM;
|
||||
if (role_string == "menuListPopup")
|
||||
return ROLE_SYSTEM_MENUPOPUP;
|
||||
if (role_string == "tree")
|
||||
return ROLE_SYSTEM_OUTLINE;
|
||||
if (role_string == "treeItem")
|
||||
return ROLE_SYSTEM_OUTLINEITEM;
|
||||
if (role_string == "tab")
|
||||
return ROLE_SYSTEM_PAGETAB;
|
||||
if (role_string == "tabList")
|
||||
return ROLE_SYSTEM_PAGETABLIST;
|
||||
if (role_string == "pane")
|
||||
return ROLE_SYSTEM_PANE;
|
||||
if (role_string == "progressIndicator")
|
||||
return ROLE_SYSTEM_PROGRESSBAR;
|
||||
if (role_string == "button")
|
||||
return ROLE_SYSTEM_PUSHBUTTON;
|
||||
if (role_string == "radioButton")
|
||||
return ROLE_SYSTEM_RADIOBUTTON;
|
||||
if (role_string == "scrollBar")
|
||||
return ROLE_SYSTEM_SCROLLBAR;
|
||||
if (role_string == "splitter")
|
||||
return ROLE_SYSTEM_SEPARATOR;
|
||||
if (role_string == "slider")
|
||||
return ROLE_SYSTEM_SLIDER;
|
||||
if (role_string == "staticText")
|
||||
return ROLE_SYSTEM_STATICTEXT;
|
||||
if (role_string == "textField")
|
||||
return ROLE_SYSTEM_TEXT;
|
||||
if (role_string == "titleBar")
|
||||
return ROLE_SYSTEM_TITLEBAR;
|
||||
if (role_string == "toolbar")
|
||||
return ROLE_SYSTEM_TOOLBAR;
|
||||
if (role_string == "webView")
|
||||
return ROLE_SYSTEM_GROUPING;
|
||||
if (role_string == "window")
|
||||
return ROLE_SYSTEM_WINDOW;
|
||||
if (role_string == "client")
|
||||
return ROLE_SYSTEM_CLIENT;
|
||||
// This is the default role for MSAA.
|
||||
return ROLE_SYSTEM_CLIENT;
|
||||
}
|
||||
|
||||
static inline int MiddleX(const CefRect& rect) {
|
||||
return rect.x + rect.width / 2;
|
||||
}
|
||||
|
||||
static inline int MiddleY(const CefRect& rect) {
|
||||
return rect.y + rect.height / 2;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
struct CefIAccessible : public IAccessible {
|
||||
public:
|
||||
// Implement IUnknown
|
||||
STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject);
|
||||
STDMETHODIMP_(ULONG) AddRef();
|
||||
STDMETHODIMP_(ULONG) Release();
|
||||
|
||||
//
|
||||
// IAccessible methods.
|
||||
//
|
||||
// Retrieves the child element or child object at a given point on the screen.
|
||||
STDMETHODIMP accHitTest(LONG x_left, LONG y_top, VARIANT* child) override;
|
||||
|
||||
// Performs the object's default action.
|
||||
STDMETHODIMP accDoDefaultAction(VARIANT var_id) override;
|
||||
|
||||
// Retrieves the specified object's current screen location.
|
||||
STDMETHODIMP accLocation(LONG* x_left, LONG* y_top, LONG* width,
|
||||
LONG* height, VARIANT var_id) override;
|
||||
|
||||
// Traverses to another UI element and retrieves the object.
|
||||
STDMETHODIMP accNavigate(LONG nav_dir, VARIANT start, VARIANT* end) override;
|
||||
|
||||
// Retrieves an IDispatch interface pointer for the specified child.
|
||||
STDMETHODIMP get_accChild(VARIANT var_child, IDispatch** disp_child) override;
|
||||
|
||||
// Retrieves the number of accessible children.
|
||||
STDMETHODIMP get_accChildCount(LONG* child_count) override;
|
||||
|
||||
// Retrieves a string that describes the object's default action.
|
||||
STDMETHODIMP get_accDefaultAction(VARIANT var_id,
|
||||
BSTR* default_action) override;
|
||||
|
||||
// Retrieves the tooltip description.
|
||||
STDMETHODIMP get_accDescription(VARIANT var_id, BSTR* desc) override;
|
||||
|
||||
// Retrieves the object that has the keyboard focus.
|
||||
STDMETHODIMP get_accFocus(VARIANT* focus_child) override;
|
||||
|
||||
// Retrieves the specified object's shortcut.
|
||||
STDMETHODIMP get_accKeyboardShortcut(VARIANT var_id,
|
||||
BSTR* access_key) override;
|
||||
|
||||
// Retrieves the name of the specified object.
|
||||
STDMETHODIMP get_accName(VARIANT var_id, BSTR* name) override;
|
||||
|
||||
// Retrieves the IDispatch interface of the object's parent.
|
||||
STDMETHODIMP get_accParent(IDispatch** disp_parent) override;
|
||||
|
||||
// Retrieves information describing the role of the specified object.
|
||||
STDMETHODIMP get_accRole(VARIANT var_id, VARIANT* role) override;
|
||||
|
||||
// Retrieves the current state of the specified object.
|
||||
STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state) override;
|
||||
|
||||
// Gets the help string for the specified object.
|
||||
STDMETHODIMP get_accHelp(VARIANT var_id, BSTR* help) override;
|
||||
|
||||
// Retrieve or set the string value associated with the specified object.
|
||||
// Setting the value is not typically used by screen readers, but it's
|
||||
// used frequently by automation software.
|
||||
STDMETHODIMP get_accValue(VARIANT var_id, BSTR* value) override;
|
||||
STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value) override;
|
||||
|
||||
// IAccessible methods not implemented.
|
||||
STDMETHODIMP get_accSelection(VARIANT* selected) override;
|
||||
STDMETHODIMP accSelect(LONG flags_sel, VARIANT var_id) override;
|
||||
STDMETHODIMP get_accHelpTopic(BSTR* help_file, VARIANT var_id,
|
||||
LONG* topic_id) override;
|
||||
STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name) override;
|
||||
|
||||
// Implement IDispatch
|
||||
STDMETHODIMP GetTypeInfoCount(unsigned int FAR* pctinfo);
|
||||
STDMETHODIMP GetTypeInfo(unsigned int iTInfo, LCID lcid,
|
||||
ITypeInfo FAR* FAR* ppTInfo);
|
||||
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames,
|
||||
unsigned int cNames, LCID lcid,
|
||||
DISPID FAR* rgDispId);
|
||||
STDMETHODIMP Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
|
||||
DISPPARAMS FAR* pDispParams, VARIANT FAR* pVarResult,
|
||||
EXCEPINFO FAR* pExcepInfo, unsigned int FAR* puArgErr);
|
||||
|
||||
CefIAccessible(OsrAXNode* node) : node_(node), ref_count_(0) {
|
||||
}
|
||||
|
||||
// Remove the node reference when OsrAXNode is destroyed, so that
|
||||
// MSAA clients get CO_E_OBJNOTCONNECTED
|
||||
void MarkDestroyed() {
|
||||
node_ = NULL;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Ref Count
|
||||
ULONG ref_count_;
|
||||
// OsrAXNode* proxy object
|
||||
OsrAXNode* node_;
|
||||
};
|
||||
|
||||
// Implement IUnknown
|
||||
// *********************
|
||||
|
||||
// Handles ref counting and querying for other supported interfaces.
|
||||
// We only support, IUnknown, IDispatch and IAccessible.
|
||||
STDMETHODIMP CefIAccessible::QueryInterface(REFIID riid, void** ppvObject) {
|
||||
if (riid == IID_IAccessible)
|
||||
*ppvObject = static_cast<IAccessible*>(this);
|
||||
else if (riid == IID_IDispatch)
|
||||
*ppvObject = static_cast<IDispatch*>(this);
|
||||
else if (riid == IID_IUnknown)
|
||||
*ppvObject = static_cast<IUnknown*>(this);
|
||||
else
|
||||
*ppvObject = NULL;
|
||||
|
||||
if (*ppvObject)
|
||||
reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
|
||||
|
||||
return (*ppvObject) ? S_OK : E_NOINTERFACE;
|
||||
}
|
||||
|
||||
// Increments COM objects refcount required by IUnknown for reference counting
|
||||
STDMETHODIMP_(ULONG) CefIAccessible::AddRef() {
|
||||
return InterlockedIncrement((LONG volatile*)&ref_count_);
|
||||
}
|
||||
|
||||
STDMETHODIMP_(ULONG) CefIAccessible::Release() {
|
||||
ULONG ulRefCnt = InterlockedDecrement((LONG volatile*)&ref_count_);
|
||||
if (ulRefCnt == 0) {
|
||||
// Remove reference from OsrAXNode
|
||||
if (node_)
|
||||
node_->Destroy();
|
||||
delete this;
|
||||
}
|
||||
|
||||
return ulRefCnt;
|
||||
}
|
||||
|
||||
// Implement IAccessible
|
||||
// *********************
|
||||
|
||||
// Returns the parent IAccessible in the form of an IDispatch interface.
|
||||
STDMETHODIMP CefIAccessible::get_accParent(IDispatch **ppdispParent) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (ppdispParent) {
|
||||
CefNativeAccessible* parent = node_->GetParentAccessibleObject();
|
||||
if (!parent) {
|
||||
// Find our parent window
|
||||
HWND hWnd = ::GetParent(node_->GetWindowHandle());
|
||||
// if we have a window attempt to get its IAccessible pointer
|
||||
if (hWnd) {
|
||||
AccessibleObjectFromWindow(hWnd, (DWORD)OBJID_CLIENT,
|
||||
IID_IAccessible, (void**)(&parent));
|
||||
}
|
||||
}
|
||||
|
||||
if (parent)
|
||||
parent->AddRef();
|
||||
*ppdispParent = parent;
|
||||
retCode = (*ppdispParent) ? S_OK : S_FALSE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Returns the number of children we have for this element.
|
||||
STDMETHODIMP CefIAccessible::get_accChildCount(long *pcountChildren) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode) && pcountChildren) {
|
||||
// Get Child node count for this from Accessibility tree
|
||||
*pcountChildren = node_->GetChildCount();
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Returns a child IAccessible object.
|
||||
STDMETHODIMP CefIAccessible::get_accChild(VARIANT varChild,
|
||||
IDispatch **ppdispChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
int numChilds = node_->GetChildCount();
|
||||
// Mark Leaf node if there are no child
|
||||
if (numChilds <= 0) {
|
||||
*ppdispChild = NULL;
|
||||
return S_FALSE;
|
||||
} else {
|
||||
if (ppdispChild && VALID_CHILDID(varChild)) {
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
*ppdispChild = this;
|
||||
} else {
|
||||
// Convert to 0 based index and get Child Node.
|
||||
OsrAXNode* child = node_->ChildAtIndex(varChild.lVal - 1);
|
||||
// Fallback to focused node
|
||||
if (!child)
|
||||
child = node_->GetAccessibilityHelper()->GetFocusedNode();
|
||||
|
||||
*ppdispChild = child->GetNativeAccessibleObject(node_);
|
||||
}
|
||||
if (*ppdispChild == NULL)
|
||||
retCode = S_FALSE;
|
||||
else
|
||||
(*ppdispChild)->AddRef();
|
||||
}
|
||||
}
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns the accessible name for element from accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accName(VARIANT varChild, BSTR *pszName) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pszName && VALID_CHILDID(varChild)) {
|
||||
std::string name = node_->AxName();
|
||||
CComBSTR bstrResult(name.c_str());
|
||||
*pszName = bstrResult.Detach();
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns the value for element from accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accValue(VARIANT varChild, BSTR *pszValue) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pszValue && VALID_CHILDID(varChild)) {
|
||||
std::string name = node_->AxValue();
|
||||
CComBSTR bstrResult(name.c_str());
|
||||
*pszValue = bstrResult.Detach();
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns the description for element from accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accDescription(VARIANT varChild,
|
||||
BSTR* pszDescription) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pszDescription && VALID_CHILDID(varChild)) {
|
||||
std::string name = node_->AxDescription();
|
||||
CComBSTR bstrResult(name.c_str());
|
||||
*pszDescription = bstrResult.Detach();
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns the MSAA Role for element from accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accRole(VARIANT varChild, VARIANT *pvarRole) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
// Get the accessibilty role and Map to MSAA Role
|
||||
if (pvarRole) {
|
||||
pvarRole->vt = VT_I4;
|
||||
pvarRole->lVal = AxRoleToMSAARole(node_->AxRole());
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns Accessibility State for element from accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accState(VARIANT varChild,
|
||||
VARIANT *pvarState) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pvarState) {
|
||||
pvarState->vt = VT_I4;
|
||||
pvarState->lVal = (GetFocus() == node_->GetWindowHandle()) ?
|
||||
STATE_SYSTEM_FOCUSED : 0;
|
||||
pvarState->lVal |= STATE_SYSTEM_PRESSED;
|
||||
pvarState->lVal |= STATE_SYSTEM_FOCUSABLE;
|
||||
|
||||
// For child
|
||||
if (varChild.lVal == CHILDID_SELF) {
|
||||
DWORD dwStyle = GetWindowLong(node_->GetWindowHandle(), GWL_STYLE);
|
||||
pvarState->lVal |= ((dwStyle & WS_VISIBLE) == 0) ?
|
||||
STATE_SYSTEM_INVISIBLE : 0;
|
||||
pvarState->lVal |= ((dwStyle & WS_DISABLED) > 0) ?
|
||||
STATE_SYSTEM_UNAVAILABLE : 0;
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Check and returns Accessibility Shortcut if any for element
|
||||
STDMETHODIMP CefIAccessible::get_accKeyboardShortcut(VARIANT varChild,
|
||||
BSTR* pszKeyboardShortcut) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pszKeyboardShortcut && VALID_CHILDID(varChild))
|
||||
*pszKeyboardShortcut = ::SysAllocString(L"None");
|
||||
else
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Return focused element from the accessibility tree
|
||||
STDMETHODIMP CefIAccessible::get_accFocus(VARIANT *pFocusChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
OsrAXNode* focusedNode = node_->GetAccessibilityHelper()->GetFocusedNode();
|
||||
CefNativeAccessible* nativeObj = NULL;
|
||||
if (focusedNode)
|
||||
nativeObj = focusedNode->GetNativeAccessibleObject(NULL);
|
||||
|
||||
if (nativeObj) {
|
||||
if (nativeObj == this) {
|
||||
pFocusChild->vt = VT_I4;
|
||||
pFocusChild->lVal = CHILDID_SELF;
|
||||
}
|
||||
else {
|
||||
pFocusChild->vt = VT_DISPATCH;
|
||||
pFocusChild->pdispVal = nativeObj;
|
||||
pFocusChild->pdispVal->AddRef();
|
||||
}
|
||||
} else {
|
||||
pFocusChild->vt = VT_EMPTY;
|
||||
}
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Return a selection list for multiple selection items.
|
||||
STDMETHODIMP CefIAccessible::get_accSelection(VARIANT *pvarChildren) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pvarChildren)
|
||||
pvarChildren->vt = VT_EMPTY;
|
||||
else
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Return a string description of the default action of our element, eg. push
|
||||
STDMETHODIMP CefIAccessible::get_accDefaultAction(VARIANT varChild,
|
||||
BSTR* pszDefaultAction) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pszDefaultAction && VALID_CHILDID(varChild))
|
||||
*pszDefaultAction = ::SysAllocString(L"Push");
|
||||
else
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// child item selectionor for an item to take focus.
|
||||
STDMETHODIMP CefIAccessible::accSelect(long flagsSelect, VARIANT varChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (VALID_CHILDID(varChild)) {
|
||||
HWND hwnd = node_->GetWindowHandle();
|
||||
// we only support SELFLAG_TAKEFOCUS.
|
||||
if (((flagsSelect & SELFLAG_TAKEFOCUS) > 0) && (GetFocus() == hwnd)) {
|
||||
RECT rcWnd;
|
||||
GetClientRect(hwnd, &rcWnd);
|
||||
InvalidateRect(hwnd, &rcWnd, FALSE);
|
||||
} else {
|
||||
retCode = S_FALSE;
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Returns back the screen coordinates of our element or one of its childs
|
||||
STDMETHODIMP CefIAccessible::accLocation(long* pxLeft, long* pyTop,
|
||||
long* pcxWidth, long* pcyHeight,
|
||||
VARIANT varChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pxLeft && pyTop && pcxWidth && pcyHeight && VALID_CHILDID(varChild)) {
|
||||
CefRect loc = node_->AxLocation();
|
||||
RECT rcItem = { loc.x, loc.y, loc.x + loc.width, loc.y + loc.height };
|
||||
HWND hwnd = node_->GetWindowHandle();
|
||||
ClientToScreen(hwnd, &rcItem);
|
||||
|
||||
*pxLeft = rcItem.left;
|
||||
*pyTop = rcItem.top;
|
||||
*pcxWidth = rcItem.right - rcItem.left;
|
||||
*pcyHeight = rcItem.bottom - rcItem.top;
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Allow clients to move the keyboard focus within the control
|
||||
// Deprecated
|
||||
STDMETHODIMP CefIAccessible::accNavigate(long navDir, VARIANT varStart,
|
||||
VARIANT* pvarEndUpAt) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// Check if the coordinates provided are within our element or child items.
|
||||
STDMETHODIMP CefIAccessible::accHitTest(long xLeft, long yTop,
|
||||
VARIANT *pvarChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode)) {
|
||||
if (pvarChild) {
|
||||
pvarChild->vt = VT_EMPTY;
|
||||
|
||||
CefRect loc = node_->AxLocation();
|
||||
RECT rcItem = { loc.x, loc.y, loc.x + loc.width, loc.y + loc.height };
|
||||
POINT pt = { xLeft, yTop };
|
||||
|
||||
ClientToScreen(node_->GetWindowHandle(), &rcItem);
|
||||
|
||||
if (PtInRect(&rcItem, pt)) {
|
||||
pvarChild->vt = VT_I4;
|
||||
pvarChild->lVal = 1;
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
}
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Forces the default action of our element. In simplest cases, send a click.
|
||||
STDMETHODIMP CefIAccessible::accDoDefaultAction(VARIANT varChild) {
|
||||
HRESULT retCode = DATACHECK(node_);
|
||||
if (SUCCEEDED(retCode) && VALID_CHILDID(varChild)) {
|
||||
// doing our default action for out button is to simply click the button.
|
||||
CefRefPtr<CefBrowser> browser = node_->GetBrowser();
|
||||
if (browser) {
|
||||
CefMouseEvent mouse_event;
|
||||
const CefRect& rect = node_->AxLocation();
|
||||
mouse_event.x = MiddleX(rect);
|
||||
mouse_event.y = MiddleY(rect);
|
||||
|
||||
mouse_event.modifiers = 0;
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, false, 1);
|
||||
browser->GetHost()->SendMouseClickEvent(mouse_event, MBT_LEFT, true, 1);
|
||||
}
|
||||
} else {
|
||||
retCode = E_INVALIDARG;
|
||||
}
|
||||
|
||||
return retCode;
|
||||
}
|
||||
|
||||
// Set the name for an element in the accessibility tree
|
||||
STDMETHODIMP CefIAccessible::put_accName(VARIANT varChild, BSTR szName) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// Set the value for an element in the accessibility tree
|
||||
STDMETHODIMP CefIAccessible::put_accValue(VARIANT varChild, BSTR szValue) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// Return E_NOTIMPL as no help file/ topic
|
||||
STDMETHODIMP CefIAccessible::get_accHelp(VARIANT varChild, BSTR *pszHelp) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CefIAccessible::get_accHelpTopic(BSTR *pszHelpFile,
|
||||
VARIANT varChild,
|
||||
long* pidTopic) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// IDispatch - We are not going to return E_NOTIMPL from IDispatch methods and
|
||||
// let Active Accessibility implement the IAccessible interface for them.
|
||||
STDMETHODIMP CefIAccessible::GetTypeInfoCount(unsigned int FAR* pctinfo) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CefIAccessible::GetTypeInfo(unsigned int iTInfo, LCID lcid,
|
||||
ITypeInfo FAR* FAR* ppTInfo) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CefIAccessible::GetIDsOfNames(REFIID riid,
|
||||
OLECHAR FAR* FAR* rgszNames,
|
||||
unsigned int cNames,
|
||||
LCID lcid,
|
||||
DISPID FAR* rgDispId) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
STDMETHODIMP CefIAccessible::Invoke(DISPID dispIdMember, REFIID riid, LCID lcid,
|
||||
WORD wFlags, DISPPARAMS FAR* pDispParams,
|
||||
VARIANT FAR* pVarResult,
|
||||
EXCEPINFO FAR* pExcepInfo,
|
||||
unsigned int FAR* puArgErr) {
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
void OsrAXNode::NotifyAccessibilityEvent(std::string event_type) const {
|
||||
if (event_type == "focus") {
|
||||
// Notify Screen Reader of focus change
|
||||
::NotifyWinEvent(EVENT_OBJECT_FOCUS, GetWindowHandle(), OBJID_CLIENT,
|
||||
node_id_);
|
||||
}
|
||||
}
|
||||
|
||||
void OsrAXNode::Destroy() {
|
||||
CefIAccessible* ptr = static_cast<CefIAccessible*>(platform_accessibility_);
|
||||
if (ptr)
|
||||
ptr->MarkDestroyed();
|
||||
platform_accessibility_ = NULL;
|
||||
}
|
||||
|
||||
// Create and return NSAccessibility Implementation Object for Window
|
||||
CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
|
||||
if (!platform_accessibility_) {
|
||||
platform_accessibility_ = new CefIAccessible(this);
|
||||
platform_accessibility_->AddRef();
|
||||
SetParent(parent);
|
||||
}
|
||||
return platform_accessibility_;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
||||
#else // !defined(CEF_USE_ATL)
|
||||
|
||||
namespace client {
|
||||
|
||||
void OsrAXNode::Destroy() {
|
||||
}
|
||||
|
||||
CefNativeAccessible* OsrAXNode::GetNativeAccessibleObject(OsrAXNode* parent) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace client
|
||||
|
||||
#endif // !defined(CEF_USE_ATL)
|
@ -5,13 +5,18 @@
|
||||
#include "tests/cefclient/browser/osr_window_win.h"
|
||||
|
||||
#include <windowsx.h>
|
||||
#if defined(CEF_USE_ATL)
|
||||
#include <oleacc.h>
|
||||
#endif
|
||||
|
||||
#include "include/base/cef_build.h"
|
||||
#include "tests/shared/browser/geometry_util.h"
|
||||
#include "tests/shared/browser/main_message_loop.h"
|
||||
#include "tests/cefclient/browser/main_context.h"
|
||||
#include "tests/cefclient/browser/resource.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/resource.h"
|
||||
#include "tests/shared/browser/util_win.h"
|
||||
|
||||
namespace client {
|
||||
@ -250,6 +255,8 @@ void OsrWindowWin::Create(HWND parent_hwnd, const RECT& rect) {
|
||||
SetUserDataPtr(hwnd_, this);
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
accessibility_root_ = NULL;
|
||||
|
||||
// Create/register the drag&drop handler.
|
||||
drop_target_ = DropTargetWin::Create(this, hwnd_);
|
||||
HRESULT register_res = RegisterDragDrop(hwnd_, drop_target_);
|
||||
@ -486,7 +493,25 @@ LRESULT CALLBACK OsrWindowWin::OsrWndProc(HWND hWnd, UINT message,
|
||||
self->OnIMECancelCompositionEvent();
|
||||
// Let WTL call::DefWindowProc() and release its resources.
|
||||
break;
|
||||
#if defined(CEF_USE_ATL)
|
||||
case WM_GETOBJECT: {
|
||||
// Only the lower 32 bits of lParam are valid when checking the object id
|
||||
// because it sometimes gets sign-extended incorrectly (but not always).
|
||||
DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
|
||||
|
||||
// Accessibility readers will send an OBJID_CLIENT message.
|
||||
if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
|
||||
if (self->accessibility_root_) {
|
||||
return LresultFromObject(IID_IAccessible, wParam,
|
||||
static_cast<IAccessible*>(self->accessibility_root_));
|
||||
} else {
|
||||
// Notify the renderer to enable accessibility.
|
||||
if (self->browser_ && self->browser_->GetHost())
|
||||
self->browser_->GetHost()->SetAccessibilityState(STATE_ENABLED);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
#endif
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_MBUTTONDOWN:
|
||||
@ -1014,6 +1039,23 @@ void OsrWindowWin::OnImeCompositionRangeChanged(
|
||||
}
|
||||
}
|
||||
|
||||
void OsrWindowWin::UpdateAccessibilityTree(CefRefPtr<CefValue> value) {
|
||||
CEF_REQUIRE_UI_THREAD();
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
if (!accessibility_handler_) {
|
||||
accessibility_handler_.reset(new OsrAccessibilityHelper(value, browser_));
|
||||
} else {
|
||||
accessibility_handler_->UpdateAccessibilityTree(value);
|
||||
}
|
||||
|
||||
// Update |accessibility_root_| because UpdateAccessibilityTree may have
|
||||
// cleared it.
|
||||
OsrAXNode* root = accessibility_handler_->GetRootNode();
|
||||
accessibility_root_ = root ? root->GetNativeAccessibleObject(NULL) : NULL;
|
||||
#endif // defined(CEF_USE_ATL)
|
||||
}
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
|
||||
CefBrowserHost::DragOperationsMask
|
||||
|
@ -11,11 +11,13 @@
|
||||
#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_renderer.h"
|
||||
|
||||
namespace client {
|
||||
|
||||
class OsrAccessibilityHelper;
|
||||
class OsrImeHandlerWin;
|
||||
|
||||
// Represents the native parent window for an off-screen browser. This object
|
||||
@ -24,7 +26,7 @@ class OsrImeHandlerWin;
|
||||
class OsrWindowWin :
|
||||
public base::RefCountedThreadSafe<OsrWindowWin, CefDeleteOnUIThread>,
|
||||
public ClientHandlerOsr::OsrDelegate
|
||||
#if defined(CEF_USE_ATL)
|
||||
#if defined(CEF_USE_ATL)
|
||||
, public OsrDragEvents
|
||||
#endif
|
||||
{
|
||||
@ -51,7 +53,7 @@ class OsrWindowWin :
|
||||
const CefBrowserSettings& settings,
|
||||
CefRefPtr<CefRequestContext> request_context,
|
||||
const std::string& startup_url);
|
||||
|
||||
|
||||
// Show the popup window with correct parent and bounds in parent coordinates.
|
||||
void ShowPopup(HWND parent_hwnd, int x, int y, size_t width, size_t height);
|
||||
|
||||
@ -145,6 +147,8 @@ class OsrWindowWin :
|
||||
const CefRange& selection_range,
|
||||
const CefRenderHandler::RectList& character_bounds) OVERRIDE;
|
||||
|
||||
void UpdateAccessibilityTree(CefRefPtr<CefValue> value);
|
||||
|
||||
#if defined(CEF_USE_ATL)
|
||||
// OsrDragEvents methods.
|
||||
CefBrowserHost::DragOperationsMask OnDragEnter(
|
||||
@ -178,6 +182,11 @@ class OsrWindowWin :
|
||||
#if defined(CEF_USE_ATL)
|
||||
CComPtr<DropTargetWin> drop_target_;
|
||||
CefRenderHandler::DragOperation current_drag_op_;
|
||||
|
||||
// Class that abstracts the accessibility information received from the
|
||||
// renderer.
|
||||
scoped_ptr<OsrAccessibilityHelper> accessibility_handler_;
|
||||
IAccessible* accessibility_root_;
|
||||
#endif
|
||||
|
||||
bool painting_popup_;
|
||||
|
@ -648,6 +648,19 @@ void RootWindowMac::OnSetLoadingState(bool isLoading,
|
||||
[back_button_ setEnabled:canGoBack];
|
||||
[forward_button_ setEnabled:canGoForward];
|
||||
}
|
||||
|
||||
// After Loading is done, check if voiceover is running and accessibility
|
||||
// should be enabled.
|
||||
if (!isLoading) {
|
||||
Boolean keyExists = false;
|
||||
// On OSX there is no API to query if VoiceOver is active or not. The value
|
||||
// however is stored in preferences that can be queried.
|
||||
if (CFPreferencesGetAppBooleanValue(CFSTR("voiceOverOnOffKey"),
|
||||
CFSTR("com.apple.universalaccess"),
|
||||
&keyExists)) {
|
||||
GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RootWindowMac::NotifyDestroyedIfDone() {
|
||||
|
@ -541,6 +541,18 @@ LRESULT CALLBACK RootWindowWin::RootWndProc(HWND hWnd, UINT message,
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case WM_GETOBJECT: {
|
||||
// Only the lower 32 bits of lParam are valid when checking the object id
|
||||
// because it sometimes gets sign-extended incorrectly (but not always).
|
||||
DWORD obj_id = static_cast<DWORD>(static_cast<DWORD_PTR>(lParam));
|
||||
|
||||
// Accessibility readers will send an OBJID_CLIENT message.
|
||||
if (static_cast<DWORD>(OBJID_CLIENT) == obj_id) {
|
||||
if (self->GetBrowser() && self->GetBrowser()->GetHost())
|
||||
self->GetBrowser()->GetHost()->SetAccessibilityState(STATE_ENABLED);
|
||||
}
|
||||
} break;
|
||||
|
||||
case WM_PAINT:
|
||||
self->OnPaint();
|
||||
return 0;
|
||||
|
@ -65,6 +65,7 @@ NSMenuItem* GetMenuItemWithAction(NSMenu* menu, SEL action_selector) {
|
||||
- (IBAction)menuTestsPrint:(id)sender;
|
||||
- (IBAction)menuTestsPrintToPdf:(id)sender;
|
||||
- (IBAction)menuTestsOtherTests:(id)sender;
|
||||
- (void)enableAccessibility:(bool)bEnable;
|
||||
@end
|
||||
|
||||
// Provide the CefAppProtocol implementation required by CEF.
|
||||
@ -132,6 +133,18 @@ NSMenuItem* GetMenuItemWithAction(NSMenu* menu, SEL action_selector) {
|
||||
[delegate tryToTerminateApplication:self];
|
||||
// Return, don't exit. The application is responsible for exiting on its own.
|
||||
}
|
||||
|
||||
// Detect dynamically if VoiceOver is running. Like Chromium, rely upon the
|
||||
// undocumented accessibility attribute @"AXEnhancedUserInterface" which is set
|
||||
// when VoiceOver is launched and unset when VoiceOver is closed.
|
||||
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute {
|
||||
if ([attribute isEqualToString:@"AXEnhancedUserInterface"]) {
|
||||
ClientAppDelegate* delegate = static_cast<ClientAppDelegate*>(
|
||||
[[NSApplication sharedApplication] delegate]);
|
||||
[delegate enableAccessibility:([value intValue] == 1)];
|
||||
}
|
||||
return [super accessibilitySetValue:value forAttribute:attribute];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ClientAppDelegate
|
||||
@ -297,6 +310,22 @@ NSMenuItem* GetMenuItemWithAction(NSMenu* menu, SEL action_selector) {
|
||||
[self testsItemSelected:ID_TESTS_OTHER_TESTS];
|
||||
}
|
||||
|
||||
- (void)enableAccessibility:(bool)bEnable {
|
||||
// Retrieve the active RootWindow.
|
||||
NSWindow* key_window = [[NSApplication sharedApplication] keyWindow];
|
||||
if (!key_window)
|
||||
return;
|
||||
|
||||
scoped_refptr<client::RootWindow> root_window =
|
||||
client::RootWindow::GetForNSWindow(key_window);
|
||||
|
||||
CefRefPtr<CefBrowser> browser = root_window->GetBrowser();
|
||||
if (browser.get()) {
|
||||
browser->GetHost()->SetAccessibilityState(bEnable ?
|
||||
STATE_ENABLED : STATE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
- (NSApplicationTerminateReply)applicationShouldTerminate:
|
||||
(NSApplication *)sender {
|
||||
return NSTerminateNow;
|
||||
|
464
tests/ceftests/accessibility_unittest.cc
Normal file
464
tests/ceftests/accessibility_unittest.cc
Normal file
@ -0,0 +1,464 @@
|
||||
// Copyright (c) 2016 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.
|
||||
|
||||
#include "include/base/cef_bind.h"
|
||||
#include "include/cef_accessibility_handler.h"
|
||||
#include "include/cef_parser.h"
|
||||
#include "include/cef_waitable_event.h"
|
||||
#include "include/wrapper/cef_closure_task.h"
|
||||
#include "tests/ceftests/test_handler.h"
|
||||
#include "tests/ceftests/test_util.h"
|
||||
#include "tests/gtest/include/gtest/gtest.h"
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
const char kTestUrl[] = "https://tests/AccessibilityTestHandler";
|
||||
const char kTipText[] = "Also known as User ID";
|
||||
|
||||
// default osr widget size
|
||||
const int kOsrWidth = 600;
|
||||
const int kOsrHeight = 400;
|
||||
|
||||
// test type
|
||||
enum AccessibilityTestType {
|
||||
// Enabling Accessibility should trigger the AccessibilityHandler callback
|
||||
// with Accessibility tree details
|
||||
TEST_ENABLE_ACCESSIBILITY,
|
||||
// Disabling Accessibility should disable accessibility notification changes
|
||||
TEST_DISABLE_ACCESSIBILITY,
|
||||
// Focus change on element should trigger Accessibility focus event
|
||||
TEST_FOCUS_CHANGE,
|
||||
// Hide/Show etc should trigger Location Change callbacks
|
||||
TEST_LOCATION_CHANGE
|
||||
};
|
||||
|
||||
class AccessibilityTestHandler : public TestHandler,
|
||||
public CefRenderHandler,
|
||||
public CefAccessibilityHandler {
|
||||
public:
|
||||
AccessibilityTestHandler(const AccessibilityTestType& type)
|
||||
: test_type_(type),
|
||||
edit_box_id_(-1),
|
||||
accessibility_disabled_(false) {
|
||||
}
|
||||
|
||||
CefRefPtr<CefAccessibilityHandler> GetAccessibilityHandler() override {
|
||||
return this;
|
||||
}
|
||||
|
||||
CefRefPtr<CefRenderHandler> GetRenderHandler() OVERRIDE {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Cef Renderer Handler Methods
|
||||
bool GetViewRect(CefRefPtr<CefBrowser> browser, CefRect& rect) OVERRIDE {
|
||||
rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetScreenInfo(CefRefPtr<CefBrowser> browser,
|
||||
CefScreenInfo& screen_info) override {
|
||||
screen_info.rect = CefRect(0, 0, kOsrWidth, kOsrHeight);
|
||||
screen_info.available_rect = screen_info.rect;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnPaint(CefRefPtr<CefBrowser> browser,
|
||||
CefRenderHandler::PaintElementType type,
|
||||
const CefRenderHandler::RectList& dirtyRects,
|
||||
const void* buffer,
|
||||
int width,
|
||||
int height) OVERRIDE {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
// OSRTestHandler functions
|
||||
void CreateOSRBrowser(const CefString& url) {
|
||||
CefWindowInfo windowInfo;
|
||||
CefBrowserSettings settings;
|
||||
|
||||
#if defined(OS_WIN)
|
||||
windowInfo.SetAsWindowless(GetDesktopWindow());
|
||||
#elif defined(OS_MACOSX)
|
||||
windowInfo.SetAsWindowless(kNullWindowHandle);
|
||||
#elif defined(OS_LINUX)
|
||||
windowInfo.SetAsWindowless(kNullWindowHandle);
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
CefBrowserHost::CreateBrowser(windowInfo, this, url, settings, NULL);
|
||||
}
|
||||
|
||||
void RunTest() override {
|
||||
std::string html =
|
||||
"<html><head><title>AccessibilityTest</title></head>"
|
||||
"<body><span id='tipspan' role='tooltip' style='color:red;"
|
||||
"margin:20px'>";
|
||||
html += kTipText;
|
||||
html += "</span>"
|
||||
"<input id='editbox' type='text' aria-describedby='tipspan' "
|
||||
"value='editbox' size='25px'/><input id='button' type='button' "
|
||||
"value='button' style='margin:20px'/></body></html>";
|
||||
AddResource(kTestUrl, html, "text/html");
|
||||
|
||||
// Create the browser
|
||||
CreateOSRBrowser(kTestUrl);
|
||||
|
||||
// Time out the test after a reasonable period of time.
|
||||
SetTestTimeout(5000);
|
||||
}
|
||||
|
||||
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
||||
CefRefPtr<CefFrame> frame,
|
||||
int httpStatusCode) override {
|
||||
// Enable Accessibility
|
||||
browser->GetHost()->SetAccessibilityState(STATE_ENABLED);
|
||||
switch(test_type_) {
|
||||
case TEST_ENABLE_ACCESSIBILITY: {
|
||||
// This should trigger OnAccessibilityTreeChange
|
||||
// And update will be validated
|
||||
} break;
|
||||
case TEST_DISABLE_ACCESSIBILITY: {
|
||||
// Post a delayed task to disable Accessibility
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::DisableAccessibility,
|
||||
this, browser), 200);
|
||||
} break;
|
||||
// Delayed task will posted later after we have initial details
|
||||
case TEST_FOCUS_CHANGE: {
|
||||
} break;
|
||||
case TEST_LOCATION_CHANGE: {
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAccessibilityTreeChange(CefRefPtr<CefValue> value) OVERRIDE {
|
||||
switch(test_type_) {
|
||||
case TEST_ENABLE_ACCESSIBILITY: {
|
||||
TestEnableAccessibilityUpdate(value);
|
||||
} break;
|
||||
case TEST_DISABLE_ACCESSIBILITY: {
|
||||
// Once Accessibility is disabled in the delayed Task
|
||||
// We should not reach here
|
||||
EXPECT_FALSE(accessibility_disabled_);
|
||||
} break;
|
||||
case TEST_LOCATION_CHANGE: {
|
||||
// find accessibility id of the edit box, before setting focus
|
||||
if (edit_box_id_ == -1) {
|
||||
EXPECT_TRUE(value.get());
|
||||
CefRefPtr<CefListValue> list = value->GetList();
|
||||
EXPECT_TRUE(list.get());
|
||||
EXPECT_GT(list->GetSize(), (size_t)0);
|
||||
|
||||
// Get the first update dict and validate event type
|
||||
CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(0);
|
||||
|
||||
// ignore other events
|
||||
if (dict->GetString("event_type").ToString() != "layoutComplete")
|
||||
break;
|
||||
|
||||
SetEditBoxIdAndRect(dict->GetDictionary("update"));
|
||||
EXPECT_TRUE(edit_box_id_ != -1);
|
||||
// Post a delayed task to hide the span and trigger location change
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::HideEditBox,
|
||||
this, GetBrowser()), 200);
|
||||
}
|
||||
} break;
|
||||
case TEST_FOCUS_CHANGE: {
|
||||
// find accessibility id of the edit box, before setting focus
|
||||
if (edit_box_id_ == -1) {
|
||||
EXPECT_TRUE(value.get());
|
||||
CefRefPtr<CefListValue> list = value->GetList();
|
||||
EXPECT_TRUE(list.get());
|
||||
EXPECT_GT(list->GetSize(), (size_t)0);
|
||||
|
||||
// Get the first update dict and validate event type
|
||||
CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(0);
|
||||
|
||||
// ignore other events
|
||||
if (dict->GetString("event_type").ToString() != "layoutComplete")
|
||||
break;
|
||||
|
||||
// Now post a delayed task to trigger focus to edit box
|
||||
SetEditBoxIdAndRect(dict->GetDictionary("update"));
|
||||
EXPECT_TRUE(edit_box_id_ != -1);
|
||||
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::SetFocusOnEditBox,
|
||||
this, GetBrowser()), 200);
|
||||
} else {
|
||||
EXPECT_TRUE(value.get());
|
||||
// Change has a valid non empty list
|
||||
EXPECT_TRUE(value->GetType() == VTYPE_LIST);
|
||||
CefRefPtr<CefListValue> list = value->GetList();
|
||||
EXPECT_TRUE(list.get());
|
||||
EXPECT_GT(list->GetSize(), (size_t)0);
|
||||
|
||||
// Get the first update dict and validate event type
|
||||
CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(0);
|
||||
|
||||
// Validate Event type is Focus change
|
||||
EXPECT_STREQ("focus",
|
||||
dict->GetString("event_type").ToString().c_str());
|
||||
|
||||
// And Focus is set to expected element edit_box
|
||||
EXPECT_TRUE(edit_box_id_ == dict->GetInt("id") );
|
||||
|
||||
// Now Post a delayed task to destroy the test giving
|
||||
// sufficient time for any accessibility updates to come through
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::DestroyTest,
|
||||
this), 500);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAccessibilityLocationChange(CefRefPtr<CefValue> value) OVERRIDE {
|
||||
if (test_type_ == TEST_LOCATION_CHANGE) {
|
||||
EXPECT_TRUE(edit_box_id_ != -1);
|
||||
EXPECT_TRUE(value.get());
|
||||
|
||||
// Change has a valid list
|
||||
EXPECT_TRUE(value->GetType() == VTYPE_LIST);
|
||||
CefRefPtr<CefListValue> list = value->GetList();
|
||||
EXPECT_TRUE(list.get());
|
||||
// Ignore empty events
|
||||
if (!list->GetSize())
|
||||
return;
|
||||
|
||||
// Hiding edit box should only change position of subsequent button
|
||||
EXPECT_EQ(list->GetSize(), (size_t)1);
|
||||
|
||||
CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(0);
|
||||
EXPECT_TRUE(dict.get());
|
||||
|
||||
// New location of button should be same as older location of edit box
|
||||
// as that is hidden now
|
||||
CefRefPtr<CefDictionaryValue> location =
|
||||
dict->GetDictionary("new_location");
|
||||
EXPECT_TRUE(location.get());
|
||||
|
||||
CefRefPtr<CefDictionaryValue> bounds = location->GetDictionary("bounds");
|
||||
EXPECT_TRUE(bounds.get());
|
||||
|
||||
EXPECT_EQ(bounds->GetInt("x"), edit_box_rect_.x);
|
||||
EXPECT_EQ(bounds->GetInt("y"), edit_box_rect_.y);
|
||||
EXPECT_EQ(bounds->GetInt("width"), edit_box_rect_.width);
|
||||
EXPECT_EQ(bounds->GetInt("height"), edit_box_rect_.height);
|
||||
|
||||
// Now Post a delayed task to destroy the test
|
||||
// giving sufficient time for any accessibility updates to come through
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::DestroyTest,
|
||||
this), 500);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void HideEditBox(CefRefPtr<CefBrowser> browser) {
|
||||
// Set focus on edit box
|
||||
// This should trigger Location update if enabled
|
||||
browser->GetMainFrame()->ExecuteJavaScript(
|
||||
"document.getElementById('editbox').style.display = 'none';",
|
||||
kTestUrl, 0);
|
||||
}
|
||||
|
||||
void SetFocusOnEditBox(CefRefPtr<CefBrowser> browser) {
|
||||
// Set focus on edit box
|
||||
// This should trigger accessibility update if enabled
|
||||
browser->GetMainFrame()->ExecuteJavaScript(
|
||||
"document.getElementById('editbox').focus();", kTestUrl, 0);
|
||||
}
|
||||
|
||||
void DisableAccessibility(CefRefPtr<CefBrowser> browser) {
|
||||
browser->GetHost()->SetAccessibilityState(STATE_DISABLED);
|
||||
accessibility_disabled_ = true;
|
||||
// Set focus on edit box
|
||||
SetFocusOnEditBox(browser);
|
||||
|
||||
// Now Post a delayed task to destroy the test
|
||||
// giving sufficient time for any accessibility updates to come through
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::DestroyTest,
|
||||
this), 500);
|
||||
}
|
||||
|
||||
void TestEnableAccessibilityUpdate(CefRefPtr<CefValue> value) {
|
||||
// Validate enabling accessibility change returns valid accessibility tree.
|
||||
// Change has a valid non empty list.
|
||||
EXPECT_TRUE(value->GetType() == VTYPE_LIST);
|
||||
CefRefPtr<CefListValue> list = value->GetList();
|
||||
EXPECT_TRUE(list.get());
|
||||
EXPECT_GT(list->GetSize(), (size_t)0);
|
||||
|
||||
// Get the first update dict and validate event type.
|
||||
CefRefPtr<CefDictionaryValue> dict = list->GetDictionary(0);
|
||||
EXPECT_STREQ("layoutComplete",
|
||||
dict->GetString("event_type").ToString().c_str());
|
||||
|
||||
// Get update and validate it has tree data
|
||||
CefRefPtr<CefDictionaryValue> update = dict->GetDictionary("update");
|
||||
EXPECT_TRUE(update.get());
|
||||
EXPECT_TRUE(update->GetBool("has_tree_data"));
|
||||
CefRefPtr<CefDictionaryValue> treeData =
|
||||
update->GetDictionary("tree_data");
|
||||
|
||||
// Validate title and Url
|
||||
EXPECT_STREQ("AccessibilityTest",
|
||||
treeData->GetString("title").ToString().c_str());
|
||||
EXPECT_STREQ(kTestUrl, treeData->GetString("url").ToString().c_str());
|
||||
|
||||
// Validate node data
|
||||
CefRefPtr<CefListValue> nodes = update->GetList("nodes");
|
||||
EXPECT_TRUE(nodes.get());
|
||||
EXPECT_GT(nodes->GetSize(), (size_t)0);
|
||||
|
||||
// Update has a valid root
|
||||
CefRefPtr<CefDictionaryValue> root;
|
||||
for(size_t index = 0; index<nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node->GetString("role").ToString() == "rootWebArea") {
|
||||
root = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(root.get());
|
||||
|
||||
// One div containing the tree elements.
|
||||
CefRefPtr<CefListValue> childIDs = root->GetList("child_ids");
|
||||
EXPECT_TRUE(childIDs.get());
|
||||
EXPECT_EQ(childIDs->GetSize(), (size_t)1);
|
||||
|
||||
// A parent Group div containing the child.
|
||||
CefRefPtr<CefDictionaryValue> group;
|
||||
for(size_t index = 0; index<nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node->GetString("role").ToString() == "group") {
|
||||
group = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(group.get());
|
||||
// Validate Group is child of root WebArea.
|
||||
EXPECT_EQ(group->GetInt("id"), childIDs->GetInt(0));
|
||||
|
||||
CefRefPtr<CefListValue> parentdiv = group->GetList("child_ids");
|
||||
EXPECT_TRUE(parentdiv.get());
|
||||
EXPECT_EQ(parentdiv->GetSize(), (size_t)3);
|
||||
|
||||
int tipId = parentdiv->GetInt(0);
|
||||
int editBoxId = parentdiv->GetInt(1);
|
||||
int buttonId = parentdiv->GetInt(2);
|
||||
|
||||
// A parent Group div containing the child.
|
||||
CefRefPtr<CefDictionaryValue> tip, editbox, button;
|
||||
for(size_t index = 0; index<nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node->GetInt("id") == tipId) {
|
||||
tip = node;
|
||||
}
|
||||
if (node->GetInt("id") == editBoxId) {
|
||||
editbox = node;
|
||||
}
|
||||
if (node->GetInt("id") == buttonId) {
|
||||
button = node;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(tip.get());
|
||||
EXPECT_STREQ("tooltip", tip->GetString("role").ToString().c_str());
|
||||
// Validate tooltip color property is Red.
|
||||
CefRefPtr<CefDictionaryValue> tipattr = tip->GetDictionary("attributes");
|
||||
EXPECT_TRUE(tipattr.get());
|
||||
EXPECT_STREQ("0xFFFF0000",
|
||||
tipattr->GetString("color").ToString().c_str());
|
||||
|
||||
EXPECT_TRUE(editbox.get());
|
||||
EXPECT_STREQ("textField",
|
||||
editbox->GetString("role").ToString().c_str());
|
||||
CefRefPtr<CefDictionaryValue> editattr =
|
||||
editbox->GetDictionary("attributes");
|
||||
// Validate ARIA Description tags for tipIdare associated with editbox.
|
||||
EXPECT_TRUE(editattr.get());
|
||||
EXPECT_EQ(tipId, editattr->GetList("describedbyIds")->GetInt(0));
|
||||
EXPECT_STREQ(kTipText,
|
||||
editattr->GetString("description").ToString().c_str());
|
||||
|
||||
EXPECT_TRUE(button.get());
|
||||
EXPECT_STREQ("button", button->GetString("role").ToString().c_str());
|
||||
|
||||
// Now Post a delayed task to destroy the test
|
||||
// giving sufficient time for any accessibility updates to come through
|
||||
CefPostDelayedTask(TID_UI,
|
||||
base::Bind(&AccessibilityTestHandler::DestroyTest,
|
||||
this), 500);
|
||||
}
|
||||
|
||||
// Find Edit box Id in accessibility tree.
|
||||
void SetEditBoxIdAndRect(CefRefPtr<CefDictionaryValue> value) {
|
||||
EXPECT_TRUE(value.get());
|
||||
// Validate node data.
|
||||
CefRefPtr<CefListValue> nodes = value->GetList("nodes");
|
||||
EXPECT_TRUE(nodes.get());
|
||||
EXPECT_GT(nodes->GetSize(), (size_t)0);
|
||||
|
||||
// Find accessibility id for the text field.
|
||||
for(size_t index = 0; index<nodes->GetSize(); index++) {
|
||||
CefRefPtr<CefDictionaryValue> node = nodes->GetDictionary(index);
|
||||
if (node->GetString("role").ToString() == "textField") {
|
||||
edit_box_id_ = node->GetInt("id");
|
||||
CefRefPtr<CefDictionaryValue> loc = node->GetDictionary("location");
|
||||
EXPECT_TRUE(loc.get());
|
||||
edit_box_rect_.x = loc->GetInt("x");
|
||||
edit_box_rect_.y = loc->GetInt("y");
|
||||
edit_box_rect_.width = loc->GetInt("width");
|
||||
edit_box_rect_.height = loc->GetInt("height");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AccessibilityTestType test_type_;
|
||||
int edit_box_id_;
|
||||
CefRect edit_box_rect_;
|
||||
bool accessibility_disabled_;
|
||||
|
||||
IMPLEMENT_REFCOUNTING(AccessibilityTestHandler);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(AccessibilityTest, EnableAccessibility) {
|
||||
CefRefPtr<AccessibilityTestHandler> handler =
|
||||
new AccessibilityTestHandler(TEST_ENABLE_ACCESSIBILITY);
|
||||
handler->ExecuteTest();
|
||||
EXPECT_TRUE(true);
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
|
||||
TEST(AccessibilityTest, DisableAccessibility) {
|
||||
CefRefPtr<AccessibilityTestHandler> handler =
|
||||
new AccessibilityTestHandler(TEST_DISABLE_ACCESSIBILITY);
|
||||
handler->ExecuteTest();
|
||||
EXPECT_TRUE(true);
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
|
||||
TEST(AccessibilityTest, FocusChange) {
|
||||
CefRefPtr<AccessibilityTestHandler> handler =
|
||||
new AccessibilityTestHandler(TEST_FOCUS_CHANGE);
|
||||
handler->ExecuteTest();
|
||||
EXPECT_TRUE(true);
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
||||
|
||||
TEST(AccessibilityTest, LocationChange) {
|
||||
CefRefPtr<AccessibilityTestHandler> handler =
|
||||
new AccessibilityTestHandler(TEST_LOCATION_CHANGE);
|
||||
handler->ExecuteTest();
|
||||
EXPECT_TRUE(true);
|
||||
ReleaseAndWaitForDestructor(handler);
|
||||
}
|
@ -107,6 +107,7 @@ const char kKeyTestWord[] = "done";
|
||||
#define VKEY_N 0x4E
|
||||
#define VKEY_E 0x45
|
||||
#define VKEY_ESCAPE 0x1B
|
||||
#define VKEY_TAB 0x09
|
||||
|
||||
const unsigned int kNativeKeyTestCodes[] = {
|
||||
XK_d,
|
||||
@ -116,6 +117,7 @@ const unsigned int kNativeKeyTestCodes[] = {
|
||||
};
|
||||
|
||||
const unsigned int kNativeKeyEscape = XK_Escape;
|
||||
const unsigned int kNativeKeyTab = XK_Tab;
|
||||
|
||||
#elif defined(OS_MACOSX)
|
||||
|
||||
@ -125,6 +127,7 @@ const unsigned int kNativeKeyEscape = XK_Escape;
|
||||
#define VKEY_N 'n'
|
||||
#define VKEY_E 'e'
|
||||
#define VKEY_ESCAPE kEscapeCharCode
|
||||
#define VKEY_TAB kTabCharCode
|
||||
|
||||
const unsigned int kNativeKeyTestCodes[] = {
|
||||
kVK_ANSI_D,
|
||||
@ -134,6 +137,7 @@ const unsigned int kNativeKeyTestCodes[] = {
|
||||
};
|
||||
|
||||
const unsigned int kNativeKeyEscape = kVK_Escape;
|
||||
const unsigned int kNativeKeyTab = kVK_Tab;
|
||||
|
||||
#endif
|
||||
|
||||
@ -154,6 +158,12 @@ enum OSRTestType {
|
||||
OSR_TEST_IS_WINDOWLESS,
|
||||
// focusing webview, LI00 will get red & repainted
|
||||
OSR_TEST_FOCUS,
|
||||
// tab key traversal should iterate the focus across HTML element and
|
||||
// subsequently after last element CefFocusHandler::OnTakeFocus should be
|
||||
// called to allow giving focus to the next component.
|
||||
OSR_TEST_TAKE_FOCUS,
|
||||
// send focus event should set focus on the webview
|
||||
OSR_TEST_GOT_FOCUS,
|
||||
// loading webview should trigger a full paint (L01)
|
||||
OSR_TEST_PAINT,
|
||||
// same as OSR_TEST_PAINT but with alpha values
|
||||
@ -512,6 +522,23 @@ class OSRTestHandler : public RoutingTestHandler,
|
||||
browser->GetHost()->SendFocusEvent(true);
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_TAKE_FOCUS:
|
||||
if (StartTest() || started()) {
|
||||
// Tab traversal across HTML element
|
||||
#if defined(OS_WIN)
|
||||
SendKeyEvent(browser, VK_TAB);
|
||||
#elif defined(OS_MACOSX) || defined(OS_LINUX)
|
||||
SendKeyEvent(browser, kNativeKeyTab, VKEY_TAB);
|
||||
#else
|
||||
#error "Unsupported platform"
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_GOT_FOCUS:
|
||||
if (StartTest()) {
|
||||
browser->GetHost()->SendFocusEvent(true);
|
||||
}
|
||||
break;
|
||||
case OSR_TEST_CURSOR:
|
||||
if (StartTest()) {
|
||||
// make mouse leave first
|
||||
@ -980,6 +1007,21 @@ class OSRTestHandler : public RoutingTestHandler,
|
||||
return false;
|
||||
}
|
||||
|
||||
void OnTakeFocus(CefRefPtr<CefBrowser> browser,
|
||||
bool next) {
|
||||
if (test_type_ == OSR_TEST_TAKE_FOCUS) {
|
||||
EXPECT_TRUE(true);
|
||||
DestroySucceededTestSoon();
|
||||
}
|
||||
}
|
||||
|
||||
void OnGotFocus(CefRefPtr<CefBrowser> browser) {
|
||||
if (test_type_ == OSR_TEST_GOT_FOCUS) {
|
||||
EXPECT_TRUE(true);
|
||||
DestroySucceededTestSoon();
|
||||
}
|
||||
}
|
||||
|
||||
void OnCursorChange(CefRefPtr<CefBrowser> browser,
|
||||
CefCursorHandle cursor,
|
||||
CursorType type,
|
||||
@ -1319,6 +1361,10 @@ OSR_TEST(Windowless, OSR_TEST_IS_WINDOWLESS, 1.0f);
|
||||
OSR_TEST(Windowless2x, OSR_TEST_IS_WINDOWLESS, 2.0f);
|
||||
OSR_TEST(Focus, OSR_TEST_FOCUS, 1.0f);
|
||||
OSR_TEST(Focus2x, OSR_TEST_FOCUS, 2.0f);
|
||||
OSR_TEST(TakeFocus, OSR_TEST_TAKE_FOCUS, 1.0f);
|
||||
OSR_TEST(TakeFocus2x, OSR_TEST_TAKE_FOCUS, 2.0f);
|
||||
OSR_TEST(GotFocus, OSR_TEST_GOT_FOCUS, 1.0f);
|
||||
OSR_TEST(GotFocus2x, OSR_TEST_GOT_FOCUS, 2.0f);
|
||||
OSR_TEST(Paint, OSR_TEST_PAINT, 1.0f);
|
||||
OSR_TEST(Paint2x, OSR_TEST_PAINT, 2.0f);
|
||||
OSR_TEST(TransparentPaint, OSR_TEST_TRANSPARENCY, 1.0f);
|
||||
|
Loading…
x
Reference in New Issue
Block a user