Add an accessibility mode for editable text fields only.

This refactors the triggering of accessibility mode from a boolean state and
command-line flag to an AccessibilityMode enum that's passed directly
when a new RenderView is created, which is also more readable and avoids the
extra command-line flag.

Adds a new mode that only syncs editable text nodes, and enables this on
Windows 8 so that we can support showing the virtual keyboard when an
editable text field gets focus.

BUG=122061,118641
TEST=Adds new test


Review URL: http://codereview.chromium.org/9939011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@132267 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/content/browser/accessibility/browser_accessibility_state_impl.cc b/content/browser/accessibility/browser_accessibility_state_impl.cc
index f3b8aaf7..b878cdb 100644
--- a/content/browser/accessibility/browser_accessibility_state_impl.cc
+++ b/content/browser/accessibility/browser_accessibility_state_impl.cc
@@ -4,9 +4,11 @@
 
 #include "content/browser/accessibility/browser_accessibility_state_impl.h"
 
+#include "base/command_line.h"
 #include "base/memory/singleton.h"
 #include "base/metrics/histogram.h"
 #include "base/timer.h"
+#include "content/public/common/content_switches.h"
 #include "ui/gfx/sys_color_change_listener.h"
 
 // Update the accessibility histogram 45 seconds after initialization.
@@ -26,6 +28,10 @@
 BrowserAccessibilityStateImpl::BrowserAccessibilityStateImpl()
     : BrowserAccessibilityState(),
       accessibility_enabled_(false) {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kForceRendererAccessibility)) {
+    OnAccessibilityEnabledManually();
+  }
   update_histogram_timer_.Start(
       FROM_HERE,
       base::TimeDelta::FromSeconds(kAccessibilityHistogramDelaySecs),
@@ -37,6 +43,10 @@
 }
 
 void BrowserAccessibilityStateImpl::OnScreenReaderDetected() {
+  if (CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kDisableRendererAccessibility)) {
+    return;
+  }
   accessibility_enabled_ = true;
 }
 
diff --git a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
index 5a7125c..d47da85 100644
--- a/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
+++ b/content/browser/accessibility/dump_accessibility_tree_browsertest.cc
@@ -89,7 +89,7 @@
   RenderViewHostImpl* view_host =
       static_cast<RenderViewHostImpl*>(RenderWidgetHostImpl::From(host));
   view_host->set_save_accessibility_tree_for_testing(true);
-  view_host->EnableRendererAccessibility();
+  view_host->SetAccessibilityMode(AccessibilityModeComplete);
 
   // Setup test paths.
   FilePath dir_test_data;
@@ -199,4 +199,3 @@
     }
   } while (!(html_file = file_enumerator.Next()).empty());
 }
-
diff --git a/content/browser/accessibility/renderer_accessibility_browsertest.cc b/content/browser/accessibility/renderer_accessibility_browsertest.cc
index 76d77fdf..e8c5acc9 100644
--- a/content/browser/accessibility/renderer_accessibility_browsertest.cc
+++ b/content/browser/accessibility/renderer_accessibility_browsertest.cc
@@ -35,7 +35,8 @@
 
   // Tell the renderer to send an accessibility tree, then wait for the
   // notification that it's been received.
-  const WebAccessibility& GetWebAccessibilityTree() {
+  const WebAccessibility& GetWebAccessibilityTree(
+      AccessibilityMode accessibility_mode = AccessibilityModeComplete) {
     ui_test_utils::WindowedNotificationObserver tree_updated_observer(
         content::NOTIFICATION_RENDER_VIEW_HOST_ACCESSIBILITY_TREE_UPDATED,
         content::NotificationService::AllSources());
@@ -45,7 +46,7 @@
         RenderWidgetHostImpl::From(host_view->GetRenderWidgetHost());
     RenderViewHostImpl* view_host = static_cast<RenderViewHostImpl*>(host);
     view_host->set_save_accessibility_tree_for_testing(true);
-    view_host->EnableRendererAccessibility();
+    view_host->SetAccessibilityMode(accessibility_mode);
     tree_updated_observer.Wait();
     return view_host->accessibility_tree_for_testing();
   }
@@ -463,4 +464,51 @@
       true, GetBoolAttr(textbox, WebAccessibility::ATTR_CAN_SET_VALUE));
 }
 
+IN_PROC_BROWSER_TEST_F(RendererAccessibilityBrowserTest,
+                       CrossPlatformEditableTextOnlyMode) {
+  const char url_str[] =
+      "data:text/html,"
+      "<!doctype html>"
+      "<h1>Heading</h1>"
+      "<input type=text value=text0>"
+      "<textarea>text1</textarea>"
+      "<div role=textbox>text2</div>"
+      "<ul>"
+      "  <li><input type=text value=text3>"
+      "  <li><textarea>text4</textarea>"
+      "  <li><div role=textbox>text5</div>"
+      "  <li><button>button</button>"
+      "</ul>";
+
+  GURL url(url_str);
+  browser()->OpenURL(OpenURLParams(
+      url, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false));
+  const WebAccessibility& tree = GetWebAccessibilityTree(
+      AccessibilityModeEditableTextOnly);
+
+  const WebAccessibility& text0 = tree.children[0];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text0.role);
+  EXPECT_STREQ("text0", UTF16ToUTF8(text0.value).c_str());
+
+  const WebAccessibility& text1 = tree.children[1];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXTAREA, text1.role);
+  EXPECT_STREQ("text1", UTF16ToUTF8(text1.value).c_str());
+
+  const WebAccessibility& text2 = tree.children[2];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text2.role);
+  EXPECT_STREQ("text2", UTF16ToUTF8(text2.value).c_str());
+
+  const WebAccessibility& text3 = tree.children[3];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text3.role);
+  EXPECT_STREQ("text3", UTF16ToUTF8(text3.value).c_str());
+
+  const WebAccessibility& text4 = tree.children[4];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXTAREA, text4.role);
+  EXPECT_STREQ("text4", UTF16ToUTF8(text4.value).c_str());
+
+  const WebAccessibility& text5 = tree.children[5];
+  EXPECT_EQ(WebAccessibility::ROLE_TEXT_FIELD, text5.role);
+  EXPECT_STREQ("text5", UTF16ToUTF8(text5.value).c_str());
+}
+
 }  // namespace
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 2800992..29724ec 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -279,7 +279,6 @@
           ALLOW_THIS_IN_INITIALIZER_LIST(cached_dibs_cleaner_(
                 FROM_HERE, base::TimeDelta::FromSeconds(5),
                 this, &RenderProcessHostImpl::ClearTransportDIBCache)),
-          accessibility_enabled_(false),
           is_initialized_(false),
           id_(ChildProcessHostImpl::GenerateChildProcessUniqueId()),
           browser_context_(browser_context),
@@ -353,14 +352,12 @@
   is_initialized_ = false;
 }
 
-bool RenderProcessHostImpl::Init(bool is_accessibility_enabled) {
+bool RenderProcessHostImpl::Init() {
   // calling Init() more than once does nothing, this makes it more convenient
   // for the view host which may not be sure in some cases
   if (channel_.get())
     return true;
 
-  accessibility_enabled_ = is_accessibility_enabled;
-
   CommandLine::StringType renderer_prefix;
 #if defined(OS_POSIX)
   // A command prefix is something prepended to the command line of the spawned
@@ -615,9 +612,6 @@
   command_line->AppendSwitchASCII(switches::kProcessType,
                                   switches::kRendererProcess);
 
-  if (accessibility_enabled_)
-    command_line->AppendSwitch(switches::kEnableAccessibility);
-
   // Now send any options from our own command line we want to propagate.
   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
   PropagateBrowserCommandLineToRenderer(browser_command_line, command_line);
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 71a41eb..b1986e77 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -58,7 +58,7 @@
 
   // RenderProcessHost implementation (public portion).
   virtual void EnableSendQueue() OVERRIDE;
-  virtual bool Init(bool is_accessibility_enabled) OVERRIDE;
+  virtual bool Init() OVERRIDE;
   virtual int GetNextRoutingID() OVERRIDE;
   virtual void CancelResourceRequests(int render_widget_id) OVERRIDE;
   virtual void CrossSiteSwapOutACK(const ViewMsg_SwapOut_Params& params)
@@ -220,9 +220,6 @@
   // Used in single-process mode.
   scoped_ptr<RendererMainThread> in_process_renderer_;
 
-  // True if this prcoess should have accessibility enabled;
-  bool accessibility_enabled_;
-
   // True after Init() has been called. We can't just check channel_ because we
   // also reset that in the case of process termination.
   bool is_initialized_;
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc
index f333925..04de1754 100644
--- a/content/browser/renderer_host/render_view_host_impl.cc
+++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -18,6 +18,9 @@
 #include "base/time.h"
 #include "base/utf_string_conversions.h"
 #include "base/values.h"
+#if defined(OS_WIN)
+#include "base/win/windows_version.h"
+#endif
 #include "content/browser/child_process_security_policy_impl.h"
 #include "content/browser/cross_site_request_manager.h"
 #include "content/browser/gpu/gpu_surface_tracker.h"
@@ -33,6 +36,7 @@
 #include "content/common/swapped_out_messages.h"
 #include "content/common/view_messages.h"
 #include "content/port/browser/render_widget_host_view_port.h"
+#include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/browser_message_filter.h"
 #include "content/public/browser/content_browser_client.h"
@@ -205,7 +209,7 @@
   // initialized it) or may not (we have our own process or the old process
   // crashed) have been initialized. Calling Init multiple times will be
   // ignored, so this is safe.
-  if (!GetProcess()->Init(renderer_accessible()))
+  if (!GetProcess()->Init())
     return false;
   DCHECK(GetProcess()->HasConnection());
   DCHECK(GetProcess()->GetBrowserContext());
@@ -245,6 +249,19 @@
           gfx::NativeViewFromId(GetNativeViewId()));
 #endif
   params.guest = guest_;
+  params.accessibility_mode =
+      BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser() ?
+          AccessibilityModeComplete :
+          AccessibilityModeOff;
+
+#if defined(OS_WIN)
+  // On Windows 8, always enable accessibility for editable text controls
+  // so we can show the virtual keyboard when one is enabled.
+  if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
+      params.accessibility_mode == AccessibilityModeOff) {
+    params.accessibility_mode = AccessibilityModeEditableTextOnly;
+  }
+#endif
 
   Send(new ViewMsg_New(params));
 
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index e6835f09..3a526a3 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -28,7 +28,6 @@
 #include "content/common/gpu/gpu_messages.h"
 #include "content/common/view_messages.h"
 #include "content/port/browser/render_widget_host_view_port.h"
-#include "content/public/browser/browser_accessibility_state.h"
 #include "content/public/browser/native_web_keyboard_event.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
@@ -106,7 +105,6 @@
       hung_renderer_delay_ms_(kHungRendererDelayMs),
       process_(process),
       routing_id_(routing_id),
-      renderer_accessible_(false),
       surface_id_(0),
       is_loading_(false),
       is_hidden_(false),
@@ -154,16 +152,6 @@
   // Because the widget initializes as is_hidden_ == false,
   // tell the process host that we're alive.
   process_->WidgetRestored();
-
-  // Enable accessibility if it was manually specified or if it was
-  // auto-detected.
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kForceRendererAccessibility)) {
-    BrowserAccessibilityState::GetInstance()->OnAccessibilityEnabledManually();
-    EnableRendererAccessibility();
-  } else if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
-    EnableRendererAccessibility();
-  }
 }
 
 RenderWidgetHostImpl::~RenderWidgetHostImpl() {
@@ -701,6 +689,10 @@
   // started again shortly, which happens to be the common use case.
 }
 
+void RenderWidgetHostImpl::EnableFullAccessibilityMode() {
+  SetAccessibilityMode(AccessibilityModeComplete);
+}
+
 void RenderWidgetHostImpl::ForwardMouseEvent(const WebMouseEvent& mouse_event) {
   TRACE_EVENT2("renderer_host", "RenderWidgetHostImpl::ForwardMouseEvent",
                "x", mouse_event.x, "y", mouse_event.y);
@@ -1519,23 +1511,6 @@
   Send(new ViewMsg_Replace(routing_id_, word));
 }
 
-void RenderWidgetHostImpl::EnableRendererAccessibility() {
-  if (renderer_accessible_)
-    return;
-
-  if (CommandLine::ForCurrentProcess()->HasSwitch(
-          switches::kDisableRendererAccessibility)) {
-    return;
-  }
-
-  renderer_accessible_ = true;
-
-  if (process_->HasConnection()) {
-    // Renderer accessibility wasn't enabled on process launch. Enable it now.
-    Send(new AccessibilityMsg_Enable(GetRoutingID()));
-  }
-}
-
 void RenderWidgetHostImpl::SetIgnoreInputEvents(bool ignore_input_events) {
   ignore_input_events_ = ignore_input_events;
 }
@@ -1611,6 +1586,10 @@
   Send(new ViewMsg_SetEditCommandsForNextKeyEvent(GetRoutingID(), commands));
 }
 
+void RenderWidgetHostImpl::SetAccessibilityMode(AccessibilityMode mode) {
+  Send(new AccessibilityMsg_SetMode(routing_id_, mode));
+}
+
 void RenderWidgetHostImpl::AccessibilityDoDefaultAction(int object_id) {
   Send(new AccessibilityMsg_DoDefaultAction(GetRoutingID(), object_id));
 }
diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h
index 2a93e2c..adb71f5 100644
--- a/content/browser/renderer_host/render_widget_host_impl.h
+++ b/content/browser/renderer_host/render_widget_host_impl.h
@@ -19,6 +19,7 @@
 #include "base/string16.h"
 #include "base/timer.h"
 #include "build/build_config.h"
+#include "content/common/view_message_enums.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/common/page_zoom.h"
 #include "ui/base/ime/text_input_type.h"
@@ -94,7 +95,7 @@
   virtual bool CopyFromBackingStoreToCGContext(const CGRect& dest_rect,
                                                CGContextRef target) OVERRIDE;
 #endif
-  virtual void EnableRendererAccessibility() OVERRIDE;
+  virtual void EnableFullAccessibilityMode() OVERRIDE;
   virtual void ForwardMouseEvent(
       const WebKit::WebMouseEvent& mouse_event) OVERRIDE;
   virtual void ForwardWheelEvent(
@@ -123,7 +124,6 @@
   void SetView(RenderWidgetHostView* view);
 
   int surface_id() const { return surface_id_; }
-  bool renderer_accessible() { return renderer_accessible_; }
 
   bool empty() const { return current_size_.IsEmpty(); }
 
@@ -298,6 +298,9 @@
   void SetEditCommandsForNextKeyEvent(
       const std::vector<EditCommand>& commands);
 
+  // Send a message to the renderer process to change the accessibility mode.
+  void SetAccessibilityMode(AccessibilityMode mode);
+
   // Relay a request from assistive technology to perform the default action
   // on a given node.
   void AccessibilityDoDefaultAction(int object_id);
@@ -550,10 +553,6 @@
   // The ID of the corresponding object in the Renderer Instance.
   int routing_id_;
 
-  // True if renderer accessibility is enabled. This should only be set when a
-  // screenreader is detected as it can potentially slow down Chrome.
-  bool renderer_accessible_;
-
   // Stores random bits of data for others to associate with this object.
   base::PropertyBag property_bag_;
 
diff --git a/content/browser/renderer_host/render_widget_host_view_win.cc b/content/browser/renderer_host/render_widget_host_view_win.cc
index daeb5383..6e749b92 100644
--- a/content/browser/renderer_host/render_widget_host_view_win.cc
+++ b/content/browser/renderer_host/render_widget_host_view_win.cc
@@ -464,7 +464,8 @@
 
 gfx::NativeViewAccessible
 RenderWidgetHostViewWin::GetNativeViewAccessible() {
-  if (render_widget_host_ && !render_widget_host_->renderer_accessible()) {
+  if (render_widget_host_ &&
+      !BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser()) {
     // Attempt to detect screen readers by sending an event with our custom id.
     NotifyWinEvent(EVENT_SYSTEM_ALERT, m_hWnd, kIdCustom, CHILDID_SELF);
   }
@@ -2289,7 +2290,8 @@
     // An MSAA client requestes our custom id. Assume that we have detected an
     // active windows screen reader.
     BrowserAccessibilityState::GetInstance()->OnScreenReaderDetected();
-    render_widget_host_->EnableRendererAccessibility();
+    if (BrowserAccessibilityState::GetInstance()->IsAccessibleBrowser())
+      render_widget_host_->SetAccessibilityMode(AccessibilityModeComplete);
 
     // Return with failure.
     return static_cast<LRESULT>(0L);
diff --git a/content/common/accessibility_messages.h b/content/common/accessibility_messages.h
index a37329e..4e6dc0d 100644
--- a/content/common/accessibility_messages.h
+++ b/content/common/accessibility_messages.h
@@ -7,6 +7,7 @@
 
 #include "base/basictypes.h"
 #include "content/common/content_export.h"
+#include "content/common/view_message_enums.h"
 #include "content/public/common/common_param_traits.h"
 #include "ipc/ipc_message_macros.h"
 #include "ipc/ipc_message_utils.h"
@@ -88,6 +89,7 @@
 
 #endif  // CONTENT_COMMON_ACCESSIBILITY_MESSAGES_H_
 
+IPC_ENUM_TRAITS(AccessibilityMode)
 IPC_ENUM_TRAITS(AccessibilityNotification)
 
 IPC_ENUM_TRAITS(webkit_glue::WebAccessibility::BoolAttribute)
@@ -133,8 +135,9 @@
 
 // Messages sent from the browser to the renderer.
 
-// Enable accessibility in the renderer process.
-IPC_MESSAGE_ROUTED0(AccessibilityMsg_Enable)
+// Change the accessibility mode in the renderer process.
+IPC_MESSAGE_ROUTED1(AccessibilityMsg_SetMode,
+                    AccessibilityMode)
 
 // Relay a request from assistive technology to set focus to a given node.
 IPC_MESSAGE_ROUTED1(AccessibilityMsg_SetFocus,
diff --git a/content/common/view_message_enums.h b/content/common/view_message_enums.h
index 29761b2..772dbca 100644
--- a/content/common/view_message_enums.h
+++ b/content/common/view_message_enums.h
@@ -5,6 +5,8 @@
 #ifndef CONTENT_COMMON_VIEW_MESSAGES_ENUMS_H_
 #define CONTENT_COMMON_VIEW_MESSAGES_ENUMS_H_
 
+#include "ipc/ipc_message_macros.h"
+
 // Values that may be OR'd together to form the 'flags' parameter of a
 // ViewHostMsg_UpdateRect_Params structure.
 struct ViewHostMsg_UpdateRect_Flags {
@@ -48,4 +50,19 @@
   };
 };
 
+enum AccessibilityMode {
+  // WebKit accessibility is off and no accessibility information is
+  // sent from the renderer to the browser process.
+  AccessibilityModeOff,
+
+  // WebKit accessibility is on, but only limited information about
+  // editable text nodes is sent to the browser process. Useful for
+  // implementing limited UIA on tablets.
+  AccessibilityModeEditableTextOnly,
+
+  // WebKit accessibility is on, and the full accessibility tree is synced
+  // to the browser process. Useful for screen readers and magnifiers.
+  AccessibilityModeComplete,
+};
+
 #endif  // CONTENT_COMMON_VIEW_MESSAGES_ENUMS_H_
diff --git a/content/common/view_messages.h b/content/common/view_messages.h
index 8d75190..246dc4a5 100644
--- a/content/common/view_messages.h
+++ b/content/common/view_messages.h
@@ -648,6 +648,9 @@
   // Indicates whether this newly created RenderView will be hosted by another
   // RenderView.
   IPC_STRUCT_MEMBER(bool, guest)
+
+  // The accessibility mode of the renderer.
+  IPC_STRUCT_MEMBER(AccessibilityMode, accessibility_mode)
 IPC_STRUCT_END()
 
 // Messages sent from the browser to the renderer.
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index eec6a6b5..c345294a 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -61,7 +61,7 @@
   // be called once before the object can be used, but can be called after
   // that with no effect. Therefore, if the caller isn't sure about whether
   // the process has been created, it should just call Init().
-  virtual bool Init(bool is_accessibility_enabled) = 0;
+  virtual bool Init() = 0;
 
   // Gets the next available routing id.
   virtual int GetNextRoutingID() = 0;
@@ -242,4 +242,3 @@
 }  // namespace content.
 
 #endif  // CONTENT_PUBLIC_BROWSER_RENDER_PROCESS_HOST_H_
-
diff --git a/content/public/browser/render_widget_host.h b/content/public/browser/render_widget_host.h
index 45db5750..a3ca330 100644
--- a/content/public/browser/render_widget_host.h
+++ b/content/public/browser/render_widget_host.h
@@ -208,9 +208,8 @@
                                                CGContextRef target) = 0;
 #endif
 
-  // Enable renderer accessibility. This should only be called when a
-  // screenreader is detected.
-  virtual void EnableRendererAccessibility() = 0;
+  // Send a command to the renderer to turn on full accessibility.
+  virtual void EnableFullAccessibilityMode() = 0;
 
   // Forwards the given message to the renderer. These are called by
   // the view when it has received a message.
diff --git a/content/public/common/content_switches.cc b/content/public/common/content_switches.cc
index 7dbe56c..5e68f10 100644
--- a/content/public/common/content_switches.cc
+++ b/content/public/common/content_switches.cc
@@ -217,9 +217,6 @@
 // Enable gpu-accelerated SVG/W3C filters.
 const char kEnableAcceleratedFilters[]      = "enable-accelerated-filters";
 
-// Enables WebKit accessibility within the renderer process.
-const char kEnableAccessibility[]           = "enable-accessibility";
-
 // Turns on extremely verbose logging of accessibility events.
 const char kEnableAccessibilityLogging[]    = "enable-accessibility-logging";
 
diff --git a/content/public/common/content_switches.h b/content/public/common/content_switches.h
index 3a0d6e46..1f28134 100644
--- a/content/public/common/content_switches.h
+++ b/content/public/common/content_switches.h
@@ -76,7 +76,6 @@
 CONTENT_EXPORT extern const char kDomAutomationController[];
 CONTENT_EXPORT extern const char kEnableAcceleratedPainting[];
 CONTENT_EXPORT extern const char kEnableAcceleratedFilters[];
-extern const char kEnableAccessibility[];
 extern const char kEnableAccessibilityLogging[];
 extern const char kEnableBrowserPlugin[];
 CONTENT_EXPORT extern const char kEnableCompositingForFixedPosition[];
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index a047050..b1aba00 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -890,7 +890,8 @@
       params.frame_name,
       params.next_page_id,
       params.screen_info,
-      params.guest);
+      params.guest,
+      params.accessibility_mode);
 }
 
 GpuChannelHost* RenderThreadImpl::EstablishGpuChannelSync(
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index a1d08061..44b0656a 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -435,7 +435,8 @@
     const string16& frame_name,
     int32 next_page_id,
     const WebKit::WebScreenInfo& screen_info,
-    bool guest)
+    bool guest,
+    AccessibilityMode accessibility_mode)
     : RenderWidget(WebKit::WebPopupTypeNone, screen_info),
       webkit_preferences_(webkit_prefs),
       send_content_state_immediately_(false),
@@ -474,6 +475,7 @@
       focused_plugin_id_(-1),
 #endif
       guest_(guest),
+      accessibility_mode_(accessibility_mode),
       ALLOW_THIS_IN_INITIALIZER_LIST(pepper_delegate_(this)) {
   routing_id_ = routing_id;
   surface_id_ = surface_id;
@@ -544,7 +546,7 @@
   // The next group of objects all implement RenderViewObserver, so are deleted
   // along with the RenderView automatically.
   devtools_agent_ = new DevToolsAgent(this);
-  renderer_accessibility_ = new RendererAccessibility(this);
+  renderer_accessibility_ = new RendererAccessibility(this, accessibility_mode);
   mouse_lock_dispatcher_ = new MouseLockDispatcher(this);
   intents_host_ = new WebIntentsHost(this);
 
@@ -634,7 +636,8 @@
     const string16& frame_name,
     int32 next_page_id,
     const WebKit::WebScreenInfo& screen_info,
-    bool guest) {
+    bool guest,
+    AccessibilityMode accessibility_mode) {
   DCHECK(routing_id != MSG_ROUTING_NONE);
   return new RenderViewImpl(
       parent_hwnd,
@@ -648,7 +651,8 @@
       frame_name,
       next_page_id,
       screen_info,
-      guest);
+      guest,
+      accessibility_mode);
 }
 
 WebPeerConnectionHandler* RenderViewImpl::CreatePeerConnectionHandler(
@@ -1512,7 +1516,8 @@
       frame_name,
       1,
       screen_info_,
-      guest_);
+      guest_,
+      accessibility_mode_);
   view->opened_by_user_gesture_ = params.user_gesture;
 
   // Record whether the creator frame is trying to suppress the opener field.
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index 432fe7ec..d6128a2 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -24,6 +24,7 @@
 #include "content/common/edit_command.h"
 #include "content/common/gpu/client/webgraphicscontext3d_command_buffer_impl.h"
 #include "content/common/navigation_gesture.h"
+#include "content/common/view_message_enums.h"
 #include "content/public/common/page_zoom.h"
 #include "content/public/common/referrer.h"
 #include "content/public/common/renderer_preferences.h"
@@ -194,7 +195,8 @@
       const string16& frame_name,
       int32 next_page_id,
       const WebKit::WebScreenInfo& screen_info,
-      bool guest);
+      bool guest,
+      AccessibilityMode accessibility_mode);
 
   // Returns the RenderViewImpl containing the given WebView.
   CONTENT_EXPORT static RenderViewImpl* FromWebView(WebKit::WebView* webview);
@@ -742,7 +744,8 @@
                  const string16& frame_name,
                  int32 next_page_id,
                  const WebKit::WebScreenInfo& screen_info,
-                 bool guest);
+                 bool guest,
+                 AccessibilityMode accessibility_mode);
 
   // Do not delete directly.  This class is reference counted.
   virtual ~RenderViewImpl();
@@ -1315,6 +1318,9 @@
   // Indicates whether this RenderView is a guest of another RenderView.
   bool guest_;
 
+  // The accessibility mode.
+  AccessibilityMode accessibility_mode_;
+
   // NOTE: pepper_delegate_ should be last member because its constructor calls
   // AddObservers method of RenderViewImpl from c-tor.
   content::PepperPluginDelegateImpl pepper_delegate_;
diff --git a/content/renderer/renderer_accessibility.cc b/content/renderer/renderer_accessibility.cc
index 7e879ef..bfb22f1f 100644
--- a/content/renderer/renderer_accessibility.cc
+++ b/content/renderer/renderer_accessibility.cc
@@ -5,7 +5,6 @@
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/message_loop.h"
-#include "content/common/accessibility_messages.h"
 #include "content/public/common/content_switches.h"
 #include "content/renderer/render_view_impl.h"
 #include "content/renderer/renderer_accessibility.h"
@@ -84,18 +83,19 @@
   return true;
 }
 
-RendererAccessibility::RendererAccessibility(RenderViewImpl* render_view)
+RendererAccessibility::RendererAccessibility(RenderViewImpl* render_view,
+                                             AccessibilityMode mode)
     : content::RenderViewObserver(render_view),
       ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
       browser_root_(NULL),
       last_scroll_offset_(gfx::Size()),
+      mode_(AccessibilityModeOff),
       ack_pending_(false),
       logging_(false) {
   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
-  if (command_line.HasSwitch(switches::kEnableAccessibility))
-    WebAccessibilityObject::enableAccessibility();
   if (command_line.HasSwitch(switches::kEnableAccessibilityLogging))
     logging_ = true;
+  OnSetMode(mode);
 }
 
 RendererAccessibility::~RendererAccessibility() {
@@ -104,7 +104,7 @@
 bool RendererAccessibility::OnMessageReceived(const IPC::Message& message) {
   bool handled = true;
   IPC_BEGIN_MESSAGE_MAP(RendererAccessibility, message)
-    IPC_MESSAGE_HANDLER(AccessibilityMsg_Enable, OnEnable)
+    IPC_MESSAGE_HANDLER(AccessibilityMsg_SetMode, OnSetMode)
     IPC_MESSAGE_HANDLER(AccessibilityMsg_SetFocus, OnSetFocus)
     IPC_MESSAGE_HANDLER(AccessibilityMsg_DoDefaultAction,
                         OnDoDefaultAction)
@@ -303,11 +303,11 @@
     }
 
     AccessibilityHostMsg_NotificationParams notification_msg;
+    BuildAccessibilityTree(obj, includes_children, &notification_msg.acc_tree);
     WebAccessibilityNotificationToAccessibilityNotification(
         notification.type, &notification_msg.notification_type);
     notification_msg.id = notification.id;
     notification_msg.includes_children = includes_children;
-    notification_msg.acc_tree = WebAccessibility(obj, includes_children);
     if (obj.axID() == root_id) {
       DCHECK_EQ(notification_msg.acc_tree.role,
                 WebAccessibility::ROLE_WEB_AREA);
@@ -486,9 +486,17 @@
   SendPendingAccessibilityNotifications();
 }
 
-void RendererAccessibility::OnEnable() {
-  if (WebAccessibilityObject::accessibilityEnabled())
+void RendererAccessibility::OnSetMode(AccessibilityMode mode) {
+  if (mode_ == mode) {
     return;
+  }
+
+  mode_ = mode;
+  if (mode_ == AccessibilityModeOff) {
+    // Note: should we have a way to turn off WebKit accessibility?
+    // Right now it's a one-way switch.
+    return;
+  }
 
   WebAccessibilityObject::enableAccessibility();
 
@@ -561,3 +569,54 @@
   else
     return WebDocument();
 }
+
+bool RendererAccessibility::IsEditableText(const WebAccessibilityObject& obj) {
+  return (obj.roleValue() == WebKit::WebAccessibilityRoleTextArea ||
+          obj.roleValue() == WebKit::WebAccessibilityRoleTextField);
+}
+
+void RendererAccessibility::RecursiveAddEditableTextNodesToTree(
+    const WebAccessibilityObject& src,
+    WebAccessibility* dst) {
+  if (IsEditableText(src)) {
+    dst->children.push_back(WebAccessibility(src, false));
+  } else {
+    int child_count = src.childCount();
+    std::set<int32> child_ids;
+    for (int i = 0; i < child_count; ++i) {
+      WebAccessibilityObject child = src.childAt(i);
+      if (!child.isValid())
+        continue;
+      RecursiveAddEditableTextNodesToTree(child, dst);
+    }
+  }
+}
+
+void RendererAccessibility::BuildAccessibilityTree(
+    const WebAccessibilityObject& src,
+    bool include_children,
+    WebAccessibility* dst) {
+  if (mode_ == AccessibilityModeComplete) {
+    dst->Init(src, include_children);
+    return;
+  }
+
+  // In Editable Text mode, we send a "collapsed" tree of only editable
+  // text nodes as direct descendants of the root.
+  CHECK_EQ(mode_, AccessibilityModeEditableTextOnly);
+  if (IsEditableText(src)) {
+    dst->Init(src, false);
+    return;
+  }
+
+  // If it's not an editable text node, it must be the root, because
+  // BuildAccessibilityTree will never be called on a non-root node
+  // that isn't already in the browser's tree.
+  CHECK_EQ(src.axID(), GetMainDocument().accessibilityObject().axID());
+
+  // Initialize the main document node, but don't add any children.
+  dst->Init(src, false);
+
+  // Find all editable text nodes and add them as children.
+  RecursiveAddEditableTextNodesToTree(src, dst);
+}
diff --git a/content/renderer/renderer_accessibility.h b/content/renderer/renderer_accessibility.h
index 835a00f..e7dd1dc 100644
--- a/content/renderer/renderer_accessibility.h
+++ b/content/renderer/renderer_accessibility.h
@@ -10,6 +10,7 @@
 
 #include "base/hash_tables.h"
 #include "base/memory/weak_ptr.h"
+#include "content/common/accessibility_messages.h"
 #include "content/public/renderer/render_view_observer.h"
 #include "third_party/WebKit/Source/WebKit/chromium/public/WebAccessibilityNotification.h"
 
@@ -32,7 +33,8 @@
 // nodes in the tree (e.g., change focus, or click on a button).
 class RendererAccessibility : public content::RenderViewObserver {
  public:
-  RendererAccessibility(RenderViewImpl* render_view);
+  RendererAccessibility(RenderViewImpl* render_view,
+                        AccessibilityMode accessibility_mode);
   virtual ~RendererAccessibility();
 
   // RenderView::Observer implementation.
@@ -85,7 +87,7 @@
   void OnChangeScrollPosition(int acc_obj_id, int scroll_x, int scroll_y);
   void OnScrollToMakeVisible(int acc_obj_id, gfx::Rect subfocus);
   void OnScrollToPoint(int acc_obj_id, gfx::Point point);
-  void OnEnable();
+  void OnSetMode(AccessibilityMode mode);
   void OnSetFocus(int acc_obj_id);
 
   void OnSetTextSelection(int acc_obj_id, int start_offset, int end_offset);
@@ -98,6 +100,23 @@
   // no view or frame.
   WebKit::WebDocument GetMainDocument();
 
+  // Checks if a WebKit accessibility object is an editable text node.
+  bool IsEditableText(const WebKit::WebAccessibilityObject& node);
+
+  // Recursively explore the tree of WebKit accessibility objects rooted
+  // at |src|, and for each editable text node encountered, add a
+  // corresponding WebAccessibility node as a child of |dst|.
+  void RecursiveAddEditableTextNodesToTree(
+      const WebKit::WebAccessibilityObject& src,
+      webkit_glue::WebAccessibility* dst);
+
+  // Build a tree of serializable WebAccessibility nodes to send to the
+  // browser process, given a WebAccessibilityObject node from WebKit.
+  // Modifies |dst| in-place, it's assumed to be empty.
+  void BuildAccessibilityTree(const WebKit::WebAccessibilityObject& src,
+                              bool include_children,
+                              webkit_glue::WebAccessibility* dst);
+
   // So we can queue up tasks to be executed later.
   base::WeakPtrFactory<RendererAccessibility> weak_factory_;
 
@@ -116,6 +135,9 @@
   // is fixed.
   gfx::Size last_scroll_offset_;
 
+  // The current accessibility mode.
+  AccessibilityMode mode_;
+
   // Set if we are waiting for an accessibility notification ack.
   bool ack_pending_;
 
diff --git a/content/test/mock_render_process_host.cc b/content/test/mock_render_process_host.cc
index 90ba216..3c1b942 100644
--- a/content/test/mock_render_process_host.cc
+++ b/content/test/mock_render_process_host.cc
@@ -42,7 +42,7 @@
 void MockRenderProcessHost::EnableSendQueue() {
 }
 
-bool MockRenderProcessHost::Init(bool is_accessibility_enabled) {
+bool MockRenderProcessHost::Init() {
   return true;
 }
 
diff --git a/content/test/mock_render_process_host.h b/content/test/mock_render_process_host.h
index 4726f66..7548b6d 100644
--- a/content/test/mock_render_process_host.h
+++ b/content/test/mock_render_process_host.h
@@ -34,7 +34,7 @@
 
   // RenderProcessHost implementation (public portion).
   virtual void EnableSendQueue() OVERRIDE;
-  virtual bool Init(bool is_accessibility_enabled) OVERRIDE;
+  virtual bool Init() OVERRIDE;
   virtual int GetNextRoutingID() OVERRIDE;
   virtual void CancelResourceRequests(int render_widget_id) OVERRIDE;
   virtual void CrossSiteSwapOutACK(
diff --git a/content/test/render_view_test.cc b/content/test/render_view_test.cc
index a64f8aac..25e5488 100644
--- a/content/test/render_view_test.cc
+++ b/content/test/render_view_test.cc
@@ -166,7 +166,8 @@
       string16(),
       1,
       WebKit::WebScreenInfo(),
-      false);
+      false,
+      AccessibilityModeOff);
   view->AddRef();
   view_ = view;