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/browser/web_contents/web_contents_impl.h" 19 #include "content/common/view_message_enums.h" 20 #include "content/grit/content_resources.h" 21 #include "content/public/browser/favicon_status.h" 22 #include "content/public/browser/navigation_entry.h" 23 #include "content/public/browser/render_process_host.h" 24 #include "content/public/browser/render_view_host.h" 25 #include "content/public/browser/render_widget_host.h" 26 #include "content/public/browser/render_widget_host_iterator.h" 27 #include "content/public/browser/web_contents.h" 28 #include "content/public/browser/web_ui_data_source.h" 29 #include "content/public/common/url_constants.h" 30 #include "net/base/escape.h" 31 32 static const char kDataFile[] = "targets-data.json"; 33 34 static const char kProcessIdField[] = "processId"; 35 static const char kRouteIdField[] = "routeId"; 36 static const char kUrlField[] = "url"; 37 static const char kNameField[] = "name"; 38 static const char kFaviconUrlField[] = "favicon_url"; 39 static const char kPidField[] = "pid"; 40 static const char kAccessibilityModeField[] = "a11y_mode"; 41 42 namespace content { 43 44 namespace { 45 46 base::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 66 base::DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) { 67 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( 68 WebContents::FromRenderViewHost(rvh)); 69 AccessibilityMode accessibility_mode = web_contents->GetAccessibilityMode(); 70 71 std::string title; 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 = base::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 93 bool HandleRequestCallback(BrowserContext* current_context, 94 const std::string& path, 95 const WebUIDataSource::GotDataCallback& callback) { 96 if (path != kDataFile) 97 return false; 98 scoped_ptr<base::ListValue> rvh_list(new base::ListValue()); 99 100 scoped_ptr<RenderWidgetHostIterator> widgets( 101 RenderWidgetHost::GetRenderWidgetHosts()); 102 103 while (RenderWidgetHost* widget = widgets->GetNextHost()) { 104 // Ignore processes that don't have a connection, such as crashed tabs. 105 if (!widget->GetProcess()->HasConnection()) 106 continue; 107 if (!widget->IsRenderView()) 108 continue; 109 RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(widget); 110 BrowserContext* context = rwhi->GetProcess()->GetBrowserContext(); 111 if (context != current_context) 112 continue; 113 114 RenderViewHost* rvh = RenderViewHost::From(widget); 115 rvh_list->Append(BuildTargetDescriptor(rvh)); 116 } 117 118 scoped_ptr<base::DictionaryValue> data(new base::DictionaryValue()); 119 data->Set("list", rvh_list.release()); 120 data->SetInteger( 121 "global_a11y_mode", 122 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()); 123 124 std::string json_string; 125 base::JSONWriter::Write(data.get(), &json_string); 126 127 callback.Run(base::RefCountedString::TakeString(&json_string)); 128 return true; 129 } 130 131 } // namespace 132 133 AccessibilityUI::AccessibilityUI(WebUI* web_ui) : WebUIController(web_ui) { 134 // Set up the chrome://accessibility source. 135 WebUIDataSource* html_source = 136 WebUIDataSource::Create(kChromeUIAccessibilityHost); 137 html_source->SetUseJsonJSFormatV2(); 138 139 web_ui->RegisterMessageCallback( 140 "toggleAccessibility", 141 base::Bind(&AccessibilityUI::ToggleAccessibility, 142 base::Unretained(this))); 143 web_ui->RegisterMessageCallback( 144 "toggleGlobalAccessibility", 145 base::Bind(&AccessibilityUI::ToggleGlobalAccessibility, 146 base::Unretained(this))); 147 web_ui->RegisterMessageCallback( 148 "requestAccessibilityTree", 149 base::Bind(&AccessibilityUI::RequestAccessibilityTree, 150 base::Unretained(this))); 151 152 // Add required resources. 153 html_source->SetJsonPath("strings.js"); 154 html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS); 155 html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS); 156 html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML); 157 html_source->SetRequestFilter( 158 base::Bind(&HandleRequestCallback, 159 web_ui->GetWebContents()->GetBrowserContext())); 160 161 BrowserContext* browser_context = 162 web_ui->GetWebContents()->GetBrowserContext(); 163 WebUIDataSource::Add(browser_context, html_source); 164 } 165 166 AccessibilityUI::~AccessibilityUI() {} 167 168 void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) { 169 std::string process_id_str; 170 std::string route_id_str; 171 int process_id; 172 int route_id; 173 CHECK_EQ(2U, args->GetSize()); 174 CHECK(args->GetString(0, &process_id_str)); 175 CHECK(args->GetString(1, &route_id_str)); 176 CHECK(base::StringToInt(process_id_str, &process_id)); 177 CHECK(base::StringToInt(route_id_str, &route_id)); 178 179 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 180 if (!rvh) 181 return; 182 WebContentsImpl* web_contents = static_cast<WebContentsImpl*>( 183 WebContents::FromRenderViewHost(rvh)); 184 AccessibilityMode mode = web_contents->GetAccessibilityMode(); 185 if ((mode & AccessibilityModeComplete) != AccessibilityModeComplete) { 186 web_contents->AddAccessibilityMode(AccessibilityModeComplete); 187 } else { 188 web_contents->SetAccessibilityMode( 189 BrowserAccessibilityStateImpl::GetInstance()->accessibility_mode()); 190 } 191 } 192 193 void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) { 194 BrowserAccessibilityStateImpl* state = 195 BrowserAccessibilityStateImpl::GetInstance(); 196 AccessibilityMode mode = state->accessibility_mode(); 197 if ((mode & AccessibilityModeComplete) != AccessibilityModeComplete) 198 state->EnableAccessibility(); 199 else 200 state->DisableAccessibility(); 201 } 202 203 void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) { 204 std::string process_id_str; 205 std::string route_id_str; 206 int process_id; 207 int route_id; 208 CHECK_EQ(2U, args->GetSize()); 209 CHECK(args->GetString(0, &process_id_str)); 210 CHECK(args->GetString(1, &route_id_str)); 211 CHECK(base::StringToInt(process_id_str, &process_id)); 212 CHECK(base::StringToInt(route_id_str, &route_id)); 213 214 RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); 215 if (!rvh) { 216 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue()); 217 result->SetInteger(kProcessIdField, process_id); 218 result->SetInteger(kRouteIdField, route_id); 219 result->Set("error", new base::StringValue("Renderer no longer exists.")); 220 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 221 return; 222 } 223 224 scoped_ptr<base::DictionaryValue> result(BuildTargetDescriptor(rvh)); 225 WebContents* web_contents = WebContents::FromRenderViewHost(rvh); 226 scoped_ptr<AccessibilityTreeFormatter> formatter( 227 AccessibilityTreeFormatter::Create(web_contents)); 228 base::string16 accessibility_contents_utf16; 229 std::vector<AccessibilityTreeFormatter::Filter> filters; 230 filters.push_back(AccessibilityTreeFormatter::Filter( 231 base::ASCIIToUTF16("*"), 232 AccessibilityTreeFormatter::Filter::ALLOW)); 233 formatter->SetFilters(filters); 234 formatter->FormatAccessibilityTree(&accessibility_contents_utf16); 235 236 result->Set("tree", 237 new base::StringValue( 238 base::UTF16ToUTF8(accessibility_contents_utf16))); 239 web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); 240 } 241 242 } // namespace content 243