Commit feb24136 authored by James Wallace-Lee's avatar James Wallace-Lee Committed by Commit Bot

chrome://accessibility: copy trees directly from request

When the "Copy tree" button is pressed, request a new tree dump and copy
to clipboard directly from the response. If the tree is displayed on the
page, update it there as well. Users can copy the tree immediately,
rather than clicking "Show tree" and then copying from the DOM.

Change-Id: Iace12fac301e860571067645c7cf42ab76be7051
Reviewed-on: https://chromium-review.googlesource.com/c/1277552
Commit-Queue: James Wallace-Lee <jamwalla@chromium.org>
Reviewed-by: default avatarDavid Tseng <dtseng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602068}
parent 746af72c
...@@ -395,11 +395,15 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree( ...@@ -395,11 +395,15 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree(
std::string route_id_str; std::string route_id_str;
int process_id; int process_id;
int route_id; int route_id;
CHECK_EQ(2U, args->GetSize()); std::string request_type;
CHECK_EQ(3U, args->GetSize());
CHECK(args->GetString(0, &process_id_str)); CHECK(args->GetString(0, &process_id_str));
CHECK(args->GetString(1, &route_id_str)); CHECK(args->GetString(1, &route_id_str));
CHECK(base::StringToInt(process_id_str, &process_id)); CHECK(base::StringToInt(process_id_str, &process_id));
CHECK(base::StringToInt(route_id_str, &route_id)); CHECK(base::StringToInt(route_id_str, &route_id));
CHECK(args->GetString(2, &request_type));
CHECK(request_type == "showTree" || request_type == "copyTree");
request_type = "accessibility." + request_type;
AllowJavascript(); AllowJavascript();
content::RenderViewHost* rvh = content::RenderViewHost* rvh =
...@@ -409,7 +413,7 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree( ...@@ -409,7 +413,7 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree(
result->SetInteger(kProcessIdField, process_id); result->SetInteger(kProcessIdField, process_id);
result->SetInteger(kRouteIdField, route_id); result->SetInteger(kRouteIdField, route_id);
result->SetString("error", "Renderer no longer exists."); result->SetString("error", "Renderer no longer exists.");
CallJavascriptFunction("accessibility.showTree", *(result.get())); CallJavascriptFunction(request_type, *(result.get()));
return; return;
} }
...@@ -426,16 +430,20 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree( ...@@ -426,16 +430,20 @@ void AccessibilityUIMessageHandler::RequestWebContentsTree(
base::string16 accessibility_contents_utf16 = base::string16 accessibility_contents_utf16 =
web_contents->DumpAccessibilityTree(internal); web_contents->DumpAccessibilityTree(internal);
result->SetString("tree", base::UTF16ToUTF8(accessibility_contents_utf16)); result->SetString("tree", base::UTF16ToUTF8(accessibility_contents_utf16));
CallJavascriptFunction("accessibility.showTree", *(result.get())); CallJavascriptFunction(request_type, *(result.get()));
} }
void AccessibilityUIMessageHandler::RequestNativeUITree( void AccessibilityUIMessageHandler::RequestNativeUITree(
const base::ListValue* args) { const base::ListValue* args) {
std::string session_id_str; std::string session_id_str;
int session_id; int session_id;
CHECK_EQ(1U, args->GetSize()); std::string request_type;
CHECK_EQ(2U, args->GetSize());
CHECK(args->GetString(0, &session_id_str)); CHECK(args->GetString(0, &session_id_str));
CHECK(base::StringToInt(session_id_str, &session_id)); CHECK(base::StringToInt(session_id_str, &session_id));
CHECK(args->GetString(1, &request_type));
CHECK(request_type == "showTree" || request_type == "copyTree");
request_type = "accessibility." + request_type;
AllowJavascript(); AllowJavascript();
...@@ -449,7 +457,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree( ...@@ -449,7 +457,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
ui::AXPlatformNode::FromNativeWindow(native_window); ui::AXPlatformNode::FromNativeWindow(native_window);
result->SetKey("tree", result->SetKey("tree",
base::Value(RecursiveDumpAXPlatformNodeAsString(node, 0))); base::Value(RecursiveDumpAXPlatformNodeAsString(node, 0)));
CallJavascriptFunction("accessibility.showTree", *(result.get())); CallJavascriptFunction(request_type, *(result.get()));
return; return;
} }
} }
...@@ -459,7 +467,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree( ...@@ -459,7 +467,7 @@ void AccessibilityUIMessageHandler::RequestNativeUITree(
result->SetInteger(kSessionIdField, session_id); result->SetInteger(kSessionIdField, session_id);
result->SetString(kTypeField, "browser"); result->SetString(kTypeField, "browser");
result->SetString("error", "Browser no longer exists."); result->SetString("error", "Browser no longer exists.");
CallJavascriptFunction("accessibility.showTree", *(result.get())); CallJavascriptFunction(request_type, *(result.get()));
} }
// static // static
......
...@@ -59,15 +59,21 @@ cr.define('accessibility', function() { ...@@ -59,15 +59,21 @@ cr.define('accessibility', function() {
} }
function requestTree(data, element) { function requestTree(data, element) {
// The calling |element| is a button with an id of the format
// <treeId>:<requestType>, where requestType is one of 'showTree',
// 'copyTree'. Send the request type to C++ so is calls the corresponding
// function with the result.
let requestType = element.id.split(':')[1];
if (data.type == 'browser') { if (data.type == 'browser') {
let delay = $('native_ui_delay').value; let delay = $('native_ui_delay').value;
setTimeout(() => { setTimeout(() => {
chrome.send('requestNativeUITree', [String(data.sessionId)]); chrome.send(
'requestNativeUITree', [String(data.sessionId), requestType]);
}, delay); }, delay);
} else { } else {
chrome.send( chrome.send(
'requestWebContentsTree', 'requestWebContentsTree',
[String(data.processId), String(data.routeId)]); [String(data.processId), String(data.routeId), requestType]);
} }
} }
...@@ -166,6 +172,7 @@ cr.define('accessibility', function() { ...@@ -166,6 +172,7 @@ cr.define('accessibility', function() {
row.appendChild(createTreeButtons(data, row.id)); row.appendChild(createTreeButtons(data, row.id));
} else { } else {
row.appendChild(createShowAccessibilityTreeElement(data, row.id, false)); row.appendChild(createShowAccessibilityTreeElement(data, row.id, false));
row.appendChild(createCopyAccessibilityTreeElement(data, row.id));
if ('error' in data) if ('error' in data)
row.appendChild(createErrorMessageElement(data, row)); row.appendChild(createErrorMessageElement(data, row));
} }
...@@ -239,7 +246,7 @@ cr.define('accessibility', function() { ...@@ -239,7 +246,7 @@ cr.define('accessibility', function() {
let row = document.createElement('span'); let row = document.createElement('span');
row.appendChild(createShowAccessibilityTreeElement(data, id, true)); row.appendChild(createShowAccessibilityTreeElement(data, id, true));
if (navigator.clipboard) { if (navigator.clipboard) {
row.appendChild(createCopyAccessibilityTreeElement(id)); row.appendChild(createCopyAccessibilityTreeElement(data, id));
} }
row.appendChild(createHideAccessibilityTreeElement(id)); row.appendChild(createHideAccessibilityTreeElement(id));
row.appendChild(createAccessibilityTreeElement(data, id)); row.appendChild(createAccessibilityTreeElement(data, id));
...@@ -276,23 +283,11 @@ cr.define('accessibility', function() { ...@@ -276,23 +283,11 @@ cr.define('accessibility', function() {
return hide; return hide;
} }
function createCopyAccessibilityTreeElement(id) { function createCopyAccessibilityTreeElement(data, id) {
let copy = document.createElement('button'); let copy = document.createElement('button');
copy.textContent = 'Copy accessibility tree'; copy.textContent = 'Copy accessibility tree';
copy.id = id + ':copyTree'; copy.id = id + ':copyTree';
copy.addEventListener('click', () => { copy.addEventListener('click', requestTree.bind(this, data, copy));
let tree = $(id + ':tree');
navigator.clipboard.writeText(tree.textContent)
.then(() => {
copy.textContent = 'Copied to clipboard!';
setTimeout(() => {
copy.textContent = 'Copy accessibility tree';
}, 5000);
})
.catch(err => {
console.error('Unable to copy accessibility tree.', err);
});
});
return copy; return copy;
} }
...@@ -324,6 +319,37 @@ cr.define('accessibility', function() { ...@@ -324,6 +319,37 @@ cr.define('accessibility', function() {
formatRow(row, data); formatRow(row, data);
} }
// Called from C++
function copyTree(data) {
let id = getIdFromData(data);
let row = $(id);
if (!row)
return;
let copy = $(id + ':copyTree');
if ('tree' in data) {
navigator.clipboard.writeText(data.tree)
.then(() => {
copy.textContent = 'Copied to clipboard!';
setTimeout(() => {
copy.textContent = 'Copy accessibility tree';
}, 5000);
})
.catch(err => {
console.error('Unable to copy accessibility tree.', err);
});
} else if ('error' in data) {
console.error('Unable to copy accessibility tree.', data.error);
}
let tree = $(id + ':tree');
// If the tree is currently shown, update it since it may have changed.
if (tree && tree.style.display != 'none') {
showTree(data);
}
}
function createNativeUITreeElement(browser) { function createNativeUITreeElement(browser) {
let id = 'browser.' + browser.id; let id = 'browser.' + browser.id;
let row = document.createElement('div'); let row = document.createElement('div');
...@@ -346,7 +372,7 @@ cr.define('accessibility', function() { ...@@ -346,7 +372,7 @@ cr.define('accessibility', function() {
} }
// These are the functions we export so they can be called from C++. // These are the functions we export so they can be called from C++.
return {initialize: initialize, showTree: showTree}; return {copyTree: copyTree, initialize: initialize, showTree: showTree};
}); });
document.addEventListener('DOMContentLoaded', accessibility.initialize); document.addEventListener('DOMContentLoaded', accessibility.initialize);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment