summaryrefslogtreecommitdiffstats
path: root/chrome/browser/browser_accessibility_manager.cc
blob: 7a7c3b3b0d575430d710d88dfe46406d04520530 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
// Copyright (c) 2006-2008 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 "chrome/browser/browser_accessibility_manager.h"

#include "chrome/browser/browser_accessibility.h"
#include "chrome/browser/renderer_host/render_process_host.h"
#include "chrome/browser/renderer_host/render_view_host.h"
#include "chrome/common/notification_service.h"
#include "chrome/common/render_messages.h"

using webkit_glue::WebAccessibility;

// The time in ms after which we give up and return an error when processing an
// accessibility message and no response has been received from the renderer.
static const int kAccessibilityMessageTimeOut = 10000;

// static
BrowserAccessibilityManager* BrowserAccessibilityManager::GetInstance() {
  return Singleton<BrowserAccessibilityManager>::get();
}

BrowserAccessibilityManager::BrowserAccessibilityManager() {
  registrar_.Add(this, NotificationType::RENDERER_PROCESS_TERMINATED,
                 NotificationService::AllSources());
}

BrowserAccessibilityManager::~BrowserAccessibilityManager() {
  // Clear hashmap.
  render_process_host_map_.clear();
}

STDMETHODIMP BrowserAccessibilityManager::CreateAccessibilityInstance(
    REFIID iid, int acc_obj_id, int routing_id, int process_id,
    HWND parent_hwnd, void** interface_ptr) {
  if (IID_IUnknown == iid || IID_IDispatch == iid || IID_IAccessible == iid) {
    CComObject<BrowserAccessibility>* instance = NULL;

    HRESULT hr = CComObject<BrowserAccessibility>::CreateInstance(&instance);
    DCHECK(SUCCEEDED(hr));

    if (!instance)
      return E_FAIL;

    CComPtr<IAccessible> accessibility_instance(instance);

    // Set class member variables.
    instance->Initialize(acc_obj_id, routing_id, process_id, parent_hwnd);

    // Retrieve the RenderViewHost connected to this request.
    RenderViewHost* rvh = RenderViewHost::FromID(process_id, routing_id);

    // Update cache with RenderProcessHost/BrowserAccessibility pair.
    if (rvh && rvh->process()) {
      render_process_host_map_.insert(
          MapEntry(rvh->process()->pid(), instance));
    } else {
      // No RenderProcess active for this instance.
      return E_FAIL;
    }

    // All is well, assign the temp instance to the output pointer.
    *interface_ptr = accessibility_instance.Detach();
    return S_OK;
  }
  // No supported interface found, return error.
  *interface_ptr = NULL;
  return E_NOINTERFACE;
}

bool BrowserAccessibilityManager::RequestAccessibilityInfo(
    WebAccessibility::InParams* in, int routing_id, int process_id) {
  // Create and populate IPC message structure, for retrieval of accessibility
  // information from the renderer.
  WebAccessibility::InParams in_params;
  in_params.object_id = in->object_id;
  in_params.function_id = in->function_id;
  in_params.child_id = in->child_id;
  in_params.input_long1 = in->input_long1;
  in_params.input_long2 = in->input_long2;

  // Retrieve the RenderViewHost connected to this request.
  RenderViewHost* rvh = RenderViewHost::FromID(process_id, routing_id);

  // Send accessibility information retrieval message to the renderer.
  bool success = false;
  if (rvh && rvh->process() && rvh->process()->HasConnection()) {
    IPC::SyncMessage* msg =
        new ViewMsg_GetAccessibilityInfo(routing_id, in_params, &out_params_);
    // Necessary for the send to keep the UI responsive.
    msg->EnableMessagePumping();
    success = rvh->process()->SendWithTimeout(msg,
        kAccessibilityMessageTimeOut);
  }
  return success;
}

bool BrowserAccessibilityManager::ChangeAccessibilityFocus(int acc_obj_id,
                                                           int process_id,
                                                           int routing_id) {
  BrowserAccessibility* browser_acc =
      GetBrowserAccessibility(process_id, routing_id);
  if (browser_acc) {
    // Notify Access Technology that there was a change in keyboard focus.
    ::NotifyWinEvent(EVENT_OBJECT_FOCUS, browser_acc->parent_hwnd(),
                     OBJID_CLIENT, static_cast<LONG>(acc_obj_id));
    return true;
  }
  return false;
}

const WebAccessibility::OutParams& BrowserAccessibilityManager::response() {
  return out_params_;
}

BrowserAccessibility* BrowserAccessibilityManager::GetBrowserAccessibility(
    int process_id, int routing_id) {
  // Retrieve the BrowserAccessibility connected to the requester's id. There
  // could be multiple BrowserAccessibility connected to the given |process_id|,
  // but they all have the same parent HWND, so using the first hit is fine.
  RenderProcessHostMap::iterator it =
      render_process_host_map_.lower_bound(process_id);

  RenderProcessHostMap::iterator end_of_matching_objects =
    render_process_host_map_.upper_bound(process_id);

  for (; it != end_of_matching_objects; ++it) {
    if (it->second && it->second->routing_id() == routing_id)
      return it->second;
  }
  return NULL;
}

void BrowserAccessibilityManager::Observe(NotificationType type,
                                          const NotificationSource& source,
                                          const NotificationDetails& details) {
  DCHECK(type == NotificationType::RENDERER_PROCESS_TERMINATED);
  RenderProcessHost* rph = Source<RenderProcessHost>(source).ptr();
  DCHECK(rph);

  RenderProcessHostMap::iterator it =
      render_process_host_map_.lower_bound(rph->pid());

  RenderProcessHostMap::iterator end_of_matching_objects =
    render_process_host_map_.upper_bound(rph->pid());

  for (; it != end_of_matching_objects; ++it) {
    if (it->second) {
      // Set all matching BrowserAccessibility instances to inactive state.
      // TODO(klink): Do more active memory cleanup as well.
      it->second->set_instance_active(false);
    }
  }
}