1// Copyright (c) 2013 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 "content/browser/accessibility/accessibility_ui.h" 6 7#include "base/bind.h" 8#include "base/bind_helpers.h" 9#include "base/json/json_writer.h" 10#include "base/strings/string_number_conversions.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/values.h" 13#include "content/browser/accessibility/accessibility_tree_formatter.h" 14#include "content/browser/accessibility/browser_accessibility_manager.h" 15#include "content/browser/accessibility/browser_accessibility_state_impl.h" 16#include "content/browser/renderer_host/render_widget_host_impl.h" 17#include "content/common/view_message_enums.h" 18#include "content/port/browser/render_widget_host_view_port.h" 19#include "content/public/browser/browser_thread.h" 20#include "content/public/browser/favicon_status.h" 21#include "content/public/browser/navigation_entry.h" 22#include "content/public/browser/render_process_host.h" 23#include "content/public/browser/render_view_host.h" 24#include "content/public/browser/render_widget_host.h" 25#include "content/public/browser/render_widget_host_iterator.h" 26#include "content/public/browser/web_contents.h" 27#include "content/public/browser/web_ui_data_source.h" 28#include "content/public/common/url_constants.h" 29#include "grit/content_resources.h" 30#include "net/base/escape.h" 31 32static const char kDataFile[] = "targets-data.json"; 33 34static const char kProcessIdField[] = "processId"; 35static const char kRouteIdField[] = "routeId"; 36static const char kUrlField[] = "url"; 37static const char kNameField[] = "name"; 38static const char kFaviconUrlField[] = "favicon_url"; 39static const char kPidField[] = "pid"; 40static const char kAccessibilityModeField[] = "a11y_mode"; 41 42namespace content { 43 44namespace { 45 46base::DictionaryValue* BuildTargetDescriptor( 47 const GURL& url, 48 const std::string& name, 49 const GURL& favicon_url, 50 int process_id, 51 int route_id, 52 AccessibilityMode accessibility_mode, 53 base::ProcessHandle handle = base::kNullProcessHandle) { 54 base::DictionaryValue* target_data = new base::DictionaryValue(); 55 target_data->SetInteger(kProcessIdField, process_id); 56 target_data->SetInteger(kRouteIdField, route_id); 57 target_data->SetString(kUrlField, url.spec()); 58 target_data->SetString(kNameField, net::EscapeForHTML(name)); 59 target_data->SetInteger(kPidField, base::GetProcId(handle)); 60 target_data->SetString(kFaviconUrlField, favicon_url.spec()); 61 target_data->SetInteger(kAccessibilityModeField, 62 accessibility_mode); 63 return target_data; 64} 65 66base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) { 67 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 68 std::string title; 69 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh); 70 AccessibilityMode accessibility_mode = rwhi->accessibility_mode(); 71 72 GURL url; 73 GURL favicon_url; 74 if (web_contents) { 75 // TODO(nasko): Fix the following code to use a consistent set of data 76 // across the URL, title, and favicon. 77 url = web_contents->GetURL(); 78 title = UTF16ToUTF8(web_contents->GetTitle()); 79 NavigationController& controller = web_contents->GetController(); 80 NavigationEntry* entry = controller.GetVisibleEntry(); 81 if (entry != NULL && entry->GetURL().is_valid()) 82 favicon_url = entry->GetFavicon().url; 83 } 84 85 return BuildTargetDescriptor(url, 86 title, 87 favicon_url, 88 rvh->GetProcess()->GetID(), 89 rvh->GetRoutingID(), 90 accessibility_mode); 91} 92 93void SendTargetsData( 94 const WebUIDataSource::GotDataCallback& callback) { 95 scoped_ptr<base::ListValue> rvh_list(new base::ListValue()); 96 97 scoped_ptr<RenderWidgetHostIterator> widgets( 98 RenderWidgetHost::GetRenderWidgetHosts()); 99 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 100 // Ignore processes that don't have a connection, such as crashed tabs. 101 if (!widget->GetProcess()->HasConnection()) 102 continue; 103 if (!widget->IsRenderView()) 104 continue; 105 106 RenderViewHost* rvh = RenderViewHost::From(widget); 107 rvh_list->Append(BuildTargetDescriptor(rvh)); 108 } 109 110 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 111 data->Set("list", rvh_list.release()); 112 scoped_ptr<base::FundamentalValue> a11y_mode(new base::FundamentalValue( 113 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode())); 114 data->Set("global_a11y_mode", a11y_mode.release()); 115 116 std::string json_string; 117 base::JSONWriter::Write(data.get(), &json_string); 118 119 callback.Run(base::RefCountedString::TakeString(&json_string)); 120} 121 122bool HandleRequestCallback( 123 const std::string& path, 124 const WebUIDataSource::GotDataCallback& callback) { 125 if (path != kDataFile) 126 return false; 127 128 SendTargetsData(callback); 129 return true; 130} 131 132} // namespace 133 134AccessibilityUI::AccessibilityUI(WebUI* web_ui) 135 : WebUIController(web_ui) { 136 // Set up the chrome://accessibility source. 137 WebUIDataSource* html_source = 138 WebUIDataSource::Create(kChromeUIAccessibilityHost); 139 html_source->SetUseJsonJSFormatV2(); 140 141 web_ui->RegisterMessageCallback( 142 "toggleAccessibility", 143 base::Bind(&AccessibilityUI::ToggleAccessibility, 144 base::Unretained(this))); 145 web_ui->RegisterMessageCallback( 146 "toggleGlobalAccessibility", 147 base::Bind(&AccessibilityUI::ToggleGlobalAccessibility, 148 base::Unretained(this))); 149 web_ui->RegisterMessageCallback( 150 "requestAccessibilityTree", 151 base::Bind(&AccessibilityUI::RequestAccessibilityTree, 152 base::Unretained(this))); 153 154 // Add required resources. 155 html_source->SetJsonPath("strings.js"); 156 html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS); 157 html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS); 158 html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML); 159 html_source->SetRequestFilter(base::Bind(&HandleRequestCallback)); 160 161 BrowserContext* browser_context = 162 web_ui->GetWebContents()->GetBrowserContext(); 163 WebUIDataSource::Add(browser_context, html_source); 164} 165 166AccessibilityUI::~AccessibilityUI() { 167} 168 169void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) { 170 std::string process_id_str; 171 std::string route_id_str; 172 int process_id; 173 int route_id; 174 CHECK(args->GetSize() == 2); 175 CHECK(args->GetString(0, &process_id_str)); 176 CHECK(args->GetString(1, &route_id_str)); 177 CHECK(base::StringToInt(process_id_str, 178 &process_id)); 179 CHECK(base::StringToInt(route_id_str, &route_id)); 180 181 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 182 if (!rvh) 183 return; 184 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh); 185 if (!rwhi) 186 return; 187 AccessibilityMode mode = rwhi->accessibility_mode(); 188 if (mode == AccessibilityModeOff) 189 rwhi->SetAccessibilityMode(AccessibilityModeComplete); 190 else 191 rwhi->SetAccessibilityMode(AccessibilityModeOff); 192} 193 194void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) { 195 BrowserAccessibilityStateImpl* state = 196 BrowserAccessibilityStateImpl::GetInstance(); 197 AccessibilityMode mode = state->accessibility_mode(); 198 AccessibilityMode new_mode = (mode == AccessibilityModeOff 199 ? AccessibilityModeComplete 200 : AccessibilityModeOff); 201 state->SetAccessibilityMode(new_mode); 202} 203 204void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) { 205 std::string process_id_str; 206 std::string route_id_str; 207 int process_id; 208 int route_id; 209 CHECK(args->GetSize() == 2); 210 CHECK(args->GetString(0, &process_id_str)); 211 CHECK(args->GetString(1, &route_id_str)); 212 CHECK(base::StringToInt(process_id_str, &process_id)); 213 CHECK(base::StringToInt(route_id_str, &route_id)); 214 215 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 216 if (!rvh) { 217 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); 218 result->SetInteger(kProcessIdField, process_id); 219 result->SetInteger(kRouteIdField, route_id); 220 result->Set("error", new base::StringValue("Renderer no longer exists.")); 221 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 222 return; 223 } 224 225 scoped_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh)); 226 RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( 227 WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView()); 228 if (!host_view) { 229 result->Set("error", 230 new base::StringValue("Could not get accessibility tree.")); 231 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 232 return; 233 } 234 scoped_ptr<AccessibilityTreeFormatter> formatter( 235 AccessibilityTreeFormatter::Create(rvh)); 236 base::string16 accessibility_contents_utf16; 237 BrowserAccessibilityManager* manager = 238 host_view->GetBrowserAccessibilityManager(); 239 if (!manager) { 240 result->Set("error", 241 new base::StringValue("Could not get accessibility tree.")); 242 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 243 return; 244 } 245 std::vector<AccessibilityTreeFormatter::Filter> filters; 246 filters.push_back(AccessibilityTreeFormatter::Filter( 247 ASCIIToUTF16("*"), 248 AccessibilityTreeFormatter::Filter::ALLOW)); 249 formatter->SetFilters(filters); 250 formatter->FormatAccessibilityTree(&accessibility_contents_utf16); 251 252 result->Set("tree", 253 new base::StringValue(UTF16ToUTF8(accessibility_contents_utf16))); 254 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 255} 256 257} // namespace content 258