Home | History | Annotate | Download | only in accessibility
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/strings/utf_string_conversions.h"
      6 #include "base/time/time.h"
      7 #include "content/common/frame_messages.h"
      8 #include "content/common/view_message_enums.h"
      9 #include "content/public/test/render_view_test.h"
     10 #include "content/renderer/accessibility/renderer_accessibility_complete.h"
     11 #include "content/renderer/render_frame_impl.h"
     12 #include "content/renderer/render_view_impl.h"
     13 #include "testing/gtest/include/gtest/gtest.h"
     14 #include "third_party/WebKit/public/platform/WebSize.h"
     15 #include "third_party/WebKit/public/web/WebAXObject.h"
     16 #include "third_party/WebKit/public/web/WebDocument.h"
     17 #include "third_party/WebKit/public/web/WebView.h"
     18 #include "ui/accessibility/ax_node_data.h"
     19 
     20 using blink::WebAXObject;
     21 using blink::WebDocument;
     22 
     23 namespace content {
     24 
     25 class TestRendererAccessibilityComplete : public RendererAccessibilityComplete {
     26  public:
     27   explicit TestRendererAccessibilityComplete(RenderFrameImpl* render_frame)
     28     : RendererAccessibilityComplete(render_frame) {
     29   }
     30 
     31   void SendPendingAccessibilityEvents() {
     32     RendererAccessibilityComplete::SendPendingAccessibilityEvents();
     33   }
     34 };
     35 
     36 class RendererAccessibilityTest : public RenderViewTest {
     37  public:
     38   RendererAccessibilityTest() {}
     39 
     40   RenderViewImpl* view() {
     41     return static_cast<RenderViewImpl*>(view_);
     42   }
     43 
     44   RenderFrameImpl* frame() {
     45     return static_cast<RenderFrameImpl*>(view()->GetMainRenderFrame());
     46   }
     47 
     48   virtual void SetUp() {
     49     RenderViewTest::SetUp();
     50     sink_ = &render_thread_->sink();
     51   }
     52 
     53   void SetMode(AccessibilityMode mode) {
     54     frame()->OnSetAccessibilityMode(mode);
     55   }
     56 
     57   void GetLastAccEvent(
     58       AccessibilityHostMsg_EventParams* params) {
     59     const IPC::Message* message =
     60         sink_->GetUniqueMessageMatching(AccessibilityHostMsg_Events::ID);
     61     ASSERT_TRUE(message);
     62     Tuple2<std::vector<AccessibilityHostMsg_EventParams>, int> param;
     63     AccessibilityHostMsg_Events::Read(message, &param);
     64     ASSERT_GE(param.a.size(), 1U);
     65     *params = param.a[0];
     66   }
     67 
     68   int CountAccessibilityNodesSentToBrowser() {
     69     AccessibilityHostMsg_EventParams event;
     70     GetLastAccEvent(&event);
     71     return event.update.nodes.size();
     72   }
     73 
     74  protected:
     75   IPC::TestSink* sink_;
     76 
     77   DISALLOW_COPY_AND_ASSIGN(RendererAccessibilityTest);
     78 
     79 };
     80 
     81 TEST_F(RendererAccessibilityTest, EditableTextModeFocusEvents) {
     82   // This is not a test of true web accessibility, it's a test of
     83   // a mode used on Windows 8 in Metro mode where an extremely simplified
     84   // accessibility tree containing only the current focused node is
     85   // generated.
     86   SetMode(AccessibilityModeEditableTextOnly);
     87 
     88   // Set a minimum size and give focus so simulated events work.
     89   view()->webwidget()->resize(blink::WebSize(500, 500));
     90   view()->webwidget()->setFocus(true);
     91 
     92   std::string html =
     93       "<body>"
     94       "  <input>"
     95       "  <textarea></textarea>"
     96       "  <p contentEditable>Editable</p>"
     97       "  <div tabindex=0 role=textbox>Textbox</div>"
     98       "  <button>Button</button>"
     99       "  <a href=#>Link</a>"
    100       "</body>";
    101 
    102   // Load the test page.
    103   LoadHTML(html.c_str());
    104 
    105   // We should have sent a message to the browser with the initial focus
    106   // on the document.
    107   {
    108     SCOPED_TRACE("Initial focus on document");
    109     AccessibilityHostMsg_EventParams event;
    110     GetLastAccEvent(&event);
    111     EXPECT_EQ(event.event_type,
    112               ui::AX_EVENT_LAYOUT_COMPLETE);
    113     EXPECT_EQ(event.id, 1);
    114     EXPECT_EQ(event.update.nodes.size(), 2U);
    115     EXPECT_EQ(event.update.nodes[0].id, 1);
    116     EXPECT_EQ(event.update.nodes[0].role,
    117               ui::AX_ROLE_ROOT_WEB_AREA);
    118     EXPECT_EQ(event.update.nodes[0].state,
    119               (1U << ui::AX_STATE_READ_ONLY) |
    120               (1U << ui::AX_STATE_FOCUSABLE) |
    121               (1U << ui::AX_STATE_FOCUSED));
    122     EXPECT_EQ(event.update.nodes[0].child_ids.size(), 1U);
    123   }
    124 
    125   // Now focus the input element, and check everything again.
    126   {
    127     SCOPED_TRACE("input");
    128     sink_->ClearMessages();
    129     ExecuteJavaScript("document.querySelector('input').focus();");
    130     AccessibilityHostMsg_EventParams event;
    131     GetLastAccEvent(&event);
    132     EXPECT_EQ(event.event_type,
    133               ui::AX_EVENT_FOCUS);
    134     EXPECT_EQ(event.id, 3);
    135     EXPECT_EQ(event.update.nodes[0].id, 1);
    136     EXPECT_EQ(event.update.nodes[0].role,
    137               ui::AX_ROLE_ROOT_WEB_AREA);
    138     EXPECT_EQ(event.update.nodes[0].state,
    139               (1U << ui::AX_STATE_READ_ONLY) |
    140               (1U << ui::AX_STATE_FOCUSABLE));
    141     EXPECT_EQ(event.update.nodes[0].child_ids.size(), 1U);
    142     EXPECT_EQ(event.update.nodes[1].id, 3);
    143     EXPECT_EQ(event.update.nodes[1].role,
    144               ui::AX_ROLE_GROUP);
    145     EXPECT_EQ(event.update.nodes[1].state,
    146               (1U << ui::AX_STATE_FOCUSABLE) |
    147               (1U << ui::AX_STATE_FOCUSED));
    148   }
    149 
    150   // Check other editable text nodes.
    151   {
    152     SCOPED_TRACE("textarea");
    153     sink_->ClearMessages();
    154     ExecuteJavaScript("document.querySelector('textarea').focus();");
    155     AccessibilityHostMsg_EventParams event;
    156     GetLastAccEvent(&event);
    157     EXPECT_EQ(event.id, 4);
    158     EXPECT_EQ(event.update.nodes[1].state,
    159               (1U << ui::AX_STATE_FOCUSABLE) |
    160               (1U << ui::AX_STATE_FOCUSED));
    161   }
    162 
    163   {
    164     SCOPED_TRACE("contentEditable");
    165     sink_->ClearMessages();
    166     ExecuteJavaScript("document.querySelector('p').focus();");
    167     AccessibilityHostMsg_EventParams event;
    168     GetLastAccEvent(&event);
    169     EXPECT_EQ(event.id, 5);
    170     EXPECT_EQ(event.update.nodes[1].state,
    171               (1U << ui::AX_STATE_FOCUSABLE) |
    172               (1U << ui::AX_STATE_FOCUSED));
    173   }
    174 
    175   {
    176     SCOPED_TRACE("role=textarea");
    177     sink_->ClearMessages();
    178     ExecuteJavaScript("document.querySelector('div').focus();");
    179     AccessibilityHostMsg_EventParams event;
    180     GetLastAccEvent(&event);
    181     EXPECT_EQ(event.id, 6);
    182     EXPECT_EQ(event.update.nodes[1].state,
    183               (1U << ui::AX_STATE_FOCUSABLE) |
    184               (1U << ui::AX_STATE_FOCUSED));
    185   }
    186 
    187   // Try focusing things that aren't editable text.
    188   {
    189     SCOPED_TRACE("button");
    190     sink_->ClearMessages();
    191     ExecuteJavaScript("document.querySelector('button').focus();");
    192     AccessibilityHostMsg_EventParams event;
    193     GetLastAccEvent(&event);
    194     EXPECT_EQ(event.id, 7);
    195     EXPECT_EQ(event.update.nodes[1].state,
    196               (1U << ui::AX_STATE_FOCUSABLE) |
    197               (1U << ui::AX_STATE_FOCUSED) |
    198               (1U << ui::AX_STATE_READ_ONLY));
    199   }
    200 
    201   {
    202     SCOPED_TRACE("link");
    203     sink_->ClearMessages();
    204     ExecuteJavaScript("document.querySelector('a').focus();");
    205     AccessibilityHostMsg_EventParams event;
    206     GetLastAccEvent(&event);
    207     EXPECT_EQ(event.id, 8);
    208     EXPECT_EQ(event.update.nodes[1].state,
    209               (1U << ui::AX_STATE_FOCUSABLE) |
    210               (1U << ui::AX_STATE_FOCUSED) |
    211               (1U << ui::AX_STATE_READ_ONLY));
    212   }
    213 
    214   // Clear focus.
    215   {
    216     SCOPED_TRACE("Back to document.");
    217     sink_->ClearMessages();
    218     ExecuteJavaScript("document.activeElement.blur()");
    219     AccessibilityHostMsg_EventParams event;
    220     GetLastAccEvent(&event);
    221     EXPECT_EQ(event.id, 1);
    222   }
    223 }
    224 
    225 TEST_F(RendererAccessibilityTest, SendFullAccessibilityTreeOnReload) {
    226   // The job of RendererAccessibilityComplete is to serialize the
    227   // accessibility tree built by WebKit and send it to the browser.
    228   // When the accessibility tree changes, it tries to send only
    229   // the nodes that actually changed or were reparented. This test
    230   // ensures that the messages sent are correct in cases when a page
    231   // reloads, and that internal state is properly garbage-collected.
    232   std::string html =
    233       "<body>"
    234       "  <div role='group' id='A'>"
    235       "    <div role='group' id='A1'></div>"
    236       "    <div role='group' id='A2'></div>"
    237       "  </div>"
    238       "</body>";
    239   LoadHTML(html.c_str());
    240 
    241   // Creating a RendererAccessibilityComplete should sent the tree
    242   // to the browser.
    243   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    244       new TestRendererAccessibilityComplete(frame()));
    245   accessibility->SendPendingAccessibilityEvents();
    246   EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
    247 
    248   // If we post another event but the tree doesn't change,
    249   // we should only send 1 node to the browser.
    250   sink_->ClearMessages();
    251   WebDocument document = view()->GetWebView()->mainFrame()->document();
    252   WebAXObject root_obj = document.accessibilityObject();
    253   accessibility->HandleAXEvent(
    254       root_obj,
    255       ui::AX_EVENT_LAYOUT_COMPLETE);
    256   accessibility->SendPendingAccessibilityEvents();
    257   EXPECT_EQ(1, CountAccessibilityNodesSentToBrowser());
    258   {
    259     // Make sure it's the root object that was updated.
    260     AccessibilityHostMsg_EventParams event;
    261     GetLastAccEvent(&event);
    262     EXPECT_EQ(root_obj.axID(), event.update.nodes[0].id);
    263   }
    264 
    265   // If we reload the page and send a event, we should send
    266   // all 4 nodes to the browser. Also double-check that we didn't
    267   // leak any of the old BrowserTreeNodes.
    268   LoadHTML(html.c_str());
    269   document = view()->GetWebView()->mainFrame()->document();
    270   root_obj = document.accessibilityObject();
    271   sink_->ClearMessages();
    272   accessibility->HandleAXEvent(
    273       root_obj,
    274       ui::AX_EVENT_LAYOUT_COMPLETE);
    275   accessibility->SendPendingAccessibilityEvents();
    276   EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
    277 
    278   // Even if the first event is sent on an element other than
    279   // the root, the whole tree should be updated because we know
    280   // the browser doesn't have the root element.
    281   LoadHTML(html.c_str());
    282   document = view()->GetWebView()->mainFrame()->document();
    283   root_obj = document.accessibilityObject();
    284   sink_->ClearMessages();
    285   const WebAXObject& first_child = root_obj.childAt(0);
    286   accessibility->HandleAXEvent(
    287       first_child,
    288       ui::AX_EVENT_LIVE_REGION_CHANGED);
    289   accessibility->SendPendingAccessibilityEvents();
    290   EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
    291 }
    292 
    293 // http://crbug.com/253537
    294 #if defined(OS_ANDROID)
    295 #define MAYBE_AccessibilityMessagesQueueWhileSwappedOut \
    296         DISABLED_AccessibilityMessagesQueueWhileSwappedOut
    297 #else
    298 #define MAYBE_AccessibilityMessagesQueueWhileSwappedOut \
    299         AccessibilityMessagesQueueWhileSwappedOut
    300 #endif
    301 
    302 TEST_F(RendererAccessibilityTest,
    303        MAYBE_AccessibilityMessagesQueueWhileSwappedOut) {
    304   std::string html =
    305       "<body>"
    306       "  <p>Hello, world.</p>"
    307       "</body>";
    308   LoadHTML(html.c_str());
    309   static const int kProxyRoutingId = 13;
    310 
    311   // Creating a RendererAccessibilityComplete should send the tree
    312   // to the browser.
    313   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    314       new TestRendererAccessibilityComplete(frame()));
    315   accessibility->SendPendingAccessibilityEvents();
    316   EXPECT_EQ(5, CountAccessibilityNodesSentToBrowser());
    317 
    318   // Post a "value changed" event, but then swap out
    319   // before sending it. It shouldn't send the event while
    320   // swapped out.
    321   sink_->ClearMessages();
    322   WebDocument document = view()->GetWebView()->mainFrame()->document();
    323   WebAXObject root_obj = document.accessibilityObject();
    324   accessibility->HandleAXEvent(
    325       root_obj,
    326       ui::AX_EVENT_VALUE_CHANGED);
    327   view()->GetMainRenderFrame()->OnSwapOut(kProxyRoutingId);
    328   accessibility->SendPendingAccessibilityEvents();
    329   EXPECT_FALSE(sink_->GetUniqueMessageMatching(
    330       AccessibilityHostMsg_Events::ID));
    331 
    332   // Navigate, so we're not swapped out anymore. Now we should
    333   // send accessibility events again. Note that the
    334   // message that was queued up before will be quickly discarded
    335   // because the element it was referring to no longer exists,
    336   // so the event here is from loading this new page.
    337   FrameMsg_Navigate_Params nav_params;
    338   nav_params.url = GURL("data:text/html,<p>Hello, again.</p>");
    339   nav_params.navigation_type = FrameMsg_Navigate_Type::NORMAL;
    340   nav_params.transition = ui::PAGE_TRANSITION_TYPED;
    341   nav_params.current_history_list_length = 1;
    342   nav_params.current_history_list_offset = 0;
    343   nav_params.pending_history_list_offset = 1;
    344   nav_params.page_id = -1;
    345   nav_params.browser_navigation_start = base::TimeTicks::FromInternalValue(1);
    346   frame()->OnNavigate(nav_params);
    347   accessibility->SendPendingAccessibilityEvents();
    348   EXPECT_TRUE(sink_->GetUniqueMessageMatching(
    349       AccessibilityHostMsg_Events::ID));
    350 }
    351 
    352 TEST_F(RendererAccessibilityTest, HideAccessibilityObject) {
    353   // Test RendererAccessibilityComplete and make sure it sends the
    354   // proper event to the browser when an object in the tree
    355   // is hidden, but its children are not.
    356   std::string html =
    357       "<body>"
    358       "  <div role='group' id='A'>"
    359       "    <div role='group' id='B'>"
    360       "      <div role='group' id='C' style='visibility:visible'>"
    361       "      </div>"
    362       "    </div>"
    363       "  </div>"
    364       "</body>";
    365   LoadHTML(html.c_str());
    366 
    367   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    368       new TestRendererAccessibilityComplete(frame()));
    369   accessibility->SendPendingAccessibilityEvents();
    370   EXPECT_EQ(4, CountAccessibilityNodesSentToBrowser());
    371 
    372   WebDocument document = view()->GetWebView()->mainFrame()->document();
    373   WebAXObject root_obj = document.accessibilityObject();
    374   WebAXObject node_a = root_obj.childAt(0);
    375   WebAXObject node_b = node_a.childAt(0);
    376   WebAXObject node_c = node_b.childAt(0);
    377 
    378   // Hide node 'B' ('C' stays visible).
    379   ExecuteJavaScript(
    380       "document.getElementById('B').style.visibility = 'hidden';");
    381   // Force layout now.
    382   ExecuteJavaScript("document.getElementById('B').offsetLeft;");
    383 
    384   // Send a childrenChanged on 'A'.
    385   sink_->ClearMessages();
    386   accessibility->HandleAXEvent(
    387       node_a,
    388       ui::AX_EVENT_CHILDREN_CHANGED);
    389 
    390   accessibility->SendPendingAccessibilityEvents();
    391   AccessibilityHostMsg_EventParams event;
    392   GetLastAccEvent(&event);
    393   ASSERT_EQ(2U, event.update.nodes.size());
    394 
    395   // RendererAccessibilityComplete notices that 'C' is being reparented,
    396   // so it clears the subtree rooted at 'A', then updates 'A' and then 'C'.
    397   EXPECT_EQ(node_a.axID(), event.update.node_id_to_clear);
    398   EXPECT_EQ(node_a.axID(), event.update.nodes[0].id);
    399   EXPECT_EQ(node_c.axID(), event.update.nodes[1].id);
    400   EXPECT_EQ(2, CountAccessibilityNodesSentToBrowser());
    401 }
    402 
    403 TEST_F(RendererAccessibilityTest, ShowAccessibilityObject) {
    404   // Test RendererAccessibilityComplete and make sure it sends the
    405   // proper event to the browser when an object in the tree
    406   // is shown, causing its own already-visible children to be
    407   // reparented to it.
    408   std::string html =
    409       "<body>"
    410       "  <div role='group' id='A'>"
    411       "    <div role='group' id='B' style='visibility:hidden'>"
    412       "      <div role='group' id='C' style='visibility:visible'>"
    413       "      </div>"
    414       "    </div>"
    415       "  </div>"
    416       "</body>";
    417   LoadHTML(html.c_str());
    418 
    419   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    420       new TestRendererAccessibilityComplete(frame()));
    421   accessibility->SendPendingAccessibilityEvents();
    422   EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
    423 
    424   // Show node 'B', then send a childrenChanged on 'A'.
    425   ExecuteJavaScript(
    426       "document.getElementById('B').style.visibility = 'visible';");
    427   ExecuteJavaScript("document.getElementById('B').offsetLeft;");
    428 
    429   sink_->ClearMessages();
    430   WebDocument document = view()->GetWebView()->mainFrame()->document();
    431   WebAXObject root_obj = document.accessibilityObject();
    432   WebAXObject node_a = root_obj.childAt(0);
    433   WebAXObject node_b = node_a.childAt(0);
    434   WebAXObject node_c = node_b.childAt(0);
    435 
    436   accessibility->HandleAXEvent(
    437       node_a,
    438       ui::AX_EVENT_CHILDREN_CHANGED);
    439 
    440   accessibility->SendPendingAccessibilityEvents();
    441   AccessibilityHostMsg_EventParams event;
    442   GetLastAccEvent(&event);
    443 
    444   ASSERT_EQ(3U, event.update.nodes.size());
    445   EXPECT_EQ(node_a.axID(), event.update.node_id_to_clear);
    446   EXPECT_EQ(node_a.axID(), event.update.nodes[0].id);
    447   EXPECT_EQ(node_b.axID(), event.update.nodes[1].id);
    448   EXPECT_EQ(node_c.axID(), event.update.nodes[2].id);
    449   EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
    450 }
    451 
    452 TEST_F(RendererAccessibilityTest, DetachAccessibilityObject) {
    453   // Test RendererAccessibilityComplete and make sure it sends the
    454   // proper event to the browser when an object in the tree
    455   // is detached, but its children are not. This can happen when
    456   // a layout occurs and an anonymous render block is no longer needed.
    457   std::string html =
    458       "<body aria-label='Body'>"
    459       "<span>1</span><span style='display:block'>2</span>"
    460       "</body>";
    461   LoadHTML(html.c_str());
    462 
    463   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    464       new TestRendererAccessibilityComplete(frame()));
    465   accessibility->SendPendingAccessibilityEvents();
    466   EXPECT_EQ(7, CountAccessibilityNodesSentToBrowser());
    467 
    468   // Initially, the accessibility tree looks like this:
    469   //
    470   //   Document
    471   //   +--Body
    472   //      +--Anonymous Block
    473   //         +--Static Text "1"
    474   //            +--Inline Text Box "1"
    475   //      +--Static Text "2"
    476   //         +--Inline Text Box "2"
    477   WebDocument document = view()->GetWebView()->mainFrame()->document();
    478   WebAXObject root_obj = document.accessibilityObject();
    479   WebAXObject body = root_obj.childAt(0);
    480   WebAXObject anonymous_block = body.childAt(0);
    481   WebAXObject text_1 = anonymous_block.childAt(0);
    482   WebAXObject text_2 = body.childAt(1);
    483 
    484   // Change the display of the second 'span' back to inline, which causes the
    485   // anonymous block to be destroyed.
    486   ExecuteJavaScript(
    487       "document.querySelectorAll('span')[1].style.display = 'inline';");
    488   // Force layout now.
    489   ExecuteJavaScript("document.body.offsetLeft;");
    490 
    491   // Send a childrenChanged on the body.
    492   sink_->ClearMessages();
    493   accessibility->HandleAXEvent(
    494       body,
    495       ui::AX_EVENT_CHILDREN_CHANGED);
    496 
    497   accessibility->SendPendingAccessibilityEvents();
    498 
    499   // Afterwards, the accessibility tree looks like this:
    500   //
    501   //   Document
    502   //   +--Body
    503   //      +--Static Text "1"
    504   //         +--Inline Text Box "1"
    505   //      +--Static Text "2"
    506   //         +--Inline Text Box "2"
    507   //
    508   // We just assert that there are now four nodes in the
    509   // accessibility tree and that only three nodes needed
    510   // to be updated (the body, the static text 1, and
    511   // the static text 2).
    512 
    513   AccessibilityHostMsg_EventParams event;
    514   GetLastAccEvent(&event);
    515   ASSERT_EQ(5U, event.update.nodes.size());
    516 
    517   EXPECT_EQ(body.axID(), event.update.nodes[0].id);
    518   EXPECT_EQ(text_1.axID(), event.update.nodes[1].id);
    519   // The third event is to update text_2, but its id changes
    520   // so we don't have a test expectation for it.
    521 }
    522 
    523 TEST_F(RendererAccessibilityTest, EventOnObjectNotInTree) {
    524   // Test RendererAccessibilityComplete and make sure it doesn't send anything
    525   // if we get a notification from Blink for an object that isn't in the
    526   // tree, like the scroll area that's the parent of the main document,
    527   // which we don't expose.
    528   std::string html = "<body><input></body>";
    529   LoadHTML(html.c_str());
    530 
    531   scoped_ptr<TestRendererAccessibilityComplete> accessibility(
    532       new TestRendererAccessibilityComplete(frame()));
    533   accessibility->SendPendingAccessibilityEvents();
    534   EXPECT_EQ(3, CountAccessibilityNodesSentToBrowser());
    535 
    536   WebDocument document = view()->GetWebView()->mainFrame()->document();
    537   WebAXObject root_obj = document.accessibilityObject();
    538   WebAXObject scroll_area = root_obj.parentObject();
    539   EXPECT_EQ(blink::WebAXRoleScrollArea, scroll_area.role());
    540 
    541   // Try to fire a message on the scroll area, and assert that we just
    542   // ignore it.
    543   sink_->ClearMessages();
    544   accessibility->HandleAXEvent(scroll_area,
    545                                ui::AX_EVENT_VALUE_CHANGED);
    546 
    547   accessibility->SendPendingAccessibilityEvents();
    548 
    549   const IPC::Message* message =
    550       sink_->GetUniqueMessageMatching(AccessibilityHostMsg_Events::ID);
    551   ASSERT_TRUE(message);
    552   Tuple2<std::vector<AccessibilityHostMsg_EventParams>, int> param;
    553   AccessibilityHostMsg_Events::Read(message, &param);
    554   ASSERT_EQ(0U, param.a.size());
    555 }
    556 
    557 }  // namespace content
    558