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/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 url = web_contents->GetURL(); 75 title = UTF16ToUTF8(web_contents->GetTitle()); 76 NavigationController& controller = web_contents->GetController(); 77 NavigationEntry* entry = controller.GetActiveEntry(); 78 if (entry != NULL && entry->GetURL().is_valid()) 79 favicon_url = entry->GetFavicon().url; 80 } 81 82 return BuildTargetDescriptor(url, 83 title, 84 favicon_url, 85 rvh->GetProcess()->GetID(), 86 rvh->GetRoutingID(), 87 accessibility_mode); 88 } 89 90 void SendTargetsData( 91 const WebUIDataSource::GotDataCallback& callback) { 92 scoped_ptr<base::ListValue> rvh_list(new base::ListValue()); 93 94 RenderWidgetHost::List widgets = RenderWidgetHost::GetRenderWidgetHosts(); 95 for (size_t i = 0; i < widgets.size(); ++i) { 96 // Ignore processes that don't have a connection, such as crashed tabs. 97 if (!widgets[i]->GetProcess()->HasConnection()) 98 continue; 99 if (!widgets[i]->IsRenderView()) 100 continue; 101 102 RenderViewHost* rvh = RenderViewHost::From(widgets[i]); 103 rvh_list->Append(BuildTargetDescriptor(rvh)); 104 } 105 106 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 107 data->Set("list", rvh_list.release()); 108 scoped_ptr<base::FundamentalValue> a11y_mode(new base::FundamentalValue( 109 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode())); 110 data->Set("global_a11y_mode", a11y_mode.release()); 111 112 std::string json_string; 113 base::JSONWriter::Write(data.get(), &json_string); 114 115 callback.Run(base::RefCountedString::TakeString(&json_string)); 116 } 117 118 bool HandleRequestCallback( 119 const std::string& path, 120 const WebUIDataSource::GotDataCallback& callback) { 121 if (path != kDataFile) 122 return false; 123 124 SendTargetsData(callback); 125 return true; 126 } 127 128 } // namespace 129 130 AccessibilityUI::AccessibilityUI(WebUI* web_ui) 131 : WebUIController(web_ui) { 132 // Set up the chrome://accessibility source. 133 WebUIDataSource* html_source = 134 WebUIDataSource::Create(kChromeUIAccessibilityHost); 135 html_source->SetUseJsonJSFormatV2(); 136 137 web_ui->RegisterMessageCallback( 138 "toggleAccessibility", 139 base::Bind(&AccessibilityUI::ToggleAccessibility, 140 base::Unretained(this))); 141 web_ui->RegisterMessageCallback( 142 "toggleGlobalAccessibility", 143 base::Bind(&AccessibilityUI::ToggleGlobalAccessibility, 144 base::Unretained(this))); 145 web_ui->RegisterMessageCallback( 146 "requestAccessibilityTree", 147 base::Bind(&AccessibilityUI::RequestAccessibilityTree, 148 base::Unretained(this))); 149 150 // Add required resources. 151 html_source->SetJsonPath("strings.js"); 152 html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS); 153 html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS); 154 html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML); 155 html_source->SetRequestFilter(base::Bind(&HandleRequestCallback)); 156 157 BrowserContext* browser_context = 158 web_ui->GetWebContents()->GetBrowserContext(); 159 WebUIDataSource::Add(browser_context, html_source); 160 } 161 162 AccessibilityUI::~AccessibilityUI() { 163 } 164 165 void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) { 166 std::string process_id_str; 167 std::string route_id_str; 168 int process_id; 169 int route_id; 170 CHECK(args->GetSize() == 2); 171 CHECK(args->GetString(0, &process_id_str)); 172 CHECK(args->GetString(1, &route_id_str)); 173 CHECK(base::StringToInt(process_id_str, 174 &process_id)); 175 CHECK(base::StringToInt(route_id_str, &route_id)); 176 177 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 178 if (!rvh) 179 return; 180 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh); 181 if (!rwhi) 182 return; 183 AccessibilityMode mode = rwhi->accessibility_mode(); 184 if (mode == AccessibilityModeOff) 185 rwhi->SetAccessibilityMode(AccessibilityModeComplete); 186 else 187 rwhi->SetAccessibilityMode(AccessibilityModeOff); 188 } 189 190 void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) { 191 BrowserAccessibilityStateImpl* state = 192 BrowserAccessibilityStateImpl::GetInstance(); 193 AccessibilityMode mode = state->accessibility_mode(); 194 AccessibilityMode new_mode = (mode == AccessibilityModeOff 195 ? AccessibilityModeComplete 196 : AccessibilityModeOff); 197 state->SetAccessibilityMode(new_mode); 198 } 199 200 void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) { 201 std::string process_id_str; 202 std::string route_id_str; 203 int process_id; 204 int route_id; 205 CHECK(args->GetSize() == 2); 206 CHECK(args->GetString(0, &process_id_str)); 207 CHECK(args->GetString(1, &route_id_str)); 208 CHECK(base::StringToInt(process_id_str, &process_id)); 209 CHECK(base::StringToInt(route_id_str, &route_id)); 210 211 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 212 if (!rvh) { 213 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); 214 result->SetInteger(kProcessIdField, process_id); 215 result->SetInteger(kRouteIdField, route_id); 216 result->Set("error", new base::StringValue("Renderer no longer exists.")); 217 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 218 return; 219 } 220 221 scoped_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh)); 222 RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( 223 WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView()); 224 if (!host_view) { 225 result->Set("error", 226 new base::StringValue("Could not get accessibility tree.")); 227 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 228 return; 229 } 230 scoped_ptr<AccessibilityTreeFormatter> formatter( 231 AccessibilityTreeFormatter::Create(rvh)); 232 string16 accessibility_contents_utf16; 233 BrowserAccessibilityManager* manager = 234 host_view->GetBrowserAccessibilityManager(); 235 if (!manager) { 236 result->Set("error", 237 new base::StringValue("Could not get accessibility tree.")); 238 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 239 return; 240 } 241 std::vector<AccessibilityTreeFormatter::Filter> filters; 242 filters.push_back(AccessibilityTreeFormatter::Filter( 243 ASCIIToUTF16("*"), 244 AccessibilityTreeFormatter::Filter::ALLOW)); 245 formatter->SetFilters(filters); 246 formatter->FormatAccessibilityTree(&accessibility_contents_utf16); 247 248 result->Set("tree", 249 new base::StringValue(UTF16ToUTF8(accessibility_contents_utf16))); 250 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 251 } 252 253 } // namespace content 254