Commit c93c9c32 authored by dmazzoni@chromium.org's avatar dmazzoni@chromium.org

Implement put_accvalue for textfields and location bar.

All interactive views implement GetAccessibleState
to provide accessibility information about the current
state of the view. This struct now includes a callback
that can be used to change the string value, since
automation software wants to fill in text fields,
and this interface is implemented for Textfield
and LocationBarView, and hooked up to put_accValue.

BUG=260266

Review URL: https://chromiumcodereview.appspot.com/23441024

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@221688 0039d316-1c4b-4281-b951-d872f2087c98
parent 70bd4e54
......@@ -190,7 +190,8 @@ LocationBarView::LocationBarView(Browser* browser,
is_popup_mode_(is_popup_mode),
show_focus_rect_(false),
template_url_service_(NULL),
animation_offset_(0) {
animation_offset_(0),
weak_ptr_factory_(this) {
if (!views::Textfield::IsViewsTextfieldEnabled())
set_id(VIEW_ID_OMNIBOX);
......@@ -634,17 +635,6 @@ void LocationBarView::ZoomChangedForActiveTab(bool can_show_bubble) {
ZoomBubbleView::ShowBubble(delegate_->GetWebContents(), true);
}
void LocationBarView::RefreshZoomView() {
DCHECK(zoom_view_);
WebContents* web_contents = GetWebContents();
if (!web_contents)
return;
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
zoom_view_->Update(zoom_controller);
}
gfx::Point LocationBarView::GetLocationEntryOrigin() const {
gfx::Point origin(location_entry_view_->bounds().origin());
// If the UI layout is RTL, the coordinate system is not transformed and
......@@ -1121,192 +1111,6 @@ const ToolbarModel* LocationBarView::GetToolbarModel() const {
return delegate_->GetToolbarModel();
}
// static
int LocationBarView::GetBuiltInHorizontalPaddingForChildViews() {
return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ?
GetItemPadding() / 2 : 0;
}
int LocationBarView::GetHorizontalEdgeThickness() const {
// In maximized popup mode, there isn't any edge.
return (is_popup_mode_ && browser_ && browser_->window() &&
browser_->window()->IsMaximized()) ? 0 : vertical_edge_thickness();
}
void LocationBarView::UpdateContentSettingViewsPreLayout() {
for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
(*i)->UpdatePreLayout(GetToolbarModel()->input_in_progress() ?
NULL : GetWebContents());
}
}
void LocationBarView::UpdateContentSettingViewsPostLayout() {
for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
(*i)->UpdatePostLayout(GetToolbarModel()->input_in_progress() ?
NULL : GetWebContents());
}
}
void LocationBarView::DeletePageActionViews() {
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i)
RemoveChildView(*i);
STLDeleteElements(&page_action_views_);
}
void LocationBarView::RefreshPageActionViews() {
if (is_popup_mode_)
return;
// Remember the previous visibility of the page actions so that we can
// notify when this changes.
std::map<ExtensionAction*, bool> old_visibility;
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i) {
old_visibility[(*i)->image_view()->page_action()] = (*i)->visible();
}
std::vector<ExtensionAction*> new_page_actions;
WebContents* contents = delegate_->GetWebContents();
if (contents) {
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(contents);
extensions::LocationBarController* controller =
extensions_tab_helper->location_bar_controller();
new_page_actions = controller->GetCurrentActions();
}
// On startup we sometimes haven't loaded any extensions. This makes sure
// we catch up when the extensions (and any page actions) load.
if (page_actions_ != new_page_actions) {
page_actions_.swap(new_page_actions);
DeletePageActionViews(); // Delete the old views (if any).
page_action_views_.resize(page_actions_.size());
View* right_anchor = open_pdf_in_reader_view_;
if (!right_anchor)
right_anchor = star_view_;
if (!right_anchor)
right_anchor = script_bubble_icon_view_;
DCHECK(right_anchor);
// Add the page actions in reverse order, so that the child views are
// inserted in left-to-right order for accessibility.
for (int i = page_actions_.size() - 1; i >= 0; --i) {
page_action_views_[i] = new PageActionWithBadgeView(
delegate_->CreatePageActionImageView(this, page_actions_[i]));
page_action_views_[i]->SetVisible(false);
AddChildViewAt(page_action_views_[i], GetIndexOf(right_anchor));
}
}
if (!page_action_views_.empty() && contents) {
Browser* browser = chrome::FindBrowserWithWebContents(contents);
GURL url = browser->tab_strip_model()->GetActiveWebContents()->GetURL();
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i) {
(*i)->UpdateVisibility(
GetToolbarModel()->input_in_progress() ? NULL : contents, url);
// Check if the visibility of the action changed and notify if it did.
ExtensionAction* action = (*i)->image_view()->page_action();
if (old_visibility.find(action) == old_visibility.end() ||
old_visibility[action] != (*i)->visible()) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionAction>(action),
content::Details<WebContents>(contents));
}
}
}
}
size_t LocationBarView::ScriptBubbleScriptsRunning() {
WebContents* contents = delegate_->GetWebContents();
if (!contents)
return false;
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(contents);
if (!extensions_tab_helper)
return false;
extensions::ScriptBubbleController* script_bubble_controller =
extensions_tab_helper->script_bubble_controller();
if (!script_bubble_controller)
return false;
size_t script_count =
script_bubble_controller->extensions_running_scripts().size();
return script_count;
}
void LocationBarView::RefreshScriptBubble() {
if (!script_bubble_icon_view_)
return;
size_t script_count = ScriptBubbleScriptsRunning();
script_bubble_icon_view_->SetVisible(script_count > 0);
if (script_count > 0)
script_bubble_icon_view_->SetScriptCount(script_count);
}
#if defined(OS_WIN) && !defined(USE_AURA)
void LocationBarView::OnMouseEvent(const ui::MouseEvent& event, UINT msg) {
OmniboxViewWin* omnibox_win = GetOmniboxViewWin(location_entry_.get());
if (omnibox_win) {
UINT flags = event.native_event().wParam;
gfx::Point screen_point(event.location());
ConvertPointToScreen(this, &screen_point);
omnibox_win->HandleExternalMsg(msg, flags, screen_point.ToPOINT());
}
}
#endif
void LocationBarView::ShowFirstRunBubbleInternal() {
#if !defined(OS_CHROMEOS)
// First run bubble doesn't make sense for Chrome OS.
Browser* browser = GetBrowserFromDelegate(delegate_);
if (!browser)
return; // Possible when browser is shutting down.
FirstRunBubble::ShowBubble(browser, location_icon_view_);
#endif
}
void LocationBarView::PaintPageActionBackgrounds(gfx::Canvas* canvas) {
WebContents* web_contents = GetWebContents();
// web_contents may be NULL while the browser is shutting down.
if (!web_contents)
return;
const int32 tab_id = SessionID::IdForTab(web_contents);
const ToolbarModel::SecurityLevel security_level =
GetToolbarModel()->GetSecurityLevel(false);
const SkColor text_color = GetColor(security_level, TEXT);
const SkColor background_color = GetColor(security_level, BACKGROUND);
for (PageActionViews::const_iterator
page_action_view = page_action_views_.begin();
page_action_view != page_action_views_.end();
++page_action_view) {
gfx::Rect bounds = (*page_action_view)->bounds();
int horizontal_padding =
GetItemPadding() - GetBuiltInHorizontalPaddingForChildViews();
// Make the bounding rectangle include the whole vertical range of the
// location bar, and the mid-point pixels between adjacent page actions.
//
// For odd horizontal_paddings, "horizontal_padding + 1" includes the
// mid-point between two page actions in the bounding rectangle. For even
// paddings, the +1 is dropped, which is right since there is no pixel at
// the mid-point.
bounds.Inset(-(horizontal_padding + 1) / 2, 0);
location_bar_util::PaintExtensionActionBackground(
*(*page_action_view)->image_view()->page_action(),
tab_id, canvas, bounds, text_color, background_color);
}
}
const char* LocationBarView::GetClassName() const {
return kViewClassName;
}
......@@ -1339,6 +1143,9 @@ bool LocationBarView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
}
void LocationBarView::GetAccessibleState(ui::AccessibleViewState* state) {
if (!location_entry_)
return;
state->role = ui::AccessibilityTypes::ROLE_LOCATION_BAR;
state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_LOCATION);
state->value = location_entry_->GetText();
......@@ -1348,6 +1155,14 @@ void LocationBarView::GetAccessibleState(ui::AccessibleViewState* state) {
location_entry_->GetSelectionBounds(&entry_start, &entry_end);
state->selection_start = entry_start;
state->selection_end = entry_end;
if (is_popup_mode_) {
state->state |= ui::AccessibilityTypes::STATE_READONLY;
} else {
state->set_value_callback =
base::Bind(&LocationBarView::AccessibilitySetValue,
weak_ptr_factory_.GetWeakPtr());
}
}
bool LocationBarView::HasFocus() const {
......@@ -1563,7 +1378,211 @@ int LocationBarView::GetInternalHeight(bool use_preferred_size) {
return std::max(total_height - (vertical_edge_thickness() * 2), 0);
}
////////////////////////////////////////////////////////////////////////////////
// LocationBarView, private:
// static
int LocationBarView::GetBuiltInHorizontalPaddingForChildViews() {
return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ?
GetItemPadding() / 2 : 0;
}
int LocationBarView::GetHorizontalEdgeThickness() const {
// In maximized popup mode, there isn't any edge.
return (is_popup_mode_ && browser_ && browser_->window() &&
browser_->window()->IsMaximized()) ? 0 : vertical_edge_thickness();
}
void LocationBarView::UpdateContentSettingViewsPreLayout() {
for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
(*i)->UpdatePreLayout(GetToolbarModel()->input_in_progress() ?
NULL : GetWebContents());
}
}
void LocationBarView::UpdateContentSettingViewsPostLayout() {
for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
i != content_setting_views_.end(); ++i) {
(*i)->UpdatePostLayout(GetToolbarModel()->input_in_progress() ?
NULL : GetWebContents());
}
}
void LocationBarView::DeletePageActionViews() {
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i)
RemoveChildView(*i);
STLDeleteElements(&page_action_views_);
}
void LocationBarView::RefreshPageActionViews() {
if (is_popup_mode_)
return;
// Remember the previous visibility of the page actions so that we can
// notify when this changes.
std::map<ExtensionAction*, bool> old_visibility;
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i) {
old_visibility[(*i)->image_view()->page_action()] = (*i)->visible();
}
std::vector<ExtensionAction*> new_page_actions;
WebContents* contents = delegate_->GetWebContents();
if (contents) {
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(contents);
extensions::LocationBarController* controller =
extensions_tab_helper->location_bar_controller();
new_page_actions = controller->GetCurrentActions();
}
// On startup we sometimes haven't loaded any extensions. This makes sure
// we catch up when the extensions (and any page actions) load.
if (page_actions_ != new_page_actions) {
page_actions_.swap(new_page_actions);
DeletePageActionViews(); // Delete the old views (if any).
page_action_views_.resize(page_actions_.size());
View* right_anchor = open_pdf_in_reader_view_;
if (!right_anchor)
right_anchor = star_view_;
if (!right_anchor)
right_anchor = script_bubble_icon_view_;
DCHECK(right_anchor);
// Add the page actions in reverse order, so that the child views are
// inserted in left-to-right order for accessibility.
for (int i = page_actions_.size() - 1; i >= 0; --i) {
page_action_views_[i] = new PageActionWithBadgeView(
delegate_->CreatePageActionImageView(this, page_actions_[i]));
page_action_views_[i]->SetVisible(false);
AddChildViewAt(page_action_views_[i], GetIndexOf(right_anchor));
}
}
if (!page_action_views_.empty() && contents) {
Browser* browser = chrome::FindBrowserWithWebContents(contents);
GURL url = browser->tab_strip_model()->GetActiveWebContents()->GetURL();
for (PageActionViews::const_iterator i(page_action_views_.begin());
i != page_action_views_.end(); ++i) {
(*i)->UpdateVisibility(
GetToolbarModel()->input_in_progress() ? NULL : contents, url);
// Check if the visibility of the action changed and notify if it did.
ExtensionAction* action = (*i)->image_view()->page_action();
if (old_visibility.find(action) == old_visibility.end() ||
old_visibility[action] != (*i)->visible()) {
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
content::Source<ExtensionAction>(action),
content::Details<WebContents>(contents));
}
}
}
}
size_t LocationBarView::ScriptBubbleScriptsRunning() {
WebContents* contents = delegate_->GetWebContents();
if (!contents)
return false;
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(contents);
if (!extensions_tab_helper)
return false;
extensions::ScriptBubbleController* script_bubble_controller =
extensions_tab_helper->script_bubble_controller();
if (!script_bubble_controller)
return false;
size_t script_count =
script_bubble_controller->extensions_running_scripts().size();
return script_count;
}
void LocationBarView::RefreshScriptBubble() {
if (!script_bubble_icon_view_)
return;
size_t script_count = ScriptBubbleScriptsRunning();
script_bubble_icon_view_->SetVisible(script_count > 0);
if (script_count > 0)
script_bubble_icon_view_->SetScriptCount(script_count);
}
void LocationBarView::RefreshZoomView() {
DCHECK(zoom_view_);
WebContents* web_contents = GetWebContents();
if (!web_contents)
return;
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
zoom_view_->Update(zoom_controller);
}
#if defined(OS_WIN) && !defined(USE_AURA)
void LocationBarView::OnMouseEvent(const ui::MouseEvent& event, UINT msg) {
OmniboxViewWin* omnibox_win = GetOmniboxViewWin(location_entry_.get());
if (omnibox_win) {
UINT flags = event.native_event().wParam;
gfx::Point screen_point(event.location());
ConvertPointToScreen(this, &screen_point);
omnibox_win->HandleExternalMsg(msg, flags, screen_point.ToPOINT());
}
}
#endif
bool LocationBarView::HasValidSuggestText() const {
return suggested_text_view_->visible() &&
!suggested_text_view_->size().IsEmpty();
}
void LocationBarView::ShowFirstRunBubbleInternal() {
#if !defined(OS_CHROMEOS)
// First run bubble doesn't make sense for Chrome OS.
Browser* browser = GetBrowserFromDelegate(delegate_);
if (!browser)
return; // Possible when browser is shutting down.
FirstRunBubble::ShowBubble(browser, location_icon_view_);
#endif
}
void LocationBarView::PaintPageActionBackgrounds(gfx::Canvas* canvas) {
WebContents* web_contents = GetWebContents();
// web_contents may be NULL while the browser is shutting down.
if (!web_contents)
return;
const int32 tab_id = SessionID::IdForTab(web_contents);
const ToolbarModel::SecurityLevel security_level =
GetToolbarModel()->GetSecurityLevel(false);
const SkColor text_color = GetColor(security_level, TEXT);
const SkColor background_color = GetColor(security_level, BACKGROUND);
for (PageActionViews::const_iterator
page_action_view = page_action_views_.begin();
page_action_view != page_action_views_.end();
++page_action_view) {
gfx::Rect bounds = (*page_action_view)->bounds();
int horizontal_padding =
GetItemPadding() - GetBuiltInHorizontalPaddingForChildViews();
// Make the bounding rectangle include the whole vertical range of the
// location bar, and the mid-point pixels between adjacent page actions.
//
// For odd horizontal_paddings, "horizontal_padding + 1" includes the
// mid-point between two page actions in the bounding rectangle. For even
// paddings, the +1 is dropped, which is right since there is no pixel at
// the mid-point.
bounds.Inset(-(horizontal_padding + 1) / 2, 0);
location_bar_util::PaintExtensionActionBackground(
*(*page_action_view)->image_view()->page_action(),
tab_id, canvas, bounds, text_color, background_color);
}
}
void LocationBarView::AccessibilitySetValue(const string16& new_value) {
location_entry_->SetUserText(new_value);
}
......@@ -9,6 +9,7 @@
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/prefs/pref_member.h"
#include "chrome/browser/extensions/extension_context_menu_model.h"
#include "chrome/browser/search_engines/template_url_service_observer.h"
......@@ -413,9 +414,6 @@ class LocationBarView : public LocationBar,
// Update the view for the zoom icon based on the current tab's zoom.
void RefreshZoomView();
// Sets the visibility of view to new_vis.
void ToggleVisibility(bool new_vis, views::View* view);
#if !defined(USE_AURA)
// Helper for the Mouse event handlers that does all the real work.
void OnMouseEvent(const ui::MouseEvent& event, UINT msg);
......@@ -431,6 +429,11 @@ class LocationBarView : public LocationBar,
// after layout, so the |page_action_views_| have their bounds.
void PaintPageActionBackgrounds(gfx::Canvas* canvas);
// Handles a request to change the value of this text field from software
// using an accessibility API (typically automation software, screen readers
// don't normally use this). Sets the value and clears the selection.
void AccessibilitySetValue(const string16& new_value);
// The Browser this LocationBarView is in. Note that at least
// chromeos::SimpleWebViewDialog uses a LocationBarView outside any browser
// window, so this may be NULL.
......@@ -543,6 +546,9 @@ class LocationBarView : public LocationBar,
// Used to register for notifications received by NotificationObserver.
content::NotificationRegistrar registrar_;
// Used to bind callback functions to this object.
base::WeakPtrFactory<LocationBarView> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(LocationBarView);
};
......
......@@ -6,6 +6,7 @@
#define UI_BASE_ACCESSIBILITY_ACCESSIBLE_VIEW_STATE_H_
#include "base/basictypes.h"
#include "base/callback.h"
#include "base/strings/string16.h"
#include "ui/base/accessibility/accessibility_types.h"
#include "ui/base/ui_export.h"
......@@ -54,6 +55,17 @@ struct UI_EXPORT AccessibleViewState {
// index and count should be -1 otherwise.
int index;
int count;
// An optional callback that can be used by accessibility clients to
// set the string value of this view. This only applies to roles where
// setting the value makes sense, like a text box. Not often used by
// screen readers, but often used by automation software to script
// things like logging into portals or filling forms.
//
// This callback is only valid for the lifetime of the view, and should
// be a safe no-op if the view is deleted. Typically, accessible views
// should use a WeakPtr when binding the callback.
base::Callback<void(const base::string16&)> set_value_callback;
};
} // namespace ui
......
......@@ -582,8 +582,8 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accState(
return S_OK;
}
STDMETHODIMP NativeViewAccessibilityWin::get_accValue(
VARIANT var_id, BSTR* value) {
STDMETHODIMP NativeViewAccessibilityWin::get_accValue(VARIANT var_id,
BSTR* value) {
if (!IsValidId(var_id) || !value)
return E_INVALIDARG;
......@@ -607,6 +607,24 @@ STDMETHODIMP NativeViewAccessibilityWin::get_accValue(
return S_OK;
}
STDMETHODIMP NativeViewAccessibilityWin::put_accValue(VARIANT var_id,
BSTR new_value) {
if (!IsValidId(var_id) || !new_value)
return E_INVALIDARG;
if (!view_)
return E_FAIL;
// Return an error if the view can't set the value.
ui::AccessibleViewState state;
view_->GetAccessibleState(&state);
if (state.set_value_callback.is_null())
return E_FAIL;
state.set_value_callback.Run(new_value);
return S_OK;
}
// IAccessible functions not supported.
STDMETHODIMP NativeViewAccessibilityWin::get_accSelection(VARIANT* selected) {
......@@ -644,12 +662,6 @@ STDMETHODIMP NativeViewAccessibilityWin::put_accName(
return E_NOTIMPL;
}
STDMETHODIMP NativeViewAccessibilityWin::put_accValue(
VARIANT var_id, BSTR put_val) {
// Deprecated.
return E_NOTIMPL;
}
//
// IAccessible2
//
......
......@@ -114,10 +114,11 @@ NativeViewAccessibilityWin
// Retrieves the current state of the specified object.
STDMETHODIMP get_accState(VARIANT var_id, VARIANT* state);
// Retrieves the current value associated with the specified object.
// 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);
// Non-supported IAccessible methods.
STDMETHODIMP put_accValue(VARIANT var_id, BSTR new_value);
// Selections not applicable to views.
STDMETHODIMP get_accSelection(VARIANT* selected);
......@@ -131,7 +132,6 @@ NativeViewAccessibilityWin
// Deprecated functions, not implemented here.
STDMETHODIMP put_accName(VARIANT var_id, BSTR put_name);
STDMETHODIMP put_accValue(VARIANT var_id, BSTR put_val);
//
// IAccessible2
......
// 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 <oleacc.h>
#include "base/win/scoped_bstr.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_variant.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/test/views_test_base.h"
namespace views {
namespace test {
typedef ViewsTestBase NativeViewAcccessibilityWinTest;
TEST_F(NativeViewAcccessibilityWinTest, TextfieldAccessibility) {
Widget widget;
Widget::InitParams init_params =
CreateParams(Widget::InitParams::TYPE_POPUP);
init_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget.Init(init_params);
View* content = new View;
widget.SetContentsView(content);
Textfield* textfield = new Textfield;
textfield->SetAccessibleName(L"Name");
textfield->SetText(L"Value");
content->AddChildView(textfield);
base::win::ScopedComPtr<IAccessible> content_accessible(
content->GetNativeViewAccessible());
LONG child_count = 0;
ASSERT_EQ(S_OK, content_accessible->get_accChildCount(&child_count));
ASSERT_EQ(1L, child_count);
base::win::ScopedComPtr<IDispatch> textfield_dispatch;
base::win::ScopedComPtr<IAccessible> textfield_accessible;
base::win::ScopedVariant child_index(1);
ASSERT_EQ(S_OK, content_accessible->get_accChild(
child_index, textfield_dispatch.Receive()));
ASSERT_EQ(S_OK, textfield_dispatch.QueryInterface(
textfield_accessible.Receive()));
base::win::ScopedBstr name;
base::win::ScopedVariant childid_self(CHILDID_SELF);
ASSERT_EQ(S_OK, textfield_accessible->get_accName(
childid_self, name.Receive()));
ASSERT_STREQ(L"Name", name);
base::win::ScopedBstr value;
ASSERT_EQ(S_OK, textfield_accessible->get_accValue(
childid_self, value.Receive()));
ASSERT_STREQ(L"Value", value);
base::win::ScopedBstr new_value(L"New value");
ASSERT_EQ(S_OK, textfield_accessible->put_accValue(childid_self, new_value));
ASSERT_STREQ(L"New value", textfield->text().c_str());
}
} // namespace test
} // namespace views
......@@ -85,7 +85,8 @@ Textfield::Textfield()
vertical_margins_were_set_(false),
vertical_alignment_(gfx::ALIGN_VCENTER),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT) {
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
weak_ptr_factory_(this) {
set_focusable(true);
if (ViewsDelegate::views_delegate) {
......@@ -110,7 +111,8 @@ Textfield::Textfield(StyleFlags style)
vertical_margins_were_set_(false),
vertical_alignment_(gfx::ALIGN_VCENTER),
placeholder_text_color_(kDefaultPlaceholderTextColor),
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT) {
text_input_type_(ui::TEXT_INPUT_TYPE_TEXT),
weak_ptr_factory_(this) {
set_focusable(true);
if (IsObscured())
SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
......@@ -502,6 +504,12 @@ void Textfield::GetAccessibleState(ui::AccessibleViewState* state) {
const ui::Range range = native_wrapper_->GetSelectedRange();
state->selection_start = range.start();
state->selection_end = range.end();
if (!read_only()) {
state->set_value_callback =
base::Bind(&Textfield::AccessibilitySetValue,
weak_ptr_factory_.GetWeakPtr());
}
}
ui::TextInputClient* Textfield::GetTextInputClient() {
......@@ -543,6 +551,9 @@ const char* Textfield::GetClassName() const {
return kViewClassName;
}
////////////////////////////////////////////////////////////////////////////////
// Textfield, private:
gfx::Insets Textfield::GetTextInsets() const {
gfx::Insets insets = GetInsets();
if (draw_border_ && native_wrapper_)
......@@ -550,6 +561,13 @@ gfx::Insets Textfield::GetTextInsets() const {
return insets;
}
void Textfield::AccessibilitySetValue(const string16& new_value) {
if (!read_only()) {
SetText(new_value);
ClearSelection();
}
}
////////////////////////////////////////////////////////////////////////////////
// NativeTextfieldWrapper, public:
......
......@@ -9,6 +9,7 @@
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "base/time/time.h"
#include "build/build_config.h"
......@@ -285,6 +286,11 @@ class VIEWS_EXPORT Textfield : public View {
// Returns the insets to the rectangle where text is actually painted.
gfx::Insets GetTextInsets() const;
// Handles a request to change the value of this text field from software
// using an accessibility API (typically automation software, screen readers
// don't normally use this). Sets the value and clears the selection.
void AccessibilitySetValue(const string16& new_value);
// This is the current listener for events from this Textfield.
TextfieldController* controller_;
......@@ -344,6 +350,9 @@ class VIEWS_EXPORT Textfield : public View {
// The duration to reveal the last typed char for obscured textfields.
base::TimeDelta obscured_reveal_duration_;
// Used to bind callback functions to this object.
base::WeakPtrFactory<Textfield> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(Textfield);
};
......
......@@ -696,6 +696,7 @@
'..',
],
'sources': [
'accessibility/native_view_accessibility_win_unittest.cc',
'accessible_pane_view_unittest.cc',
'animation/bounds_animator_unittest.cc',
'bubble/bubble_border_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment