Bug 1341737: Remove SpatialNavigation.jsm. r=blassey

MozReview-Commit-ID: JIbo1jvwZRW

--HG--
extra : rebase_source : b4e13e75d5dd9ac0e1229c4c54a968ea3f3eda73
This commit is contained in:
Dave Townsend 2017-02-22 10:46:56 -08:00
parent aee8d03ee6
commit 428143ec78
6 changed files with 0 additions and 688 deletions

View File

@ -25,9 +25,6 @@ if (AppConstants.ACCESSIBILITY) {
XPCOMUtils.defineLazyModuleGetter(this, "Manifests",
"resource://gre/modules/Manifest.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SpatialNavigation",
"resource://gre/modules/SpatialNavigation.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadNotifications",
"resource://gre/modules/DownloadNotifications.jsm");
@ -545,7 +542,6 @@ var BrowserApp = {
});
InitLater(() => LightWeightThemeWebInstaller.init());
InitLater(() => SpatialNavigation.init(BrowserApp.deck, null), window, "SpatialNavigation");
InitLater(() => CastingApps.init(), window, "CastingApps");
InitLater(() => Services.search.init(), Services, "search");
InitLater(() => DownloadNotifications.init(), window, "DownloadNotifications");

View File

@ -1,596 +0,0 @@
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
/* 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(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);
var eventListenerService = Cc["@mozilla.org/eventlistenerservice;1"]
.getService(Ci.nsIEventListenerService);
var focusManager = Cc["@mozilla.org/focus-manager;1"]
.getService(Ci.nsIFocusManager);
var 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;
}
// 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.ownerGlobal;
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;
}
if ((currentlyFocused instanceof Ci.nsIDOMHTMLInputElement &&
currentlyFocused.mozIsTextField(false)) ||
currentlyFocused instanceof Ci.nsIDOMHTMLTextAreaElement) {
// If there is a text selection, remain in the element.
if (currentlyFocused.selectionEnd - currentlyFocused.selectionStart != 0) {
return;
}
// If there is no text, there is nothing special to do.
if (currentlyFocused.textLength > 0) {
if (key == PrefObserver["keyCodeRight"] ||
key == PrefObserver["keyCodeDown"] ) {
// We are moving forward into the document.
if (currentlyFocused.textLength != currentlyFocused.selectionEnd) {
return;
}
} else if (currentlyFocused.selectionStart != 0) {
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 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) {
bestDist = _spatialDistanceOfCorner(currentlyFocused, nodes[i], key);
if (bestDist >= 0) {
best = nodes[i];
}
continue;
}
// Of the remaining nodes, pick the one closest to the currently focused
// node.
let curDist = _spatialDistanceOfCorner(currentlyFocused, nodes[i], key);
if ((curDist > bestDist) || curDist === -1) {
continue;
} else if (curDist === bestDist) {
let midCurDist = _spatialDistance(currentlyFocused, nodes[i]);
let midBestDist = _spatialDistance(currentlyFocused, best);
if (midCurDist > midBestDist)
continue;
}
best = nodes[i];
bestDist = curDist;
}
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.right = newRect.left;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
newRect.bottom = cssPageRect.bottom;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
break;
case PrefObserver["keyCodeRight"]:
newRect.left = newRect.right;
newRect.right = cssPageRect.right;
newRect.width = newRect.right - newRect.left;
newRect.bottom = cssPageRect.bottom;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
break;
case PrefObserver["keyCodeUp"]:
newRect.bottom = newRect.top;
newRect.top = cssPageRect.top;
newRect.height = newRect.bottom - newRect.top;
newRect.right = cssPageRect.right;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
break;
case PrefObserver["keyCodeDown"]:
newRect.top = newRect.bottom;
newRect.bottom = cssPageRect.bottom;
newRect.height = newRect.bottom - newRect.top;
newRect.right = cssPageRect.right;
newRect.left = cssPageRect.left;
newRect.width = newRect.right - newRect.left;
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));
}
// Get the distance between the corner of two nodes
function _spatialDistanceOfCorner(from, to, key) {
let fromRect = from.getBoundingClientRect();
let toRect = to.getBoundingClientRect();
let fromMid = _getMidpoint(from);
let toMid = _getMidpoint(to);
let hDistance = 0;
let vDistance = 0;
switch (key) {
case PrefObserver["keyCodeLeft"]:
// Make sure the "to" node is really at the left side of "from" node by
// 1. Check the mid point
// 2. The right border of "to" node must be less than the "from" node
if ((fromMid.x - toMid.x) < 0 || toRect.right >= fromRect.right)
return -1;
hDistance = Math.abs(fromRect.left - toRect.right);
if (toRect.bottom <= fromRect.top) {
vDistance = fromRect.top - toRect.bottom;
} else if (fromRect.bottom <= toRect.top) {
vDistance = toRect.top - fromRect.bottom;
} else {
vDistance = 0;
}
break;
case PrefObserver["keyCodeRight"]:
if ((toMid.x - fromMid.x) < 0 || toRect.left <= fromRect.left)
return -1;
hDistance = Math.abs(toRect.left - fromRect.right);
if (toRect.bottom <= fromRect.top) {
vDistance = fromRect.top - toRect.bottom;
} else if (fromRect.bottom <= toRect.top) {
vDistance = toRect.top - fromRect.bottom;
} else {
vDistance = 0;
}
break;
case PrefObserver["keyCodeUp"]:
if ((fromMid.y - toMid.y) < 0 || toRect.bottom >= fromRect.bottom)
return -1;
vDistance = Math.abs(fromRect.top - toRect.bottom);
if (fromRect.right <= toRect.left) {
hDistance = toRect.left - fromRect.right;
} else if (toRect.right <= fromRect.left) {
hDistance = fromRect.left - toRect.right;
} else {
hDistance = 0;
}
break;
case PrefObserver["keyCodeDown"]:
if ((toMid.y - fromMid.y) < 0 || toRect.top <= fromRect.top)
return -1;
vDistance = Math.abs(toRect.top - fromRect.bottom);
if (fromRect.right <= toRect.left) {
hDistance = toRect.left - fromRect.right;
} else if (toRect.right <= fromRect.left) {
hDistance = fromRect.left - toRect.right;
} else {
hDistance = 0;
}
break;
}
return Math.round(Math.pow(hDistance, 2) +
Math.pow(vDistance, 2));
}
// Snav preference observer
var PrefObserver = {
register() {
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(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();

View File

@ -6,7 +6,6 @@
XPCSHELL_TESTS_MANIFESTS += ['tests/xpcshell/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['tests/browser/browser.ini']
MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
TESTING_JS_MODULES += [
@ -91,7 +90,6 @@ EXTRA_JS_MODULES += [
'sessionstore/XPathGenerator.jsm',
'ShortcutUtils.jsm',
'Sntp.jsm',
'SpatialNavigation.jsm',
'Sqlite.jsm',
'Task.jsm',
'Timer.jsm',

View File

@ -1,7 +0,0 @@
"use strict";
module.exports = {
"extends": [
"../../../../testing/mochitest/mochitest.eslintrc.js"
]
};

View File

@ -1,3 +0,0 @@
[DEFAULT]
[test_spatial_navigation.html]

View File

@ -1,76 +0,0 @@
<!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(document.activeElement.id, top.id,
"Spatial navigation up key is not handled correctly.");
center.focus();
synthesizeKey("VK_DOWN", { });
is(document.activeElement.id, bottom.id,
"Spatial navigation down key is not handled correctly.");
center.focus();
synthesizeKey("VK_RIGHT", { });
is(document.activeElement.id, right.id,
"Spatial navigation right key is not handled correctly.");
center.focus();
synthesizeKey("VK_LEFT", { });
is(document.activeElement.id, left.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">
<button id="lefttop">1</button><button id="top">2</button><button id="righttop">3</button><br>
<button id="left">4</button><button id="center">5</button><button id="right">6</button><br>
<button id="leftbottom">7</button><button id="bottom">8</button><button id="rightbottom">9</button><br>
</div>
<pre id="test">
</pre>
</body>
</html>