mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 698437 - Allow spatial navigation with directional controller. r=kats
This commit is contained in:
parent
9c90069d5d
commit
01b53453c2
@ -24,6 +24,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=843725
|
||||
* various key events while it is in various states.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks arrow keydown events:
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
|
@ -26,6 +26,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=853525
|
||||
* ugly rounding errors.
|
||||
**/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks arrow keydown events:
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
test();
|
||||
SimpleTest.finish();
|
||||
|
@ -22,6 +22,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=633058
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off Spatial Navigation so that the 'keypress' event fires.
|
||||
SpecialPowers.setBoolPref('snav.enabled', false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
var nbExpectedKeyPress = 8;
|
||||
var inputGotKeyPress = 0;
|
||||
|
@ -19,6 +19,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=674558
|
||||
|
||||
/** Test for Bug 674558 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks VK_RIGHT and VK_LEFT keydown
|
||||
// events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(function() {
|
||||
function textAreaCtor() {
|
||||
return document.createElement("textarea");
|
||||
|
@ -41,6 +41,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=265203
|
||||
|
||||
/** Test for Bug 265203 **/
|
||||
|
||||
// Turn off spatial navigation because it hijacks VK_RIGHT and VK_LEFT keydown
|
||||
// events
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
var gTestStarted = false;
|
||||
var expectedResult = [ null, 0, null ];
|
||||
var nextTest;
|
||||
|
@ -23,7 +23,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=795785
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Turn off spatial navigation because it hijacks arrow key events and VK_RETURN
|
||||
// events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForFocus(runTests);
|
||||
|
||||
var textarea = document.getElementById("textarea");
|
||||
|
@ -28,6 +28,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=345267
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 345267 **/
|
||||
|
||||
// Turn off Spatial Navigation to stop if from hijacking "left" keypress event.
|
||||
SpecialPowers.setBoolPref('snav.enabled', false);
|
||||
|
||||
is($("d1").value, "abcde",
|
||||
"Displayed initial value should not be truncated by maxlength");
|
||||
is($("u1").value, "abcdef",
|
||||
|
@ -74,6 +74,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=365410
|
||||
|
||||
/** Test for Bug 365410 **/
|
||||
|
||||
// Turn off spatial nav so that it does not hijack the up and down events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
function pageUpDownTest(id,index) {
|
||||
var elm = document.getElementById(id);
|
||||
elm.focus();
|
||||
|
@ -37,6 +37,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=563642
|
||||
|
||||
/** Test for Bug 563642 **/
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks down and up key events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
function pageUpDownTest(id,index) {
|
||||
var elm = document.getElementById(id);
|
||||
elm.focus();
|
||||
|
@ -11,6 +11,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=291082
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 291082 **/
|
||||
|
||||
|
||||
// Turn off Spatial Navigation because it hijacks arrow keydown events.
|
||||
SpecialPowers.setBoolPref("snav.enabled", false);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function preventDefault(event) {
|
||||
|
@ -787,3 +787,5 @@ pref("general.useragent.override.youtube.com", "Android; Tablet;#Android; Mobile
|
||||
// When true, phone number linkification is enabled.
|
||||
pref("browser.ui.linkify.phone", false);
|
||||
|
||||
// Enables/disables Spatial Navigation
|
||||
pref("snav.enabled", true);
|
||||
|
@ -18,6 +18,7 @@ Cu.import("resource://gre/modules/JNI.jsm");
|
||||
Cu.import('resource://gre/modules/Payment.jsm');
|
||||
Cu.import("resource://gre/modules/PermissionPromptHelper.jsm");
|
||||
Cu.import("resource://gre/modules/ContactService.jsm");
|
||||
Cu.import("resource://gre/modules/SpatialNavigation.jsm");
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
Cu.import("resource://gre/modules/accessibility/AccessFu.jsm");
|
||||
@ -4111,6 +4112,9 @@ var BrowserEventHandler = {
|
||||
BrowserApp.deck.addEventListener("touchstart", this, true);
|
||||
BrowserApp.deck.addEventListener("click", InputWidgetHelper, true);
|
||||
BrowserApp.deck.addEventListener("click", SelectHelper, true);
|
||||
|
||||
SpatialNavigation.init(BrowserApp.deck, null);
|
||||
|
||||
document.addEventListener("MozMagnifyGesture", this, true);
|
||||
|
||||
Services.prefs.addObserver("browser.zoom.reflowOnZoom", this, false);
|
||||
|
@ -4464,3 +4464,6 @@ pref("urlclassifier.malware_table", "goog-malware-shavar");
|
||||
pref("urlclassifier.phish_table", "goog-phish-shavar");
|
||||
pref("urlclassifier.download_block_table", "goog-badbinurl-shavar");
|
||||
pref("urlclassifier.download_allow_table", "goog-downloadwhite-digest256");
|
||||
|
||||
// Turn off Spatial navigation by default.
|
||||
pref("snav.enabled", false);
|
||||
|
505
toolkit/modules/SpatialNavigation.jsm
Normal file
505
toolkit/modules/SpatialNavigation.jsm
Normal file
@ -0,0 +1,505 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Import this module through
|
||||
*
|
||||
* Components.utils.import("resource://gre/modules/SpatialNavigation.jsm");
|
||||
*
|
||||
* Usage: (Literal class)
|
||||
*
|
||||
* SpatialNavigation.init(browser_element, optional_callback);
|
||||
*
|
||||
* optional_callback will be called when a new element is focused.
|
||||
*
|
||||
* function optional_callback(element) {}
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["SpatialNavigation"];
|
||||
|
||||
var SpatialNavigation = {
|
||||
init: function(browser, callback) {
|
||||
browser.addEventListener("keydown", function (event) {
|
||||
_onInputKeyPress(event, callback);
|
||||
}, true);
|
||||
}
|
||||
};
|
||||
|
||||
// Private stuff
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu["import"]("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
let eventListenerService = Cc["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Ci.nsIEventListenerService);
|
||||
let focusManager = Cc["@mozilla.org/focus-manager;1"]
|
||||
.getService(Ci.nsIFocusManager);
|
||||
let windowMediator = Cc['@mozilla.org/appshell/window-mediator;1']
|
||||
.getService(Ci.nsIWindowMediator);
|
||||
|
||||
// Debug helpers:
|
||||
function dump(a) {
|
||||
Services.console.logStringMessage("SpatialNavigation: " + a);
|
||||
}
|
||||
|
||||
function dumpRect(desc, rect) {
|
||||
dump(desc + " " + Math.round(rect.left) + " " + Math.round(rect.top) + " " +
|
||||
Math.round(rect.right) + " " + Math.round(rect.bottom) + " width:" +
|
||||
Math.round(rect.width) + " height:" + Math.round(rect.height));
|
||||
}
|
||||
|
||||
function dumpNodeCoord(desc, node) {
|
||||
let rect = node.getBoundingClientRect();
|
||||
dump(desc + " " + node.tagName + " x:" + Math.round(rect.left + rect.width/2) +
|
||||
" y:" + Math.round(rect.top + rect.height / 2));
|
||||
}
|
||||
|
||||
// modifier values
|
||||
|
||||
const kAlt = "alt";
|
||||
const kShift = "shift";
|
||||
const kCtrl = "ctrl";
|
||||
const kNone = "none";
|
||||
|
||||
function _onInputKeyPress (event, callback) {
|
||||
//If Spatial Navigation isn't enabled, return.
|
||||
if (!PrefObserver['enabled']) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use whatever key value is available (either keyCode or charCode).
|
||||
// It might be useful for addons or whoever wants to set different
|
||||
// key to be used here (e.g. "a", "F1", "arrowUp", ...).
|
||||
var key = event.which || event.keyCode;
|
||||
|
||||
if (key != PrefObserver['keyCodeDown'] &&
|
||||
key != PrefObserver['keyCodeRight'] &&
|
||||
key != PrefObserver['keyCodeUp'] &&
|
||||
key != PrefObserver['keyCodeLeft'] &&
|
||||
key != PrefObserver['keyCodeReturn']) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key == PrefObserver['keyCodeReturn']) {
|
||||
// We report presses of the action button on a gamepad "A" as the return
|
||||
// key to the DOM. The behaviour of hitting the return key and clicking an
|
||||
// element is the same for some elements, but not all, so we handle the
|
||||
// ones we want (like the Select element) here:
|
||||
if (event.target instanceof Ci.nsIDOMHTMLSelectElement &&
|
||||
event.target.click) {
|
||||
event.target.click();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
} else {
|
||||
// Leave the action key press to get reported to the DOM as a return
|
||||
// keypress.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is not using the modifiers it should, return.
|
||||
if (!event.altKey && PrefObserver['modifierAlt'] ||
|
||||
!event.shiftKey && PrefObserver['modifierShift'] ||
|
||||
!event.crtlKey && PrefObserver['modifierCtrl']) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentlyFocused = event.target;
|
||||
let currentlyFocusedWindow = currentlyFocused.ownerDocument.defaultView;
|
||||
let bestElementToFocus = null;
|
||||
|
||||
// If currentlyFocused is an nsIDOMHTMLBodyElement then the page has just been
|
||||
// loaded, and this is the first keypress in the page.
|
||||
if (currentlyFocused instanceof Ci.nsIDOMHTMLBodyElement) {
|
||||
focusManager.moveFocus(currentlyFocusedWindow, null, focusManager.MOVEFOCUS_FIRST, 0);
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
let windowUtils = currentlyFocusedWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
let cssPageRect = _getRootBounds(windowUtils);
|
||||
let searchRect = _getSearchRect(currentlyFocused, key, cssPageRect);
|
||||
|
||||
let nodes = {};
|
||||
nodes.length = 0;
|
||||
|
||||
let searchRectOverflows = false;
|
||||
|
||||
while (!bestElementToFocus && !searchRectOverflows) {
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
case PrefObserver['keyCodeRight']: {
|
||||
if (searchRect.top < cssPageRect.top &&
|
||||
searchRect.bottom > cssPageRect.bottom) {
|
||||
searchRectOverflows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PrefObserver['keyCodeUp']:
|
||||
case PrefObserver['keyCodeDown']: {
|
||||
if (searchRect.left < cssPageRect.left &&
|
||||
searchRect.right > cssPageRect.right) {
|
||||
searchRectOverflows = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nodes = windowUtils.nodesFromRect(searchRect.left, searchRect.top,
|
||||
0, searchRect.width, searchRect.height, 0,
|
||||
true, false);
|
||||
// Make the search rectangle "wider": double it's size in the direction
|
||||
// that is not the keypress.
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
case PrefObserver['keyCodeRight']: {
|
||||
searchRect.top = searchRect.top - (searchRect.height / 2);
|
||||
searchRect.bottom = searchRect.top + (searchRect.height * 2);
|
||||
searchRect.height = searchRect.height * 2;
|
||||
break;
|
||||
}
|
||||
case PrefObserver['keyCodeUp']:
|
||||
case PrefObserver['keyCodeDown']: {
|
||||
searchRect.left = searchRect.left - (searchRect.width / 2);
|
||||
searchRect.right = searchRect.left + (searchRect.width * 2);
|
||||
searchRect.width = searchRect.width * 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
bestElementToFocus = _getBestToFocus(nodes, key, currentlyFocused);
|
||||
}
|
||||
|
||||
|
||||
if (bestElementToFocus === null) {
|
||||
// Couldn't find an element to focus.
|
||||
return;
|
||||
}
|
||||
|
||||
focusManager.setFocus(bestElementToFocus, focusManager.FLAG_SHOWRING);
|
||||
|
||||
//if it is a text element, select all.
|
||||
if ((bestElementToFocus instanceof Ci.nsIDOMHTMLInputElement &&
|
||||
bestElementToFocus.mozIsTextField(false)) ||
|
||||
bestElementToFocus instanceof Ci.nsIDOMHTMLTextAreaElement) {
|
||||
bestElementToFocus.selectionStart = 0;
|
||||
bestElementToFocus.selectionEnd = bestElementToFocus.textLength;
|
||||
}
|
||||
|
||||
if (callback != undefined) {
|
||||
callback(bestElementToFocus);
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// Returns the bounds of the page relative to the viewport.
|
||||
function _getRootBounds(windowUtils) {
|
||||
let cssPageRect = windowUtils.getRootBounds();
|
||||
|
||||
let scrollX = {};
|
||||
let scrollY = {};
|
||||
windowUtils.getScrollXY(false, scrollX, scrollY);
|
||||
|
||||
let cssPageRectCopy = {};
|
||||
|
||||
cssPageRectCopy.right = cssPageRect.right - scrollX.value;
|
||||
cssPageRectCopy.left = cssPageRect.left - scrollX.value;
|
||||
cssPageRectCopy.top = cssPageRect.top - scrollY.value;
|
||||
cssPageRectCopy.bottom = cssPageRect.bottom - scrollY.value;
|
||||
cssPageRectCopy.width = cssPageRect.width;
|
||||
cssPageRectCopy.height = cssPageRect.height;
|
||||
|
||||
return cssPageRectCopy;
|
||||
}
|
||||
|
||||
// Returns the best node to focus from the list of nodes returned by the hit
|
||||
// test.
|
||||
function _getBestToFocus(nodes, key, currentlyFocused) {
|
||||
let best = null;
|
||||
let bestDist;
|
||||
let bestMid;
|
||||
let nodeMid;
|
||||
let currentlyFocusedMid = _getMidpoint(currentlyFocused);
|
||||
let currentlyFocusedRect = currentlyFocused.getBoundingClientRect();
|
||||
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
// Reject the currentlyFocused, and all node types we can't focus
|
||||
if (!_canFocus(nodes[i]) || nodes[i] === currentlyFocused) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reject all nodes that aren't "far enough" in the direction of the
|
||||
// keypress
|
||||
nodeMid = _getMidpoint(nodes[i]);
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
if (nodeMid.x >= (currentlyFocusedMid.x - currentlyFocusedRect.width / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeRight']:
|
||||
if (nodeMid.x <= (currentlyFocusedMid.x + currentlyFocusedRect.width / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeUp']:
|
||||
if (nodeMid.y >= (currentlyFocusedMid.y - currentlyFocusedRect.height / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeDown']:
|
||||
if (nodeMid.y <= (currentlyFocusedMid.y + currentlyFocusedRect.height / 2)) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Initialize best to the first viable value:
|
||||
if (!best) {
|
||||
best = nodes[i];
|
||||
bestDist = _spatialDistance(best, currentlyFocused);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Of the remaining nodes, pick the one closest to the currently focused
|
||||
// node.
|
||||
let curDist = _spatialDistance(nodes[i], currentlyFocused);
|
||||
if (curDist > bestDist) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bestMid = _getMidpoint(best);
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
if (nodeMid.x > bestMid.x) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeRight']:
|
||||
if (nodeMid.x < bestMid.x) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeUp']:
|
||||
if (nodeMid.y > bestMid.y) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
case PrefObserver['keyCodeDown']:
|
||||
if (nodeMid.y < bestMid.y) {
|
||||
best = nodes[i];
|
||||
bestDist = curDist;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
// Returns the midpoint of a node.
|
||||
function _getMidpoint(node) {
|
||||
let mid = {};
|
||||
let box = node.getBoundingClientRect();
|
||||
mid.x = box.left + (box.width / 2);
|
||||
mid.y = box.top + (box.height / 2);
|
||||
|
||||
return mid;
|
||||
}
|
||||
|
||||
// Returns true if the node is a type that we want to focus, false otherwise.
|
||||
function _canFocus(node) {
|
||||
if (node instanceof Ci.nsIDOMHTMLLinkElement ||
|
||||
node instanceof Ci.nsIDOMHTMLAnchorElement) {
|
||||
return true;
|
||||
}
|
||||
if ((node instanceof Ci.nsIDOMHTMLButtonElement ||
|
||||
node instanceof Ci.nsIDOMHTMLInputElement ||
|
||||
node instanceof Ci.nsIDOMHTMLLinkElement ||
|
||||
node instanceof Ci.nsIDOMHTMLOptGroupElement ||
|
||||
node instanceof Ci.nsIDOMHTMLSelectElement ||
|
||||
node instanceof Ci.nsIDOMHTMLTextAreaElement) &&
|
||||
node.disabled === false) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns a rectangle that extends to the end of the screen in the direction that
|
||||
// the key is pressed.
|
||||
function _getSearchRect(currentlyFocused, key, cssPageRect) {
|
||||
let currentlyFocusedRect = currentlyFocused.getBoundingClientRect();
|
||||
|
||||
let newRect = {};
|
||||
newRect.left = currentlyFocusedRect.left;
|
||||
newRect.top = currentlyFocusedRect.top;
|
||||
newRect.right = currentlyFocusedRect.right;
|
||||
newRect.bottom = currentlyFocusedRect.bottom;
|
||||
newRect.width = currentlyFocusedRect.width;
|
||||
newRect.height = currentlyFocusedRect.height;
|
||||
|
||||
switch (key) {
|
||||
case PrefObserver['keyCodeLeft']:
|
||||
newRect.left = cssPageRect.left;
|
||||
newRect.width = newRect.right - newRect.left;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeRight']:
|
||||
newRect.right = cssPageRect.right;
|
||||
newRect.width = newRect.right - newRect.left;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeUp']:
|
||||
newRect.top = cssPageRect.top;
|
||||
newRect.height = newRect.bottom - newRect.top;
|
||||
break;
|
||||
|
||||
case PrefObserver['keyCodeDown']:
|
||||
newRect.bottom = cssPageRect.bottom;
|
||||
newRect.height = newRect.bottom - newRect.top;
|
||||
break;
|
||||
}
|
||||
return newRect;
|
||||
}
|
||||
|
||||
// Gets the distance between two points a and b.
|
||||
function _spatialDistance(a, b) {
|
||||
let mida = _getMidpoint(a);
|
||||
let midb = _getMidpoint(b);
|
||||
|
||||
return Math.round(Math.pow(mida.x - midb.x, 2) +
|
||||
Math.pow(mida.y - midb.y, 2));
|
||||
}
|
||||
|
||||
// Snav preference observer
|
||||
var PrefObserver = {
|
||||
register: function() {
|
||||
this.prefService = Cc["@mozilla.org/preferences-service;1"]
|
||||
.getService(Ci.nsIPrefService);
|
||||
|
||||
this._branch = this.prefService.getBranch("snav.");
|
||||
this._branch.QueryInterface(Ci.nsIPrefBranch2);
|
||||
this._branch.addObserver("", this, false);
|
||||
|
||||
// set current or default pref values
|
||||
this.observe(null, "nsPref:changed", "enabled");
|
||||
this.observe(null, "nsPref:changed", "xulContentEnabled");
|
||||
this.observe(null, "nsPref:changed", "keyCode.modifier");
|
||||
this.observe(null, "nsPref:changed", "keyCode.right");
|
||||
this.observe(null, "nsPref:changed", "keyCode.up");
|
||||
this.observe(null, "nsPref:changed", "keyCode.down");
|
||||
this.observe(null, "nsPref:changed", "keyCode.left");
|
||||
this.observe(null, "nsPref:changed", "keyCode.return");
|
||||
},
|
||||
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic != "nsPref:changed") {
|
||||
return;
|
||||
}
|
||||
|
||||
// aSubject is the nsIPrefBranch we're observing (after appropriate QI)
|
||||
// aData is the name of the pref that's been changed (relative to aSubject)
|
||||
switch (aData) {
|
||||
case "enabled":
|
||||
try {
|
||||
this.enabled = this._branch.getBoolPref("enabled");
|
||||
} catch(e) {
|
||||
this.enabled = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "xulContentEnabled":
|
||||
try {
|
||||
this.xulContentEnabled = this._branch.getBoolPref("xulContentEnabled");
|
||||
} catch(e) {
|
||||
this.xulContentEnabled = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case "keyCode.modifier": {
|
||||
let keyCodeModifier;
|
||||
try {
|
||||
keyCodeModifier = this._branch.getCharPref("keyCode.modifier");
|
||||
|
||||
// resetting modifiers
|
||||
this.modifierAlt = false;
|
||||
this.modifierShift = false;
|
||||
this.modifierCtrl = false;
|
||||
|
||||
if (keyCodeModifier != this.kNone) {
|
||||
// we are using '+' as a separator in about:config.
|
||||
let mods = keyCodeModifier.split(/\++/);
|
||||
for (let i = 0; i < mods.length; i++) {
|
||||
let mod = mods[i].toLowerCase();
|
||||
if (mod === "")
|
||||
continue;
|
||||
else if (mod == kAlt)
|
||||
this.modifierAlt = true;
|
||||
else if (mod == kShift)
|
||||
this.modifierShift = true;
|
||||
else if (mod == kCtrl)
|
||||
this.modifierCtrl = true;
|
||||
else {
|
||||
keyCodeModifier = kNone;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(e) { }
|
||||
break;
|
||||
}
|
||||
|
||||
case "keyCode.up":
|
||||
try {
|
||||
this.keyCodeUp = this._branch.getIntPref("keyCode.up");
|
||||
} catch(e) {
|
||||
this.keyCodeUp = Ci.nsIDOMKeyEvent.DOM_VK_UP;
|
||||
}
|
||||
break;
|
||||
case "keyCode.down":
|
||||
try {
|
||||
this.keyCodeDown = this._branch.getIntPref("keyCode.down");
|
||||
} catch(e) {
|
||||
this.keyCodeDown = Ci.nsIDOMKeyEvent.DOM_VK_DOWN;
|
||||
}
|
||||
break;
|
||||
case "keyCode.left":
|
||||
try {
|
||||
this.keyCodeLeft = this._branch.getIntPref("keyCode.left");
|
||||
} catch(e) {
|
||||
this.keyCodeLeft = Ci.nsIDOMKeyEvent.DOM_VK_LEFT;
|
||||
}
|
||||
break;
|
||||
case "keyCode.right":
|
||||
try {
|
||||
this.keyCodeRight = this._branch.getIntPref("keyCode.right");
|
||||
} catch(e) {
|
||||
this.keyCodeRight = Ci.nsIDOMKeyEvent.DOM_VK_RIGHT;
|
||||
}
|
||||
break;
|
||||
case "keyCode.return":
|
||||
try {
|
||||
this.keyCodeReturn = this._branch.getIntPref("keyCode.return");
|
||||
} catch(e) {
|
||||
this.keyCodeReturn = Ci.nsIDOMKeyEvent.DOM_VK_RETURN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
PrefObserver.register();
|
@ -35,6 +35,7 @@ EXTRA_JS_MODULES += [
|
||||
'SelectContentHelper.jsm',
|
||||
'SelectParentHelper.jsm',
|
||||
'Sntp.jsm',
|
||||
'SpatialNavigation.jsm',
|
||||
'Sqlite.jsm',
|
||||
'Task.jsm',
|
||||
'TelemetryTimestamps.jsm',
|
||||
|
1
toolkit/modules/tests/mochitest/mochitest.ini
Normal file
1
toolkit/modules/tests/mochitest/mochitest.ini
Normal file
@ -0,0 +1 @@
|
||||
[test_spatial_navigation.html]
|
78
toolkit/modules/tests/mochitest/test_spatial_navigation.html
Normal file
78
toolkit/modules/tests/mochitest/test_spatial_navigation.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=698437
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 698437</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 698437 **/
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function Test() {
|
||||
if (!SpecialPowers.getBoolPref("snav.enabled")) {
|
||||
todo(false, "Enable spatial navigiation on this platform.");
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
var center = document.getElementById("center");
|
||||
var right = document.getElementById("right");
|
||||
var left = document.getElementById("left");
|
||||
var top = document.getElementById("top");
|
||||
var bottom = document.getElementById("bottom");
|
||||
|
||||
console.log(top);
|
||||
console.log(bottom);
|
||||
console.log(center);
|
||||
console.log(left);
|
||||
console.log(right);
|
||||
|
||||
center.focus();
|
||||
is(center.id, document.activeElement.id, "How did we call focus on center and it did" +
|
||||
" not become the active element?");
|
||||
|
||||
synthesizeKey("VK_UP", { });
|
||||
is(top.id, document.activeElement.id,
|
||||
"Spatial navigation up key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_DOWN", { });
|
||||
is(bottom.id, document.activeElement.id,
|
||||
"Spatial navigation down key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_RIGHT", { });
|
||||
is(right.id, document.activeElement.id,
|
||||
"Spatial navigation right key is not handled correctly.");
|
||||
|
||||
center.focus();
|
||||
synthesizeKey("VK_LEFT", { });
|
||||
is(left.id, document.activeElement.id,
|
||||
"Spatial navigation left key is not handled correctly.");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="Test();">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=698437">Mozilla Bug 698437</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
<p> This is a <a id="top" href="#">really</a> long sentence </p>
|
||||
<p> <a id="left" href="#">This</a> is a
|
||||
<a id="center" href="#">really</a> long
|
||||
<a id="right" href="#">sentence</a> </p>
|
||||
<p> This is a <a id="bottom" href="#">really</a> long sentence </p>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -9,3 +9,5 @@ DIRS += ['browser']
|
||||
MODULE = 'test_toolkit_general'
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini']
|
||||
|
||||
MOCHITEST_MANIFESTS += ['mochitest/mochitest.ini']
|
||||
|
@ -1438,6 +1438,9 @@ static unsigned int ConvertAndroidKeyCodeToDOMKeyCode(int androidKeyCode)
|
||||
case AKEYCODE_KANA: return NS_VK_KANA;
|
||||
case AKEYCODE_ASSIST: return NS_VK_HELP;
|
||||
|
||||
// the A key is the action key for gamepad devices.
|
||||
case AKEYCODE_BUTTON_A: return NS_VK_RETURN;
|
||||
|
||||
default:
|
||||
ALOG("ConvertAndroidKeyCodeToDOMKeyCode: "
|
||||
"No DOM keycode for Android keycode %d", androidKeyCode);
|
||||
|
Loading…
Reference in New Issue
Block a user