Bug 609940 - caret (cursor) can hide underneath the formfill helper [r=mfinkle]

This commit is contained in:
Vivien Nicolas 2011-01-14 17:39:16 +01:00
Родитель 6f76879ff8
Коммит fa24470ee5
9 изменённых файлов: 347 добавлений и 36 удалений

Просмотреть файл

@ -159,6 +159,7 @@ pref("signon.SignonFileName", "signons.txt");
/* form helper */
pref("formhelper.enabled", true);
pref("formhelper.autozoom", true);
pref("formhelper.autozoom.caret", true);
pref("formhelper.restore", false);
/* find helper */

Просмотреть файл

@ -247,9 +247,6 @@ var Browser = {
this.styles[style] = stylesheet.cssRules[index].style;
}
// Init the cache used in the resize handler
window.cachedWidth = window.innerWidth;
// Saved the scrolls values before the resizing of the window, to restore
// the scrollbox position once the resize has finished.
// The last parameter of addEventListener is true to be sure we performed
@ -284,10 +281,10 @@ var Browser = {
Browser.styles["window-width"].width = w + "px";
Browser.styles["window-height"].height = h + "px";
Browser.styles["toolbar-height"].height = toolbarHeight + "px";
ViewableAreaObserver.update();
// Tell the UI to resize the browser controls
BrowserUI.sizeControls(w, h);
ViewableAreaObserver.update();
// Restore the previous scroll position
let restorePosition = Browser.controlsPosition;
@ -307,20 +304,6 @@ var Browser = {
Browser.tryFloatToolbar(0, 0);
}
let oldWidth = window.cachedWidth || w;
window.cachedWidth = w;
for (let i = Browser.tabs.length - 1; i >= 0; i--) {
let tab = Browser.tabs[i];
let oldContentWindowWidth = tab.browser.contentWindowWidth;
tab.updateViewportSize();
// If the viewport width is still the same, the page layout has not
// changed, so we can keep keep the same content on-screen.
if (tab.browser.contentWindowWidth == oldContentWindowWidth)
tab.restoreViewportPosition(oldWidth, w);
}
// We want to keep the current focused element into view if possible
let currentElement = document.activeElement;
let [scrollbox, scrollInterface] = ScrollUtils.getScrollboxFromElement(currentElement);
@ -1005,7 +988,6 @@ var Browser = {
* @return Rect in viewport coordinates, or null
*/
_getZoomRectForRect: function _getZoomRectForRect(rect, y) {
let oldZoomLevel = getBrowser().scale;
let zoomLevel = this._getZoomLevelForRect(rect);
return this._getZoomRectForPoint(rect.center().x, y, zoomLevel);
},
@ -1208,7 +1190,7 @@ Browser.MainDragger.prototype = {
switch (aEvent.type) {
case "PanBegin": {
let browser = Browser.selectedBrowser;
let width = window.innerWidth, height = window.innerHeight;
let width = ViewableAreaObserver.width, height = ViewableAreaObserver.height;
let contentWidth = browser.contentDocumentWidth * browser.scale;
let contentHeight = browser.contentDocumentHeight * browser.scale;
@ -1291,13 +1273,13 @@ Browser.MainDragger.prototype = {
_showScrollbars: function _showScrollbars() {
let scaleX = this._scrollScales.x, scaleY = this._scrollScales.y;
if (scaleX) {
this._horizontalScrollbar.width = ViewableAreaObserver.width * scaleX;
this._horizontalScrollbar.setAttribute("panning", "true");
this._horizontalScrollbar.width = window.innerWidth * scaleX;
}
if (scaleY) {
this._verticalScrollbar.height = ViewableAreaObserver.height * scaleY;
this._verticalScrollbar.setAttribute("panning", "true");
this._verticalScrollbar.height = window.innerHeight * scaleY;
}
},
@ -2474,8 +2456,8 @@ Tab.prototype = {
if (!browser)
return;
let screenW = window.innerWidth;
let screenH = window.innerHeight;
let screenW = ViewableAreaObserver.width;
let screenH = ViewableAreaObserver.height;
let viewportW, viewportH;
let metadata = this.metadata;
@ -2797,22 +2779,46 @@ var ViewableAreaObserver = {
},
get height() {
return this._height || window.innerHeight;
return (this._height || window.innerHeight) - Elements.contentNavigator.getBoundingClientRect().height;
},
observe: function va_observe(aSubject, aTopic, aData) {
let rect = Rect.fromRect(JSON.parse(aData));
this._height = rect.bottom - rect.top;
this._width = rect.right - rect.left;
let height = rect.bottom - rect.top;
let width = rect.right - rect.left;
if (height == window.innerHeight && width == window.innerWidth) {
this._height = null;
this._width = null;
}
else {
this._height = height;
this._width = width;
}
this.update();
},
update: function va_update() {
let oldHeight = parseInt(Browser.styles["viewable-height"].height);
let newHeight = this.height - Elements.contentNavigator.getBoundingClientRect().height;
if (newHeight != oldHeight) {
let oldWidth = parseInt(Browser.styles["viewable-width"].width);
let newWidth = this.width;
let newHeight = this.height;
if (newHeight != oldHeight || newWidth != oldWidth) {
Browser.styles["viewable-height"].height = newHeight + "px";
Browser.styles["viewable-width"].width = this.width + "px";
Browser.styles["viewable-height"].maxHeight = newHeight + "px";
Browser.styles["viewable-width"].width = newWidth + "px";
Browser.styles["viewable-width"].maxWidth = newWidth + "px";
for (let i = Browser.tabs.length - 1; i >= 0; i--) {
let tab = Browser.tabs[i];
tab.updateViewportSize();
// If the viewport width is still the same, the page layout has not
// changed, so we can keep keep the same content on-screen.
if (tab.browser.contentWindowWidth == oldWidth)
tab.restoreViewportPosition(oldWidth, w);
}
// setTimeout(callback, 0) to ensure the resize event handler dispatch is finished
setTimeout(function() {

Просмотреть файл

@ -859,6 +859,7 @@ var FormHelperUI = {
} else if (aElementRect && !Browser.selectedTab.allowZoom && autozoomEnabled) {
// Even if zooming is disabled we could need to reposition the view in
// order to keep the element on-screen
zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, browser.scale);
Browser.animatedZoomTo(zoomRect);
}
@ -866,7 +867,7 @@ var FormHelperUI = {
},
_ensureCaretVisible: function _ensureCaretVisible(aCaretRect) {
if (!aCaretRect)
if (!aCaretRect || !Services.prefs.getBoolPref("formhelper.autozoom.caret"))
return;
// the scrollX/scrollY position can change because of the animated zoom so
@ -884,7 +885,7 @@ var FormHelperUI = {
let zoomRect = Rect.fromRect(browser.getBoundingClientRect());
this._currentCaretRect = aCaretRect;
let caretRect = aCaretRect.scale(browser.scale, browser.scale);
let caretRect = aCaretRect.clone().scale(browser.scale, browser.scale);
let scroll = browser.getPosition();
zoomRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height);

Просмотреть файл

@ -95,7 +95,12 @@ FormAssistant.prototype = {
if (element) {
this._currentIndex = aIndex;
gFocusManager.setFocus(element, Ci.nsIFocusManager.FLAG_NOSCROLL);
sendAsyncMessage("FormAssist:Show", this._getJSON());
// To ensure we get the current caret positionning of the focused
// element we need to delayed a bit the event
this._executeDelayed(function(self) {
sendAsyncMessage("FormAssist:Show", self._getJSON());
});
}
return element;
},
@ -473,9 +478,10 @@ FormAssistant.prototype = {
/** Caret is used to input text for this element. */
_getCaretRect: function _formHelperGetCaretRect() {
let element = this.currentElement;
let focusedElement = gFocusManager.getFocusedElementForWindow(content, true, {});
if ((element instanceof HTMLTextAreaElement ||
(element instanceof HTMLInputElement && element.type == "text")) &&
gFocusManager.focusedElement == element) {
focusedElement == element) {
let utils = Util.getWindowUtils(element.ownerDocument.defaultView);
let rect = utils.sendQueryContentEvent(utils.QUERY_CARET_RECT, element.selectionEnd, 0, 0, 0);
if (rect) {

Просмотреть файл

@ -50,7 +50,9 @@ _BROWSER_FILES = \
head.js \
remote_head.js \
remote_forms.js \
remote_formsZoom.js \
remote_vkb.js \
browser_addons.js \
browser_awesomescreen.js \
browser_blank_01.html \
browser_blank_02.html \
@ -61,9 +63,10 @@ _BROWSER_FILES = \
browser_click_content.js \
browser_contacts.js \
browser_find.js \
browser_addons.js \
browser_forms.html \
browser_forms.js \
browser_formsZoom.html \
browser_formsZoom.js \
browser_mainui.js \
browser_navigation.js \
browser_preferences_basic.js \

Просмотреть файл

@ -0,0 +1,66 @@
<html>
<head>
<title>Browser Zoom Test</title>
</head>
<body>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<textarea id="textarea-1" spellcheck="false" rows="10">textarea 1</textarea><br>
<textarea id="textarea-2" spellcheck="false" rows="10">textarea 2</textarea><br>
<textarea id="textarea-3" spellcheck="false" rows="10">textarea 3
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
z
</textarea><br>
<textarea id="textarea-4" spellcheck="false" rows="10">textarea 4
a
b
c
d
e
f
g
h
i
j
k
l
m
n
o
p
q
r
s
t
u
v
w
x
y
</textarea>
<br>
</body>
</html>

Просмотреть файл

@ -0,0 +1,216 @@
let testURL_01 = chromeRoot + "browser_formsZoom.html";
let baseURI = "http://mochi.test:8888/browser/mobile/chrome/";
let testURL_02 = baseURI + "browser_formsZoom.html";
messageManager.loadFrameScript(baseURI + "remote_formsZoom.js", true);
// A queue to order the tests and a handle for each test
var gTests = [];
var gCurrentTest = null;
//------------------------------------------------------------------------------
// Entry point (must be named "test")
function test() {
// The "runNextTest" approach is async, so we need to call "waitForExplicitFinish()"
// We call "finish()" when the tests are finished
waitForExplicitFinish();
// Start the tests
runNextTest();
}
function waitForPageShow(aPageURL, aCallback) {
messageManager.addMessageListener("pageshow", function(aMessage) {
if (aMessage.target.currentURI.spec == aPageURL) {
messageManager.removeMessageListener("pageshow", arguments.callee);
setTimeout(function() { aCallback(); }, 0);
}
});
};
//------------------------------------------------------------------------------
// Iterating tests by shifting test out one by one as runNextTest is called.
function runNextTest() {
// Run the next test until all tests completed
if (gTests.length > 0) {
gCurrentTest = gTests.shift();
info(gCurrentTest.desc);
setTimeout(gCurrentTest.run, 0);
}
else {
// Cleanup. All tests are completed at this point
try {
// Add any cleanup code here
}
finally {
// We must finialize the tests
finish();
}
}
}
function waitForZoom(aCallback) {
if (AnimatedZoom.isZooming()) {
let self = this;
window.addEventListener("AnimatedZoomEnd", function() {
window.removeEventListener("AnimatedZoomEnd", arguments.callee, false);
setTimeout(aCallback, 0);
}, false);
}
else setTimeout(aCallback, 0);
}
function isElementVisible(aElement) {
let elementRect = Rect.fromRect(aElement.rect);
let caretRect = Rect.fromRect(aElement.caretRect);
let browser = getBrowser();
let zoomRect = Rect.fromRect(browser.getBoundingClientRect());
let scroll = browser.getPosition();
let browserRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height);
info("CanZoom: " +Browser.selectedTab.allowZoom);
info("Browser rect: " + browserRect + " - scale: " + browser.scale);
info("Element rect: " + elementRect + " - caret rect: " + caretRect);
info("Scale element rect: " + elementRect.clone().scale(browser.scale, browser.scale) + " - scale caretRect: " + caretRect.clone().scale(browser.scale, browser.scale));
info("Resulting zoom rect: " + Browser._getZoomRectForPoint(elementRect.center().x, elementRect.y, browser.scale));
let isCaretSyncEnabled = Services.prefs.getBoolPref("formhelper.autozoom.caret");
if (isCaretSyncEnabled) {
ok(browserRect.contains(caretRect.clone().scale(browser.scale, browser.scale)), "Caret rect should be completely visible");
}
else {
elementRect = elementRect.clone().scale(browser.scale, browser.scale);
let resultRect = browserRect.intersect(elementRect);
ok(!resultRect.isEmpty() && elementRect.x > browserRect.x && elementRect.y > browserRect.y, "Element should be partially visible");
}
}
//------------------------------------------------------------------------------
// Case: Loading a page and Zoom into textarea field with caret sync disabled
gTests.push({
desc: "Loading a page and Zoom into textarea field with caret sync enabled",
elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"],
_currentTab: null,
run: function() {
Services.prefs.setBoolPref("formhelper.autozoom.caret", false);
gCurrentTest._currentTab = BrowserUI.newTab(testURL_01);
waitForPageShow(testURL_01, function() { gCurrentTest.zoomNext(); });
},
zoomNext: function() {
let id = this.elements.shift();
if (!id) {
BrowserUI.closeTab();
runNextTest();
return;
}
info("Zooming to " + id);
AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) {
waitForZoom(function() {
isElementVisible(json.current);
gCurrentTest.zoomNext();
});
});
}
});
//------------------------------------------------------------------------------
// Case: Loading a page and Zoom into textarea field with caret sync enabled
gTests.push({
desc: "Loading a page and Zoom into textarea field with caret sync enabled",
elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"],
_currentTab: null,
run: function() {
Services.prefs.setBoolPref("formhelper.autozoom.caret", true);
gCurrentTest._currentTab = BrowserUI.newTab(testURL_01);
waitForPageShow(testURL_01, function() { gCurrentTest.zoomNext(); });
},
zoomNext: function() {
let id = this.elements.shift();
if (!id) {
BrowserUI.closeTab();
runNextTest();
return;
}
AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) {
waitForZoom(function() {
isElementVisible(json.current);
gCurrentTest.zoomNext();
});
});
}
});
//------------------------------------------------------------------------------
// Case: Loading a remote page and Zoom into textarea field with caret sync disabled
gTests.push({
desc: "Loading a remote page and Zoom into textarea field with caret sync disabled",
elements: ["textarea-1", "textarea-2", "textarea-3", "textarea-4"],
_currentTab: null,
run: function() {
Services.prefs.setBoolPref("formhelper.autozoom.caret", false);
gCurrentTest._currentTab = BrowserUI.newTab(testURL_02);
waitForPageShow(testURL_02, function() { gCurrentTest.zoomNext(); });
},
zoomNext: function() {
let id = this.elements.shift();
if (!id) {
BrowserUI.closeTab();
runNextTest();
return;
}
AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) {
waitForZoom(function() {
isElementVisible(json.current);
gCurrentTest.zoomNext();
});
});
}
});
//------------------------------------------------------------------------------
// Case: Loading a remote page and Zoom into textarea field with caret sync enabled
gTests.push({
desc: "Loading a remote page and Zoom into textarea field with caret sync enabled",
elements: ["textarea-1", "textarea-2"],
_currentTab: null,
run: function() {
Services.prefs.setBoolPref("formhelper.autozoom.caret", true);
gCurrentTest._currentTab = BrowserUI.newTab(testURL_02);
waitForPageShow(testURL_02, function() { gCurrentTest.zoomNext(); });
},
zoomNext: function() {
let id = this.elements.shift();
if (!id) {
todo(false, "textarea-3 caret should be synced, but for some reason it is not");
todo(false, "textarea-4 caret should be synced, but for some reason it is not");
BrowserUI.closeTab();
runNextTest();
return;
}
AsyncTests.waitFor("FormAssist:Show", { id: id }, function(json) {
waitForZoom(function() {
isElementVisible(json.current);
gCurrentTest.zoomNext();
});
});
}
});

Просмотреть файл

@ -72,5 +72,8 @@ let AsyncTests = {
};
let chromeRoot = getRootDirectory(gTestPath);
messageManager.loadFrameScript(chromeRoot + "remote_head.js", true);
// For some security reasons (which?), loading remote_head using chromeRoot
// instead of baseURI make the browser_formsZoom.js test fails.
let baseURI = "http://mochi.test:8888/browser/mobile/chrome/";
messageManager.loadFrameScript(baseURI + "remote_head.js", true);
messageManager.loadFrameScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", true);

Просмотреть файл

@ -0,0 +1,9 @@
dump("====================== Content Script Loaded =======================\n");
let assistant = Content._formAssistant;
AsyncTests.add("FormAssist:Show", function(aMessage, aJson) {
let element = content.document.getElementById(aJson.id);
assistant.open(element);
});