Bug 832957 - Fix selection overlay issues with context menus and touch input, and clean up Content.js elementFromPoint. r=mbrubeck

This commit is contained in:
Jim Mathies 2013-02-27 10:27:47 -06:00
parent 94963201eb
commit f356039a48
3 changed files with 62 additions and 19 deletions

View File

@ -19,11 +19,13 @@
<implementation implements="nsIDOMEventListener">
<constructor>
<![CDATA[
this._selectionOverlay.addEventListener('contextmenu', this);
]]>
</constructor>
<destructor>
<![CDATA[
this._selectionOverlay.removeEventListener('contextmenu', this);
]]>
</destructor>
@ -87,6 +89,31 @@
</body>
</method>
<method name="_onContextMenu">
<parameter name="aEvent"/>
<body>
<![CDATA[
// forward this over. frame script will treat this like
// a bubbling contextmenu event.
Browser.selectedTab.browser.messageManager.sendAsyncMessage("Browser:InvokeContextAtPoint", {
xPos: aEvent.clientX, yPos: aEvent.clientY });
]]>
</body>
</method>
<method name="handleEvent">
<parameter name="aEvent"/>
<body>
<![CDATA[
switch (aEvent.type) {
case 'contextmenu':
this._onContextMenu(aEvent);
break;
}
]]>
</body>
</method>
<method name="addDebugRect">
<parameter name="aLeft"/>
<parameter name="aTop"/>

View File

@ -167,32 +167,38 @@ const ElementTouchHelper = {
*/
/*
* elementFromPoint
* elementFromPoint - find the closes element at a point. searches
* sub-frames.
*
* @param x,y Browser coordinates
* @return Element at position, null if no active browser or no element found
* @param aX, aY browser coordinates
* @return
* element - element at the position, or null if no active browser or
* element was found.
* frameX - x position within the subframe element was found. aX if no
* sub-frame was found.
* frameY - y position within the subframe element was found. aY if no
* sub-frame was found.
*/
function elementFromPoint(x, y) {
function elementFromPoint(aX, aY) {
// browser's elementFromPoint expect browser-relative client coordinates.
// subtract browser's scroll values to adjust
let cwu = Util.getWindowUtils(content);
let elem = ElementTouchHelper.getClosest(cwu, x, y);
let elem = ElementTouchHelper.getClosest(cwu, aX, aY);
// step through layers of IFRAMEs and FRAMES to find innermost element
while (elem && (elem instanceof HTMLIFrameElement ||
elem instanceof HTMLFrameElement)) {
// adjust client coordinates' origin to be top left of iframe viewport
let rect = elem.getBoundingClientRect();
x -= rect.left;
y -= rect.top;
aX -= rect.left;
aY -= rect.top;
let windowUtils = elem.contentDocument
.defaultView
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
elem = ElementTouchHelper.getClosest(windowUtils, x, y);
elem = ElementTouchHelper.getClosest(windowUtils, aX, aY);
}
return elem;
return { element: elem, frameX: aX, frameY: aY };
}
/*
@ -400,7 +406,7 @@ let Content = {
*/
_genericMouseDown: function _genericMouseDown(x, y) {
let element = elementFromPoint(x, y);
let { element } = elementFromPoint(x, y);
if (!element)
return;
@ -417,7 +423,7 @@ let Content = {
_genericMouseClick: function _genericMouseClick(aEvent) {
ContextMenuHandler.reset();
let element = elementFromPoint(aEvent.clientX, aEvent.clientY);
let { element: element } = elementFromPoint(aEvent.clientX, aEvent.clientY);
if (!element)
return;

View File

@ -91,8 +91,9 @@ var ContextMenuHandler = {
_onContextAtPoint: function _onContextCommand(aMessage) {
// we need to find popupNode as if the context menu were
// invoked on underlying content.
let elem = elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
this._processPopupNode(elem, aMessage.json.xPos, aMessage.json.yPos,
let { element, frameX, frameY } =
elementFromPoint(aMessage.json.xPos, aMessage.json.yPos);
this._processPopupNode(element, frameX, frameY,
Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH);
},
@ -171,7 +172,12 @@ var ContextMenuHandler = {
offsetX += rect.left;
offsetY += rect.top;
}
return { offsetX: offsetX, offsetY: offsetY };
let win = null;
if (element == aPopupNode)
win = content;
else
win = element.contentDocument.defaultView;
return { targetWindow: win, offsetX: offsetX, offsetY: offsetY };
},
/*
@ -183,8 +189,12 @@ var ContextMenuHandler = {
_processPopupNode: function _processPopupNode(aPopupNode, aX, aY, aInputSrc) {
if (!aPopupNode)
return;
let { offsetX: offsetX, offsetY: offsetY } =
let { targetWindow: targetWindow,
offsetX: offsetX,
offsetY: offsetY } =
this._translateToTopLevelWindow(aPopupNode);
let popupNode = this.popupNode = aPopupNode;
let imageUrl = "";
@ -298,9 +308,9 @@ var ContextMenuHandler = {
if (isText) {
// If this is text and has a selection, we want to bring
// up the copy option on the context menu.
if (content && content.getSelection() &&
content.getSelection().toString().length > 0) {
state.string = content.getSelection().toString();
let selection = targetWindow.getSelection();
if (selection && selection.toString().length > 0) {
state.string = targetWindow.getSelection().toString();
state.types.push("copy");
state.types.push("selected-text");
} else {