Merge of iframe scrolling patch to tiles. r=stuart

This commit is contained in:
Ben Combee 2009-08-07 18:08:06 -07:00
Родитель 3037d574d0
Коммит a1da836547
4 изменённых файлов: 176 добавлений и 315 удалений

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

@ -378,7 +378,14 @@ BrowserView.prototype = {
this._renderMode--;
if (renderNow || this._renderMode == 0)
this._tileManager.criticalRectPaint();
this.renderNow();
},
/**
* Called while rendering is paused to allow update of critical area
*/
renderNow: function renderNow() {
this._tileManager.criticalRectPaint();
},
isRendering: function isRendering() {

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

@ -42,7 +42,6 @@
*
* ***** END LICENSE BLOCK ***** */
/**
* InputHandler
*
@ -56,7 +55,6 @@
* On the Fennec global chrome window:
* URLChanged
* TabSelect
* mouseout
* mousedown
* mouseup
* mousemove
@ -122,9 +120,6 @@ function InputHandler(browserViewContainer) {
this.listenFor(window, "URLChanged");
this.listenFor(window, "TabSelect");
/* used to stop everything if mouse leaves window on desktop */
this.listenFor(window, "mouseout");
/* these handle dragging of both chrome elements and content */
this.listenFor(window, "mousedown");
this.listenFor(window, "mouseup");
@ -140,8 +135,6 @@ function InputHandler(browserViewContainer) {
this.addModule(new MouseModule(this));
this.addModule(new ScrollwheelModule(this, Browser._browserView, browserViewContainer));
//this.addModule(new ContentPanningModule(this, browserViewContainer, useEarlyMouseMoves));
//this.addModule(new ContentClickingModule(this));
}
@ -177,20 +170,21 @@ InputHandler.prototype = {
},
/**
* A module calls grab(this) to grab event focus from the input handler.
* In grabbed state, the input handler forwards all events directly to
* the grabber module, and not to any other modules. The this reference
* passed is essentially a ceritificate to the input handler --- collateral
* for the grab. A grabber module may make nested calls to grab() but
* should symmetrically ungrab(). Other modules cannot grab a grabbed input
* handler, and only the grabber module can ungrab the input handler.
* A module calls grab(this) to grab event focus from the input
* handler. In grabbed state, the input handler forwards all events
* directly to the grabber module, and not to any other modules.
* The this reference passed is essentially a ceritificate to the
* input handler --- collateral for the grab. A grabber module may
* make nested calls to grab() but should symmetrically ungrab().
* Other modules cannot grab a grabbed input handler, and only the
* grabber module can ungrab the input handler.
*
* grab(null) aborts all input handlers. This is used in situations
* like the page changing to a different URL where you want to abort
* drags in progress or kinetic movement.
*
* Returns true if the grab succeeded, false otherwise.
*/
// XXX grab(null) is supported because the old grab() supported it,
// but I'm not sure why. The comment on that was "grab(null) is allowed
// because of mouseout handling". Feel free to remove if that is no longer
// relevant, or remove this comment if it still is.
grab: function grab(grabber) {
if (grabber == null) {
this._grabber = null;
@ -369,8 +363,9 @@ InputHandler.EventInfo.prototype = {
* following interface: (The `scroller' argument is given for convenience, and
* is the object reference to the element's scrollbox object).
*
* dragStart(scroller)
* Signals the beginning of a drag.
* dragStart(cX, cY, target, scroller)
* Signals the beginning of a drag. Coordinates are passed as
* client coordinates. target is copied from the event.
*
* dragStop(dx, dy, scroller)
* Signals the end of a drag. The dx, dy parameters may be non-zero to
@ -479,7 +474,7 @@ MouseModule.prototype = {
this._owner.grab(this);
if (targetScrollInterface) {
this._doDragStart(evInfo.event.screenX, evInfo.event.screenY);
this._doDragStart(evInfo.event);
}
this._recordEvent(evInfo);
@ -587,13 +582,13 @@ MouseModule.prototype = {
/**
* Inform our dragger of a dragStart and update kinetic with new data.
*/
_doDragStart: function _doDragStart(sX, sY) {
_doDragStart: function _doDragStart(event) {
let dragData = this._dragData;
dragData.setDragStart(sX, sY);
this._kinetic.addData(sX, sY);
dragData.setDragStart(event.screenX, event.screenY);
this._kinetic.addData(event.screenX, event.screenY);
this._dragger.dragStart(this._targetScrollInterface);
this._dragger.dragStart(event.clientX, event.clientY, event.target, this._targetScrollInterface);
},
/**
@ -738,7 +733,7 @@ MouseModule.prototype = {
* regular scrollBy calls on the scroller.
*/
_defaultDragger: {
dragStart: function dragStart(scroller) {},
dragStart: function dragStart(cx, cy, target, scroller) {},
dragStop : function dragStop(dx, dy, scroller)
{ return this.dragMove(dx, dy, scroller); },
@ -903,7 +898,7 @@ DragData.prototype = {
},
isPointOutsideRadius: function isPointOutsideRadius(sX, sY) {
if (this._originX == undefined)
if (this._originX === null)
return false;
return (Math.pow(sX - this._originX, 2) + Math.pow(sY - this._originY, 2)) >
(2 * Math.pow(this._dragRadius, 2));
@ -1043,7 +1038,8 @@ KineticController.prototype = {
},
end: function end() {
this._beforeEnd();
if (this._beforeEnd)
this._beforeEnd();
this._reset();
},
@ -1062,253 +1058,11 @@ KineticController.prototype = {
return;
}
// Util.dumpLn("adding t:", now, ", sx: ", sx, ", sy: ", sy);
this.momentumBuffer.push({'t': now, 'sx' : sx, 'sy' : sy});
}
};
function ContentPanningModule(owner, browserCanvas, useEarlyMouseMoves) {
this._owner = owner;
this._browserCanvas = browserCanvas;
this._dragData = new DragData(this, 50, 200);
this._useEarlyMouseMoves = useEarlyMouseMoves;
var self = this;
this._kinetic = new KineticController( function (dx, dy) { return self._dragBy(dx, dy); } );
}
ContentPanningModule.prototype = {
handleEvent: function handleEvent(aEvent) {
// exit early for events outside displayed content area
if (aEvent.target !== this._browserCanvas)
return;
switch (aEvent.type) {
case "mousedown":
this._onMouseDown(aEvent);
break;
case "mousemove":
this._onMouseMove(aEvent);
break;
case "mouseout":
case "mouseup":
this._onMouseUp(aEvent);
break;
}
},
/* If someone else grabs events ahead of us, cancel any pending
* timeouts we may have.
*/
cancelPending: function cancelPending() {
this._kinetic.end();
this._dragData.reset();
},
_dragStart: function _dragStart(sX, sY) {
let dragData = this._dragData;
dragData.setDragStart(sX, sY);
[sX, sY] = dragData.lockAxis(sX, sY);
//ws.dragStart(sX, sY);
//Browser.canvasBrowser.startPanning();
},
_dragStop: function _dragStop(sX, sY) {
let dragData = this._dragData;
this._owner.ungrab(this);
[sX, sY] = dragData.lockMouseMove(sX, sY);
// start kinetic scrolling here for canvas only
this._kinetic.start(sX, sY);
dragData.reset();
},
_dragBy: function _dragBy(dx, dy) {
/* XXX
let panned = ws.dragBy(dx, dy);
return panned;
*/
return false;
},
_dragMove: function _dragMove(sX, sY) {
let dragData = this._dragData;
[sX, sY] = dragData.lockMouseMove(sX, sY);
//XXX let panned = ws.dragMove(sX, sY);
let panned = false;
dragData.setDragPosition(sX, sY);
return panned;
},
_onMouseDown: function _onMouseDown(aEvent) {
let dragData = this._dragData;
// if we're in the process of kineticly scrolling, stop and start over
if (this._kinetic.isActive()) {
this._kinetic.end();
this._owner.ungrab(this);
dragData.reset();
}
this._dragStart(aEvent.screenX, aEvent.screenY);
this._onMouseMove(aEvent); // treat this as a mouse move too
},
_onMouseUp: function _onMouseUp(aEvent) {
let dragData = this._dragData;
if (dragData.dragging) {
this._onMouseMove(aEvent); // treat this as a mouse move, incase our x/y are different
this._dragStop(aEvent.screenX, aEvent.screenY);
}
dragData.reset(); // be sure to reset the timer
},
_onMouseMove: function _onMouseMove(aEvent) {
// don't do anything if we're in the process of kineticly scrolling
if (this._kinetic.isActive())
return;
let dragData = this._dragData;
// if we move enough, start a grab to prevent click from getting events
if (dragData.isPointOutsideRadius(aEvent.screenX, aEvent.screenY))
this._owner.grab(this);
// if we never received a mouseDown, we need to go ahead and set this data
if (!dragData.sX)
dragData.setDragPosition(aEvent.screenX, aEvent.screenY);
let [sX, sY] = dragData.lockMouseMove(aEvent.screenX, aEvent.screenY);
// even if we haven't started dragging yet, we should queue up the
// mousemoves in case we do start
if (this._useEarlyMouseMoves || dragData.dragging)
this._kinetic.addData(sX, sY);
if (dragData.dragging)
this._dragMove(sX, sY);
}
};
/**
* Mouse click handlers
*/
function ContentClickingModule(owner) {
this._owner = owner;
this._clickTimeout = -1;
this._events = [];
this._zoomedTo = null;
}
ContentClickingModule.prototype = {
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
// UI panning events
case "mousedown":
this._events.push({event: aEvent, time: Date.now()});
case "mouseup":
// keep an eye out for mouseups that didn't start with a mousedown
if (!(this._events.length % 2)) {
this._reset();
break;
}
this._events.push({event: aEvent, time: Date.now()});
if (this._clickTimeout == -1) {
this._clickTimeout = window.setTimeout(function _clickTimeout(self) { self._sendSingleClick(); }, 400, this);
} else {
window.clearTimeout(this._clickTimeout);
this._clickTimeout = -1;
this._sendDoubleClick();
}
break;
}
},
/* If someone else grabs events ahead of us, cancel any pending
* timeouts we may have.
*/
cancelPending: function cancelPending() {
this._reset();
},
_reset: function _reset() {
if (this._clickTimeout != -1)
window.clearTimeout(this._clickTimeout);
this._clickTimeout = -1;
this._events = [];
},
_sendSingleClick: function _sendSingleClick() {
this._owner.grab(this);
this._dispatchContentMouseEvent(this._events[0].event);
this._dispatchContentMouseEvent(this._events[1].event);
this._owner.ungrab(this);
this._reset();
},
_sendDoubleClick: function _sendDoubleClick() {
this._owner.grab(this);
function optimalElementForPoint(cX, cY) {
var element = Browser.canvasBrowser.elementFromPoint(cX, cY);
return element;
}
let firstEvent = this._events[0].event;
let zoomElement = optimalElementForPoint(firstEvent.clientX, firstEvent.clientY);
if (zoomElement) {
if (zoomElement != this._zoomedTo) {
this._zoomedTo = zoomElement;
Browser.canvasBrowser.zoomToElement(zoomElement);
} else {
this._zoomedTo = null;
Browser.canvasBrowser.zoomFromElement(zoomElement);
}
}
this._owner.ungrab(this);
this._reset();
},
_dispatchContentMouseEvent: function _dispatchContentMouseEvent(aEvent, aType) {
if (!(aEvent instanceof MouseEvent)) {
Cu.reportError("_dispatchContentMouseEvent called with a non-mouse event");
return;
}
let cb = Browser.canvasBrowser;
var [x, y] = cb._clientToContentCoords(aEvent.clientX, aEvent.clientY);
var cwu = cb.contentDOMWindowUtils;
// Redispatch the mouse event, ignoring the root scroll frame
cwu.sendMouseEvent(aType || aEvent.type,
x, y,
aEvent.button || 0,
aEvent.detail || 1,
0, true);
}
};
/**
* Input module for basic scrollwheel input. Currently just zooms the browser
* view accordingly.

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

@ -22,6 +22,7 @@
*
* Contributor(s):
* Roy Frostig <rfrostig@mozilla.com>
* Ben Combee <bcombee@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
@ -56,8 +57,13 @@ let Util = {
for (let key in instance)
if (instance[key] instanceof Function)
instance[key] = bind(instance[key], instance);
}
},
dumpLn: function dumpLn() {
// like dump, but each arg is handled and there's an automatic newline
for (var i = 0; i < arguments.length; i++) { dump(arguments[i]); }
dump("\n");
}
};
@ -554,4 +560,3 @@ wsRect.prototype = {
return this;
}
};

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

@ -28,7 +28,8 @@
* Stuart Parmenter <stuart@mozilla.com>
* Taras Glek <tglek@mozilla.com>
* Roy Frostig <rfrostig@mozilla.com>
*
* Ben Combee <bcombee@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@ -339,18 +340,19 @@ var Browser = {
let container = document.getElementById("tile-container");
let bv = this._browserView = new BrowserView(container, Browser.getVisibleRect());
/* handles dispatching clicks on tiles into clicks in content or zooms */
container.customClicker = this._createContentCustomClicker(bv);
/* vertically scrolling box that contains tiles and the urlbar */
let contentScrollbox = this.contentScrollbox = document.getElementById("tile-container-container");
this.contentScrollboxScroller = contentScrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
let controlsScrollbox = this.controlsScrollbox = document.getElementById("scrollbox");
this.controlsScrollboxScroller = controlsScrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
contentScrollbox.customDragger = new Browser.MainDragger(bv);
/* horizontally scrolling box that holds the sidebars as well as the contentScrollbox */
let controlsScrollbox = this.controlsScrollbox = document.getElementById("scrollbox");
this.controlsScrollboxScroller = controlsScrollbox.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
controlsScrollbox.customDragger = {
dragStart: function dragStart(scroller) {},
dragStart: function dragStart(cx, cy, target, scroller) {},
dragStop: function dragStop(dx, dy, scroller) { return false; },
dragMove: function dragMove(dx, dy, scroller) { return false; }
};
@ -848,24 +850,6 @@ var Browser = {
// XXX we probably shouldn't generate this dynamically like this, but
// actually make it a prototype somewhere and instantiate it and such...
function transformClientToBrowser(cX, cY) {
return Browser.clientToBrowserView(cX, cY).map(browserView.viewportToBrowser);
}
function elementFromPoint(browser, x, y) {
let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
let scrollX = { value: 0 };
let scrollY = { value: 0 };
cwu.getScrollXY(false, scrollX, scrollY);
dump('elementFromPoint: ' + x + ', ' + y + '\n');
return cwu.elementFromPoint(x - scrollX.value,
y - scrollY.value,
true, // ignore root scroll frame
false); // don't flush layout
}
function dispatchContentClick(browser, x, y) {
let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
let scrollX = { value: 0 };
@ -885,26 +869,23 @@ var Browser = {
singleClick: function singleClick(cX, cY) {
let browser = browserView.getBrowser();
if (browser) {
let [x, y] = transformClientToBrowser(cX, cY);
let [x, y] = Browser.transformClientToBrowser(cX, cY);
dispatchContentClick(browser, x, y);
}
},
doubleClick: function doubleClick(cX1, cY1, cX2, cY2) {
let browser = browserView.getBrowser();
if (browser) {
let [x, y] = transformClientToBrowser(cX2, cY2);
let zoomElement = elementFromPoint(browser, x, y);
let [x, y] = Browser.transformClientToBrowser(cX2, cY2);
let zoomElement = Browser.elementFromPoint(x, y);
if (zoomElement) {
dump('@@@ zoomElement is ' + zoomElement + ' :: ' + zoomElement.id + ' :: ' + zoomElement.name + '\n');
this.zoomIn = !this.zoomIn;
if (zoomElement) {
this.zoomIn = !this.zoomIn;
if (this.zoomIn)
Browser.zoomToElement(zoomElement);
else
Browser.zoomFromElement(zoomElement);
}
if (this.zoomIn)
Browser.zoomToElement(zoomElement);
else
Browser.zoomFromElement(zoomElement);
}
},
@ -1153,6 +1134,49 @@ var Browser = {
return (arguments.length > 1) ? [x - x0, y - y0] : (x - x0);
},
/**
* turn client coordinates into page-relative ones (adjusted for
* zoom and page position)
*/
transformClientToBrowser: function transformClientToBrowser(cX, cY) {
return this.clientToBrowserView(cX, cY).map(this._browserView.viewportToBrowser);
},
/**
* return element at client coordinates of browser, returns null if
* there's no active browser or if no element can be found
*/
elementFromPoint: function elementFromPoint(x, y) {
Util.dumpLn("*** elementFromPoint: page ", x, ",", y);
let browser = this._browserView.getBrowser();
if (!browser) return null;
let cwu = BrowserView.Util.getBrowserDOMWindowUtils(browser);
let scrollX = { value: 0 }, scrollY = { value: 0 };
cwu.getScrollXY(false, scrollX, scrollY);
x = x - scrollX.value;
y = y - scrollY.value;
let elem = cwu.elementFromPoint(x, y,
true, /* ignore root scroll frame*/
false); /* don't flush layout */
// step through layers of IFRAMEs and FRAMES to find innermost element
while (elem && (elem instanceof HTMLIFrameElement || elem instanceof HTMLFrameElement)) {
let frameWin = elem.ownerDocument.defaultView;
let frameUtils = frameWin.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
frameUtils.getScrollXY(false, scrollX, scrollY);
x = x - elem.offsetLeft + scrollX.value;
y = y - elem.offsetTop + scrollY.value;
elem = elem.contentDocument.elementFromPoint(x, y);
}
return elem;
},
/**
* Return the visible rect in coordinates with origin at the (left, top) of
* the tile container, i.e. BrowserView coordinates.
@ -1193,19 +1217,86 @@ Browser.MainDragger = function MainDragger(browserView) {
this.scrollingOuterX = true;
this.bv = browserView;
this.floatedWhileDragging = false;
this.draggedFrame = null;
};
Browser.MainDragger.prototype = {
dragStart: function dragStart(scroller) {
_targetIsContent: function _targetIsContent(target) {
let tileBox = document.getElementById("tile-container");
while (target) {
if (target === window)
return false;
if (target === tileBox)
return true;
target = target.parentNode;
}
return false;
},
dragStart: function dragStart(clientX, clientY, target, scroller) {
this.draggedFrame = null;
if (this._targetIsContent(target)) {
// since we're dealing with content, look to see if user has started
// a drag while over a IFRAME/FRAME element
let [x, y] = Browser.transformClientToBrowser(clientX, clientY);
let element = Browser.elementFromPoint(x, y);
if (element && element.ownerDocument != Browser.selectedBrowser.contentDocument) {
Util.dumpLn("*** dragStart got element ", element, " ownerDoc ", element.ownerDocument,
" selectedBrowser.contentDoc ", Browser.selectedBrowser.contentDocument);
this.draggedFrame = element.ownerDocument.defaultView;
}
}
this.bv.pauseRendering();
this.floatedWhileDragging = false;
},
_panFrame: function _panFrame(dx, dy) {
if (this.draggedFrame === null)
return false;
if (dx == 0 && dy == 0)
return true;
let panned = false;
let elem = this.draggedFrame;
// top-level window will have itself as its parent, so stop
// there to allow canvasbrowser/widgetstack to pan instead
// of doing scrolling
while (elem && elem !== elem.parent.document.defaultView) {
let windowUtils = elem.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let origX = {}, origY = {};
windowUtils.getScrollXY(false, origX, origY);
elem.scrollBy(dx, dy);
let newX = {}, newY = {};
windowUtils.getScrollXY(false, newX, newY);
panned = (origX.value != newX.value) || (origY.value != newY.value);
if (panned) {
// get critical area to redraw after we move frame
// NOTE: may need to rate limit these for performance
this.bv.renderNow();
break;
}
elem = elem.parent.document.defaultView;
}
return panned;
},
dragStop: function dragStop(dx, dy, scroller) {
let dx = this.dragMove(dx, dy, scroller, true);
dx += this.dragMove(Browser.snapSidebars(), 0, scroller, true);
Browser.tryUnfloatToolbar();
this.bv.resumeRendering();
@ -1217,6 +1308,10 @@ Browser.MainDragger.prototype = {
dragMove: function dragMove(dx, dy, scroller, doReturnDX) {
let outrv = 0;
// first see if we need to adjust internal IFRAME/FRAME
if (this._panFrame(dx, dy))
return true;
if (this.scrollingOuterX) {
let odx = 0;
let ody = 0;