diff --git a/accessible/jsat/AccessFu.jsm b/accessible/jsat/AccessFu.jsm index 56e3049f629..8f5dae6f7ff 100644 --- a/accessible/jsat/AccessFu.jsm +++ b/accessible/jsat/AccessFu.jsm @@ -885,6 +885,7 @@ var Input = { }, setEditState: function setEditState(aEditState) { + Logger.debug(() => { return ['setEditState', JSON.stringify(aEditState)] }); this.editState = aEditState; }, diff --git a/accessible/jsat/EventManager.jsm b/accessible/jsat/EventManager.jsm index 35630b523e3..cbad990e7f4 100644 --- a/accessible/jsat/EventManager.jsm +++ b/accessible/jsat/EventManager.jsm @@ -191,38 +191,18 @@ this.EventManager.prototype = { } case Events.TEXT_CARET_MOVED: { - let acc = aEvent.accessible; - let characterCount = acc. - QueryInterface(Ci.nsIAccessibleText).characterCount; + let acc = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText); let caretOffset = aEvent. QueryInterface(Ci.nsIAccessibleCaretMoveEvent).caretOffset; - // Update editing state, both for presenter and other things - let state = Utils.getState(acc); - let editState = { - editing: state.contains(States.EDITABLE), - multiline: state.contains(States.MULTI_LINE), - atStart: caretOffset == 0, - atEnd: caretOffset == characterCount - }; - - // Not interesting - if (!editState.editing && editState.editing == this.editState.editing) - break; - - if (editState.editing != this.editState.editing) - this.present(Presentation.editingModeChanged(editState.editing)); - - if (editState.editing != this.editState.editing || - editState.multiline != this.editState.multiline || - editState.atEnd != this.editState.atEnd || - editState.atStart != this.editState.atStart) - this.sendMsgFunc("AccessFu:Input", editState); - + // We could get a caret move in an accessible that is not focused, + // it doesn't mean we are not on any editable accessible. just not + // on this one.. + if (Utils.getState(acc).contains(States.FOCUSED)) { + this._setEditingMode(aEvent, caretOffset); + } this.present(Presentation.textSelectionChanged(acc.getText(0,-1), caretOffset, caretOffset, 0, 0, aEvent.isFromUserInput)); - - this.editState = editState; break; } case Events.OBJECT_ATTRIBUTE_CHANGED: @@ -268,6 +248,7 @@ this.EventManager.prototype = { // Put vc where the focus is at let acc = aEvent.accessible; let doc = aEvent.accessibleDocument; + this._setEditingMode(aEvent); if ([Roles.CHROME_WINDOW, Roles.DOCUMENT, Roles.APPLICATION].indexOf(acc.role) < 0) { @@ -293,6 +274,54 @@ this.EventManager.prototype = { } }, + _setEditingMode: function _setEditingMode(aEvent, aCaretOffset) { + let acc = aEvent.accessible; + let accText, characterCount; + let caretOffset = aCaretOffset; + + try { + accText = acc.QueryInterface(Ci.nsIAccessibleText); + } catch (e) { + // No text interface on this accessible. + } + + if (accText) { + characterCount = accText.characterCount; + if (caretOffset === undefined) { + caretOffset = accText.caretOffset; + } + } + + // Update editing state, both for presenter and other things + let state = Utils.getState(acc); + + let editState = { + editing: state.contains(States.EDITABLE) && + state.contains(States.FOCUSED), + multiline: state.contains(States.MULTI_LINE), + atStart: caretOffset === 0, + atEnd: caretOffset === characterCount + }; + + // Not interesting + if (!editState.editing && editState.editing === this.editState.editing) { + return; + } + + if (editState.editing !== this.editState.editing) { + this.present(Presentation.editingModeChanged(editState.editing)); + } + + if (editState.editing !== this.editState.editing || + editState.multiline !== this.editState.multiline || + editState.atEnd !== this.editState.atEnd || + editState.atStart !== this.editState.atStart) { + this.sendMsgFunc("AccessFu:Input", editState); + } + + this.editState = editState; + }, + _handleShow: function _handleShow(aEvent) { let {liveRegion, isPolite} = this._handleLiveRegion(aEvent, ['additions', 'all']); diff --git a/accessible/tests/mochitest/jsat/doc_content_text.html b/accessible/tests/mochitest/jsat/doc_content_text.html index 6abdfb148ee..4e73dc6e77c 100644 --- a/accessible/tests/mochitest/jsat/doc_content_text.html +++ b/accessible/tests/mochitest/jsat/doc_content_text.html @@ -10,5 +10,6 @@

You're a good guy, mon frere. That means brother in French. I don't know how I know that. I took four years of Spanish.

+ \ No newline at end of file diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js index e9e5fe5c2c2..1a1a6f0f9bc 100644 --- a/accessible/tests/mochitest/jsat/jsatcommon.js +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -177,7 +177,8 @@ function AccessFuContentTest(aFuncResultPairs) { } AccessFuContentTest.prototype = { - currentPair: null, + expected: [], + currentAction: null, start: function(aFinishedCallback) { Logger.logLevel = Logger.DEBUG; @@ -236,6 +237,7 @@ AccessFuContentTest.prototype = { } aMessageManager.addMessageListener('AccessFu:Present', this); + aMessageManager.addMessageListener('AccessFu:Input', this); aMessageManager.addMessageListener('AccessFu:CursorCleared', this); aMessageManager.addMessageListener('AccessFu:Ready', function () { aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback); @@ -252,17 +254,25 @@ AccessFuContentTest.prototype = { }, pump: function() { - this.currentPair = this.queue.shift(); + this.expected.shift(); + if (this.expected.length) { + return; + } - if (this.currentPair) { - if (typeof this.currentPair[0] === 'function') { - this.currentPair[0](this.mms[0]); - } else if (this.currentPair[0]) { - this.mms[0].sendAsyncMessage(this.currentPair[0].name, - this.currentPair[0].json); + var currentPair = this.queue.shift(); + + if (currentPair) { + this.currentAction = currentPair[0]; + if (typeof this.currentAction === 'function') { + this.currentAction(this.mms[0]); + } else if (this.currentAction) { + this.mms[0].sendAsyncMessage(this.currentAction.name, + this.currentAction.json); } - if (!this.currentPair[1]) { + this.expected = currentPair.slice(1, currentPair.length); + + if (!this.expected[0]) { this.pump(); } } else { @@ -271,12 +281,12 @@ AccessFuContentTest.prototype = { }, receiveMessage: function(aMessage) { - if (!this.currentPair) { + var expected = this.expected[0]; + + if (!expected) { return; } - var expected = this.currentPair[1] || {}; - // |expected| can simply be a name of a message, no more further testing. if (aMessage.name === expected) { ok(true, 'Received ' + expected); @@ -284,17 +294,20 @@ AccessFuContentTest.prototype = { return; } - var speech = this.extractUtterance(aMessage.json); - var android = this.extractAndroid(aMessage.json, expected.android); - if ((speech && expected.speak) || (android && expected.android)) { + var editState = this.extractEditeState(aMessage); + var speech = this.extractUtterance(aMessage); + var android = this.extractAndroid(aMessage, expected.android); + if ((speech && expected.speak) + || (android && expected.android) + || (editState && expected.editState)) { if (expected.speak) { var checkFunc = SimpleTest[expected.speak_checkFunc] || isDeeply; checkFunc.apply(SimpleTest, [speech, expected.speak, 'spoken: ' + JSON.stringify(speech) + ' expected: ' + JSON.stringify(expected.speak) + - ' after: ' + (typeof this.currentPair[0] === 'function' ? - this.currentPair[0].toString() : - JSON.stringify(this.currentPair[0]))]); + ' after: ' + (typeof this.currentAction === 'function' ? + this.currentAction.toString() : + JSON.stringify(this.currentAction))]); } if (expected.android) { @@ -303,10 +316,20 @@ AccessFuContentTest.prototype = { this.lazyCompare(android, expected.android)); } + if (expected.editState) { + var checkFunc = SimpleTest[expected.editState_checkFunc] || isDeeply; + checkFunc.apply(SimpleTest, [editState, expected.editState, + 'editState: ' + JSON.stringify(editState) + + ' expected: ' + JSON.stringify(expected.editState) + + ' after: ' + (typeof this.currentAction === 'function' ? + this.currentAction.toString() : + JSON.stringify(this.currentAction))]); + } + if (expected.focused) { var doc = currentTabDocument(); is(doc.activeElement, doc.querySelector(expected.focused), - 'Correct element is focused'); + 'Correct element is focused: ' + expected.focused); } this.pump(); @@ -337,12 +360,20 @@ AccessFuContentTest.prototype = { return [matches, delta.join(' ')]; }, - extractUtterance: function(aData) { - if (!aData) { + extractEditeState: function(aMessage) { + if (!aMessage || aMessage.name !== 'AccessFu:Input') { return null; } - for (var output of aData) { + return aMessage.json; + }, + + extractUtterance: function(aMessage) { + if (!aMessage || aMessage.name !== 'AccessFu:Present') { + return null; + } + + for (var output of aMessage.json) { if (output && output.type === 'B2G') { if (output.details && output.details.data[0].string !== 'clickAction') { return output.details.data; @@ -353,12 +384,12 @@ AccessFuContentTest.prototype = { return null; }, - extractAndroid: function(aData, aExpectedEvents) { - if (!aData) { + extractAndroid: function(aMessage, aExpectedEvents) { + if (!aMessage || aMessage.name !== 'AccessFu:Present') { return null; } - for (var output of aData) { + for (var output of aMessage.json) { if (output && output.type === 'Android') { for (var i in output.details) { // Only extract if event types match expected event types. diff --git a/accessible/tests/mochitest/jsat/test_content_text.html b/accessible/tests/mochitest/jsat/test_content_text.html index 5b6e1705fb1..328e0f21510 100644 --- a/accessible/tests/mochitest/jsat/test_content_text.html +++ b/accessible/tests/mochitest/jsat/test_content_text.html @@ -132,27 +132,36 @@ // Editable text tests. [ContentMessages.focusSelector('textarea'), { - speak: ['Please refrain from Mayoneggs during this ' + - 'salmonella scare.', {string: 'textarea'}] - }], - [null, { // When we first focus, caret is at 0. - android: [{ - eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED, - brailleOutput: { - selectionStart: 0, - selectionEnd: 0 - } - }] + editState: { + editing: true, + multiline: true, + atStart: true, + atEnd: false } - ], + }, { + speak: ['Please refrain from Mayoneggs during this ' + + 'salmonella scare.', {string: 'textarea'}] + }, { // When we first focus, caret is at 0. + android: [{ + eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED, + brailleOutput: { + selectionStart: 0, + selectionEnd: 0 + } + }] + }], [ContentMessages.activateCurrent(10), { android: [{ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, fromIndex: 0, toIndex: 10 }] - }], - [null, { + }, { + editState: { editing: true, + multiline: true, + atStart: false, + atEnd: false } + }, { android: [{ eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED, brailleOutput: { @@ -167,8 +176,7 @@ fromIndex: 10, toIndex: 20 }] - }], - [null, { + }, { android: [{ eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED, brailleOutput: { @@ -217,7 +225,66 @@ eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY, fromIndex: 53, toIndex: 59 - }] + }], + focused: 'textarea' + }], + + // bug xxx + [ContentMessages.simpleMoveNext, { + speak: ['So we don\'t get dessert?', {string: 'label'}], + focused: 'html' + }, { + editState: { + editing: false, + multiline: false, + atStart: true, + atEnd: false } + }], + [ContentMessages.simpleMoveNext, { + speak: [{ string : 'entry' }], + focused: 'html' + }], + [ContentMessages.activateCurrent(0), { + editState: { + editing: true, + multiline: false, + atStart: true, + atEnd: true + }, + focused: 'input[type=text]' + }], + [ContentMessages.simpleMovePrevious, { + editState: { + editing: false, + multiline: false, + atStart: true, + atEnd: false + }, + focused: 'html' + }], + [ContentMessages.simpleMoveNext, { + speak: [{ string : 'entry' }], + focused: 'html' + }], + [ContentMessages.activateCurrent(0), { + editState: { + editing: true, + multiline: false, + atStart: true, + atEnd: true + }, + focused: 'input[type=text]' + }], + [ContentMessages.simpleMovePrevious, { + speak: [ 'So we don\'t get dessert?', {string: 'label'} ] + }, { + editState: { + editing: false, + multiline: false, + atStart: true, + atEnd: false + }, + focused: 'html' }] ]);