diff --git a/mobile/chrome/content/InputHandler.js b/mobile/chrome/content/InputHandler.js index 70465ce061d..36cc42eeafc 100644 --- a/mobile/chrome/content/InputHandler.js +++ b/mobile/chrome/content/InputHandler.js @@ -533,15 +533,13 @@ MouseModule.prototype = { */ _onMouseUp: function _onMouseUp(evInfo) { let dragData = this._dragData; + let oldIsPan = dragData.isPan(); if (dragData.dragging) { dragData.setDragPosition(evInfo.event.screenX, evInfo.event.screenY); let [sX, sY] = dragData.panPosition(); this._doDragStop(sX, sY, !dragData.isPan()); } - if (this._clicker) - this._clicker.mouseUp(evInfo.event.clientX, evInfo.event.clientY); - if (this._targetIsContent(evInfo.event)) { // User possibly clicked on something in content this._recordEvent(evInfo); @@ -564,6 +562,14 @@ MouseModule.prototype = { this._owner.suppressNextClick(); } + let clicker = this._clicker; + if (clicker) { + // Let clicker know when mousemove begins a pan + if (!oldIsPan && dragData.isPan()) + clicker.panBegin(); + clicker.mouseUp(evInfo.event.clientX, evInfo.event.clientY); + } + this._owner.ungrab(this); }, @@ -574,6 +580,7 @@ MouseModule.prototype = { let dragData = this._dragData; if (dragData.dragging) { + let oldIsPan = dragData.isPan(); dragData.setDragPosition(evInfo.event.screenX, evInfo.event.screenY); evInfo.event.stopPropagation(); evInfo.event.preventDefault(); @@ -581,6 +588,11 @@ MouseModule.prototype = { // Only pan when mouse event isn't part of a click. Prevent jittering on tap. let [sX, sY] = dragData.panPosition(); this._doDragMove(sX, sY); + + // Let clicker know when mousemove begins a pan + let clicker = this._clicker; + if (!oldIsPan && clicker) + clicker.panBegin(); } } }, diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index ae55e5f27a9..113641efba3 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -1144,6 +1144,33 @@ var Browser = { } }, + getContentClientRects: function getContentClientRects(contentElem) { + // XXX don't copy getBoundingContentRect + let browser = Browser._browserView.getBrowser(); + + if (!browser) + return null; + + let offset = BrowserView.Util.getContentScrollOffset(browser); + let nativeRects = contentElem.getClientRects(); + + // step out of iframes and frames, offsetting scroll values + let rect; + for (let frame = contentElem.ownerDocument.defaultView; frame != browser.contentWindow; frame = frame.parent) { + // adjust client coordinates' origin to be top left of iframe viewport + rect = frame.frameElement.getBoundingClientRect(); + offset.add(rect.left, rect.top); + } + + let result = []; + let r; + for (let i = nativeRects.length - 1; i >= 0; i--) { + r = nativeRects[i]; + result.push(new Rect(r.left + offset.x, r.top + offset.y, r.width, r.height)); + } + return result; + }, + getBoundingContentRect: function getBoundingContentRect(contentElem) { let document = contentElem.ownerDocument; while(document.defaultView.frameElement) @@ -1702,6 +1729,9 @@ const BrowserSearch = { /** Watches for mouse events in chrome and sends them to content. */ function ContentCustomClicker(browserView) { this._browserView = browserView; + this._overlay = document.getElementById("content-overlay"); + this._width = 0; + this._height = 0; } ContentCustomClicker.prototype = { @@ -1717,13 +1747,79 @@ ContentCustomClicker.prototype = { } }, + /** Returns a node if selecting this node causes a focus. */ + _getFocusable: function(node) { + if (node && node.mozMatchesSelector("*:link,*:visited,*:link *,*:visited *,button,input,option,select,textarea")) + return node; + return null; + }, + + /** Stop highlighting current element. */ + _hideCanvas: function _hideCanvas() { + let overlay = this._overlay; + overlay.style.display = "none"; + overlay.getContext("2d").clearRect(0, 0, this._width, this._height); + }, + + /** Make sure canvas is at least width x height. */ + _ensureSize: function _ensureSize(width, height) { + if (this._width <= width) { + this._width = width; + this._overlay.width = width; + } + if (this._height <= height) { + this._height = height; + this._overlay.height = height; + } + }, + mouseDown: function mouseDown(cX, cY) { + // This code is sensitive to performance. Please profile changes you make to + // keep this running fast. + + let bv = this._browserView; + let overlay = this._overlay; + let ctx = overlay.getContext("2d"); + let [elementX, elementY] = Browser.transformClientToBrowser(cX, cY); + let element = this._getFocusable(Browser.elementFromPoint(elementX, elementY)); + if (!element) + return; + + let rects = Browser.getContentClientRects(element); + let union = rects.reduce(function(a, b) { + return a.expandToContain(b); + }, new Rect(0, 0, 0, 0)).map(bv.browserToViewport); + + let vis = Browser.getVisibleRect(); + let canvasArea = vis.intersect(union); + this._ensureSize(canvasArea.width, canvasArea.height); + + ctx.save(); + ctx.translate(-canvasArea.left, -canvasArea.top); + bv.browserToViewportCanvasContext(ctx); + + overlay.style.left = canvasArea.left + "px"; + overlay.style.top = canvasArea.top + "px"; + ctx.fillStyle = "rgba(0, 145, 255, .5)"; + let rect; + for (let i = rects.length - 1; i >= 0; i--) { + rect = rects[i]; + ctx.fillRect(rect.left, rect.top, rect.width, rect.height); + } + ctx.restore(); + overlay.style.display = "block"; }, mouseUp: function mouseUp(cX, cY) { }, + panBegin: function panBegin() { + this._hideCanvas(); + }, + singleClick: function singleClick(cX, cY, modifiers) { + this._hideCanvas(); + let [elementX, elementY] = Browser.transformClientToBrowser(cX, cY); let element = Browser.elementFromPoint(elementX, elementY); if (modifiers == 0) { @@ -1746,6 +1842,8 @@ ContentCustomClicker.prototype = { }, doubleClick: function doubleClick(cX1, cY1, cX2, cY2) { + this._hideCanvas(); + const kDoubleClickRadius = 32; let maxRadius = kDoubleClickRadius * Browser._browserView.getZoomLevel(); diff --git a/mobile/chrome/content/browser.xul b/mobile/chrome/content/browser.xul index 8b367c740e6..c935a081d7a 100644 --- a/mobile/chrome/content/browser.xul +++ b/mobile/chrome/content/browser.xul @@ -242,9 +242,11 @@ - + + + - diff --git a/mobile/chrome/content/content.css b/mobile/chrome/content/content.css index 103a8ebe50c..87e4f6a285e 100644 --- a/mobile/chrome/content/content.css +++ b/mobile/chrome/content/content.css @@ -38,19 +38,20 @@ /* make clicking on links stand out a bit (bug 532206) */ html *:not(embed):focus, *:focus > font { - color: #000000 !important; - background-color: #ffffa0 !important; + outline: 2px solid #8db8d8 !important; + /* + XXX How do I preserve mac focusring without blowing focus color on other platforms? + outline-color: -moz-mac-focusring !important; + */ } html *|*:link:focus, *|*:visited:focus { - outline: 1px solid -moz-mac-focusring !important; - -moz-outline-radius: 3px; - outline-offset: 1px; + outline-offset: -2px; } html button::-moz-focus-inner, input[type="reset"]::-moz-focus-inner, input[type="button"]::-moz-focus-inner, input[type="submit"]::-moz-focus-inner { padding: 1px 2px 1px 2px; - border: 1px solid transparent !important; + border: 0px !important; } html button:focus::-moz-focus-inner, input[type="reset"]:focus::-moz-focus-inner, input[type="button"]:focus::-moz-focus-inner, input[type="submit"]:focus::-moz-focus-inner {