blob: 95cfcc9c068849f4939e79f54a7878da1c65ac4b [file] [log] [blame]
// 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';
// Note: keep these values in sync with the values in
// content/common/accessibility_mode_enums.h
const AXMode = {
kNativeAPIs: 1 << 0,
kWebContents: 1 << 1,
kInlineTextBoxes: 1 << 2,
kScreenReader: 1 << 3,
kHTML: 1 << 4,
get kAXModeWebContentsOnly() {
return AXMode.kWebContents | AXMode.kInlineTextBoxes |
AXMode.kScreenReader | AXMode.kHTML;
},
get kAXModeComplete() {
return AXMode.kNativeAPIs | AXMode.kWebContents |
AXMode.kInlineTextBoxes | AXMode.kScreenReader | AXMode.kHTML;
}
};
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 [];
}
function toggleAccessibility(data, element, mode) {
chrome.send(
'toggleAccessibility',
[String(data.processId), String(data.routeId), mode]);
document.location.reload();
}
function requestWebContentsTree(data, element) {
chrome.send(
'requestWebContentsTree',
[String(data.processId), String(data.routeId)]);
}
function initialize() {
console.log('initialize');
var data = requestData();
bindCheckbox('native', data['native']);
bindCheckbox('web', data['web']);
bindCheckbox('text', data['text']);
bindCheckbox('screenreader', data['screenreader']);
bindCheckbox('html', data['html']);
bindCheckbox('internal', data['internal']);
$('pages').textContent = '';
var list = data['list'];
for (var i = 0; i < list.length; i++) {
addToPagesList(list[i]);
}
var showNativeUI = $('showNativeUI');
showNativeUI.addEventListener('click', function() {
var delay = $('native_ui_delay').value;
setTimeout(function() {
chrome.send('requestNativeUITree');
}, delay);
});
}
function bindCheckbox(name, value) {
if (value == 'on')
$(name).checked = true;
if (value == 'disabled') {
$(name).disabled = true;
$(name).labels[0].classList.add('disabled');
}
$(name).addEventListener('change', function() {
chrome.send('setGlobalFlag', [name, $(name).checked]);
document.location.reload();
});
}
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 siteInfo = document.createElement('div');
var properties = ['favicon_url', 'name', 'url'];
for (var j = 0; j < properties.length; j++)
siteInfo.appendChild(formatValue(data, properties[j]));
row.appendChild(siteInfo);
row.appendChild(createModeElement(AXMode.kNativeAPIs, data));
row.appendChild(createModeElement(AXMode.kWebContents, data));
row.appendChild(createModeElement(AXMode.kInlineTextBoxes, data));
row.appendChild(createModeElement(AXMode.kScreenReader, data));
row.appendChild(createModeElement(AXMode.kHTML, data));
row.appendChild(document.createTextNode(' | '));
if ('tree' in data) {
row.appendChild(createShowAccessibilityTreeElement(data, row, true));
if (navigator.clipboard) {
row.appendChild(createCopyAccessibilityTreeElement(row.id));
}
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 insertHeadingInline(parentElement, headingText) {
var h4 = document.createElement('h4');
h4.textContent = headingText;
h4.style.display = 'inline';
parentElement.appendChild(h4);
}
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');
var content = ' ' + text + ' ';
if (property == 'name') {
insertHeadingInline(span, content);
} else {
span.textContent = content;
}
span.className = property;
return span;
}
function getNameForAccessibilityMode(mode) {
switch (mode) {
case AXMode.kNativeAPIs:
return 'Native';
case AXMode.kWebContents:
return 'Web';
case AXMode.kInlineTextBoxes:
return 'Inline text';
case AXMode.kScreenReader:
return 'Screen reader';
case AXMode.kHTML:
return 'HTML';
}
return 'unknown';
}
function createModeElement(mode, data) {
var currentMode = data['a11y_mode'];
var link = document.createElement('a', 'action-link');
link.setAttribute('role', 'button');
var stateText = ((currentMode & mode) != 0) ? 'true' : 'false';
link.textContent = getNameForAccessibilityMode(mode) + ': ' + stateText;
link.setAttribute('aria-pressed', stateText);
link.addEventListener(
'click', toggleAccessibility.bind(this, data, link, mode));
return link;
}
function createShowAccessibilityTreeElement(data, row, opt_refresh) {
let show = document.createElement('button');
if (opt_refresh) {
show.textContent = 'Refresh accessibility tree';
} else {
show.textContent = 'Show accessibility tree';
}
show.id = row.id + ':showTree';
show.addEventListener(
'click', requestWebContentsTree.bind(this, data, show));
return show;
}
function createHideAccessibilityTreeElement(id) {
let hide = document.createElement('button');
hide.textContent = 'Hide accessibility tree';
hide.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 hide;
}
function createCopyAccessibilityTreeElement(id) {
let copy = document.createElement('button');
copy.textContent = 'Copy accessibility tree';
copy.addEventListener('click', () => {
// |id| refers to the div containing accessibility information for a
// single page, so there should only be one <pre> child containing the
// accessibility tree as a string.
let tree = $(id).getElementsByTagName('pre')[0];
navigator.clipboard.writeText(tree.textContent)
.then(() => {
copy.textContent = 'Copied to clipboard!';
setTimeout(() => {
copy.textContent = 'Copy accessibility tree';
}, 5000);
})
.catch(err => {
console.err('Unable to copy accessibility tree.', err);
});
});
return copy;
}
function createErrorMessageElement(data) {
var errorMessageElement = document.createElement('div');
var errorMessage = data.error;
errorMessageElement.innerHTML = errorMessage + '&nbsp;';
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;
}
// Called from C++
function showTree(data) {
var id = data.processId + '.' + data.routeId;
var row = $(id);
if (!row)
return;
row.textContent = '';
formatRow(row, data);
}
// Called from C++
function showNativeUITree(data) {
var treeContainer = document.querySelector('#native_ui div');
if (!treeContainer) {
var treeContainer = document.createElement('div');
$('native_ui').appendChild(treeContainer);
}
var dstIds =
new Set(Array.prototype.map.call(treeContainer.children, el => el.id));
data.forEach(function(browser) {
var srcId = 'browser_' + browser.id;
if (dstIds.has(srcId)) {
// Update browser windows in place.
dstIds.delete(srcId);
var title = document.querySelector('#' + srcId + ' h4');
title.textContent = browser.title;
var tree = document.querySelector('#' + srcId + ' pre');
tree.textContent = browser.tree;
} else {
// Add new browser windows.
var browserElement = createNativeUITreeElement(browser);
treeContainer.appendChild(browserElement);
}
});
dstIds.forEach(function(dstId) {
// Remove browser windows that no longer exist.
var browserElement = document.querySelector('#' + dstId);
treeContainer.removeChild(browserElement);
});
}
function createNativeUITreeElement(browser) {
var details = document.createElement('details');
var summary = document.createElement('summary');
var treeElement = document.createElement('pre');
insertHeadingInline(summary, browser.title);
treeElement.textContent = browser.tree;
details.id = 'browser_' + browser.id;
details.appendChild(summary);
details.appendChild(treeElement);
return details;
}
function createAccessibilityTreeElement(data) {
var treeElement = document.createElement('pre');
var tree = data.tree;
treeElement.textContent = tree;
return treeElement;
}
// These are the functions we export so they can be called from C++.
return {
initialize: initialize,
showTree: showTree,
showNativeUITree: showNativeUITree
};
});
document.addEventListener('DOMContentLoaded', accessibility.initialize);