Index: chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
diff --git a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
index 453b1d25c674e3d8bce6b869e26a8d65b126718e..3748fd18be24fcdce6f76a17a9cfc94d136037cf 100644 |
--- a/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
+++ b/chrome/browser/resources/chromeos/chromevox/cvox2/background/background.js |
@@ -796,6 +796,36 @@ Background.prototype = { |
cvox.BrailleCaptionsBackground.setActive( |
!cvox.BrailleCaptionsBackground.isEnabled()); |
return false; |
+ case 'copy': |
+ var textarea = document.createElement('textarea'); |
+ document.body.appendChild(textarea); |
+ textarea.focus(); |
+ document.execCommand('paste'); |
+ var clipboardContent = textarea.value; |
+ textarea.remove(); |
+ cvox.ChromeVox.tts.speak( |
+ Msgs.getMsg('copy', [clipboardContent]), cvox.QueueMode.FLUSH); |
+ this.pageSel_ = null; |
+ return true; |
+ case 'toggleSelection': |
+ if (!this.pageSel_) { |
+ this.pageSel_ = this.currentRange; |
+ } else { |
+ var root = this.currentRange_.start.node.root; |
+ if (root && root.anchorObject && root.focusObject) { |
+ var sel = new cursors.Range( |
+ new cursors.Cursor(root.anchorObject, root.anchorOffset), |
+ new cursors.Cursor(root.focusObject, root.focusOffset) |
+ ); |
+ var o = new Output() |
+ .format('@end_selection') |
+ .withSpeechAndBraille(sel, sel, Output.EventType.NAVIGATE) |
+ .go(); |
+ } |
+ this.pageSel_ = null; |
+ return false; |
+ } |
+ break; |
default: |
return true; |
} |
@@ -884,12 +914,64 @@ Background.prototype = { |
var prevRange = this.currentRange_; |
this.setCurrentRange(range); |
- range.select(); |
+ var o = new Output(); |
+ var selectedRange; |
+ if (this.pageSel_ && |
+ this.pageSel_.isValid() && |
+ range.isValid()) { |
+ // Compute the direction of the endpoints of each range. |
+ |
+ // Casts are ok because isValid checks node start and end nodes are |
+ // non-null; Closure just doesn't eval enough to see it. |
+ var startDir = |
+ AutomationUtil.getDirection(this.pageSel_.start.node, |
+ /** @type {!AutomationNode} */ (range.start.node)); |
+ var endDir = |
+ AutomationUtil.getDirection(this.pageSel_.end.node, |
+ /** @type {!AutomationNode} */ (range.end.node)); |
+ |
+ // Selection across roots isn't supported. |
+ var pageRootStart = this.pageSel_.start.node.root; |
+ var pageRootEnd = this.pageSel_.end.node.root; |
+ var curRootStart = range.start.node.root; |
+ var curRootEnd = range.end.node.root; |
+ |
+ // Disallow crossing over the start of the page selection and roots. |
+ if (startDir == Dir.BACKWARD || |
+ pageRootStart != pageRootEnd || |
+ pageRootStart != curRootStart || |
+ pageRootEnd != curRootEnd) { |
+ o.format('@end_selection'); |
+ this.pageSel_ = null; |
+ } else { |
+ // Expand or shrink requires different feedback. |
+ var msg; |
+ if (endDir == Dir.FORWARD && |
+ (this.pageSel_.end.node != range.end.node || |
+ this.pageSel_.end.index <= range.end.index)) { |
+ msg = '@selected'; |
+ } else { |
+ msg = '@unselected'; |
+ selectedRange = prevRange; |
+ } |
+ this.pageSel_ = new cursors.Range( |
+ this.pageSel_.start, |
+ range.end |
+ ); |
+ if (this.pageSel_) |
+ this.pageSel_.select(); |
+ } |
+ } else { |
+ range.select(); |
+ } |
- var o = new Output().withRichSpeechAndBraille( |
- range, prevRange, Output.EventType.NAVIGATE) |
+ o.withRichSpeechAndBraille( |
+ selectedRange || range, prevRange, Output.EventType.NAVIGATE) |
.withQueueMode(cvox.QueueMode.FLUSH); |
+ if (msg) |
+ o.format(msg); |
+ |
for (var prop in opt_speechProps) |
o.format('!' + prop); |