diff options
author | aboxhall@chromium.org <aboxhall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-12 00:09:10 +0000 |
---|---|---|
committer | aboxhall@chromium.org <aboxhall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-03-12 00:09:10 +0000 |
commit | a17df8e38de1dd7ecc68f3bdf7a9c87bb2410e16 (patch) | |
tree | 64ae44e814964de0596fc8b2ae9a41ed571de193 | |
parent | dab7720f282ec9d55f3a5eb56ee78a38e4a305c2 (diff) | |
download | chromium_src-a17df8e38de1dd7ecc68f3bdf7a9c87bb2410e16.zip chromium_src-a17df8e38de1dd7ecc68f3bdf7a9c87bb2410e16.tar.gz chromium_src-a17df8e38de1dd7ecc68f3bdf7a9c87bb2410e16.tar.bz2 |
Adds a chrome://accessibility page, which:
Adds a toggle for global accessibility mode (on/off)
For each renderer:
- shows an accessibility mode toggle
- if accessibility mode is On, shows a 'Show(/hide) accessibility tree' toggle, which shows(/hides) a text representation of the accessibility tree inline in the page. The accessibility tree may also be refreshed.
BUG=178756
Review URL: https://chromiumcodereview.appspot.com/11791028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187417 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/autocomplete/builtin_provider_unittest.cc | 22 | ||||
-rw-r--r-- | chrome/browser/browser_about_handler.cc | 1 | ||||
-rw-r--r-- | content/browser/accessibility/accessibility_ui.cc | 282 | ||||
-rw-r--r-- | content/browser/accessibility/accessibility_ui.h | 31 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_impl.cc | 2 | ||||
-rw-r--r-- | content/browser/renderer_host/render_widget_host_impl.h | 7 | ||||
-rw-r--r-- | content/browser/resources/accessibility/accessibility.css | 30 | ||||
-rw-r--r-- | content/browser/resources/accessibility/accessibility.html | 25 | ||||
-rw-r--r-- | content/browser/resources/accessibility/accessibility.js | 212 | ||||
-rw-r--r-- | content/browser/webui/content_web_ui_controller_factory.cc | 6 | ||||
-rw-r--r-- | content/content_browser.gypi | 2 | ||||
-rw-r--r-- | content/content_resources.grd | 3 | ||||
-rw-r--r-- | content/public/common/url_constants.cc | 1 | ||||
-rw-r--r-- | content/public/common/url_constants.h | 1 |
14 files changed, 604 insertions, 21 deletions
diff --git a/chrome/browser/autocomplete/builtin_provider_unittest.cc b/chrome/browser/autocomplete/builtin_provider_unittest.cc index 1d71131..0beaf29 100644 --- a/chrome/browser/autocomplete/builtin_provider_unittest.cc +++ b/chrome/browser/autocomplete/builtin_provider_unittest.cc @@ -143,8 +143,6 @@ TEST_F(BuiltinProviderTest, ChromeURLs) { // This makes assumptions about the chrome URLs listed by the BuiltinProvider. // Currently they are derived from ChromePaths() in browser_about_handler.cc. - const string16 kHostA = ASCIIToUTF16(chrome::kChromeUIAppCacheInternalsHost); - const GURL kURLA = GURL(kChrome + kSeparator3 + kHostA); const string16 kHostM1 = ASCIIToUTF16(chrome::kChromeUIMediaInternalsHost); const string16 kHostM2 = ASCIIToUTF16(chrome::kChromeUIMemoryHost); const GURL kURLM1 = GURL(kChrome + kSeparator3 + kHostM1); @@ -161,23 +159,7 @@ TEST_F(BuiltinProviderTest, ChromeURLs) { {kChrome + kSeparator2 + ASCIIToUTF16("host"), 0, {}}, {kChrome + kSeparator3 + ASCIIToUTF16("host"), 0, {}}, - // Typing an about URL for a unique host should provide that full URL. - {kAbout + kSeparator1 + kHostA.substr(0, 1), 1, {kURLA}}, - {kAbout + kSeparator2 + kHostA.substr(0, 2), 1, {kURLA}}, - {kAbout + kSeparator3 + kHostA.substr(0, kHostA.length() - 1), 1, {kURLA}}, - {kAbout + kSeparator1 + kHostA, 1, {kURLA}}, - {kAbout + kSeparator2 + kHostA, 1, {kURLA}}, - {kAbout + kSeparator3 + kHostA, 1, {kURLA}}, - - // Typing a chrome URL for a unique host should provide that full URL. - {kChrome + kSeparator1 + kHostA.substr(0, 1), 1, {kURLA}}, - {kChrome + kSeparator2 + kHostA.substr(0, 2), 1, {kURLA}}, - {kChrome + kSeparator3 + kHostA.substr(0, kHostA.length() - 1), 1, {kURLA}}, - {kChrome + kSeparator1 + kHostA, 1, {kURLA}}, - {kChrome + kSeparator2 + kHostA, 1, {kURLA}}, - {kChrome + kSeparator3 + kHostA, 1, {kURLA}}, - - // Typing an about URL with a non-unique host should provide matching URLs. + // Typing an about URL should provide matching URLs. {kAbout + kSeparator1 + kHostM1.substr(0, 1), 2, {kURLM1, kURLM2}}, {kAbout + kSeparator2 + kHostM1.substr(0, 2), 2, {kURLM1, kURLM2}}, {kAbout + kSeparator3 + kHostM1.substr(0, 3), 1, {kURLM1}}, @@ -185,7 +167,7 @@ TEST_F(BuiltinProviderTest, ChromeURLs) { {kAbout + kSeparator3 + kHostM1, 1, {kURLM1}}, {kAbout + kSeparator2 + kHostM2, 1, {kURLM2}}, - // Typing a chrome URL with a non-unique host should provide matching URLs. + // Typing a chrome URL should provide matching URLs. {kChrome + kSeparator1 + kHostM1.substr(0, 1), 2, {kURLM1, kURLM2}}, {kChrome + kSeparator2 + kHostM1.substr(0, 2), 2, {kURLM1, kURLM2}}, {kChrome + kSeparator3 + kHostM1.substr(0, 3), 1, {kURLM1}}, diff --git a/chrome/browser/browser_about_handler.cc b/chrome/browser/browser_about_handler.cc index dcbd918..b09c624 100644 --- a/chrome/browser/browser_about_handler.cc +++ b/chrome/browser/browser_about_handler.cc @@ -21,6 +21,7 @@ namespace { // Add paths here to be included in chrome://chrome-urls (about:about). // These paths will also be suggested by BuiltinProvider. const char* const kPaths[] = { + chrome::kChromeUIAccessibilityHost, chrome::kChromeUIAppCacheInternalsHost, chrome::kChromeUIBlobInternalsHost, chrome::kChromeUICacheHost, diff --git a/content/browser/accessibility/accessibility_ui.cc b/content/browser/accessibility/accessibility_ui.cc new file mode 100644 index 0000000..9307e9a --- /dev/null +++ b/content/browser/accessibility/accessibility_ui.cc @@ -0,0 +1,282 @@ +// Copyright (c) 2013 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 "content/browser/accessibility/accessibility_ui.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/json/json_writer.h" +#include "base/strings/string_number_conversions.h" +#include "base/utf_string_conversions.h" +#include "base/values.h" +#include "content/browser/accessibility/accessibility_tree_formatter.h" +#include "content/browser/accessibility/browser_accessibility_manager.h" +#include "content/browser/accessibility/browser_accessibility_state_impl.h" +#include "content/browser/renderer_host/render_widget_host_impl.h" +#include "content/common/view_message_enums.h" +#include "content/port/browser/render_widget_host_view_port.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/favicon_status.h" +#include "content/public/browser/navigation_entry.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/render_view_host.h" +#include "content/public/browser/render_widget_host.h" +#include "content/public/browser/web_contents.h" +#include "content/public/browser/web_ui_data_source.h" +#include "content/public/common/url_constants.h" +#include "grit/content_resources.h" +#include "net/base/escape.h" + +using base::FundamentalValue; +using base::ListValue; + +static const char kDataFile[] = "targets-data.json"; + +static const char kProcessIdField[] = "processId"; +static const char kRouteIdField[] = "routeId"; +static const char kUrlField[] = "url"; +static const char kNameField[] = "name"; +static const char kFaviconUrlField[] = "favicon_url"; +static const char kPidField[] = "pid"; +static const char kAccessibilityModeField[] = "a11y_mode"; + +namespace content { + +namespace { + +DictionaryValue* BuildTargetDescriptor( + const GURL& url, + const std::string& name, + const GURL& favicon_url, + int process_id, + int route_id, + AccessibilityMode accessibility_mode, + base::ProcessHandle handle = base::kNullProcessHandle) { + DictionaryValue* target_data = new DictionaryValue(); + target_data->SetInteger(kProcessIdField, process_id); + target_data->SetInteger(kRouteIdField, route_id); + target_data->SetString(kUrlField, url.spec()); + target_data->SetString(kNameField, net::EscapeForHTML(name)); + target_data->SetInteger(kPidField, base::GetProcId(handle)); + target_data->SetString(kFaviconUrlField, favicon_url.spec()); + target_data->SetInteger(kAccessibilityModeField, + accessibility_mode); + return target_data; +} + +DictionaryValue* BuildTargetDescriptor(RenderViewHost* rvh) { + WebContents* web_contents = WebContents::FromRenderViewHost(rvh); + std::string title; + RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh); + AccessibilityMode accessibility_mode = rwhi->accessibility_mode(); + + GURL url; + GURL favicon_url; + if (web_contents) { + url = web_contents->GetURL(); + title = UTF16ToUTF8(web_contents->GetTitle()); + NavigationController& controller = web_contents->GetController(); + NavigationEntry* entry = controller.GetActiveEntry(); + if (entry != NULL && entry->GetURL().is_valid()) + favicon_url = entry->GetFavicon().url; + } + + return BuildTargetDescriptor(url, + title, + favicon_url, + rvh->GetProcess()->GetID(), + rvh->GetRoutingID(), + accessibility_mode); +} + +void SendTargetsData( + const WebUIDataSource::GotDataCallback& callback) { + scoped_ptr<ListValue> rvh_list(new ListValue()); + + for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); + !it.IsAtEnd(); it.Advance()) { + RenderProcessHost* render_process_host = it.GetCurrentValue(); + DCHECK(render_process_host); + + // Ignore processes that don't have a connection, such as crashed tabs. + if (!render_process_host->HasConnection()) + continue; + + RenderProcessHost::RenderWidgetHostsIterator rwh_it( + render_process_host->GetRenderWidgetHostsIterator()); + for (; !rwh_it.IsAtEnd(); rwh_it.Advance()) { + const RenderWidgetHost* rwh = rwh_it.GetCurrentValue(); + DCHECK(rwh); + if (!rwh || !rwh->IsRenderView()) + continue; + + RenderViewHost* rvh = + RenderViewHost::From(const_cast<RenderWidgetHost*>(rwh)); + + rvh_list->Append(BuildTargetDescriptor(rvh)); + } + } + + scoped_ptr<DictionaryValue> data(new DictionaryValue()); + data->Set("list", rvh_list.release()); + scoped_ptr<FundamentalValue> a11y_mode(new FundamentalValue( + BrowserAccessibilityStateImpl::GetInstance()->GetAccessibilityMode())); + data->Set("global_a11y_mode", a11y_mode.release()); + + std::string json_string; + base::JSONWriter::Write(data.get(), &json_string); + + callback.Run(base::RefCountedString::TakeString(&json_string)); +} + +bool HandleRequestCallback( + const std::string& path, + const WebUIDataSource::GotDataCallback& callback) { + if (path != kDataFile) + return false; + + SendTargetsData(callback); + return true; +} + +} // namespace + +AccessibilityUI::AccessibilityUI(WebUI* web_ui) + : WebUIController(web_ui) { + // Set up the chrome://accessibility source. + WebUIDataSource* html_source = + WebUIDataSource::Create(chrome::kChromeUIAccessibilityHost); + html_source->SetUseJsonJSFormatV2(); + + web_ui->RegisterMessageCallback( + "toggleAccessibility", + base::Bind(&AccessibilityUI::ToggleAccessibility, + base::Unretained(this))); + web_ui->RegisterMessageCallback( + "toggleGlobalAccessibility", + base::Bind(&AccessibilityUI::ToggleGlobalAccessibility, + base::Unretained(this))); + web_ui->RegisterMessageCallback( + "requestAccessibilityTree", + base::Bind(&AccessibilityUI::RequestAccessibilityTree, + base::Unretained(this))); + + // Add required resources. + html_source->SetJsonPath("strings.js"); + html_source->AddResourcePath("accessibility.css", IDR_ACCESSIBILITY_CSS); + html_source->AddResourcePath("accessibility.js", IDR_ACCESSIBILITY_JS); + html_source->SetDefaultResource(IDR_ACCESSIBILITY_HTML); + html_source->SetRequestFilter(base::Bind(&HandleRequestCallback)); + + BrowserContext* browser_context = + web_ui->GetWebContents()->GetBrowserContext(); + WebUIDataSource::Add(browser_context, html_source); +} + +AccessibilityUI::~AccessibilityUI() { +} + +void AccessibilityUI::ToggleAccessibility(const base::ListValue* args) { + std::string process_id_str; + std::string route_id_str; + int process_id; + int route_id; + CHECK(args->GetSize() == 2); + CHECK(args->GetString(0, &process_id_str)); + CHECK(args->GetString(1, &route_id_str)); + CHECK(base::StringToInt(process_id_str, + &process_id)); + CHECK(base::StringToInt(route_id_str, &route_id)); + + RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); + if (!rvh) + return; + RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rvh); + if (!rwhi) + return; + AccessibilityMode mode = rwhi->accessibility_mode(); + if (mode == AccessibilityModeOff) + rwhi->SetAccessibilityMode(AccessibilityModeComplete); + else + rwhi->SetAccessibilityMode(AccessibilityModeOff); +} + +void AccessibilityUI::ToggleGlobalAccessibility(const base::ListValue* args) { + BrowserAccessibilityStateImpl* state = + BrowserAccessibilityStateImpl::GetInstance(); + AccessibilityMode mode = state->GetAccessibilityMode(); + AccessibilityMode new_mode = (mode == AccessibilityModeOff + ? AccessibilityModeComplete + : AccessibilityModeOff); + state->SetAccessibilityMode(new_mode); + for (RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); + !it.IsAtEnd(); it.Advance()) { + RenderProcessHost* render_process_host = it.GetCurrentValue(); + DCHECK(render_process_host); + + // Ignore processes that don't have a connection, such as crashed tabs. + if (!render_process_host->HasConnection()) + continue; + + RenderProcessHost::RenderWidgetHostsIterator rwit( + render_process_host->GetRenderWidgetHostsIterator()); + for (; !rwit.IsAtEnd(); rwit.Advance()) { + RenderWidgetHost* rwh = const_cast<RenderWidgetHost*>( + rwit.GetCurrentValue()); + DCHECK(rwh); + if (!rwh || !rwh->IsRenderView()) + continue; + RenderWidgetHostImpl* rwhi = RenderWidgetHostImpl::From(rwh); + rwhi->SetAccessibilityMode(new_mode); + } + } +} + +void AccessibilityUI::RequestAccessibilityTree(const base::ListValue* args) { + std::string process_id_str; + std::string route_id_str; + int process_id; + int route_id; + CHECK(args->GetSize() == 2); + CHECK(args->GetString(0, &process_id_str)); + CHECK(args->GetString(1, &route_id_str)); + CHECK(base::StringToInt(process_id_str, &process_id)); + CHECK(base::StringToInt(route_id_str, &route_id)); + + RenderViewHost* rvh = RenderViewHost::FromID(process_id, route_id); + if (!rvh) { + scoped_ptr<DictionaryValue> result(new DictionaryValue()); + result->SetInteger(kProcessIdField, process_id); + result->SetInteger(kRouteIdField, route_id); + result->Set("error", new StringValue("Renderer no longer exists.")); + web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); + return; + } + + scoped_ptr<DictionaryValue> result(BuildTargetDescriptor(rvh)); + RenderWidgetHostViewPort* host_view = static_cast<RenderWidgetHostViewPort*>( + WebContents::FromRenderViewHost(rvh)->GetRenderWidgetHostView()); + if (!host_view) { + result->Set("error", new StringValue("Could not get accessibility tree.")); + web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); + return; + } + scoped_ptr<AccessibilityTreeFormatter> formatter( + AccessibilityTreeFormatter::Create(rvh)); + string16 accessibility_contents_utf16; + BrowserAccessibilityManager* manager = + host_view->GetBrowserAccessibilityManager(); + if (!manager) { + result->Set("error", new StringValue("Could not get accessibility tree.")); + web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); + return; + } + formatter->FormatAccessibilityTree(&accessibility_contents_utf16); + + result->Set("tree", + new StringValue(UTF16ToUTF8(accessibility_contents_utf16))); + web_ui()->CallJavascriptFunction("accessibility.showTree", *(result.get())); +} + +} // namespace content diff --git a/content/browser/accessibility/accessibility_ui.h b/content/browser/accessibility/accessibility_ui.h new file mode 100644 index 0000000..1b239f8 --- /dev/null +++ b/content/browser/accessibility/accessibility_ui.h @@ -0,0 +1,31 @@ +// Copyright (c) 2013 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. + +#ifndef CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_ +#define CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_ + +#include "content/public/browser/web_ui_controller.h" + +namespace base { + class ListValue; +} // namespace base + +namespace content { + +class AccessibilityUI : public WebUIController { + public: + explicit AccessibilityUI(WebUI* web_ui); + virtual ~AccessibilityUI(); + + private: + void ToggleAccessibility(const base::ListValue* args); + void ToggleGlobalAccessibility(const base::ListValue* args); + void RequestAccessibilityTree(const base::ListValue* args); + + DISALLOW_COPY_AND_ASSIGN(AccessibilityUI); +}; + +} // namespace content + +#endif // CHROME_BROWSER_UI_WEBUI_ACCESSIBILITY_UI_H_ diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc index 176784c..716227a 100644 --- a/content/browser/renderer_host/render_widget_host_impl.cc +++ b/content/browser/renderer_host/render_widget_host_impl.cc @@ -148,6 +148,7 @@ RenderWidgetHostImpl::RenderWidgetHostImpl(RenderWidgetHostDelegate* delegate, waiting_for_screen_rects_ack_(false), mouse_move_pending_(false), mouse_wheel_pending_(false), + accessibility_mode_(AccessibilityModeOff), select_range_pending_(false), move_caret_pending_(false), needs_repainting_on_restore_(false), @@ -2217,6 +2218,7 @@ void RenderWidgetHostImpl::SetEditCommandsForNextKeyEvent( } void RenderWidgetHostImpl::SetAccessibilityMode(AccessibilityMode mode) { + accessibility_mode_ = mode; Send(new ViewMsg_SetAccessibilityMode(routing_id_, mode)); } diff --git a/content/browser/renderer_host/render_widget_host_impl.h b/content/browser/renderer_host/render_widget_host_impl.h index 61d8bfd..9485aa8 100644 --- a/content/browser/renderer_host/render_widget_host_impl.h +++ b/content/browser/renderer_host/render_widget_host_impl.h @@ -328,6 +328,11 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, void SetEditCommandsForNextKeyEvent( const std::vector<EditCommand>& commands); + // Gets the accessibility mode. + AccessibilityMode accessibility_mode() const { + return accessibility_mode_; + } + // Send a message to the renderer process to change the accessibility mode. void SetAccessibilityMode(AccessibilityMode mode); @@ -738,6 +743,8 @@ class CONTENT_EXPORT RenderWidgetHostImpl : virtual public RenderWidgetHost, // would be queued) results in very slow scrolling. WheelEventQueue coalesced_mouse_wheel_events_; + AccessibilityMode accessibility_mode_; + // (Similar to |mouse_move_pending_|.) True while waiting for SelectRange_ACK. bool select_range_pending_; diff --git a/content/browser/resources/accessibility/accessibility.css b/content/browser/resources/accessibility/accessibility.css new file mode 100644 index 0000000..e00a922 --- /dev/null +++ b/content/browser/resources/accessibility/accessibility.css @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2013 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. + */ + +body { + font-family: Arial, sans-serif; + font-size: 12px; + margin: 10px; + min-width: 47em; + padding-bottom: 65px; +} + +img { + float: left; + height: 16px; + padding-right: 5px; + width: 16px; +} + +.row { + border-bottom: 1px solid #A0A0A0; + padding: 5px; +} + +.url { + color: #A0A0A0; +} + diff --git a/content/browser/resources/accessibility/accessibility.html b/content/browser/resources/accessibility/accessibility.html new file mode 100644 index 0000000..c1a2d8d --- /dev/null +++ b/content/browser/resources/accessibility/accessibility.html @@ -0,0 +1,25 @@ +<!DOCTYPE HTML> +<html> +<!-- +Copyright (c) 2013 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. +--> +<head> + <meta charset="utf-8"> + <title>Accessibility</title> + <link rel="stylesheet" href="accessibility.css"> + <script src="chrome://resources/js/cr.js"></script> + <script src="chrome://resources/js/load_time_data.js"></script> + <script src="chrome://resources/js/util.js"></script> + <script src="strings.js"></script> + <script src="accessibility.js"></script> +</head> +<body> + <h1>Accessibility</h1> + <div id="global" class="row">Global accessibility mode: + <a id="toggle_global" href="#"></a></div> + <div id="pages" class="list"></div> + <script src="chrome://resources/js/i18n_template2.js"></script> +</body> +</html> diff --git a/content/browser/resources/accessibility/accessibility.js b/content/browser/resources/accessibility/accessibility.js new file mode 100644 index 0000000..5d98b48 --- /dev/null +++ b/content/browser/resources/accessibility/accessibility.js @@ -0,0 +1,212 @@ +// Copyright (c) 2013 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. + +cr.define('accessibility', function() { + 'use strict'; + + function requestData() { + var xhr = new XMLHttpRequest(); + xhr.open('GET', 'targets-data.json', false); + xhr.send(null); + if (xhr.status === 200) { + console.log(xhr.responseText); + return JSON.parse(xhr.responseText); + } + return []; + } + + // TODO(aboxhall): add a mechanism to request individual and global a11y + // mode, xhr them on toggle... or just re-requestData and be smarter about + // ID-ing rows? + + function toggleAccessibility(data, element) { + chrome.send('toggleAccessibility', + [String(data.processId), String(data.routeId)]); + var a11y_was_on = (element.textContent.match(/on/) != null); + element.textContent = ' accessibility ' + (a11y_was_on ? ' off' : ' on'); + var row = element.parentElement; + if (a11y_was_on) { + while (row.lastChild != element) + row.removeChild(row.lastChild); + } else { + row.appendChild(document.createTextNode(' | ')); + row.appendChild(createShowAccessibilityTreeElement(data, row, false)); + } + } + + function requestAccessibilityTree(data, element) { + chrome.send('requestAccessibilityTree', + [String(data.processId), String(data.routeId)]); + } + + function toggleGlobalAccessibility() { + chrome.send('toggleGlobalAccessibility'); + document.location.reload(); // FIXME see TODO above + } + + function initialize() { + console.log('initialize'); + var data = requestData(); + + addGlobalAccessibilityModeToggle(data['global_a11y_mode']); + + $('pages').textContent = ''; + + var list = data['list']; + for (var i = 0; i < list.length; i++) { + addToPagesList(list[i]); + } + } + + function addGlobalAccessibilityModeToggle(global_a11y_mode) { + $('toggle_global').textContent = (global_a11y_mode == 0 ? 'off' : 'on'); + $('toggle_global').addEventListener('click', + toggleGlobalAccessibility); + } + + function addToPagesList(data) { + // TODO: iterate through data and pages rows instead + var id = data['processId'] + '.' + data['routeId']; + var row = document.createElement('div'); + row.className = 'row'; + row.id = id; + formatRow(row, data); + + row.processId = data.processId; + row.routeId = data.routeId; + + var list = $('pages'); + list.appendChild(row); + } + + function formatRow(row, data) { + if (!('url' in data)) { + if ('error' in data) { + row.appendChild(createErrorMessageElement(data, row)); + return; + } + } + var properties = ['favicon_url', 'name', 'url']; + for (var j = 0; j < properties.length; j++) + row.appendChild(formatValue(data, properties[j])); + + row.appendChild(createToggleAccessibilityElement(data)); + if (data['a11y_mode'] != 0) { + row.appendChild(document.createTextNode(' | ')); + if ('tree' in data) { + row.appendChild(createShowAccessibilityTreeElement(data, row, true)); + row.appendChild(document.createTextNode(' | ')); + row.appendChild(createHideAccessibilityTreeElement(row.id)); + row.appendChild(createAccessibilityTreeElement(data)); + } + else { + row.appendChild(createShowAccessibilityTreeElement(data, row, false)); + if ('error' in data) + row.appendChild(createErrorMessageElement(data, row)); + } + } + } + + function formatValue(data, property) { + var value = data[property]; + + if (property == 'favicon_url') { + var faviconElement = document.createElement('img'); + if (value) + faviconElement.src = value; + faviconElement.alt = ""; + return faviconElement; + } + + var text = value ? String(value) : ''; + if (text.length > 100) + text = text.substring(0, 100) + '\u2026'; // ellipsis + + var span = document.createElement('span'); + span.textContent = ' ' + text + ' '; + span.className = property; + return span; + } + + function createToggleAccessibilityElement(data) { + var link = document.createElement('a'); + link.setAttribute('href', '#'); + var a11y_mode = data['a11y_mode']; + link.textContent = 'accessibility ' + (a11y_mode == 0 ? 'off' : 'on'); + link.addEventListener('click', + toggleAccessibility.bind(this, data, link)); + return link; + } + + function createShowAccessibilityTreeElement(data, row, opt_refresh) { + var link = document.createElement('a'); + link.setAttribute('href', '#'); + if (opt_refresh) + link.textContent = 'refresh accessibility tree'; + else + link.textContent = 'show accessibility tree'; + link.id = row.id + ':showTree'; + link.addEventListener('click', + requestAccessibilityTree.bind(this, data, link)); + return link; + } + + function createHideAccessibilityTreeElement(id) { + var link = document.createElement('a'); + link.setAttribute('href', '#'); + link.textContent = 'hide accessibility tree'; + link.addEventListener('click', + function() { + $(id + ':showTree').textContent = 'show accessibility tree'; + var existingTreeElements = $(id).getElementsByTagName('pre'); + for (var i = 0; i < existingTreeElements.length; i++) + $(id).removeChild(existingTreeElements[i]); + var row = $(id); + while (row.lastChild != $(id + ':showTree')) + row.removeChild(row.lastChild); + }); + return link; + } + + function createErrorMessageElement(data) { + var errorMessageElement = document.createElement('div'); + var errorMessage = data.error; + errorMessageElement.innerHTML = errorMessage + ' '; + var closeLink = document.createElement('a'); + closeLink.href='#'; + closeLink.textContent = '[close]'; + closeLink.addEventListener('click', function() { + var parentElement = errorMessageElement.parentElement; + parentElement.removeChild(errorMessageElement); + if (parentElement.childElementCount == 0) + parentElement.parentElement.removeChild(parentElement); + }); + errorMessageElement.appendChild(closeLink); + return errorMessageElement; + } + + function showTree(data) { + var id = data.processId + '.' + data.routeId; + var row = $(id); + if (!row) + return; + + row.textContent = ''; + formatRow(row, data); + } + + function createAccessibilityTreeElement(data) { + var treeElement = document.createElement('pre'); + var tree = data.tree; + treeElement.textContent = tree; + return treeElement; + } + + return { + initialize: initialize, + showTree: showTree + }; +}); + +document.addEventListener('DOMContentLoaded', accessibility.initialize); diff --git a/content/browser/webui/content_web_ui_controller_factory.cc b/content/browser/webui/content_web_ui_controller_factory.cc index 38a5de4..7f486d8 100644 --- a/content/browser/webui/content_web_ui_controller_factory.cc +++ b/content/browser/webui/content_web_ui_controller_factory.cc @@ -4,6 +4,7 @@ #include "content/browser/webui/content_web_ui_controller_factory.h" +#include "content/browser/accessibility/accessibility_ui.h" #include "content/browser/gpu/gpu_internals_ui.h" #include "content/browser/media/media_internals_ui.h" #include "content/browser/media/webrtc_internals_ui.h" @@ -21,7 +22,8 @@ WebUI::TypeID ContentWebUIControllerFactory::GetWebUIType( url.host() == chrome::kChromeUITracingHost || #endif url.host() == chrome::kChromeUIGpuHost || - url.host() == chrome::kChromeUIMediaInternalsHost) { + url.host() == chrome::kChromeUIMediaInternalsHost || + url.host() == chrome::kChromeUIAccessibilityHost) { return const_cast<ContentWebUIControllerFactory*>(this); } return WebUI::kNoWebUI; @@ -45,6 +47,8 @@ WebUIController* ContentWebUIControllerFactory::CreateWebUIControllerForURL( return new GpuInternalsUI(web_ui); if (url.host() == chrome::kChromeUIMediaInternalsHost) return new MediaInternalsUI(web_ui); + if (url.host() == chrome::kChromeUIAccessibilityHost) + return new AccessibilityUI(web_ui); #if !defined(OS_ANDROID) if (url.host() == chrome::kChromeUITracingHost) return new TracingUI(web_ui); diff --git a/content/content_browser.gypi b/content/content_browser.gypi index 733a0ba..5cf5a91 100644 --- a/content/content_browser.gypi +++ b/content/content_browser.gypi @@ -186,6 +186,8 @@ 'browser/accessibility/accessibility_tree_formatter_utils_win.cc', 'browser/accessibility/accessibility_tree_formatter_utils_win.h', 'browser/accessibility/accessibility_tree_formatter_win.cc', + 'browser/accessibility/accessibility_ui.cc', + 'browser/accessibility/accessibility_ui.h', 'browser/accessibility/browser_accessibility.cc', 'browser/accessibility/browser_accessibility.h', 'browser/accessibility/browser_accessibility_cocoa.h', diff --git a/content/content_resources.grd b/content/content_resources.grd index 04ebe89..83b0cb4 100644 --- a/content/content_resources.grd +++ b/content/content_resources.grd @@ -11,6 +11,9 @@ <translations /> <release seq="1"> <includes> + <include name="IDR_ACCESSIBILITY_HTML" file="browser/resources/accessibility/accessibility.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> + <include name="IDR_ACCESSIBILITY_CSS" file="browser/resources/accessibility/accessibility.css" type="BINDATA" /> + <include name="IDR_ACCESSIBILITY_JS" file="browser/resources/accessibility/accessibility.js" flattenhtml="true" type="BINDATA" /> <include name="IDR_GPU_BLACKLIST" file="browser/gpu/software_rendering_list.json" type="BINDATA" /> <include name="IDR_GPU_INTERNALS_HTML" file="browser/resources/gpu/gpu_internals.html" flattenhtml="true" allowexternalscript="true" type="BINDATA" /> <include name="IDR_GPU_INTERNALS_JS" file="browser/resources/gpu/gpu_internals.js" flattenhtml="true" type="BINDATA" /> diff --git a/content/public/common/url_constants.cc b/content/public/common/url_constants.cc index 057545c..40b3b03 100644 --- a/content/public/common/url_constants.cc +++ b/content/public/common/url_constants.cc @@ -33,6 +33,7 @@ const char kViewSourceScheme[] = "view-source"; const char kAboutBlankURL[] = "about:blank"; const char kAboutSrcDocURL[] = "about:srcdoc"; const char kChromeUIAppCacheInternalsHost[] = "appcache-internals"; +const char kChromeUIAccessibilityHost[] = "accessibility"; const char kChromeUIBlobInternalsHost[] = "blob-internals"; const char kChromeUIBrowserCrashHost[] = "inducebrowsercrashforrealz"; const char kChromeUIDevToolsHost[] = "devtools"; diff --git a/content/public/common/url_constants.h b/content/public/common/url_constants.h index ece09e65..ef45d26 100644 --- a/content/public/common/url_constants.h +++ b/content/public/common/url_constants.h @@ -39,6 +39,7 @@ CONTENT_EXPORT extern const char kViewSourceScheme[]; // Hosts for about URLs. CONTENT_EXPORT extern const char kAboutBlankURL[]; CONTENT_EXPORT extern const char kAboutSrcDocURL[]; +CONTENT_EXPORT extern const char kChromeUIAccessibilityHost[]; CONTENT_EXPORT extern const char kChromeUIAppCacheInternalsHost[]; CONTENT_EXPORT extern const char kChromeUIBlobInternalsHost[]; CONTENT_EXPORT extern const char kChromeUIBrowserCrashHost[]; |