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