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:
Nishant Kaushik 2017-05-12 18:28:25 +00:00 committed by Marshall Greenblatt
parent ece935318d
commit 406867f293
51 changed files with 3476 additions and 8 deletions

View File

@ -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",
]
}

View File

@ -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',

View File

@ -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',

View 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_

View File

@ -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;

View File

@ -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.

View 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_

View File

@ -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_

View File

@ -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.

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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();
}

View File

@ -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;

View 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

View 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_

View File

@ -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;
}

View File

@ -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;

View 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;

View 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_

View File

@ -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,

View File

@ -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;

View 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;

View 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_

View File

@ -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.

View File

@ -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_

View File

@ -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();

View File

@ -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,

View File

@ -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));

View File

@ -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));

View File

@ -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,

View File

@ -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_);

View File

@ -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();

View File

@ -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);

View File

@ -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();

View File

@ -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

View File

@ -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_;

View 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

View 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_

View 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

View 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_

View 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

View 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)

View File

@ -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

View File

@ -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_;

View File

@ -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() {

View File

@ -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;

View File

@ -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;

View 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);
}

View File

@ -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);