mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-03-09 04:25:38 +00:00
Bug 462425: Convert Fennec to use WidgetStack framework for panning, r=stuart
This commit is contained in:
parent
4f628ba099
commit
196c91951a
573
mobile/chrome/content/CanvasBrowser.js
Normal file
573
mobile/chrome/content/CanvasBrowser.js
Normal file
@ -0,0 +1,573 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Mobile Browser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Stuart Parmenter <stuart@mozilla.com>
|
||||
* Brad Lassey <blassey@mozilla.com>
|
||||
* Mark Finkle <mfinkle@mozilla.com>
|
||||
* Gavin Sharp <gavin.sharp@gmail.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"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
function CanvasBrowser(canvas) {
|
||||
this._canvas = canvas;
|
||||
this._zoomLevel = 1;
|
||||
this._browser = null;
|
||||
this._pageX = 0;
|
||||
this._pageY = 0;
|
||||
}
|
||||
|
||||
CanvasBrowser.prototype = {
|
||||
_canvas: null,
|
||||
_zoomLevel: 1,
|
||||
_browser: null,
|
||||
_pageX: 0,
|
||||
_pageY: 0,
|
||||
_screenX: 0,
|
||||
_screenY: 0,
|
||||
|
||||
get viewportDimensions() {
|
||||
var rect = this._canvas.getBoundingClientRect();
|
||||
return [rect.width, rect.height];
|
||||
},
|
||||
|
||||
get _effectiveViewportDimensions() {
|
||||
var [w, h] = this.viewportDimensions;
|
||||
return [this._screenToPage(w), this._screenToPage(h)];
|
||||
},
|
||||
|
||||
get _effectiveCanvasDimensions() {
|
||||
let canvasRect = this._canvas.getBoundingClientRect();
|
||||
return [this._screenToPage(canvasRect.width),
|
||||
this._screenToPage(canvasRect.height)];
|
||||
},
|
||||
|
||||
setCurrentBrowser: function(browser) {
|
||||
let currentBrowser = this._browser;
|
||||
if (currentBrowser) {
|
||||
// stop monitor paint events for this browser
|
||||
currentBrowser.removeEventListener("MozAfterPaint", this._paintHandler, false);
|
||||
currentBrowser.setAttribute("type", "content");
|
||||
}
|
||||
|
||||
browser.setAttribute("type", "content-primary");
|
||||
|
||||
// start monitoring paint events for this browser
|
||||
var self = this;
|
||||
this._paintHandler = function(ev) { self._handleMozAfterPaint(ev); }
|
||||
|
||||
browser.addEventListener("MozAfterPaint", this._paintHandler, false);
|
||||
|
||||
this._browser = browser;
|
||||
|
||||
self.zoomToPage();
|
||||
},
|
||||
|
||||
startLoading: function() {
|
||||
// Clear the whole canvas
|
||||
// we clear the whole canvas because the browser's width or height
|
||||
// could be less than the area we end up actually drawing.
|
||||
|
||||
var ctx = this._canvas.getContext("2d");
|
||||
ctx.fillStyle = "rgb(255,255,255)";
|
||||
ctx.fillRect(0, 0, this._canvas.width, this._canvas.height);
|
||||
ctx.fillStyle = "rgb(0,0,0)";
|
||||
ctx.fillText("Loading...", 20, 20);
|
||||
|
||||
this._resizeInterval = setInterval(function(self) { self.zoomToPage(); }, 1000, this);
|
||||
},
|
||||
|
||||
endLoading: function() {
|
||||
clearInterval(this._resizeInterval);
|
||||
this.zoomToPage();
|
||||
},
|
||||
|
||||
viewportHandler: function(bounds, oldBounds) {
|
||||
let pageBounds = bounds.clone();
|
||||
pageBounds.top = Math.floor(this._screenToPage(bounds.top));
|
||||
pageBounds.left = Math.floor(this._screenToPage(bounds.left));
|
||||
pageBounds.bottom = Math.ceil(this._screenToPage(bounds.bottom));
|
||||
pageBounds.right = Math.ceil(this._screenToPage(bounds.right));
|
||||
|
||||
if (0) {
|
||||
if (true /*!oldBounds*/) {
|
||||
this._pageX = pageBounds.x;
|
||||
this._pageY = pageBounds.y;
|
||||
|
||||
var ctx = this._canvas.getContext("2d");
|
||||
|
||||
ctx.save();
|
||||
ctx.scale(this._zoomLevel, this._zoomLevel);
|
||||
|
||||
try {
|
||||
dump("drawWindow: " + pageBounds.x + " " + pageBounds.y + " " + pageBounds.width + " " + pageBounds.height + "\n");
|
||||
ctx.drawWindow(this._browser.contentWindow,
|
||||
pageBounds.x, pageBounds.y, pageBounds.width, pageBounds.height,
|
||||
"white",
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
||||
} catch (e) {
|
||||
dump("DRAWWINDOW FAILED\n");
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!oldBounds) {
|
||||
// no old bounds means we resized the viewport, so redraw everything
|
||||
this._screenX = bounds.x;
|
||||
this._screenY = bounds.y;
|
||||
this._pageX = pageBounds.x;
|
||||
this._pageY = pageBounds.y;
|
||||
|
||||
return this._redrawRect(pageBounds.x, pageBounds.y,
|
||||
pageBounds.width, pageBounds.height);
|
||||
}
|
||||
|
||||
let dx = this._screenX - bounds.x;
|
||||
let dy = this._screenY - bounds.y;
|
||||
|
||||
let [soffX, soffY] = this._canvasPageOffset;
|
||||
|
||||
this._screenX = bounds.x;
|
||||
this._screenY = bounds.y;
|
||||
this._pageX = pageBounds.x;
|
||||
this._pageY = pageBounds.y;
|
||||
|
||||
let [offX, offY] = this._drawOffset;
|
||||
|
||||
// take in to account the canvas offset when we blit
|
||||
let [eoffX, eoffY] = this._canvasPageOffset;
|
||||
let [coffX, coffY] = [Math.floor(this._pageToScreen(eoffX - soffX)),
|
||||
Math.floor(this._pageToScreen(eoffY - soffY))];
|
||||
|
||||
//dump("viewportHandler: " + bounds.toSource() + " " + oldBounds.toSource() + "\n");
|
||||
|
||||
//dump("page offset " + eoffX + " " + eoffY + "\n");
|
||||
|
||||
// deal with repainting
|
||||
let srcRect = { x: 0, y: 0,
|
||||
width: this._canvas.width, height: this._canvas.height };
|
||||
let dstRect = { x: dx - coffX, y: dy - coffY,
|
||||
width: this._canvas.width, height: this._canvas.height };
|
||||
|
||||
// we don't need to do anything if the source and destination are the same
|
||||
if (srcRect.x == dstRect.x && srcRect.y == dstRect.y &&
|
||||
srcRect.width == dstRect.width && srcRect.height == dstRect.height) {
|
||||
dump("avoiding dumb paint\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// blit what we can
|
||||
var ctx = this._canvas.getContext("2d");
|
||||
ctx.drawImage(this._canvas,
|
||||
srcRect.x, srcRect.y,
|
||||
srcRect.width, srcRect.height,
|
||||
dstRect.x, dstRect.y,
|
||||
dstRect.width, dstRect.height);
|
||||
|
||||
//dump("blitting " + srcRect.toSource() + " to " + dstRect.toSource() + "\n");
|
||||
|
||||
// redraw the rest
|
||||
var rgn = Cc["@mozilla.org/gfx/region;1"].createInstance(Ci.nsIScriptableRegion);
|
||||
rgn.setToRect(srcRect.x, srcRect.y, srcRect.width, srcRect.height);
|
||||
rgn.subtractRect(dstRect.x, dstRect.y, dstRect.width, dstRect.height);
|
||||
|
||||
let outX = {}; let outY = {}; let outW = {}; let outH = {};
|
||||
rgn.getBoundingBox(outX, outY, outW, outH);
|
||||
dstRect = { x: outX.value, y: outY.value, width: outW.value, height: outH.value };
|
||||
|
||||
if (dstRect.width > 0 && dstRect.height > 0) {
|
||||
dstRect.width += 1;
|
||||
dstRect.height += 1;
|
||||
|
||||
|
||||
//dump("redrawing: offset " + dstRect.x + " " + dstRect.y + "\n");
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(dstRect.x, dstRect.y);
|
||||
ctx.scale(this._zoomLevel, this._zoomLevel);
|
||||
|
||||
var [offX, offY] = this._drawOffset;
|
||||
let scaledRect = { x: offX + this._screenToPage(dstRect.x),
|
||||
y: offY + this._screenToPage(dstRect.y),
|
||||
width: this._screenToPage(dstRect.width),
|
||||
height: this._screenToPage(dstRect.height) };
|
||||
|
||||
//dump(" rect " + scaledRect.toSource() + "\n");
|
||||
|
||||
ctx.drawWindow(this._browser.contentWindow,
|
||||
scaledRect.x, scaledRect.y,
|
||||
scaledRect.width, scaledRect.height,
|
||||
"white",
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
||||
|
||||
// for testing
|
||||
//ctx.fillStyle = "rgba(255,0,0,0.5)";
|
||||
//ctx.fillRect(0, 0, scaledRect.width, scaledRect.height);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
|
||||
get _canvasPageOffset() {
|
||||
/*
|
||||
let [canvasW, canvasH] = this._effectiveCanvasDimensions;
|
||||
let [viewportW, viewportH] = this._effectiveViewportDimensions;
|
||||
let offscreenCanvasW = (canvasW - viewportW);
|
||||
let offscreenCanvasH = (canvasH - viewportH);
|
||||
let [contentWidth, contentHeight] = this._contentAreaDimensions;
|
||||
|
||||
let [pageX, pageY] = this._pageOffset();
|
||||
|
||||
let left = Math.max(-pageX, -(offscreenCanvasW / 2));
|
||||
let rightMost = (contentWidth - canvasW);
|
||||
if (left > rightMost && rightMost > 0)
|
||||
left = rightMost;
|
||||
|
||||
let top = Math.max(-pageY, -(offscreenCanvasH / 2));
|
||||
let bottomMost = (contentHeight - canvasH);
|
||||
if (top > bottomMost && bottomMost > 0)
|
||||
top = bottomMost;
|
||||
|
||||
return [left, top];
|
||||
*/
|
||||
return [0, 0];
|
||||
},
|
||||
|
||||
get _drawOffset() {
|
||||
let [offX, offY] = this._canvasPageOffset;
|
||||
let [pageX, pageY] = this._pageOffset();
|
||||
|
||||
//dump(offX + " " + offY + " " + pageX + " " + pageY + "\n");
|
||||
return [pageX + offX, pageY + offY];
|
||||
},
|
||||
|
||||
_handleMozAfterPaint: function(aEvent) {
|
||||
let cwin = this._browser.contentWindow;
|
||||
|
||||
for (let i = 0; i < aEvent.clientRects.length; i++) {
|
||||
let e = aEvent.clientRects.item(i);
|
||||
//dump(Math.floor(e.left + cwin.scrollX),
|
||||
// Math.floor(e.top + cwin.scrollY),
|
||||
// Math.ceil(e.width), Math.ceil(e.height));
|
||||
this._redrawRect(Math.floor(e.left + cwin.scrollX),
|
||||
Math.floor(e.top + cwin.scrollY),
|
||||
Math.ceil(e.width), Math.ceil(e.height));
|
||||
}
|
||||
},
|
||||
|
||||
_redrawRect: function(x, y, width, height) {
|
||||
function intersect(r1, r2) {
|
||||
let xmost1 = r1.x + r1.width;
|
||||
let ymost1 = r1.y + r1.height;
|
||||
let xmost2 = r2.x + r2.width;
|
||||
let ymost2 = r2.y + r2.height;
|
||||
|
||||
let x = Math.max(r1.x, r2.x);
|
||||
let y = Math.max(r1.y, r2.y);
|
||||
|
||||
let temp = Math.min(xmost1, xmost2);
|
||||
if (temp <= x)
|
||||
return null;
|
||||
|
||||
let width = temp - x;
|
||||
|
||||
temp = Math.min(ymost1, ymost2);
|
||||
if (temp <= y)
|
||||
return null;
|
||||
|
||||
let height = temp - y;
|
||||
|
||||
return { x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
height: height };
|
||||
}
|
||||
|
||||
let r1 = { x : x,
|
||||
y : y,
|
||||
width : width,
|
||||
height: height };
|
||||
|
||||
// check to see if the input coordinates are inside the visiable destination
|
||||
let [canvasW, canvasH] = this._effectiveCanvasDimensions;
|
||||
let r2 = { x : this._pageX,
|
||||
y : this._pageY,
|
||||
width : canvasW,
|
||||
height: canvasH };
|
||||
|
||||
let dest = intersect(r1, r2);
|
||||
|
||||
if (!dest)
|
||||
return;
|
||||
|
||||
//dump(dest.toSource() + "\n");
|
||||
|
||||
var ctx = this._canvas.getContext("2d");
|
||||
|
||||
ctx.save();
|
||||
ctx.scale(this._zoomLevel, this._zoomLevel);
|
||||
|
||||
var [offX, offY] = this._drawOffset;
|
||||
//dump("offx, offy: " + offX + " " + offY + "\n");
|
||||
ctx.translate(dest.x - offX, dest.y - offY);
|
||||
|
||||
//dump("drawWindow#2: " + dest.x + " " + dest.y + " " + dest.width + " " + dest.height + " @ " + (dest.x - offX) + " " + (dest.y - offY) + "\n");
|
||||
ctx.drawWindow(this._browser.contentWindow,
|
||||
dest.x, dest.y,
|
||||
dest.width, dest.height,
|
||||
"white",
|
||||
ctx.DRAWWINDOW_DO_NOT_FLUSH);
|
||||
|
||||
ctx.restore();
|
||||
},
|
||||
|
||||
_clampZoomLevel: function(aZoomLevel) {
|
||||
const min = 0.2;
|
||||
const max = 2.0;
|
||||
|
||||
return Math.min(Math.max(min, aZoomLevel), max);
|
||||
},
|
||||
|
||||
set zoomLevel(val) {
|
||||
this._zoomLevel = this._clampZoomLevel(val);
|
||||
Browser.updateViewportSize();
|
||||
},
|
||||
|
||||
get zoomLevel() {
|
||||
return this._zoomLevel;
|
||||
},
|
||||
|
||||
zoom: function(aDirection) {
|
||||
if (aDirection == 0)
|
||||
return;
|
||||
|
||||
var zoomDelta = 0.05; // 1/20
|
||||
if (aDirection >= 0)
|
||||
zoomDelta *= -1;
|
||||
|
||||
this.zoomLevel = this._zoomLevel + zoomDelta;
|
||||
},
|
||||
|
||||
zoomToPage: function() {
|
||||
//dump("zoom to page\n");
|
||||
// Adjust the zoomLevel to fit the page contents in our window
|
||||
// width
|
||||
let [contentW, ] = this._contentAreaDimensions;
|
||||
let [viewportW, ] = this.viewportDimensions;
|
||||
|
||||
if (contentW > viewportW)
|
||||
this.zoomLevel = viewportW / contentW;
|
||||
},
|
||||
|
||||
zoomToElement: function(aElement) {
|
||||
const margin = 15;
|
||||
|
||||
// XXX The widget stack code doesn't do what we want when you change
|
||||
// the viewport bounds to something smaller than your current position
|
||||
// so pan back to 0,0 before we resize and then pan to our destination
|
||||
ws.panTo(0, 0);
|
||||
|
||||
// scale to the element's width
|
||||
let [viewportW, ] = this.viewportDimensions;
|
||||
|
||||
let elRect = this._getPagePosition(aElement);
|
||||
let zoomLevel = viewportW / (elRect.width + (2 * margin));
|
||||
this.zoomLevel = Math.min(zoomLevel, 10);
|
||||
|
||||
// pan to the element
|
||||
ws.panTo(Math.floor(Math.max(this._pageToScreen(elRect.x) - margin, 0)),
|
||||
Math.floor(Math.max(this._pageToScreen(elRect.y) - margin, 0)));
|
||||
},
|
||||
|
||||
zoomFromElement: function(aElement) {
|
||||
let elRect = this._getPagePosition(aElement);
|
||||
|
||||
// XXX The widget stack code doesn't do what we want when you change
|
||||
// the viewport bounds to something smaller than your current position
|
||||
// so pan back to 0,0 before we resize and then pan to our destination
|
||||
ws.panTo(0, 0);
|
||||
|
||||
// pan to the element
|
||||
// don't bother with x since we're zooming all the way out
|
||||
this.zoomToPage();
|
||||
|
||||
// XXX have this center the element on the page
|
||||
ws.panTo(0, Math.floor(Math.max(0, this._pageToScreen(elRect.y))));
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the content element for a given point in client coordinates
|
||||
* (relative to the top left corner of the chrome window).
|
||||
*/
|
||||
elementFromPoint: function(aX, aY) {
|
||||
let [x, y] = this._clientToContentCoords(aX, aY);
|
||||
let cwu = this._browser.contentWindow
|
||||
.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
let element = cwu.elementFromPoint(x, y,
|
||||
true, /* ignore root scroll frame*/
|
||||
false); /* don't flush layout */
|
||||
|
||||
return element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the page position for a given element
|
||||
* (relative to the document origin).
|
||||
*/
|
||||
_getPagePosition: function(aElement) {
|
||||
let r = aElement.getBoundingClientRect();
|
||||
let cwin = this._browser.contentWindow;
|
||||
let retVal = {
|
||||
width: r.width,
|
||||
height: r.height,
|
||||
x: r.left + cwin.scrollX,
|
||||
y: r.top + cwin.scrollY
|
||||
};
|
||||
|
||||
return retVal;
|
||||
},
|
||||
|
||||
_pageOffset: function() {
|
||||
// return [this._screenToPage(ws._viewport.viewportInnerBounds.x),
|
||||
// this._screenToPage(ws._viewport.viewportInnerBounds.y)];
|
||||
return [this._pageX, this._pageY];
|
||||
},
|
||||
|
||||
/* Given a set of client coordinates (relative to the app window),
|
||||
* returns the content coordinates relative to the viewport.
|
||||
*/
|
||||
_clientToContentCoords: function(aClientX, aClientY) {
|
||||
// Determine position relative to the document origin
|
||||
// Need to adjust for the deckbrowser not being at 0,0
|
||||
// (e.g. due to other browser UI)
|
||||
|
||||
let browserRect = this._canvas.getBoundingClientRect();
|
||||
let clickOffsetX = this._screenToPage(aClientX - browserRect.left) + this._pageX;
|
||||
let clickOffsetY = this._screenToPage(aClientY - browserRect.top) + this._pageY;
|
||||
|
||||
// Take scroll offset into account to return coordinates relative to the viewport
|
||||
let cwin = this._browser.contentWindow;
|
||||
return [clickOffsetX - cwin.scrollX,
|
||||
clickOffsetY - cwin.scrollY];
|
||||
},
|
||||
|
||||
get _contentAreaDimensions() {
|
||||
var cdoc = this._browser.contentDocument;
|
||||
|
||||
// Return the document width/height for XUL documents (which is
|
||||
// essentially the same as the viewport width/height).
|
||||
if (cdoc instanceof XULDocument)
|
||||
return [cdoc.width, cdoc.height];
|
||||
|
||||
if (cdoc instanceof SVGDocument) {
|
||||
let rect = cdoc.rootElement.getBoundingClientRect();
|
||||
return [rect.width, rect.height];
|
||||
}
|
||||
|
||||
// These might not exist yet depending on page load state
|
||||
var body = cdoc.body || {};
|
||||
var html = cdoc.documentElement || {};
|
||||
|
||||
var w = Math.max(body.scrollWidth, html.scrollWidth);
|
||||
var h = Math.max(body.scrollHeight, html.scrollHeight);
|
||||
|
||||
if (isNaN(w) || isNaN(h) || w == 0 || h == 0)
|
||||
return [this._canvas.width, this._canvas.height];
|
||||
|
||||
return [w, h];
|
||||
},
|
||||
|
||||
_screenToPage: function(aValue) {
|
||||
return aValue / this._zoomLevel;
|
||||
},
|
||||
|
||||
_pageToScreen: function(aValue) {
|
||||
return aValue * this._zoomLevel;
|
||||
},
|
||||
|
||||
/* ensures that a given content element is visible */
|
||||
ensureElementIsVisible: function(aElement) {
|
||||
let elRect = this._getPagePosition(aElement);
|
||||
let [viewportW, viewportH] = this._effectiveViewportDimensions;
|
||||
let curRect = {
|
||||
x: this._pageX,
|
||||
y: this._pageY,
|
||||
width: viewportW,
|
||||
height: viewportH
|
||||
};
|
||||
|
||||
// Adjust for part of our viewport being offscreen
|
||||
// XXX this assumes that the browser is meant to be fullscreen
|
||||
let browserRect = this._currentBrowser.getBoundingClientRect();
|
||||
curRect.height -= this._screenToPage(Math.abs(browserRect.top));
|
||||
if (browserRect.top < 0)
|
||||
curRect.y -= this._screenToPage(browserRect.top);
|
||||
curRect.width -= this._screenToPage(Math.abs(browserRect.left));
|
||||
if (browserRect.left < 0)
|
||||
curRect.x -= this._screenToPage(browserRect.left);
|
||||
|
||||
let newx = curRect.x;
|
||||
let newy = curRect.y;
|
||||
|
||||
if (elRect.x + elRect.width > curRect.x + curRect.width) {
|
||||
newx = curRect.x + ((elRect.x + elRect.width)-(curRect.x + curRect.width));
|
||||
} else if (elRect.x < curRect.x) {
|
||||
newx = elRect.x;
|
||||
}
|
||||
|
||||
if (elRect.y + elRect.height > curRect.y + curRect.height) {
|
||||
newy = curRect.y + ((elRect.y + elRect.height)-(curRect.y + curRect.height));
|
||||
} else if (elRect.y < curRect.y) {
|
||||
newy = elRect.y;
|
||||
}
|
||||
|
||||
this.panTo(newx, newy);
|
||||
},
|
||||
|
||||
/* Pans directly to a given content element */
|
||||
panToElement: function(aElement) {
|
||||
var elRect = this._getPagePosition(aElement);
|
||||
|
||||
this.panTo(elRect.x, elRect.y);
|
||||
},
|
||||
|
||||
panTo: function(x, y) {
|
||||
ws.panTo(x, y);
|
||||
}
|
||||
};
|
646
mobile/chrome/content/InputHandler.js
Normal file
646
mobile/chrome/content/InputHandler.js
Normal file
@ -0,0 +1,646 @@
|
||||
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
|
||||
/*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is Mozilla Mobile Browser.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Stuart Parmenter <stuart@mozilla.com>
|
||||
* Brad Lassey <blassey@mozilla.com>
|
||||
* Mark Finkle <mfinkle@mozilla.com>
|
||||
* Gavin Sharp <gavin.sharp@gmail.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"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/**
|
||||
* Everything that is registed in _modules gets called with each event that the
|
||||
* InputHandler is registered to listen for.
|
||||
*
|
||||
* When one of the handlers decides it wants to handle the event, it should call
|
||||
* grab() on its owner which will cause it to receive all of the events until it
|
||||
* calls ungrab(). Calling grab will notify the other handlers via a
|
||||
* cancelPending() notification. This tells them to stop what they're doing and
|
||||
* give up hope for being the one to process the events.
|
||||
*/
|
||||
|
||||
function InputHandler() {
|
||||
let stack = document.getElementById("browser-container");
|
||||
stack.addEventListener("DOMMouseScroll", this, true);
|
||||
|
||||
let content = document.getElementById("canvas");
|
||||
content.addEventListener("mouseout", this, true);
|
||||
content.addEventListener("mousedown", this, true);
|
||||
content.addEventListener("mouseup", this, true);
|
||||
content.addEventListener("mousemove", this, true);
|
||||
content.addEventListener("keydown", this, true);
|
||||
content.addEventListener("keyup", this, true);
|
||||
|
||||
let prefsvc = Components.classes["@mozilla.org/preferences-service;1"].
|
||||
getService(Components.interfaces.nsIPrefBranch2);
|
||||
let allowKinetic = prefsvc.getBoolPref("browser.ui.panning.kinetic");
|
||||
//let allowKinetic = false;
|
||||
|
||||
if (allowKinetic)
|
||||
this._modules.push(new KineticPanningModule(this));
|
||||
else
|
||||
this._modules.push(new PanningModule(this));
|
||||
|
||||
this._modules.push(new ClickingModule(this));
|
||||
this._modules.push(new ScrollwheelModule(this));
|
||||
}
|
||||
|
||||
InputHandler.prototype = {
|
||||
_modules : [],
|
||||
_grabbed : null,
|
||||
|
||||
grab: function(obj) {
|
||||
//dump("grabbing\n");
|
||||
this._grabbed = obj;
|
||||
|
||||
for each(mod in this._modules) {
|
||||
if (mod != obj)
|
||||
mod.cancelPending();
|
||||
}
|
||||
// only send events to this object
|
||||
// call cancel on all modules
|
||||
},
|
||||
|
||||
ungrab: function(obj) {
|
||||
//dump("unggrabbing\n");
|
||||
this._grabbed = null;
|
||||
// only send events to this object
|
||||
// call cancel on all modules
|
||||
},
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
if (this._grabbed) {
|
||||
this._grabbed.handleEvent(aEvent);
|
||||
} else {
|
||||
for each(mod in this._modules)
|
||||
mod.handleEvent(aEvent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Kinetic panning code
|
||||
*/
|
||||
|
||||
function KineticPanningModule(owner) {
|
||||
this._owner = owner;
|
||||
}
|
||||
|
||||
KineticPanningModule.prototype = {
|
||||
_owner: null,
|
||||
_dragData: {
|
||||
dragging: false,
|
||||
sX: 0,
|
||||
sY: 0,
|
||||
dragStartTimeout: -1,
|
||||
|
||||
reset: function() {
|
||||
this.dragging = false;
|
||||
this.sX = 0;
|
||||
this.sY = 0;
|
||||
if (this.dragStartTimeout != -1)
|
||||
clearTimeout(this.dragStartTimeout);
|
||||
this.dragStartTimeout = -1;
|
||||
}
|
||||
},
|
||||
|
||||
_kineticData: {
|
||||
// const
|
||||
kineticStepSize: 15,
|
||||
kineticDecelloration: 0.004,
|
||||
momentumBufferSize: 3,
|
||||
|
||||
momentumBuffer: [],
|
||||
momentumBufferIndex: 0,
|
||||
lastTime: 0,
|
||||
kineticDuration: 0,
|
||||
kineticDirX: 0,
|
||||
kineticDirY: 0,
|
||||
kineticHandle : -1,
|
||||
kineticStep : 0,
|
||||
kineticStartX : 0,
|
||||
kineticStartY : 0,
|
||||
kineticInitialVel: 0,
|
||||
|
||||
reset: function() {
|
||||
if (this.kineticHandle != -1) {
|
||||
window.clearInterval(this.kineticHandle);
|
||||
this.kineticHandle = -1;
|
||||
}
|
||||
|
||||
this.momentumBuffer = [];
|
||||
this.momentumBufferIndex = 0;
|
||||
this.lastTime = 0;
|
||||
this.kineticDuration = 0;
|
||||
this.kineticDirX = 0;
|
||||
this.kineticDirY = 0;
|
||||
this.kineticStep = 0;
|
||||
this.kineticStartX = 0;
|
||||
this.kineticStartY = 0;
|
||||
this.kineticInitialVel = 0;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "mousedown":
|
||||
return this._onMouseDown(aEvent);
|
||||
break;
|
||||
case "mousemove":
|
||||
return this._onMouseMove(aEvent);
|
||||
case "mouseout":
|
||||
case "mouseup":
|
||||
return this._onMouseUp(aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/* If someone else grabs events ahead of us, cancel any pending
|
||||
* timeouts we may have.
|
||||
*/
|
||||
cancelPending: function() {
|
||||
this._dragData.reset();
|
||||
// XXX we should cancel kinetic here as well
|
||||
//dump("canceling drag\n");
|
||||
},
|
||||
|
||||
_dragStart: function(sX, sY) {
|
||||
this._dragData.dragging = true;
|
||||
this._dragData.dragStartTimeout = -1;
|
||||
|
||||
// grab all events until we stop the drag
|
||||
this._owner.grab(this);
|
||||
|
||||
ws.dragStart(sX, sY);
|
||||
|
||||
// set the kinetic start time
|
||||
this._kineticData.lastTime = Date.now();
|
||||
},
|
||||
|
||||
_dragStop: function(sX, sY) {
|
||||
// start kinetic scrolling here.
|
||||
if (!this._startKinetic(sX, sY)) {
|
||||
this._endKinetic(sX, sY);
|
||||
}
|
||||
},
|
||||
|
||||
_dragMove: function(sX, sY) {
|
||||
ws.dragMove(sX, sY);
|
||||
},
|
||||
|
||||
_onMouseDown: function(aEvent) {
|
||||
// if we're in the process of kineticly scrolling, stop and start over
|
||||
if (this.kineticHandle != -1)
|
||||
this._endKinetic(aEvent.screenX, aEvent.screenY);
|
||||
|
||||
let dragData = this._dragData;
|
||||
|
||||
dragData.sX = aEvent.screenX;
|
||||
dragData.sY = aEvent.screenY;
|
||||
|
||||
dragData.dragStartTimeout = setTimeout(function(self, sX, sY) { self._dragStart(sX, sY) },
|
||||
200, this, aEvent.screenX, aEvent.screenY);
|
||||
},
|
||||
_onMouseUp: function(aEvent) {
|
||||
let dragData = this._dragData;
|
||||
|
||||
if (dragData.dragging)
|
||||
this._dragStop(aEvent.screenX, aEvent.screenY);
|
||||
else
|
||||
this._dragData.reset(); // be sure to reset the timer
|
||||
},
|
||||
|
||||
_onMouseMove: function(aEvent) {
|
||||
// don't do anything if we're in the process of kineticly scrolling
|
||||
if (this._kineticData.kineticHandle != -1)
|
||||
return;
|
||||
|
||||
let dragData = this._dragData;
|
||||
|
||||
let dx = dragData.sX - aEvent.screenX;
|
||||
let dy = dragData.sY - aEvent.screenY;
|
||||
|
||||
if (!dragData.dragging && dragData.dragStartTimeout != -1) {
|
||||
if ((Math.abs(dx*dx) + Math.abs(dy*dy)) > 100) {
|
||||
clearTimeout(dragData.dragStartTimeout);
|
||||
this._dragStart(aEvent.screenX, aEvent.screenY);
|
||||
}
|
||||
}
|
||||
if (!dragData.dragging)
|
||||
return;
|
||||
|
||||
this._dragMove(aEvent.screenX, aEvent.screenY);
|
||||
|
||||
dragData.sX = aEvent.screenX;
|
||||
dragData.sY = aEvent.screenY;
|
||||
|
||||
// update our kinetic data
|
||||
let kineticData = this._kineticData;
|
||||
let t = Date.now();
|
||||
let dt = t - kineticData.lastTime;
|
||||
kineticData.lastTime = t;
|
||||
let momentumBuffer = { dx: -dx, dy: -dy, dt: dt }
|
||||
|
||||
kineticData.momentumBuffer[kineticData.momentumBufferIndex] = momentumBuffer;
|
||||
kineticData.momentumBufferIndex++;
|
||||
kineticData.momentumBufferIndex %= kineticData.momentumBufferSize;
|
||||
},
|
||||
|
||||
_startKinetic: function(sX, sY) {
|
||||
let kineticData = this._kineticData;
|
||||
|
||||
let dx = 0;
|
||||
let dy = 0;
|
||||
let dt = 0;
|
||||
if (kineticData.kineticInitialVel)
|
||||
return true;
|
||||
|
||||
if (!kineticData.momentumBuffer)
|
||||
return false;
|
||||
|
||||
for (let i = 0; i < kineticData.momentumBufferSize; i++) {
|
||||
let me = kineticData.momentumBuffer[(kineticData.momentumBufferIndex + i) % kineticData.momentumBufferSize];
|
||||
if (!me)
|
||||
return false;
|
||||
|
||||
dx += me.dx;
|
||||
dy += me.dy;
|
||||
dt += me.dt;
|
||||
}
|
||||
if (dt <= 0)
|
||||
return false;
|
||||
|
||||
let dist = Math.sqrt(dx*dx+dy*dy);
|
||||
let vel = dist/dt;
|
||||
if (vel < 1)
|
||||
return false;
|
||||
|
||||
kineticData.kineticDirX = dx/dist;
|
||||
kineticData.kineticDirY = dy/dist;
|
||||
if (kineticData.kineticDirX > 0.9) {
|
||||
kineticData.kineticDirX = 1;
|
||||
kineticData.kineticDirY = 0;
|
||||
} else if (kineticData.kineticDirY < -0.9) {
|
||||
kineticData.kineticDirX = 0;
|
||||
kineticData.kineticDirY = -1;
|
||||
} else if (kineticData.kineticDirX < -0.9) {
|
||||
kineticData.kineticDirX = -1;
|
||||
kineticData.kineticDirY = 0;
|
||||
} else if (kineticData.kineticDirY > 0.9) {
|
||||
kineticData.kineticDirX = 0;
|
||||
kineticData.kineticDirY = 1;
|
||||
}
|
||||
|
||||
kineticData.kineticDuration = vel/(2 * kineticData.kineticDecelloration);
|
||||
kineticData.kineticStep = 0;
|
||||
kineticData.kineticStartX = sX;
|
||||
kineticData.kineticStartY = sY;
|
||||
kineticData.kineticInitialVel = vel;
|
||||
kineticData.kineticHandle = window.setInterval(this._doKinetic, kineticData.kineticStepSize, this);
|
||||
return true;
|
||||
},
|
||||
|
||||
_doKinetic: function(self) {
|
||||
let kineticData = self._kineticData;
|
||||
|
||||
let t = kineticData.kineticStep * kineticData.kineticStepSize;
|
||||
kineticData.kineticStep++;
|
||||
if (t > kineticData.kineticDuration)
|
||||
t = kineticData.kineticDuration;
|
||||
let dist = kineticData.kineticInitialVel * t -
|
||||
kineticData.kineticDecelloration * t * t;
|
||||
let newX = Math.floor(kineticData.kineticDirX * dist + kineticData.kineticStartX);
|
||||
let newY = Math.floor(kineticData.kineticDirY * dist + kineticData.kineticStartY);
|
||||
|
||||
self._dragMove(newX, newY);
|
||||
|
||||
if(t >= kineticData.kineticDuration)
|
||||
self._endKinetic(newX, newY);
|
||||
},
|
||||
|
||||
_endKinetic: function(sX, sY) {
|
||||
ws.dragStop(sX, sY);
|
||||
this._owner.ungrab(this);
|
||||
this._dragData.reset();
|
||||
this._kineticData.reset();
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Regular non-kinetic panning code
|
||||
*/
|
||||
|
||||
function PanningModule(owner) {
|
||||
this._owner = owner;
|
||||
}
|
||||
|
||||
PanningModule.prototype = {
|
||||
_owner: null,
|
||||
_dragData: {
|
||||
dragging: false,
|
||||
sX: 0,
|
||||
sY: 0,
|
||||
dragStartTimeout: -1,
|
||||
|
||||
reset: function() {
|
||||
this.dragging = false;
|
||||
this.sX = 0;
|
||||
this.sY = 0;
|
||||
if (this.dragStartTimeout != -1)
|
||||
clearTimeout(this.dragStartTimeout);
|
||||
this.dragStartTimeout = -1;
|
||||
}
|
||||
},
|
||||
|
||||
handleEvent: function(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "mousedown":
|
||||
return this._onMouseDown(aEvent);
|
||||
break;
|
||||
case "mousemove":
|
||||
return this._onMouseMove(aEvent);
|
||||
case "mouseout":
|
||||
case "mouseup":
|
||||
return this._onMouseUp(aEvent);
|
||||
}
|
||||
},
|
||||
|
||||
/* If someone else grabs events ahead of us, cancel any pending
|
||||
* timeouts we may have.
|
||||
*/
|
||||
cancelPending: function() {
|
||||
this._dragData.reset();
|
||||
//dump("canceling drag\n");
|
||||
},
|
||||
|
||||
_dragStart: function(sX, sY) {
|
||||
//dump("starting drag\n");
|
||||
this._dragData.dragging = true;
|
||||
this._dragData.dragStartTimeout = -1;
|
||||
|
||||
// grab all events until we stop the drag
|
||||
this._owner.grab(this);
|
||||
|
||||
ws.dragStart(sX, sY);
|
||||
},
|
||||
|
||||
_dragStop: function(sX, sY) {
|
||||
//dump("ending drag\n");
|
||||
this._dragData.reset();
|
||||
|
||||
ws.dragStop(sX, sY);
|
||||
|
||||
this._owner.ungrab(this);
|
||||
},
|
||||
|
||||
_dragMove: function(sX, sY) {
|
||||
//dump("moving drag" + sX + " " + sY + "\n");
|
||||
ws.dragMove(sX, sY);
|
||||
},
|
||||
|
||||
_onMouseDown: function(aEvent) {
|
||||
let dragData = this._dragData;
|
||||
|
||||
dragData.sX = aEvent.screenX;
|
||||
dragData.sY = aEvent.screenY;
|
||||
|
||||
dragData.dragStartTimeout = setTimeout(function(self, sX, sY) { self._dragStart(sX, sY) },
|
||||
200, this, aEvent.screenX, aEvent.screenY);
|
||||
},
|
||||
|
||||
_onMouseUp: function(aEvent) {
|
||||
let dragData = this._dragData;
|
||||
|
||||
if (dragData.dragging)
|
||||
this._dragStop(aEvent.screenX, aEvent.screenY);
|
||||
else
|
||||
this._dragData.reset(); // be sure to reset the timer
|
||||
},
|
||||
|
||||
_onMouseMove: function(aEvent) {
|
||||
let dragData = this._dragData;
|
||||
|
||||
let dx = dragData.sX - aEvent.screenX;
|
||||
let dy = dragData.sY - aEvent.screenY;
|
||||
|
||||
if (!dragData.dragging && dragData.dragStartTimeout != -1) {
|
||||
if ((Math.abs(dx*dx) + Math.abs(dy*dy)) > 100) {
|
||||
clearTimeout(dragData.dragStartTimeout);
|
||||
this._dragStart(aEvent.screenX, aEvent.screenY);
|
||||
}
|
||||
}
|
||||
if (!dragData.dragging)
|
||||
return;
|
||||
|
||||
this._dragMove(aEvent.screenX, aEvent.screenY);
|
||||
|
||||
dragData.sX = aEvent.screenX;
|
||||
dragData.sY = aEvent.screenY;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mouse click handlers
|
||||
*/
|
||||
|
||||
function ClickingModule(owner) {
|
||||
this._owner = owner;
|
||||
}
|
||||
|
||||
|
||||
ClickingModule.prototype = {
|
||||
_clickTimeout : -1,
|
||||
_events : [],
|
||||
_zoomed : false,
|
||||
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
// UI panning events
|
||||
case "mousedown":
|
||||
//dump("mousedown\n");
|
||||
this._events.push({event: aEvent, time: Date.now()});
|
||||
|
||||
// we're waiting for a click
|
||||
if (this._clickTimeout != -1) {
|
||||
// if we just got another mousedown, don't send anything until we get another mousedown
|
||||
clearTimeout(this._clickTimeout);
|
||||
this.clickTimeout = -1
|
||||
}
|
||||
break;
|
||||
case "mouseup":
|
||||
// keep an eye out for mouseups that didn't start with a mousedown
|
||||
if (!(this._events.length % 2)) {
|
||||
this._reset();
|
||||
break;
|
||||
}
|
||||
|
||||
//dump("mouseup\n");
|
||||
this._events.push({event: aEvent, time: Date.now()});
|
||||
|
||||
if (this._clickTimeout == -1) {
|
||||
this._clickTimeout = setTimeout(function(self) { self._sendSingleClick() }, 400, this);
|
||||
} else {
|
||||
clearTimeout(this._clickTimeout);
|
||||
this._sendDoubleClick();
|
||||
}
|
||||
break;
|
||||
case "mouseout":
|
||||
this._reset();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/* If someone else grabs events ahead of us, cancel any pending
|
||||
* timeouts we may have.
|
||||
*/
|
||||
cancelPending: function() {
|
||||
//dump("canceling click\n");
|
||||
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_reset: function() {
|
||||
if (this._clickTimeout != -1)
|
||||
clearTimeout(this._clickTimeout);
|
||||
this._clickTimeout = -1;
|
||||
|
||||
this._events = [];
|
||||
},
|
||||
|
||||
_sendSingleClick: function() {
|
||||
this._owner.grab(this);
|
||||
this._redispatchMouseEvent(this._events[0].event);
|
||||
this._redispatchMouseEvent(this._events[1].event);
|
||||
this._owner.ungrab(this);
|
||||
|
||||
this._reset();
|
||||
},
|
||||
|
||||
_sendDoubleClick: function() {
|
||||
this._owner.grab(this);
|
||||
|
||||
// XXX disable zooming until it works properly.
|
||||
|
||||
function optimalElementForPoint(cX, cY) {
|
||||
var element = Browser.content.elementFromPoint(cX, cY);
|
||||
if (!element)
|
||||
return null;
|
||||
|
||||
// Find the nearest non-inline ancestor
|
||||
while (element.parentNode) {
|
||||
let display = window.getComputedStyle(element, "").getPropertyValue("display");
|
||||
let zoomable = /table/.test(display) || /block/.test(display);
|
||||
if (zoomable)
|
||||
break;
|
||||
|
||||
element = element.parentNode;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
let firstEvent = this._events[0].event;
|
||||
let zoomElement = optimalElementForPoint(firstEvent.clientX, firstEvent.clientY);
|
||||
|
||||
if (zoomElement) {
|
||||
if (this._zoomed) {
|
||||
// zoom out
|
||||
this._zoomed = false;
|
||||
Browser.content.zoomFromElement(zoomElement);
|
||||
} else {
|
||||
// zoom in
|
||||
this._zoomed = true;
|
||||
Browser.content.zoomToElement(zoomElement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this._owner.ungrab(this);
|
||||
|
||||
this._reset();
|
||||
},
|
||||
|
||||
|
||||
_redispatchMouseEvent: function(aEvent, aType) {
|
||||
if (!(aEvent instanceof MouseEvent)) {
|
||||
Components.utils.reportError("_redispatchMouseEvent called with a non-mouse event");
|
||||
return;
|
||||
}
|
||||
|
||||
var [x, y] = Browser.content._clientToContentCoords(aEvent.clientX, aEvent.clientY);
|
||||
//dump("sending mouse event to: " + x + " " + y + "\n");
|
||||
|
||||
var cwin = Browser.currentBrowser.contentWindow;
|
||||
var cwu = cwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
// Redispatch the mouse event, ignoring the root scroll frame
|
||||
cwu.sendMouseEvent(aType || aEvent.type,
|
||||
x, y,
|
||||
aEvent.button || 0,
|
||||
aEvent.detail || 1,
|
||||
0, true);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Scrollwheel zooming handler
|
||||
*/
|
||||
|
||||
function ScrollwheelModule(owner) {
|
||||
this._owner = owner;
|
||||
}
|
||||
|
||||
ScrollwheelModule.prototype = {
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
// UI panning events
|
||||
case "DOMMouseScroll":
|
||||
this._owner.grab(this);
|
||||
this.deckbrowser.zoom(aEvent.detail);
|
||||
this._owner.ungrab(this);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/* If someone else grabs events ahead of us, cancel any pending
|
||||
* timeouts we may have.
|
||||
*/
|
||||
cancelPending: function() {
|
||||
}
|
||||
};
|
||||
|
1464
mobile/chrome/content/WidgetStack.js
Normal file
1464
mobile/chrome/content/WidgetStack.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -55,7 +55,7 @@ const kMaxEngines = 4;
|
||||
const kDefaultFavIconURL = "chrome://browser/skin/images/default-favicon.png";
|
||||
|
||||
[
|
||||
["gContentBox", "contentBox"],
|
||||
["gContentBox", "content"],
|
||||
].forEach(function (elementGlobal) {
|
||||
var [name, id] = elementGlobal;
|
||||
window.__defineGetter__(name, function () {
|
||||
@ -81,20 +81,6 @@ var BrowserUI = {
|
||||
_favicon : null,
|
||||
_faviconLink : null,
|
||||
|
||||
_setContentPosition : function (aProp, aValue) {
|
||||
let value = Math.round(aValue);
|
||||
if (aProp == "left") {
|
||||
gContentBox.style.marginLeft = value + "px";
|
||||
gContentBox.style.marginRight = -value + "px";
|
||||
} else if (aProp == "top") {
|
||||
gContentBox.style.marginTop = value + "px";
|
||||
gContentBox.style.marginBottom = -value + "px";
|
||||
}
|
||||
},
|
||||
get _contentTop() {
|
||||
return parseInt(gContentBox.style.marginTop);
|
||||
},
|
||||
|
||||
_titleChanged : function(aDocument) {
|
||||
var browser = Browser.currentBrowser;
|
||||
if (browser && aDocument != browser.contentDocument)
|
||||
@ -130,21 +116,7 @@ var BrowserUI = {
|
||||
this.setURI();
|
||||
this._titleChanged(browser.contentDocument);
|
||||
this._favicon.src = browser.mIconURL || kDefaultFavIconURL;
|
||||
this.updateIcon(browser);
|
||||
|
||||
let toolbar = document.getElementById("toolbar-main");
|
||||
if (Browser.content.currentTab.chromeTop) {
|
||||
// content box was panned, so let's reset it
|
||||
this._setContentPosition("top", Browser.content.currentTab.chromeTop);
|
||||
this._setContentPosition("left", 0);
|
||||
toolbar.top = this._contentTop - toolbar.boxObject.height;
|
||||
}
|
||||
else {
|
||||
// Must be initial conditions
|
||||
toolbar.top = 0;
|
||||
this._setContentPosition("top", toolbar.boxObject.height);
|
||||
this._setContentPosition("left", 0);
|
||||
}
|
||||
this.updateIcon();
|
||||
|
||||
this.show(UIMODE_NONE);
|
||||
},
|
||||
@ -154,8 +126,7 @@ var BrowserUI = {
|
||||
var faviconURI = ios.newURI(aURI, null, null);
|
||||
|
||||
var fis = Cc["@mozilla.org/browser/favicon-service;1"].getService(Ci.nsIFaviconService);
|
||||
if (faviconURI.schemeIs("javascript") ||
|
||||
fis.isFailedFavicon(faviconURI))
|
||||
if (faviconURI.schemeIs("javascript") || fis.isFailedFavicon(faviconURI))
|
||||
faviconURI = ios.newURI(kDefaultFavIconURL, null, null);
|
||||
|
||||
var browser = getBrowser();
|
||||
@ -209,166 +180,13 @@ var BrowserUI = {
|
||||
return items;
|
||||
},
|
||||
|
||||
_dragData : {
|
||||
dragging : false,
|
||||
startX : 0,
|
||||
startY : 0,
|
||||
dragX : 0,
|
||||
dragY : 0,
|
||||
lastX : 0,
|
||||
lastY : 0,
|
||||
sTop : 0,
|
||||
sLeft : 0,
|
||||
uiMode : UIMODE_NONE
|
||||
},
|
||||
|
||||
_scrollToolbar : function bui_scrollToolbar(aEvent) {
|
||||
var [scrollWidth, ] = Browser.content._contentAreaDimensions;
|
||||
var [viewportW, ] = Browser.content._effectiveViewportDimensions;
|
||||
|
||||
var pannedUI = false;
|
||||
|
||||
if (this._dragData.dragging && Browser.content.scrollY == 0) {
|
||||
let toolbar = document.getElementById("toolbar-main");
|
||||
let dy = this._dragData.lastY - aEvent.screenY;
|
||||
this._dragData.dragY += dy;
|
||||
|
||||
// NOTE: We should only be scrolling the toolbar if the sidebars are not
|
||||
// visible (gContentBox.style.marginLeft == "0px")
|
||||
let sidebarVisible = gContentBox.style.marginLeft != "0px";
|
||||
let newTop = null;
|
||||
if (dy > 0 && (toolbar.top > -toolbar.boxObject.height && !sidebarVisible)) {
|
||||
// Scroll the toolbar up unless it is already scrolled up
|
||||
newTop = this._dragData.sTop - dy;
|
||||
|
||||
// Clip the adjustment to just enough to hide the toolbar
|
||||
if (newTop < -toolbar.boxObject.height)
|
||||
newTop = -toolbar.boxObject.height;
|
||||
|
||||
// Reset the browser start point
|
||||
Browser.content.dragData.sX = aEvent.screenX;
|
||||
Browser.content.dragData.sY = aEvent.screenY;
|
||||
}
|
||||
else if (dy < 0 && (toolbar.top < 0 && !sidebarVisible)) {
|
||||
// Scroll the toolbar down unless it is already down
|
||||
newTop = this._dragData.sTop - dy;
|
||||
|
||||
// Clip the adjustment to just enough to fully show the toolbar
|
||||
if (newTop > 0)
|
||||
newTop = 0;
|
||||
}
|
||||
|
||||
// Update the toolbar and browser tops. Stop the mousemove from
|
||||
// getting to the deckbrowser.
|
||||
if (newTop != null) {
|
||||
toolbar.top = newTop;
|
||||
this._setContentPosition("top", newTop + toolbar.boxObject.height);
|
||||
|
||||
// Cache the current top so we can use it when switching tabs
|
||||
Browser.content.currentTab.chromeTop = this._contentTop;
|
||||
|
||||
pannedUI = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._dragData.dragging && (Browser.content.scrollX == 0 || (Browser.content.scrollX + viewportW) == scrollWidth)) {
|
||||
let tabbar = document.getElementById("tab-list-container");
|
||||
let sidebar = document.getElementById("browser-controls");
|
||||
let panelUI = document.getElementById("panel-container");
|
||||
let toolbar = document.getElementById("toolbar-main");
|
||||
let dx = this._dragData.lastX - aEvent.screenX;
|
||||
this._dragData.dragX += dx;
|
||||
|
||||
if (Math.abs(this._dragData.screenX - aEvent.screenX) > 30) {
|
||||
let newLeft = this._dragData.sLeft - dx;
|
||||
let oldLeft = tabbar.left;
|
||||
|
||||
let tabbarW = tabbar.boxObject.width;
|
||||
let sidebarW = sidebar.boxObject.width;
|
||||
let contentW = gContentBox.boxObject.width;
|
||||
|
||||
// Limit the panning
|
||||
if (newLeft > 0)
|
||||
newLeft = 0;
|
||||
else if (newLeft < -(tabbarW + sidebarW))
|
||||
newLeft = -(tabbarW + sidebarW);
|
||||
|
||||
// Set the UI mode based on where we ended up
|
||||
var noneMode = (gContentBox.style.marginTop == "0px" ? UIMODE_NONE : UIMODE_URLVIEW);
|
||||
if (newLeft > -(tabbarW - tabbarW / 3) && newLeft <= 0) {
|
||||
if (this._dragData.uiMode == UIMODE_CONTROLS) {
|
||||
this.mode = noneMode;
|
||||
return;
|
||||
}
|
||||
this.mode = UIMODE_TABS;
|
||||
}
|
||||
else if (newLeft >= -(tabbarW + sidebarW) && newLeft < -(tabbarW + sidebarW / 3)) {
|
||||
if (this._dragData.uiMode == UIMODE_TABS) {
|
||||
this.mode = noneMode;
|
||||
return;
|
||||
}
|
||||
this.mode = UIMODE_CONTROLS;
|
||||
}
|
||||
else
|
||||
this.mode = noneMode;
|
||||
|
||||
tabbar.left = newLeft;
|
||||
|
||||
// Never let the toolbar pan off the screen
|
||||
let newToolbarLeft = newLeft;
|
||||
if (newToolbarLeft < 0)
|
||||
newToolbarLeft = 0;
|
||||
toolbar.left = newToolbarLeft;
|
||||
|
||||
// Make the toolbar appear/disappear depending on the state of the sidebars
|
||||
if (newLeft + tabbarW != 0)
|
||||
toolbar.top = 0;
|
||||
else
|
||||
toolbar.top = this._contentTop - toolbar.boxObject.height;
|
||||
|
||||
this._setContentPosition("left", newLeft + tabbarW);
|
||||
sidebar.left = newLeft + tabbarW + contentW;
|
||||
panelUI.left = newLeft + tabbarW + contentW + sidebarW;
|
||||
|
||||
pannedUI = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pannedUI) {
|
||||
aEvent.stopPropagation();
|
||||
|
||||
// Force a sync redraw
|
||||
window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
|
||||
.getInterface(Components.interfaces.nsIDOMWindowUtils)
|
||||
.processUpdates();
|
||||
}
|
||||
else {
|
||||
// Reset our start point while the browser is doing its panning
|
||||
this._dragData.lastX = aEvent.screenX;
|
||||
this._dragData.lastY = aEvent.screenY;
|
||||
}
|
||||
},
|
||||
|
||||
_showToolbar : function(aShow) {
|
||||
var toolbar = document.getElementById("toolbar-main");
|
||||
|
||||
if (aShow) {
|
||||
// Always show the toolbar, either by floating or panning
|
||||
if (toolbar.top == -toolbar.boxObject.height) {
|
||||
// Float the toolbar over content
|
||||
toolbar.top = 0;
|
||||
}
|
||||
else if (toolbar.top < 0) {
|
||||
// Partially showing, so show it completely
|
||||
toolbar.top = 0;
|
||||
this._setContentPosition("top", toolbar.boxObject.height);
|
||||
}
|
||||
ws.freeze("toolbar-main");
|
||||
ws.moveFrozenTo("toolbar-main", 0, 0);
|
||||
}
|
||||
else {
|
||||
// If we are floating the toolbar, then hide it again
|
||||
if (gContentBox.style.marginTop == "0px") {
|
||||
toolbar.top = -toolbar.boxObject.height;
|
||||
}
|
||||
ws.unfreeze("toolbar-main");
|
||||
}
|
||||
},
|
||||
|
||||
@ -390,31 +208,37 @@ var BrowserUI = {
|
||||
},
|
||||
|
||||
_showPanel : function(aMode) {
|
||||
let tabbar = document.getElementById("tab-list-container");
|
||||
let sidebar = document.getElementById("browser-controls");
|
||||
let panelUI = document.getElementById("panel-container");
|
||||
let toolbar = document.getElementById("toolbar-main");
|
||||
let tabbar = document.getElementById("tabs-container");
|
||||
let sidebar = document.getElementById("browser-controls");
|
||||
let panelUI = document.getElementById("panel-container");
|
||||
let toolbar = document.getElementById("toolbar-main");
|
||||
let canvas = document.getElementById("canvas");
|
||||
|
||||
let tabbarW = tabbar.boxObject.width;
|
||||
let sidebarW = sidebar.boxObject.width;
|
||||
let contentW = gContentBox.boxObject.width;
|
||||
let tabbarW = tabbar.boxObject.width;
|
||||
let sidebarW = sidebar.boxObject.width;
|
||||
let contentW = canvas.width;
|
||||
|
||||
let newLeft = -tabbarW;
|
||||
switch (aMode) {
|
||||
case UIMODE_NONE:
|
||||
Shortcuts.deinit();
|
||||
break;
|
||||
case UIMODE_PANEL:
|
||||
newLeft = -contentW;
|
||||
this._initPanel();
|
||||
break;
|
||||
case UIMODE_CONTROLS:
|
||||
newLeft = -(tabbarW + sidebarW);
|
||||
break;
|
||||
case UIMODE_TABS:
|
||||
newLeft = 0;
|
||||
break;
|
||||
}
|
||||
let newLeft = -tabbarW;
|
||||
switch (aMode) {
|
||||
case UIMODE_NONE:
|
||||
Shortcuts.deinit();
|
||||
break;
|
||||
case UIMODE_PANEL:
|
||||
newLeft = -contentW;
|
||||
this._initPanel();
|
||||
break;
|
||||
case UIMODE_CONTROLS:
|
||||
newLeft = -(tabbarW + sidebarW);
|
||||
break;
|
||||
case UIMODE_TABS:
|
||||
newLeft = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
// XXX some form of this code should be in Browser.panHandler so the UIMODE is
|
||||
// set correctly when panning.
|
||||
// OR maybe we should try to removing as much of UIMODE as possible
|
||||
/*
|
||||
tabbar.left = newLeft;
|
||||
|
||||
let newToolbarLeft = newLeft;
|
||||
@ -428,6 +252,7 @@ var BrowserUI = {
|
||||
sidebar.left = newLeft + tabbarW + contentW;
|
||||
panelUI.left = newLeft + tabbarW + contentW + sidebarW;
|
||||
panelUI.width = contentW;
|
||||
*/
|
||||
},
|
||||
|
||||
_initPanel : function() {
|
||||
@ -451,21 +276,25 @@ var BrowserUI = {
|
||||
var rect = document.getElementById("browser-container").getBoundingClientRect();
|
||||
var containerW = rect.right - rect.left;
|
||||
var containerH = rect.bottom - rect.top;
|
||||
|
||||
var toolbar = document.getElementById("toolbar-main");
|
||||
var toolbarH = toolbar.boxObject.height;
|
||||
|
||||
var popup = document.getElementById("popup_autocomplete");
|
||||
popup.height = containerH - toolbarH;
|
||||
|
||||
// XXX need to handle make some of these work again
|
||||
/*
|
||||
var sidebar = document.getElementById("browser-controls");
|
||||
var panelUI = document.getElementById("panel-container");
|
||||
var tabbar = document.getElementById("tab-list-container");
|
||||
var tabbar = document.getElementById("tabs-container");
|
||||
tabbar.left = -tabbar.boxObject.width;
|
||||
panelUI.left = containerW + sidebar.boxObject.width;
|
||||
sidebar.left = containerW;
|
||||
sidebar.height = tabbar.height = (panelUI.height = containerH) - toolbarH;
|
||||
panelUI.width = containerW - sidebar.boxObject.width - tabbar.boxObject.width;
|
||||
|
||||
var popup = document.getElementById("popup_autocomplete");
|
||||
toolbar.width = containerW;
|
||||
popup.height = containerH - toolbarH;
|
||||
*/
|
||||
},
|
||||
|
||||
init : function() {
|
||||
@ -480,58 +309,58 @@ var BrowserUI = {
|
||||
this._favicon.addEventListener("error", this, false);
|
||||
this._autocompleteNavbuttons = document.getElementById("autocomplete_navbuttons");
|
||||
|
||||
Browser.content.addEventListener("DOMTitleChanged", this, true);
|
||||
Browser.content.addEventListener("DOMLinkAdded", this, true);
|
||||
// XXX these really want to listen whatever is the current browser, not any browser
|
||||
let browsers = document.getElementById("browsers");
|
||||
browsers.addEventListener("DOMTitleChanged", this, true);
|
||||
browsers.addEventListener("DOMLinkAdded", this, true);
|
||||
|
||||
document.getElementById("tab-list").addEventListener("TabSelect", this, true);
|
||||
|
||||
Browser.content.addEventListener("mousedown", this, true);
|
||||
Browser.content.addEventListener("mouseup", this, true);
|
||||
Browser.content.addEventListener("mousemove", this, true);
|
||||
document.getElementById("tabs").addEventListener("TabSelect", this, true);
|
||||
|
||||
window.addEventListener("resize", this, false);
|
||||
Shortcuts.restore();
|
||||
},
|
||||
|
||||
update : function(aState, aBrowser) {
|
||||
if (aState == TOOLBARSTATE_INDETERMINATE) {
|
||||
this._faviconLink = null;
|
||||
aState = TOOLBARSTATE_LOADED;
|
||||
this.setURI();
|
||||
}
|
||||
|
||||
update : function(aState) {
|
||||
var toolbar = document.getElementById("toolbar-main");
|
||||
if (aState == TOOLBARSTATE_LOADING) {
|
||||
this.show(UIMODE_URLVIEW);
|
||||
Browser.content.setLoading(aBrowser, true);
|
||||
|
||||
toolbar.top = 0;
|
||||
toolbar.setAttribute("mode", "loading");
|
||||
this._favicon.src = "";
|
||||
this._faviconLink = null;
|
||||
this.updateIcon(aBrowser);
|
||||
}
|
||||
else if (aState == TOOLBARSTATE_LOADED) {
|
||||
this._setContentPosition("top", toolbar.boxObject.height);
|
||||
Browser.content.setLoading(aBrowser, false);
|
||||
switch (aState) {
|
||||
case TOOLBARSTATE_INDETERMINATE:
|
||||
this._faviconAdded = false;
|
||||
aState = TOOLBARSTATE_LOADED;
|
||||
this.setURI();
|
||||
|
||||
toolbar.setAttribute("mode", "view");
|
||||
case TOOLBARSTATE_LOADED:
|
||||
toolbar.setAttribute("mode", "view");
|
||||
|
||||
if (!this._faviconLink) {
|
||||
this._faviconLink = aBrowser.currentURI.prePath + "/favicon.ico";
|
||||
}
|
||||
this._setIcon(this._faviconLink);
|
||||
this.updateIcon(aBrowser);
|
||||
if (!this._faviconLink) {
|
||||
this._faviconLink = Browser.currentBrowser.currentURI.prePath + "/favicon.ico";
|
||||
}
|
||||
this._setIcon(this._faviconLink);
|
||||
this.updateIcon();
|
||||
break;
|
||||
|
||||
case TOOLBARSTATE_LOADING:
|
||||
toolbar.setAttribute("mode", "loading");
|
||||
this.show(UIMODE_URLVIEW);
|
||||
|
||||
ws.panTo(0,0, true);
|
||||
|
||||
this._favicon.src = "";
|
||||
this._faviconLink = null;
|
||||
this.updateIcon();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
updateIcon : function(browser) {
|
||||
if (Browser.content.isLoading(browser)) {
|
||||
document.getElementById("urlbar-image-deck").selectedIndex = 0;
|
||||
updateIcon : function() {
|
||||
if (Browser.currentTab.isLoading()) {
|
||||
this._throbber.hidden = false;
|
||||
this._throbber.setAttribute("loading", "true");
|
||||
this._favicon.hidden = true;
|
||||
}
|
||||
else {
|
||||
document.getElementById("urlbar-image-deck").selectedIndex = 1;
|
||||
this._favicon.hidden = false;
|
||||
this._throbber.hidden = true;
|
||||
this._throbber.removeAttribute("loading");
|
||||
}
|
||||
},
|
||||
@ -553,7 +382,7 @@ var BrowserUI = {
|
||||
setURI : function() {
|
||||
var browser = Browser.currentBrowser;
|
||||
|
||||
// FIXME: deckbrowser should not fire TebSelect on the initial tab (bug 454028)
|
||||
// FIXME: deckbrowser should not fire TabSelect on the initial tab (bug 454028)
|
||||
if (!browser.currentURI)
|
||||
return;
|
||||
|
||||
@ -600,15 +429,13 @@ var BrowserUI = {
|
||||
this.show(UIMODE_URLVIEW);
|
||||
},
|
||||
|
||||
updateAutoComplete : function(showDefault)
|
||||
{
|
||||
updateAutoComplete : function(showDefault) {
|
||||
this.updateSearchEngines();
|
||||
if (showDefault || this._edit.getAttribute("nomatch"))
|
||||
this._edit.showHistoryPopup();
|
||||
},
|
||||
|
||||
doButtonSearch : function(button)
|
||||
{
|
||||
doButtonSearch : function(button) {
|
||||
if (!("engine" in button) || !button.engine)
|
||||
return;
|
||||
|
||||
@ -625,17 +452,10 @@ var BrowserUI = {
|
||||
if (this.engines)
|
||||
return;
|
||||
|
||||
// XXXndeakin remove the try-catch once the search service is properly built
|
||||
try {
|
||||
var searchService = Cc["@mozilla.org/browser/search-service;1"].
|
||||
getService(Ci.nsIBrowserSearchService);
|
||||
} catch (ex) {
|
||||
this.engines = [ ];
|
||||
return;
|
||||
}
|
||||
|
||||
var searchService = Cc["@mozilla.org/browser/search-service;1"].getService(Ci.nsIBrowserSearchService);
|
||||
var engines = searchService.getVisibleEngines({ });
|
||||
this.engines = engines;
|
||||
|
||||
const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
var container = this._autocompleteNavbuttons;
|
||||
for (var e = 0; e < kMaxEngines && e < engines.length; e++) {
|
||||
@ -666,8 +486,7 @@ var BrowserUI = {
|
||||
var urllist = document.getElementById("urllist-container");
|
||||
var container = document.getElementById("browser-container");
|
||||
|
||||
if (aMode == UIMODE_URLVIEW)
|
||||
{
|
||||
if (aMode == UIMODE_URLVIEW) {
|
||||
this._showToolbar(true);
|
||||
this._editToolbar(false);
|
||||
|
||||
@ -783,17 +602,18 @@ var BrowserUI = {
|
||||
},
|
||||
|
||||
newTab : function() {
|
||||
Browser.content.newTab(true);
|
||||
ws.panTo(0,0, true);
|
||||
Browser.newTab(true);
|
||||
this.show(UIMODE_URLEDIT);
|
||||
},
|
||||
|
||||
closeTab : function(aTab) {
|
||||
Browser.content.removeTab(aTab);
|
||||
Browser.closeTab(aTab);
|
||||
this.show(UIMODE_NONE);
|
||||
},
|
||||
|
||||
selectTab : function(aTab) {
|
||||
Browser.content.selectTab(aTab);
|
||||
Browser.selectTab(aTab);
|
||||
this.show(UIMODE_NONE);
|
||||
},
|
||||
|
||||
@ -826,26 +646,6 @@ var BrowserUI = {
|
||||
case "error":
|
||||
this._favicon.src = "chrome://browser/skin/images/default-favicon.png";
|
||||
break;
|
||||
// UI panning events
|
||||
case "mousedown":
|
||||
this._dragData.dragging = true;
|
||||
this._dragData.dragX = 0;
|
||||
this._dragData.dragY = 0;
|
||||
this._dragData.screenX = this._dragData.lastX = aEvent.screenX;
|
||||
this._dragData.screenY = this._dragData.lastY = aEvent.screenY;
|
||||
this._dragData.sTop = document.getElementById("toolbar-main").top;
|
||||
this._dragData.sLeft = document.getElementById("tab-list-container").left;
|
||||
this._dragData.uiMode = this.mode;
|
||||
break;
|
||||
case "mouseup":
|
||||
this._dragData.dragging = false;
|
||||
this._dragData.uiMode = UIMODE_NONE;
|
||||
// Cause the UI to snap, if needed
|
||||
this._showPanel(this.mode);
|
||||
break;
|
||||
case "mousemove":
|
||||
this._scrollToolbar(aEvent);
|
||||
break;
|
||||
// Window size events
|
||||
case "resize":
|
||||
this._sizeControls(aEvent);
|
||||
@ -943,7 +743,7 @@ var BrowserUI = {
|
||||
this.newTab();
|
||||
break;
|
||||
case "cmd_closeTab":
|
||||
Browser.content.removeTab(Browser.content.browser);
|
||||
this.closeTab();
|
||||
break;
|
||||
case "cmd_sanitize":
|
||||
Sanitizer.sanitize();
|
||||
|
@ -1,17 +1,13 @@
|
||||
deckbrowser {
|
||||
-moz-binding: url("chrome://browser/content/deckbrowser.xml#deckbrowser");
|
||||
}
|
||||
|
||||
#urlbar-edit {
|
||||
-moz-binding: url("chrome://browser/content/urlbar.xml#autocomplete-aligned");
|
||||
}
|
||||
|
||||
#tab-list {
|
||||
-moz-binding: url("chrome://browser/content/deckbrowser.xml#tablist");
|
||||
#tabs {
|
||||
-moz-binding: url("chrome://browser/content/tabs.xml#tablist");
|
||||
}
|
||||
|
||||
richlistitem[type="documenttab"] {
|
||||
-moz-binding: url("chrome://browser/content/deckbrowser.xml#documenttab");
|
||||
-moz-binding: url("chrome://browser/content/tabs.xml#documenttab");
|
||||
}
|
||||
|
||||
#prefs-container > scrollbox
|
||||
|
@ -25,6 +25,7 @@
|
||||
* Mark Finkle <mfinkle@mozilla.com>
|
||||
* Aleks Totic <a@totic.org>
|
||||
* Johnathan Nightingale <johnath@mozilla.com>
|
||||
* Stuart Parmenter <stuart@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
|
||||
@ -60,13 +61,71 @@ __defineGetter__("gPrefService", function () {
|
||||
});
|
||||
|
||||
function getBrowser() {
|
||||
return Browser.content.browser;
|
||||
return Browser.currentBrowser;
|
||||
}
|
||||
|
||||
var ws = null;
|
||||
var ih = null;
|
||||
|
||||
var Browser = {
|
||||
_content : null,
|
||||
_tabs : [],
|
||||
_currentTab : null,
|
||||
|
||||
startup: function() {
|
||||
var self = this;
|
||||
|
||||
// initalize the CanvasBrowser
|
||||
this._content = new CanvasBrowser(document.getElementById("canvas"));
|
||||
|
||||
// initialize the WidgetStack
|
||||
ws = new WidgetStack(document.getElementById("browser-container"));
|
||||
ws.setViewportBounds({ top: 0, left: 0, right: 800, bottom: 480 });
|
||||
|
||||
// XXX this should live elsewhere
|
||||
window.gSidebarVisible = false;
|
||||
function panHandler(vr) {
|
||||
var visibleNow = ws.isWidgetVisible("browser-controls") || ws.isWidgetVisible("tabs-container");
|
||||
|
||||
// XXX add code here to snap side panels fully out if they start to appear,
|
||||
// or snap them back if they only showed up for a little bit
|
||||
|
||||
if (visibleNow && !gSidebarVisible) {
|
||||
ws.freeze("toolbar-main");
|
||||
ws.moveFrozenTo("toolbar-main", 0, 0);
|
||||
}
|
||||
else if (!visibleNow && gSidebarVisible) {
|
||||
ws.unfreeze("toolbar-main");
|
||||
}
|
||||
gSidebarVisible = visibleNow;
|
||||
|
||||
// deal with checkerboard
|
||||
/*
|
||||
let stack = document.getElementById("browser-container");
|
||||
stack.style.backgroundPosition = -vr.left + "px " + -vr.top + "px";
|
||||
*/
|
||||
|
||||
// this is really only necessary for maemo, where we don't
|
||||
// always repaint fast enough
|
||||
window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils).processUpdates();
|
||||
}
|
||||
|
||||
ws.setPanHandler(panHandler);
|
||||
|
||||
function resizeHandler() { ws.updateSize(); }
|
||||
window.addEventListener("resize", resizeHandler, false);
|
||||
|
||||
setTimeout(resizeHandler, 0);
|
||||
|
||||
function viewportHandler(b, ob) { self._content.viewportHandler(b, ob); }
|
||||
ws.setViewportHandler(viewportHandler);
|
||||
|
||||
// initialize input handling
|
||||
ih = new InputHandler();
|
||||
|
||||
// Create the first tab
|
||||
this.newTab(true);
|
||||
|
||||
startup : function() {
|
||||
window.controllers.appendController(this);
|
||||
window.controllers.appendController(BrowserUI);
|
||||
|
||||
@ -86,25 +145,24 @@ var Browser = {
|
||||
var styleURI = ios.newURI("chrome://browser/content/scrollbars.css", null, null);
|
||||
styleSheets.loadAndRegisterSheet(styleURI, styleSheets.AGENT_SHEET);
|
||||
|
||||
this._content = document.getElementById("content");
|
||||
this._content.progressListenerCreator = function (content, browser) {
|
||||
return new ProgressController(content, browser);
|
||||
};
|
||||
|
||||
var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
|
||||
os.addObserver(gXPInstallObserver, "xpinstall-install-blocked", false);
|
||||
os.addObserver(gXPInstallObserver, "xpinstall-download-started", false);
|
||||
|
||||
// XXX hook up memory-pressure notification to clear out tab browsers
|
||||
//os.addObserver(function(subject, topic, data) self.destroyEarliestBrowser(), "memory-pressure", false);
|
||||
|
||||
BrowserUI.init();
|
||||
|
||||
window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = new nsBrowserAccess();
|
||||
|
||||
this._content.addEventListener("command", this._handleContentCommand, false);
|
||||
this._content.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
|
||||
this._content.tabList = document.getElementById("tab-list");
|
||||
this._content.newTab(true);
|
||||
let browsers = document.getElementById("browsers");
|
||||
browsers.addEventListener("command", this._handleContentCommand, false);
|
||||
browsers.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
|
||||
|
||||
var deckbrowser = this.content;
|
||||
/* Initialize Spatial Navigation */
|
||||
/*
|
||||
var deckbrowser = content;
|
||||
function panCallback(aElement) {
|
||||
// SpatialNav calls commandDispatcher.advanceFocus/rewindFocus, which
|
||||
// can mess the scroll state up. Reset it.
|
||||
@ -115,12 +173,18 @@ var Browser = {
|
||||
|
||||
deckbrowser.ensureElementIsVisible(aElement);
|
||||
}
|
||||
SpatialNavigation.init(this.content, panCallback);
|
||||
SpatialNavigation.init(content, panCallback);
|
||||
*/
|
||||
|
||||
/* Initialize Geolocation */
|
||||
this.setupGeolocationPrompt();
|
||||
|
||||
|
||||
/* Login Manager */
|
||||
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
|
||||
|
||||
|
||||
/* Command line arguments/initial homepage */
|
||||
// If this is an intial window launch (was a nsICommandLine passed via window params)
|
||||
// we execute some logic to load the initial launch page
|
||||
if (window.arguments && window.arguments[0]) {
|
||||
@ -171,13 +235,26 @@ var Browser = {
|
||||
gPrefService.setBoolPref("temporary.disablePlugins", false);
|
||||
},
|
||||
|
||||
updateViewportSize: function() {
|
||||
// XXX make sure this is right, and then add a better function for it.
|
||||
var [w,h] = this._content._contentAreaDimensions;
|
||||
w = Math.ceil(this._content._pageToScreen(w));
|
||||
h = Math.ceil(this._content._pageToScreen(h));
|
||||
|
||||
if (!this._currentViewportBounds || w != this._currentViewportBounds.width || h != this._currentViewportBounds.height) {
|
||||
this._currentViewportBounds = {width: w, height: h};
|
||||
let bounds = { top: 0, left: 0, right: Math.max(800, w), bottom: Math.max(480, h) }
|
||||
//dump("setViewportBounds: " + bounds.toSource() + "\n");
|
||||
ws.setViewportBounds(bounds);
|
||||
}
|
||||
},
|
||||
|
||||
setPluginState: function(enabled)
|
||||
{
|
||||
var phs = Components.classes["@mozilla.org/plugin/host;1"]
|
||||
.getService(Components.interfaces.nsIPluginHost);
|
||||
var phs = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
|
||||
var plugins = phs.getPluginTags({ });
|
||||
for (var i = 0; i < plugins.length; ++i)
|
||||
plugins[i].disabled = !enabled;
|
||||
for (var i = 0; i < plugins.length; ++i)
|
||||
plugins[i].disabled = !enabled;
|
||||
},
|
||||
|
||||
setupGeolocationPrompt: function() {
|
||||
@ -234,14 +311,94 @@ var Browser = {
|
||||
* have more than one
|
||||
*/
|
||||
get currentBrowser() {
|
||||
return this._content.browser;
|
||||
return this._currentTab.browser;
|
||||
},
|
||||
|
||||
supportsCommand : function(cmd) {
|
||||
get currentTab() {
|
||||
return this._currentTab;
|
||||
},
|
||||
|
||||
getTabAtIndex: function(index) {
|
||||
if (index > this._tabs.length || index < 0)
|
||||
return null;
|
||||
return this._tabs[index];
|
||||
},
|
||||
|
||||
getTabFromContent: function(content) {
|
||||
for (var t = 0; t < this._tabs.length; t++) {
|
||||
if (this._tabs[t].content == content)
|
||||
return this._tabs[t];
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
newTab: function(bringFront) {
|
||||
let newTab = new Tab();
|
||||
newTab.create();
|
||||
this._tabs.push(newTab);
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabOpen", true, false);
|
||||
newTab.content.dispatchEvent(event);
|
||||
|
||||
if (bringFront)
|
||||
this.selectTab(newTab);
|
||||
|
||||
return newTab;
|
||||
},
|
||||
|
||||
closeTab: function(tab) {
|
||||
if (tab instanceof XULElement)
|
||||
tab = this.getTabFromContent(tab);
|
||||
|
||||
if (!tab)
|
||||
return;
|
||||
|
||||
let tabIndex = this._tabs.indexOf(tab);
|
||||
|
||||
let nextTab = this._currentTab;
|
||||
if (this._currentTab == tab) {
|
||||
nextTab = this.getTabAtIndex(tabIndex + 1) || this.getTabAtIndex(tabIndex - 1);
|
||||
if (!nextTab)
|
||||
return;
|
||||
}
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabClose", true, false);
|
||||
tab.content.dispatchEvent(event);
|
||||
|
||||
tab.destroy();
|
||||
this._tabs.splice(tabIndex, 1);
|
||||
|
||||
// redraw the tabs
|
||||
for (let t = tabIndex; t < this._tabs.length; t++)
|
||||
this._tabs[t].updateThumbnail();
|
||||
|
||||
this.selectTab(nextTab);
|
||||
},
|
||||
|
||||
selectTab: function(tab) {
|
||||
if (tab instanceof XULElement)
|
||||
tab = this.getTabFromContent(tab);
|
||||
|
||||
if (!tab || this._currentTab == tab)
|
||||
return;
|
||||
|
||||
this._currentTab = tab;
|
||||
this._content.setCurrentBrowser(this.currentBrowser);
|
||||
document.getElementById("tabs").selectedItem = tab.content;
|
||||
|
||||
ws.panTo(0,0, true);
|
||||
|
||||
let event = document.createEvent("Events");
|
||||
event.initEvent("TabSelect", true, false);
|
||||
tab.content.dispatchEvent(event);
|
||||
},
|
||||
|
||||
supportsCommand: function(cmd) {
|
||||
var isSupported = false;
|
||||
switch (cmd) {
|
||||
case "cmd_fullscreen":
|
||||
case "cmd_downloads":
|
||||
isSupported = true;
|
||||
break;
|
||||
default:
|
||||
@ -251,12 +408,12 @@ var Browser = {
|
||||
return isSupported;
|
||||
},
|
||||
|
||||
isCommandEnabled : function(cmd) {
|
||||
isCommandEnabled: function(cmd) {
|
||||
return true;
|
||||
},
|
||||
|
||||
doCommand : function(cmd) {
|
||||
var browser = this.content.browser;
|
||||
doCommand: function(cmd) {
|
||||
var browser = this.currentBrowser;
|
||||
|
||||
switch (cmd) {
|
||||
case "cmd_fullscreen":
|
||||
@ -265,7 +422,7 @@ var Browser = {
|
||||
}
|
||||
},
|
||||
|
||||
getNotificationBox : function() {
|
||||
getNotificationBox: function() {
|
||||
return document.getElementById("notifications");
|
||||
},
|
||||
|
||||
@ -276,7 +433,7 @@ var Browser = {
|
||||
var findbar = document.getElementById("findbar");
|
||||
var browser = findbar.browser;
|
||||
if (!browser) {
|
||||
browser = this.content.browser;
|
||||
browser = this.currentBrowser;
|
||||
findbar.browser = browser;
|
||||
}
|
||||
|
||||
@ -378,174 +535,26 @@ var Browser = {
|
||||
}
|
||||
};
|
||||
|
||||
function ProgressController(aTabBrowser, aBrowser) {
|
||||
this._tabbrowser = aTabBrowser;
|
||||
this.init(aBrowser);
|
||||
}
|
||||
|
||||
ProgressController.prototype = {
|
||||
_browser : null,
|
||||
|
||||
init : function(aBrowser) {
|
||||
this._browser = aBrowser;
|
||||
},
|
||||
|
||||
onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
// we currently only care about state changes for the main document,
|
||||
// not sub-frames
|
||||
if (aWebProgress.DOMWindow != this._browser.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START)
|
||||
BrowserUI.update(TOOLBARSTATE_LOADING, this._browser);
|
||||
else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
|
||||
BrowserUI.update(TOOLBARSTATE_LOADED, this._browser);
|
||||
}
|
||||
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
||||
aWebProgress.DOMWindow.focus();
|
||||
|
||||
// update the viewport
|
||||
this._tabbrowser.updateCanvasState();
|
||||
|
||||
// update the tab canvas image
|
||||
this._tabbrowser.updateBrowser(this._browser, true);
|
||||
|
||||
// linkify phone numbers
|
||||
Browser.translatePhoneNumbers();
|
||||
|
||||
//aWebProgress.DOMWindow.scrollbars.visible = false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// This method is called to indicate progress changes for the currently
|
||||
// loading page.
|
||||
onProgressChange : function(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
|
||||
},
|
||||
|
||||
// This method is called to indicate a change to the current location.
|
||||
onLocationChange : function(aWebProgress, aRequest, aLocationURI) {
|
||||
|
||||
var location = aLocationURI ? aLocationURI.spec : "";
|
||||
this._hostChanged = true;
|
||||
|
||||
// This code here does not compare uris exactly when determining
|
||||
// whether or not the message(s) should be hidden since the message
|
||||
// may be prematurely hidden when an install is invoked by a click
|
||||
// on a link that looks like this:
|
||||
//
|
||||
// <a href="#" onclick="return install();">Install Foo</a>
|
||||
//
|
||||
// - which fires a onLocationChange message to uri + '#'...
|
||||
cBrowser = Browser.currentBrowser;
|
||||
if (cBrowser.lastURI) {
|
||||
var oldSpec = cBrowser.lastURI.spec;
|
||||
var oldIndexOfHash = oldSpec.indexOf("#");
|
||||
if (oldIndexOfHash != -1)
|
||||
oldSpec = oldSpec.substr(0, oldIndexOfHash);
|
||||
var newSpec = location;
|
||||
var newIndexOfHash = newSpec.indexOf("#");
|
||||
if (newIndexOfHash != -1)
|
||||
newSpec = newSpec.substr(0, newSpec.indexOf("#"));
|
||||
if (newSpec != oldSpec) {
|
||||
// Remove all the notifications, except for those which want to
|
||||
// persist across the first location change.
|
||||
var nBox = Browser.getNotificationBox();
|
||||
nBox.removeTransientNotifications();
|
||||
}
|
||||
}
|
||||
cBrowser.lastURI = aLocationURI;
|
||||
|
||||
|
||||
if (aWebProgress.DOMWindow == this._browser.contentWindow) {
|
||||
BrowserUI.setURI();
|
||||
this._tabbrowser.updateBrowser(this._browser, false);
|
||||
}
|
||||
},
|
||||
|
||||
// This method is called to indicate a status changes for the currently
|
||||
// loading page. The message is already formatted for display.
|
||||
onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
|
||||
},
|
||||
|
||||
// Properties used to cache security state used to update the UI
|
||||
_state: null,
|
||||
_host: undefined,
|
||||
_hostChanged: false, // onLocationChange will flip this bit
|
||||
|
||||
// This method is called when the security state of the browser changes.
|
||||
onSecurityChange : function(aWebProgress, aRequest, aState) {
|
||||
|
||||
// Don't need to do anything if the data we use to update the UI hasn't
|
||||
// changed
|
||||
if (this._state == aState &&
|
||||
!this._hostChanged) {
|
||||
return;
|
||||
}
|
||||
this._state = aState;
|
||||
|
||||
try {
|
||||
this._host = getBrowser().contentWindow.location.host;
|
||||
} catch(ex) {
|
||||
this._host = null;
|
||||
}
|
||||
|
||||
this._hostChanged = false;
|
||||
|
||||
// Don't pass in the actual location object, since it can cause us to
|
||||
// hold on to the window object too long. Just pass in the fields we
|
||||
// care about. (bug 424829)
|
||||
var location = getBrowser().contentWindow.location;
|
||||
var locationObj = {};
|
||||
try {
|
||||
locationObj.host = location.host;
|
||||
locationObj.hostname = location.hostname;
|
||||
locationObj.port = location.port;
|
||||
} catch (ex) {
|
||||
// Can sometimes throw if the URL being visited has no host/hostname,
|
||||
// e.g. about:blank. The _state for these pages means we won't need these
|
||||
// properties anyways, though.
|
||||
}
|
||||
getIdentityHandler().checkIdentity(this._state, locationObj);
|
||||
|
||||
},
|
||||
|
||||
QueryInterface : function(aIID) {
|
||||
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
|
||||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
|
||||
aIID.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
function nsBrowserAccess()
|
||||
{
|
||||
}
|
||||
|
||||
nsBrowserAccess.prototype =
|
||||
{
|
||||
QueryInterface : function(aIID)
|
||||
{
|
||||
if (aIID.equals(Ci.nsIBrowserDOMWindow) ||
|
||||
aIID.equals(Ci.nsISupports))
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Ci.nsIBrowserDOMWindow) || aIID.equals(Ci.nsISupports))
|
||||
return this;
|
||||
throw Components.results.NS_NOINTERFACE;
|
||||
},
|
||||
|
||||
openURI : function(aURI, aOpener, aWhere, aContext)
|
||||
{
|
||||
openURI: function(aURI, aOpener, aWhere, aContext) {
|
||||
var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
|
||||
|
||||
if (isExternal && aURI && aURI.schemeIs("chrome")) {
|
||||
dump("use -chrome command-line option to load external chrome urls\n");
|
||||
return null;
|
||||
}
|
||||
|
||||
var loadflags = isExternal ?
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
|
||||
Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
|
||||
@ -567,17 +576,10 @@ nsBrowserAccess.prototype =
|
||||
"all,dialog=no", url, null, null, null);
|
||||
}
|
||||
else {
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
|
||||
var tab = Browser._content.newTab(true);
|
||||
if (tab) {
|
||||
var content = Browser._content;
|
||||
var browser = content.getBrowserForDisplay(content.getDisplayForTab(tab));
|
||||
newWindow = browser.contentWindow;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB)
|
||||
newWindow = Browser.newTab(true).browser.contentWindow;
|
||||
else
|
||||
newWindow = aOpener ? aOpener.top : browser.contentWindow;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -585,9 +587,8 @@ nsBrowserAccess.prototype =
|
||||
if (aURI) {
|
||||
if (aOpener) {
|
||||
location = aOpener.location;
|
||||
referrer = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(location, null, null);
|
||||
referrer = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService)
|
||||
.newURI(location, null, null);
|
||||
}
|
||||
newWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
@ -599,8 +600,7 @@ nsBrowserAccess.prototype =
|
||||
return newWindow;
|
||||
},
|
||||
|
||||
isTabContentWindow : function(aWindow)
|
||||
{
|
||||
isTabContentWindow: function(aWindow) {
|
||||
return Browser._content.browsers.some(function (browser) browser.contentWindow == aWindow);
|
||||
}
|
||||
}
|
||||
@ -638,7 +638,7 @@ IdentityHandler.prototype = {
|
||||
/**
|
||||
* Build out a cache of the elements that we need frequently.
|
||||
*/
|
||||
_cacheElements : function() {
|
||||
_cacheElements: function() {
|
||||
this._identityPopup = document.getElementById("identity-popup");
|
||||
this._identityBox = document.getElementById("identity-box");
|
||||
this._identityPopupContentBox = document.getElementById("identity-popup-content-box");
|
||||
@ -653,7 +653,7 @@ IdentityHandler.prototype = {
|
||||
* Handler for mouseclicks on the "More Information" button in the
|
||||
* "identity-popup" panel.
|
||||
*/
|
||||
handleMoreInfoClick : function(event) {
|
||||
handleMoreInfoClick: function(event) {
|
||||
displaySecurityInfo();
|
||||
event.stopPropagation();
|
||||
},
|
||||
@ -662,7 +662,7 @@ IdentityHandler.prototype = {
|
||||
* Helper to parse out the important parts of _lastStatus (of the SSL cert in
|
||||
* particular) for use in constructing identity UI strings
|
||||
*/
|
||||
getIdentityData : function() {
|
||||
getIdentityData: function() {
|
||||
var result = {};
|
||||
var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
|
||||
var cert = status.serverCert;
|
||||
@ -700,7 +700,7 @@ IdentityHandler.prototype = {
|
||||
* @param JS Object location that mirrors an nsLocation (i.e. has .host and
|
||||
* .hostname and .port)
|
||||
*/
|
||||
checkIdentity : function(state, location) {
|
||||
checkIdentity: function(state, location) {
|
||||
var currentStatus = getBrowser().securityUI
|
||||
.QueryInterface(Components.interfaces.nsISSLStatusProvider)
|
||||
.SSLStatus;
|
||||
@ -718,7 +718,7 @@ IdentityHandler.prototype = {
|
||||
/**
|
||||
* Return the eTLD+1 version of the current hostname
|
||||
*/
|
||||
getEffectiveHost : function() {
|
||||
getEffectiveHost: function() {
|
||||
// Cache the eTLDService if this is our first time through
|
||||
if (!this._eTLDService)
|
||||
this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
|
||||
@ -736,7 +736,7 @@ IdentityHandler.prototype = {
|
||||
* Update the UI to reflect the specified mode, which should be one of the
|
||||
* IDENTITY_MODE_* constants.
|
||||
*/
|
||||
setMode : function(newMode) {
|
||||
setMode: function(newMode) {
|
||||
if (!this._identityBox) {
|
||||
// No identity box means the identity box is not visible, in which
|
||||
// case there's nothing to do.
|
||||
@ -757,7 +757,7 @@ IdentityHandler.prototype = {
|
||||
*
|
||||
* @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
|
||||
*/
|
||||
setIdentityMessages : function(newMode) {
|
||||
setIdentityMessages: function(newMode) {
|
||||
if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
|
||||
var iData = this.getIdentityData();
|
||||
|
||||
@ -807,7 +807,7 @@ IdentityHandler.prototype = {
|
||||
*
|
||||
* @param newMode The newly set identity mode. Should be one of the IDENTITY_MODE_* constants.
|
||||
*/
|
||||
setPopupMessages : function(newMode) {
|
||||
setPopupMessages: function(newMode) {
|
||||
|
||||
this._identityPopup.className = newMode;
|
||||
this._identityPopupContentBox.className = newMode;
|
||||
@ -857,14 +857,14 @@ IdentityHandler.prototype = {
|
||||
this._identityPopupContentVerif.textContent = verifier;
|
||||
},
|
||||
|
||||
hideIdentityPopup : function() {
|
||||
hideIdentityPopup: function() {
|
||||
this._identityPopup.hidePopup();
|
||||
},
|
||||
|
||||
/**
|
||||
* Click handler for the identity-box element in primary chrome.
|
||||
*/
|
||||
handleIdentityButtonEvent : function(event) {
|
||||
handleIdentityButtonEvent: function(event) {
|
||||
|
||||
event.stopPropagation();
|
||||
|
||||
@ -1047,3 +1047,292 @@ const gXPInstallObserver = {
|
||||
function getNotificationBox(aWindow) {
|
||||
return Browser.getNotificationBox();
|
||||
}
|
||||
|
||||
|
||||
function ProgressController(tab) {
|
||||
this._tab = tab;
|
||||
}
|
||||
|
||||
ProgressController.prototype = {
|
||||
get browser() {
|
||||
return this._tab.browser;
|
||||
},
|
||||
|
||||
onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
// ignore notification that aren't about the main document (iframes, etc)
|
||||
if (aWebProgress.DOMWindow != this._tab.browser.contentWindow)
|
||||
return;
|
||||
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START)
|
||||
this._networkStart();
|
||||
else if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP)
|
||||
this._networkStop();
|
||||
} else if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
|
||||
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
|
||||
this._documentStop();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// This method is called to indicate progress changes for the currently
|
||||
// loading page.
|
||||
onProgressChange: function(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
|
||||
},
|
||||
|
||||
// This method is called to indicate a change to the current location.
|
||||
onLocationChange: function(aWebProgress, aRequest, aLocationURI) {
|
||||
// XXX this code is not multiple-tab friendly.
|
||||
|
||||
var location = aLocationURI ? aLocationURI.spec : "";
|
||||
this._hostChanged = true;
|
||||
|
||||
// This code here does not compare uris exactly when determining
|
||||
// whether or not the message(s) should be hidden since the message
|
||||
// may be prematurely hidden when an install is invoked by a click
|
||||
// on a link that looks like this:
|
||||
//
|
||||
// <a href="#" onclick="return install();">Install Foo</a>
|
||||
//
|
||||
// - which fires a onLocationChange message to uri + '#'...
|
||||
currentBrowser = Browser.currentBrowser;
|
||||
if (currentBrowser.lastURI) {
|
||||
var oldSpec = currentBrowser.lastURI.spec;
|
||||
var oldIndexOfHash = oldSpec.indexOf("#");
|
||||
if (oldIndexOfHash != -1)
|
||||
oldSpec = oldSpec.substr(0, oldIndexOfHash);
|
||||
var newSpec = location;
|
||||
var newIndexOfHash = newSpec.indexOf("#");
|
||||
if (newIndexOfHash != -1)
|
||||
newSpec = newSpec.substr(0, newSpec.indexOf("#"));
|
||||
if (newSpec != oldSpec) {
|
||||
// Remove all the notifications, except for those which want to
|
||||
// persist across the first location change.
|
||||
|
||||
// XXX
|
||||
// var nBox = Browser.getNotificationBox();
|
||||
// nBox.removeTransientNotifications();
|
||||
}
|
||||
}
|
||||
currentBrowser.lastURI = aLocationURI;
|
||||
if (aWebProgress.DOMWindow == Browser.currentBrowser.contentWindow) {
|
||||
BrowserUI.setURI();
|
||||
}
|
||||
},
|
||||
|
||||
// This method is called to indicate a status changes for the currently
|
||||
// loading page. The message is already formatted for display.
|
||||
onStatusChange: function(aWebProgress, aRequest, aStatus, aMessage) {
|
||||
},
|
||||
|
||||
_networkStart: function() {
|
||||
this._tab.setLoading(true);
|
||||
//dump("started loading network\n");
|
||||
|
||||
if (Browser.currentBrowser == this.browser) {
|
||||
Browser.content.startLoading();
|
||||
BrowserUI.update(TOOLBARSTATE_LOADING);
|
||||
}
|
||||
},
|
||||
|
||||
_networkStop: function() {
|
||||
this._tab.setLoading(false);
|
||||
|
||||
if (Browser.currentBrowser == this.browser) {
|
||||
BrowserUI.update(TOOLBARSTATE_LOADED);
|
||||
}
|
||||
},
|
||||
|
||||
_documentStop: function() {
|
||||
//dump("stop, hammer time\n");
|
||||
|
||||
// translate any phone numbers
|
||||
Browser.translatePhoneNumbers();
|
||||
|
||||
if (Browser.currentBrowser == this.browser) {
|
||||
// focus the dom window
|
||||
this.browser.contentWindow.focus();
|
||||
|
||||
Browser.content.endLoading();
|
||||
}
|
||||
this._tab.updateThumbnail();
|
||||
},
|
||||
|
||||
// Properties used to cache security state used to update the UI
|
||||
_state: null,
|
||||
_host: undefined,
|
||||
_hostChanged: false, // onLocationChange will flip this bit
|
||||
|
||||
// This method is called when the security state of the browser changes.
|
||||
onSecurityChange: function(aWebProgress, aRequest, aState) {
|
||||
// Don't need to do anything if the data we use to update the UI hasn't
|
||||
// changed
|
||||
if (this._state == aState &&
|
||||
!this._hostChanged) {
|
||||
return;
|
||||
}
|
||||
this._state = aState;
|
||||
|
||||
try {
|
||||
this._host = getBrowser().contentWindow.location.host;
|
||||
}
|
||||
catch(ex) {
|
||||
this._host = null;
|
||||
}
|
||||
|
||||
this._hostChanged = false;
|
||||
|
||||
// Don't pass in the actual location object, since it can cause us to
|
||||
// hold on to the window object too long. Just pass in the fields we
|
||||
// care about. (bug 424829)
|
||||
var location = getBrowser().contentWindow.location;
|
||||
var locationObj = {};
|
||||
try {
|
||||
locationObj.host = location.host;
|
||||
locationObj.hostname = location.hostname;
|
||||
locationObj.port = location.port;
|
||||
}
|
||||
catch (ex) {
|
||||
// Can sometimes throw if the URL being visited has no host/hostname,
|
||||
// e.g. about:blank. The _state for these pages means we won't need these
|
||||
// properties anyways, though.
|
||||
}
|
||||
getIdentityHandler().checkIdentity(this._state, locationObj);
|
||||
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
|
||||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
|
||||
aIID.equals(Components.interfaces.nsISupports))
|
||||
return this;
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function Tab() {
|
||||
}
|
||||
|
||||
Tab.prototype = {
|
||||
_id: null,
|
||||
_browser: null,
|
||||
_state: null,
|
||||
_listener: null,
|
||||
_loading: false,
|
||||
_content: null,
|
||||
|
||||
get browser() {
|
||||
return this._browser;
|
||||
},
|
||||
|
||||
get content() {
|
||||
return this._content;
|
||||
},
|
||||
|
||||
isLoading: function() {
|
||||
return this._loading;
|
||||
},
|
||||
|
||||
setLoading: function(b) {
|
||||
this._loading = b;
|
||||
},
|
||||
|
||||
create: function() {
|
||||
this._content = document.createElement("richlistitem");
|
||||
this._content.setAttribute("type", "documenttab");
|
||||
document.getElementById("tabs").addTab(this._content);
|
||||
|
||||
this._createBrowser();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this._destroyBrowser();
|
||||
document.getElementById("tabs").removeTab(this._content);
|
||||
},
|
||||
|
||||
_createBrowser: function() {
|
||||
if (this._browser)
|
||||
throw "Browser already exists";
|
||||
|
||||
let browser = this._browser = document.createElement("browser");
|
||||
browser.className = "deckbrowser-browser";
|
||||
browser.setAttribute("style", "overflow: hidden; visibility: hidden; width: 1024px; height: 800px;");
|
||||
browser.setAttribute("contextmenu", document.getElementById("canvas").getAttribute("contextmenu"));
|
||||
browser.setAttribute("autocompletepopup", document.getElementById("canvas").getAttribute("autocompletepopup"));
|
||||
browser.setAttribute("type", "content");
|
||||
|
||||
document.getElementById("browsers").appendChild(browser);
|
||||
|
||||
this._listener = new ProgressController(this);
|
||||
browser.addProgressListener(this._listener);
|
||||
},
|
||||
|
||||
_destroyBrowser: function() {
|
||||
document.getElementById("browsers").removeChild(this._browser);
|
||||
},
|
||||
|
||||
saveState: function() {
|
||||
let state = { };
|
||||
|
||||
this._url = browser.contentWindow.location.toString();
|
||||
var browser = this.getBrowserForDisplay(display);
|
||||
var doc = browser.contentDocument;
|
||||
if (doc instanceof HTMLDocument) {
|
||||
var tags = ["input", "textarea", "select"];
|
||||
|
||||
for (var t = 0; t < tags.length; t++) {
|
||||
var elements = doc.getElementsByTagName(tags[t]);
|
||||
for (var e = 0; e < elements.length; e++) {
|
||||
var element = elements[e];
|
||||
var id;
|
||||
if (element.id)
|
||||
id = "#" + element.id;
|
||||
else if (element.name)
|
||||
id = "$" + element.name;
|
||||
|
||||
if (id)
|
||||
state[id] = element.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state._scrollX = browser.contentWindow.scrollX;
|
||||
state._scrollY = browser.contentWindow.scrollY;
|
||||
|
||||
this._state = state;
|
||||
},
|
||||
|
||||
restoreState: function() {
|
||||
let state = this._state;
|
||||
if (!state)
|
||||
return;
|
||||
|
||||
let doc = this._browser.contentDocument;
|
||||
for (item in state) {
|
||||
var elem = null;
|
||||
if (item.charAt(0) == "#") {
|
||||
elem = doc.getElementById(item.substring(1));
|
||||
}
|
||||
else if (item.charAt(0) == "$") {
|
||||
var list = doc.getElementsByName(item.substring(1));
|
||||
if (list.length)
|
||||
elem = list[0];
|
||||
}
|
||||
|
||||
if (elem)
|
||||
elem.value = state[item];
|
||||
}
|
||||
|
||||
this._browser.contentWindow.scrollTo(state._scrollX, state._scrollY);
|
||||
},
|
||||
|
||||
updateThumbnail: function() {
|
||||
if (!this._browser)
|
||||
return;
|
||||
|
||||
let srcCanvas = (Browser.currentBrowser == this._browser) ? document.getElementById("canvas") : null;
|
||||
this._content.updateThumbnail(this._browser, srcCanvas);
|
||||
}
|
||||
}
|
||||
|
@ -57,13 +57,17 @@
|
||||
title="&brandShortName;"
|
||||
titlemodifier="&brandShortName;"
|
||||
titleseparator="&mainWindow.titleseparator;"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/commandUtil.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/browser-ui.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/sanitize.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/CanvasBrowser.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/WidgetStack.js"/>
|
||||
<script type="application/x-javascript" src="chrome://browser/content/InputHandler.js"/>
|
||||
|
||||
<stringbundleset id="stringbundleset">
|
||||
<stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
|
||||
@ -194,25 +198,72 @@
|
||||
</panel>
|
||||
</popupset>
|
||||
|
||||
<stack id="browser-container" flex="1" style="overflow: hidden;">
|
||||
<vbox id="contentBox" style="-moz-stack-sizing: ignore; margin-top: 60px; margin: 0;">
|
||||
<notificationbox id="notifications" flex="1">
|
||||
<deckbrowser id="content"
|
||||
autocompletepopup="popup_autocomplete_content"
|
||||
flex="1"
|
||||
onnewtab="CommandUpdater.doCommand('cmd_newTab');"/>
|
||||
</notificationbox>
|
||||
</vbox>
|
||||
<!-- stupid stack needs to be in a box. not sure why -->
|
||||
<box>
|
||||
<stack id="browser-container" flex="1" style="width: 800px; height: 480px; max-width: 800px; max-height: 480px; background: lightgrey; /*background-image: url('chrome://browser/content/checkerboard.png')*/">
|
||||
|
||||
<toolbar id="toolbar-main" style="-moz-stack-sizing: ignore; height: 60px" top="0" left="0">
|
||||
<!-- begin: Browser View -->
|
||||
<hbox id="canvasbox"
|
||||
style="-moz-stack-sizing: ignore; height: 1440px; width: 800px;"
|
||||
left="0" top="-480"
|
||||
vptargetx="0"
|
||||
vptargety="0"
|
||||
vptargetw="800"
|
||||
vptargeth="480"
|
||||
viewport="true">
|
||||
|
||||
<html:canvas id="canvas"
|
||||
moz-opaque="true"
|
||||
viewport="true"
|
||||
style="height: 1440px; width: 800px;"
|
||||
height="1440" width="800"
|
||||
autocompletepopup="popup_autocomplete_content"
|
||||
onnewtab="CommandUpdater.doCommand('cmd_newTab');"/>
|
||||
|
||||
</hbox>
|
||||
|
||||
<!-- end: Browser View -->
|
||||
|
||||
<!-- start: some barriers -->
|
||||
<spacer style="-moz-stack-sizing: ignore; width: 1px; height: 1px;" barriertype="vertical" size="30" left="0" constraint="vp-relative"/>
|
||||
<spacer style="-moz-stack-sizing: ignore; width: 1px; height: 1px;" barriertype="vertical" size="30" left="800" constraint="vp-relative"/>
|
||||
<!-- end: barriers -->
|
||||
|
||||
<!-- begin: left bar -->
|
||||
<vbox id="tabs-container" style="-moz-stack-sizing: ignore; width: 132px; height: 420px;"
|
||||
left="-132" top="60"
|
||||
constraint="ignore-y,vp-relative">
|
||||
<richlistbox id="tabs" onselect="BrowserUI.selectTab(this.selectedItem);" onclosetab="BrowserUI.closeTab(this);"/>
|
||||
<hbox>
|
||||
<toolbarbutton id="newtab-button" command="cmd_newTab"/>
|
||||
<toolbarbutton id="retrievetab-button" command="" hidden="true"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<!-- end: left bar -->
|
||||
|
||||
<!-- begin: right bar -->
|
||||
<vbox id="browser-controls" style="-moz-stack-sizing: ignore; width: 80px; height: 420px;"
|
||||
left="800" top="60"
|
||||
constraint="ignore-y,vp-relative">
|
||||
<toolbarbutton id="tool-star" class="browser-control-button" command="cmd_star"/>
|
||||
<toolbarbutton id="tool-back" class="browser-control-button" command="cmd_back"/>
|
||||
<toolbarbutton id="tool-forward" class="browser-control-button" command="cmd_forward"/>
|
||||
<toolbarspring/>
|
||||
<toolbarbutton id="tool-actions" class="browser-control-button" command="cmd_actions" hidden="true"/>
|
||||
<toolbarbutton id="tool-panel" class="browser-control-button" command="cmd_panel" type="checkbox"/>
|
||||
</vbox>
|
||||
<!-- end: right bar -->
|
||||
|
||||
<!-- begin: Main Toolbar -->
|
||||
<toolbar id="toolbar-main" style="-moz-stack-sizing: ignore; width: 800px; height: 60px" top="-60" left="0" constraint="ignore-x,vp-relative">
|
||||
<hbox id="urlbar-container" flex="1">
|
||||
<box id="identity-box"
|
||||
onclick="getIdentityHandler().handleIdentityButtonEvent(event);"
|
||||
onkeypress="getIdentityHandler().handleIdentityButtonEvent(event);">
|
||||
<deck id="urlbar-image-deck">
|
||||
<box id="urlbar-image-box">
|
||||
<image id="urlbar-throbber"/>
|
||||
<image id="urlbar-favicon"/>
|
||||
</deck>
|
||||
<image id="urlbar-favicon" hidden="true"/>
|
||||
</box>
|
||||
</box>
|
||||
<hbox id="urlbar-editarea" flex="1">
|
||||
<description id="urlbar-caption" crop="end" flex="1"/>
|
||||
@ -238,25 +289,17 @@
|
||||
</hbox>
|
||||
<toolbarbutton id="tool-bookmarks" class="urlbar-button" command="cmd_bookmarks"/>
|
||||
</toolbar>
|
||||
<!-- end: Main Toolbar -->
|
||||
|
||||
<vbox id="browser-controls" style="-moz-stack-sizing: ignore;" top="60" left="0">
|
||||
<toolbarbutton id="tool-star" class="browser-control-button" command="cmd_star"/>
|
||||
<toolbarbutton id="tool-back" class="browser-control-button" command="cmd_back"/>
|
||||
<toolbarbutton id="tool-forward" class="browser-control-button" command="cmd_forward"/>
|
||||
<toolbarspring/>
|
||||
<toolbarbutton id="tool-actions" class="browser-control-button" command="cmd_actions" hidden="true"/>
|
||||
<toolbarbutton id="tool-panel" class="browser-control-button" command="cmd_panel" type="checkbox"/>
|
||||
</vbox>
|
||||
|
||||
<hbox id="panel-container" style="-moz-stack-sizing: ignore;" top="0" left="0">
|
||||
<hbox id="panel-container" hidden="true" style="-moz-stack-sizing: ignore;" top="60" left="880" constraint="ignore-y,vp-relative">
|
||||
<vbox id="panel-controls" oncommand="BrowserUI.switchPane(event.target.getAttribute('linkedpanel'));">
|
||||
<toolbarspring/>
|
||||
<toolbarbutton id="tool-addons" type="radio" group="1" class="panel-button" linkedpanel="addons-container"/>
|
||||
<toolbarbutton id="tool-downloads" type="radio" group="1" class="panel-button" linkedpanel="downloads-container"/>
|
||||
<toolbarbutton id="tool-preferences" type="radio" group="1" class="panel-button" linkedpanel="prefs-container" checked="true"/>
|
||||
<toolbarbutton id="tool-preferences" type="radio" group="1" class="panel-button" linkedpanel="prefs-container"/>
|
||||
<toolbarbutton id="tool-shortcuts" type="radio" group="1" class="panel-button" linkedpanel="shortcuts-container" hidden="true"/>
|
||||
</vbox>
|
||||
<deck id="panel-items" flex="1" selectedIndex="2">
|
||||
<deck id="panel-items" flex="1">
|
||||
<iframe id="addons-container" flex="1"/>
|
||||
<iframe id="downloads-container" flex="1"/>
|
||||
|
||||
@ -272,8 +315,7 @@
|
||||
<richpref pref="javascript.enabled" type="bool" title="&javascript.enabled.title;">
|
||||
&javascript.enabled.description;
|
||||
</richpref>
|
||||
<richpref pref="plugins.enabled" type="bool" title="&plugins.enabled.title;" id="plugins.enabled"
|
||||
onsynctopreference="Browser.setPluginState(this.value);">
|
||||
<richpref pref="plugins.enabled" type="bool" title="&plugins.enabled.title;" onsyncfrompreference="Browser.setPluginState(this.value);">
|
||||
&plugins.enabled.description;
|
||||
</richpref>
|
||||
|
||||
@ -305,14 +347,6 @@
|
||||
</deck>
|
||||
</hbox>
|
||||
|
||||
<vbox id="tab-list-container" style="-moz-stack-sizing: ignore;" top="60" left="0">
|
||||
<richlistbox id="tab-list" onselect="BrowserUI.selectTab(this.selectedItem);" onclosetab="BrowserUI.closeTab(this);"/>
|
||||
<hbox>
|
||||
<toolbarbutton id="newtab-button" command="cmd_newTab"/>
|
||||
<toolbarbutton id="retrievetab-button" command="" hidden="true"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<vbox id="urllist-container" hidden="true" style="-moz-stack-sizing: ignore;" top="0" left="0">
|
||||
<hbox id="urllist-items-container" flex="1">
|
||||
<richlistbox id="urllist-items" flex="1"
|
||||
@ -355,12 +389,17 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
|
||||
</stack>
|
||||
|
||||
</box>
|
||||
|
||||
<vbox id="findpanel-placeholder" sizetopopup="always">
|
||||
<panel id="findpanel" onpopupshown="Browser.doFind()">
|
||||
<findbar id="findbar"/>
|
||||
</panel>
|
||||
</vbox>
|
||||
|
||||
<!-- where all the (hidden) <browser> elements go; I wish this could be display: none -->
|
||||
<box id="browsers"/>
|
||||
</window>
|
||||
|
File diff suppressed because it is too large
Load Diff
143
mobile/chrome/content/tabs.xml
Normal file
143
mobile/chrome/content/tabs.xml
Normal file
@ -0,0 +1,143 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE bindings PUBLIC "-//MOZILLA//DTD XBL V1.0//EN" "http://www.mozilla.org/xbl">
|
||||
|
||||
<bindings
|
||||
xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<binding id="documenttab"
|
||||
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
|
||||
<content>
|
||||
<xul:stack anonid="page" class="documenttab-container" flex="1">
|
||||
<html:canvas anonid="canvas" class="documenttab-canvas" width="80" height="60"/>
|
||||
<xul:vbox align="start">
|
||||
<xul:image anonid="close" class="documenttab-close"/>
|
||||
</xul:vbox>
|
||||
</xul:stack>
|
||||
</content>
|
||||
<implementation>
|
||||
<constructor><![CDATA[
|
||||
let close = document.getAnonymousElementByAttribute(this, "anonid", "close");
|
||||
let closefn = new Function("event", this.control.getAttribute("onclosetab"));
|
||||
var self = this;
|
||||
close.addEventListener("mousedown", function(event) { closefn.call(self, event); event.stopPropagation(); }, true);
|
||||
]]></constructor>
|
||||
|
||||
<method name="updateThumbnail">
|
||||
<parameter name="browser"/>
|
||||
<parameter name="srcCanvas"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
const tabWidth = 80;
|
||||
const tabHeight = 60;
|
||||
let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
|
||||
|
||||
let domWin = browser.contentWindow;
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
if (srcCanvas) {
|
||||
ctx.drawImage(srcCanvas, 0, 0, tabWidth, tabHeight)
|
||||
}
|
||||
else {
|
||||
let width = domWin.innerWidth;
|
||||
let height = domWin.innerHeight;
|
||||
ctx.clearRect(0, 0, tabWidth, tabHeight);
|
||||
ctx.save();
|
||||
ctx.scale(tabWidth / width, tabHeight / height);
|
||||
ctx.drawWindow(domWin, 0, 0, width, height, "white");
|
||||
ctx.restore();
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
|
||||
<method name="markInvalid">
|
||||
<parameter name="browser"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
let canvas = document.getAnonymousElementByAttribute(this, "anonid", "canvas");
|
||||
let ctx = canvas.getContext("2d");
|
||||
|
||||
ctx.save();
|
||||
ctx.strokeStyle = "red";
|
||||
ctx.moveTo(63, 43);
|
||||
ctx.lineTo(78, 58);
|
||||
ctx.moveTo(78, 43);
|
||||
ctx.lineTo(63, 58);
|
||||
ctx.stroke();
|
||||
ctx.restore();
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
<!-- very hacky, used to display richlistitems in multiple columns -->
|
||||
<binding id="tablist"
|
||||
extends="chrome://global/content/bindings/richlistbox.xml#richlistbox">
|
||||
<content>
|
||||
<children includes="listheader"/>
|
||||
<xul:scrollbox allowevents="true" orient="horizontal" anonid="main-box"
|
||||
flex="1" style="overflow: auto;">
|
||||
<children/>
|
||||
</xul:scrollbox>
|
||||
</content>
|
||||
|
||||
<implementation>
|
||||
<field name="tabsPerColumn">4</field>
|
||||
<property name="children" readonly="true">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
var childNodes = [];
|
||||
for (var box = this.firstChild; box; box = box.nextSibling) {
|
||||
for (var child = box.firstChild; child; child = child.nextSibling) {
|
||||
if (child instanceof Components.interfaces.nsIDOMXULSelectControlItemElement)
|
||||
childNodes.push(child);
|
||||
}
|
||||
}
|
||||
return childNodes;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
<method name="addTab">
|
||||
<parameter name="tab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.children.length % this.tabsPerColumn == 0)
|
||||
this.appendChild(document.createElement("vbox"));
|
||||
this.lastChild.appendChild(tab);
|
||||
|
||||
return tab;
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="removeTab">
|
||||
<parameter name="tab"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var idx = this.getIndexOfItem(tab);
|
||||
if (idx == -1)
|
||||
return;
|
||||
|
||||
// remove all later tabs and readd them so that there aren't empty columns
|
||||
var count = this.itemCount - 1;
|
||||
var tomove = [ ];
|
||||
for (var c = count; c >= idx; c--) {
|
||||
var tab = this.getItemAtIndex(c);
|
||||
tomove.push(tab.parentNode.removeChild(tab));
|
||||
if (!this.lastChild.hasChildNodes())
|
||||
this.removeChild(this.lastChild);
|
||||
}
|
||||
|
||||
// subtract 2 because the tab to remove should not be added back again
|
||||
for (var m = tomove.length - 2; m >= 0; m--)
|
||||
this.addTab(tomove[m]);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
||||
</bindings>
|
@ -16,7 +16,7 @@ browser.jar:
|
||||
content/browser-ui.js (content/browser-ui.js)
|
||||
content/commandUtil.js (content/commandUtil.js)
|
||||
content/urlbar.xml (content/urlbar.xml)
|
||||
content/deckbrowser.xml (content/deckbrowser.xml)
|
||||
content/tabs.xml (content/tabs.xml)
|
||||
content/notification.xml (content/notification.xml)
|
||||
content/browser.css (content/browser.css)
|
||||
content/scrollbars.css (content/scrollbars.css)
|
||||
@ -28,6 +28,9 @@ browser.jar:
|
||||
content/preferences/richpref.xml (content/preferences/richpref.xml)
|
||||
* content/sanitize.xul (content/sanitize.xul)
|
||||
* content/sanitize.js (content/sanitize.js)
|
||||
content/WidgetStack.js (content/WidgetStack.js)
|
||||
content/CanvasBrowser.js (content/CanvasBrowser.js)
|
||||
content/InputHandler.js (content/InputHandler.js)
|
||||
|
||||
classic.jar:
|
||||
% skin browser classic/1.0 %
|
||||
|
@ -177,7 +177,7 @@ toolbarbutton.urlbar-cap-button {
|
||||
}
|
||||
|
||||
/* favicon images are 16x16 */
|
||||
#urlbar-image-deck {
|
||||
#urlbar-image-box {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
min-width: 24px;
|
||||
@ -189,6 +189,11 @@ toolbarbutton.urlbar-cap-button {
|
||||
list-style-image: url("chrome://browser/skin/images/throbber.png");
|
||||
}
|
||||
|
||||
#urlbar-favicon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
#urlbar-editarea {
|
||||
min-height: 49px;
|
||||
-moz-box-align: center;
|
||||
@ -466,12 +471,12 @@ toolbarbutton.panel-button {
|
||||
}
|
||||
|
||||
/* Left sidebar (tabs) ---------------------------------------------------- */
|
||||
#tab-list-container {
|
||||
#tabs-container {
|
||||
background: url("images/left_sidebar_middle.png") rgb(87,87,87) top right repeat-y;
|
||||
min-width: 132px;
|
||||
}
|
||||
|
||||
#tab-list {
|
||||
#tabs {
|
||||
-moz-appearance: none;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
|
@ -4,7 +4,7 @@ cmd_reload.name=Reload page
|
||||
cmd_stop.name=Stop loading
|
||||
cmd_search.name=Search
|
||||
cmd_go.name=Load URL
|
||||
cmd_openLocation=Open location
|
||||
cmd_openLocation.name=Open location
|
||||
cmd_star.name=Star page
|
||||
cmd_bookmarks.name=View bookmarks
|
||||
cmd_find.name=Find in page
|
||||
|
605
mobile/tests/wsTests.js
Normal file
605
mobile/tests/wsTests.js
Normal file
@ -0,0 +1,605 @@
|
||||
|
||||
const RED = "data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEA5BIEgA7";
|
||||
const BLUE = "data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAAAAAP8AAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEAxBJFAA7";
|
||||
const ORANGE = "data:image/gif;base64,R0lGODdhAgACALMAAAAAAP///wAAAP//AP8AAP+AAAD/AAAAAAAA//8A/wAAAAAAAAAAAAAAAAAAAAAAACwAAAAAAgACAAAEA7DIEgA7";
|
||||
|
||||
var gCheckCount = 0;
|
||||
|
||||
// test helpers
|
||||
function XUL(s, id, attrs) {
|
||||
let e = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", s);
|
||||
if (id)
|
||||
e.setAttribute("id", id);
|
||||
|
||||
if (attrs) {
|
||||
for (var a in attrs)
|
||||
e.setAttribute(a, attrs[a]);
|
||||
}
|
||||
return e;
|
||||
}
|
||||
|
||||
function e(id) {
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function attr(id, a, v) {
|
||||
e(id).setAttribute(a, v);
|
||||
}
|
||||
|
||||
function CleanDocument(x, y) {
|
||||
gCheckCount = 0;
|
||||
|
||||
var root = document.getElementById("testroot");
|
||||
while (root.firstChild)
|
||||
root.removeChild(root.firstChild);
|
||||
|
||||
if (x && y) {
|
||||
var tr = document.getElementById("testroot");
|
||||
tr.style.width = x + "px";
|
||||
tr.style.height = y + "px";
|
||||
}
|
||||
}
|
||||
|
||||
function IMAGE(src, id, x, y, w, h) {
|
||||
return XUL("image", id, { style: "-moz-stack-sizing: ignore; opacity: 0.5", src: src, width: w, height: h, left: x, top: y });
|
||||
}
|
||||
|
||||
function SetupFourBoxes() {
|
||||
var s = XUL("stack", "s");
|
||||
var a = IMAGE(RED, "a", 0, 0, 100, 100);
|
||||
var b = IMAGE(BLUE, "b", 100, 0, 100, 100);
|
||||
var c = IMAGE(BLUE, "c", 0, 100, 100, 100);
|
||||
var d = IMAGE(RED, "d", 100, 100, 100, 100);
|
||||
|
||||
s.appendChild(a);
|
||||
s.appendChild(b);
|
||||
s.appendChild(c);
|
||||
s.appendChild(d);
|
||||
|
||||
document.getElementById("testroot").appendChild(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function SetupOneBoxAndViewport(x,y) {
|
||||
var s = XUL("stack", "s");
|
||||
|
||||
var b = IMAGE(RED, "b", x-20, y-20, 20, 20);
|
||||
|
||||
s.appendChild(b);
|
||||
|
||||
var vp = IMAGE(ORANGE, "vp", x, y, 50, 50);
|
||||
vp.setAttribute("viewport", "true");
|
||||
s.appendChild(vp);
|
||||
|
||||
document.getElementById("testroot").appendChild(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function SetupEightBoxes(x, y, sz, _wantStack) {
|
||||
var s = XUL("stack", "s");
|
||||
|
||||
x = x || 0;
|
||||
y = y || 0;
|
||||
sz = sz || 20;
|
||||
|
||||
// boxes are b1-b9 from the top left, going clockwise
|
||||
var b1 = IMAGE(RED, "b1", x-sz, y-sz, sz, sz);
|
||||
var b2 = IMAGE(BLUE, "b2", x, y-sz, sz, sz);
|
||||
var b3 = IMAGE(RED, "b3", x+sz, y-sz, sz, sz);
|
||||
var b4 = IMAGE(BLUE, "b4", x+sz, y, sz, sz);
|
||||
var b5 = IMAGE(RED, "b5", x+sz, y+sz, sz, sz);
|
||||
var b6 = IMAGE(BLUE, "b6", x, y+sz, sz, sz);
|
||||
var b7 = IMAGE(RED, "b7", x-sz, y+sz, sz, sz);
|
||||
var b8 = IMAGE(BLUE, "b8", x-sz, y, sz, sz);
|
||||
|
||||
s.appendChild(b1);
|
||||
s.appendChild(b2);
|
||||
s.appendChild(b3);
|
||||
s.appendChild(b4);
|
||||
s.appendChild(b5);
|
||||
s.appendChild(b6);
|
||||
s.appendChild(b7);
|
||||
s.appendChild(b8);
|
||||
|
||||
if (_wantStack)
|
||||
return s;
|
||||
|
||||
document.getElementById("testroot").appendChild(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function SetupNineBoxes(x, y, sz) {
|
||||
var s = SetupEightBoxes(x, y, sz, true);
|
||||
|
||||
var b0 = IMAGE(ORANGE, "b0", x, y, sz, sz);
|
||||
|
||||
s.appendChild(b0);
|
||||
|
||||
document.getElementById("testroot").appendChild(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function SetupEightBoxesAndViewport(x, y, sz) {
|
||||
var s = SetupEightBoxes(x, y, sz, true);
|
||||
|
||||
var vp = IMAGE(ORANGE, "vp", x, y, 20, 20);
|
||||
vp.setAttribute("viewport", "true");
|
||||
s.appendChild(vp);
|
||||
|
||||
document.getElementById("testroot").appendChild(s);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
function Barrier(x, y, type, vpr) {
|
||||
if (x != undefined && y != undefined)
|
||||
throw "Bumper with both x and y given, that won't work";
|
||||
|
||||
var spacer = XUL("spacer", null, { style: "-moz-stack-sizing: ignore;", barriertype: type, size: '10' });
|
||||
|
||||
if (x != undefined)
|
||||
spacer.setAttribute("left", x);
|
||||
if (y != undefined)
|
||||
spacer.setAttribute("top", y);
|
||||
if (vpr)
|
||||
spacer.setAttribute("constraint", "vp-relative");
|
||||
|
||||
document.getElementById("s").appendChild(spacer);
|
||||
return spacer;
|
||||
}
|
||||
|
||||
function checkInnerBoundsInner(ws, x, y, w, h) {
|
||||
let vwib = ws._viewport.viewportInnerBounds;
|
||||
if (!((vwib.x != x ||
|
||||
vwib.y != y ||
|
||||
(w != undefined && vwib.width != w) ||
|
||||
(h != undefined && vwib.height != h))))
|
||||
return null;
|
||||
|
||||
return [ vwib.x, vwib.y, vwib.width, vwib.height ];
|
||||
}
|
||||
|
||||
function checkInnerBounds(ws, x, y, w, h) {
|
||||
gCheckCount++;
|
||||
|
||||
var res = checkInnerBoundsInner(ws, x, y, w, h);
|
||||
if (!res)
|
||||
return null;
|
||||
|
||||
var err;
|
||||
if (w == undefined || h == undefined) {
|
||||
err = "(" + gCheckCount + ") expected [" + x + "," + y + "] got [" + res[0] + "," + res[1] + "]";
|
||||
} else {
|
||||
err = "(" + gCheckCount + ") expected [" + x + "," + y + "," + w + "," + h + "] got [" + res[0] + "," + res[1] + "," + res[2] + "," + res[3] + "]";
|
||||
}
|
||||
|
||||
throw "checkInnerBounds failed: " + err;
|
||||
}
|
||||
|
||||
function checkRectInner(ws, id, x, y, w, h) {
|
||||
var e = document.getElementById(id);
|
||||
var bb = e.getBoundingClientRect();
|
||||
var wsb = ws._el.getBoundingClientRect();
|
||||
|
||||
if (!((bb.left - wsb.left) != x ||
|
||||
(bb.top - wsb.left) != y ||
|
||||
(w != undefined && (bb.right - bb.left) != w) ||
|
||||
(h != undefined && (bb.bottom - bb.top) != h)))
|
||||
return null;
|
||||
|
||||
return [(bb.left - wsb.left), (bb.top - wsb.left), (bb.right - bb.left), (bb.bottom - bb.top)];
|
||||
}
|
||||
|
||||
function checkRect(ws, id, x, y, w, h) {
|
||||
gCheckCount++;
|
||||
|
||||
var res = checkRectInner(ws, id, x, y, w, h);
|
||||
if (!res)
|
||||
return; // ok
|
||||
|
||||
var err;
|
||||
if (w == undefined || h == undefined) {
|
||||
err = "(" + gCheckCount + ") expected [" + x + "," + y + "] got [" + res[0] + "," + res[1] + "]";
|
||||
} else {
|
||||
err = "(" + gCheckCount + ") expected [" + x + "," + y + "," + w + "," + h + "] got [" + res[0] + "," + res[1] + "," + res[2] + "," + res[3] + "]";
|
||||
}
|
||||
|
||||
throw "checkRect failed: " + err;
|
||||
}
|
||||
|
||||
//
|
||||
// check that simple stuff works
|
||||
//
|
||||
function simple1() {
|
||||
CleanDocument();
|
||||
var s = SetupFourBoxes();
|
||||
|
||||
var ws = new WidgetStack(s);
|
||||
|
||||
checkRect(ws, "a", 0, 0);
|
||||
checkRect(ws, "b", 100, 0);
|
||||
checkRect(ws, "c", 0, 100);
|
||||
checkRect(ws, "d", 100, 100);
|
||||
|
||||
ws.panBy(-10, -10);
|
||||
|
||||
checkRect(ws, "a", -10, -10);
|
||||
checkRect(ws, "b", 90, -10);
|
||||
checkRect(ws, "c", -10, 90);
|
||||
checkRect(ws, "d", 90, 90);
|
||||
|
||||
// should be the same as panBy(10,10)
|
||||
ws.dragStart(50, 50);
|
||||
ws.dragMove(0, 0);
|
||||
ws.dragMove(60, 60);
|
||||
ws.dragStop();
|
||||
|
||||
checkRect(ws, "a", 0, 0);
|
||||
checkRect(ws, "b", 100, 0);
|
||||
checkRect(ws, "c", 0, 100);
|
||||
checkRect(ws, "d", 100, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// check that ignore-x, ignore-y, and frozen work
|
||||
function simple2() {
|
||||
CleanDocument();
|
||||
var s = SetupFourBoxes();
|
||||
|
||||
attr("b", "constraint", "ignore-x");
|
||||
attr("c", "constraint", "ignore-y");
|
||||
attr("d", "constraint", "frozen");
|
||||
|
||||
var ws = new WidgetStack(s);
|
||||
|
||||
ws.panBy(-20, -20);
|
||||
|
||||
checkRect(ws, "a", -20, -20);
|
||||
checkRect(ws, "b", 100, -20);
|
||||
checkRect(ws, "c", -20, 100);
|
||||
checkRect(ws, "d", 100, 100);
|
||||
|
||||
ws.panBy(20, 20);
|
||||
|
||||
checkRect(ws, "a", 0, 0);
|
||||
checkRect(ws, "b", 100, 0);
|
||||
checkRect(ws, "c", 0, 100);
|
||||
checkRect(ws, "d", 100, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function simple3() {
|
||||
CleanDocument(50,50);
|
||||
|
||||
var s = SetupNineBoxes(0, 0, 50);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
Barrier(0, undefined, "vertical");
|
||||
Barrier(25, undefined, "vertical");
|
||||
Barrier(50, undefined, "vertical");
|
||||
Barrier(undefined, 50, "horizontal");
|
||||
|
||||
var ws = new WidgetStack(s, 50, 50);
|
||||
|
||||
ws.panBy(-15, 0);
|
||||
|
||||
checkRect(ws, "b0", -5, 0);
|
||||
|
||||
// test that dragging does the same thing
|
||||
ws.dragStart(0, 0);
|
||||
ws.dragMove(5, 0);
|
||||
ws.dragMove(10, 0);
|
||||
ws.dragMove(15, 0);
|
||||
ws.dragStop();
|
||||
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
// because there's a 10-px bumper, this pan should have no effect
|
||||
ws.panBy(-5, 0);
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
// now we pan beyond the right barrier by 5px
|
||||
ws.panBy(-15, 0);
|
||||
checkRect(ws, "b0", -5, 0);
|
||||
|
||||
// and then we go back. We should just need 5 to get back to 0.
|
||||
ws.panBy(5, 0);
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
// check that we hit the middle barrier correctly
|
||||
ws.panBy(-30, 0);
|
||||
checkRect(ws, "b0", -20, 0);
|
||||
|
||||
// this should hit the middle barrier
|
||||
ws.panBy(-10, 0);
|
||||
checkRect(ws, "b0", -20, 0);
|
||||
|
||||
// and then go past it
|
||||
ws.panBy(-20, 0);
|
||||
checkRect(ws, "b0", -30, 0);
|
||||
|
||||
// reset
|
||||
ws.panBy(40, 0);
|
||||
|
||||
// now let's do a simpler test of the horizontal barriers; there's only one at 50
|
||||
|
||||
ws.panBy(0, -5);
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
ws.panBy(0, 5);
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
ws.panBy(0, -20);
|
||||
checkRect(ws, "b0", 0, -10);
|
||||
|
||||
ws.panBy(0, 10);
|
||||
checkRect(ws, "b0", 0, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// now check some viewport stuff
|
||||
function vp1() {
|
||||
CleanDocument(50, 50);
|
||||
|
||||
var s = SetupOneBoxAndViewport(0, 0);
|
||||
attr("b", "constraint", "vp-relative");
|
||||
|
||||
var ws = new WidgetStack(s, 50, 50);
|
||||
|
||||
// explicitly use this form of svb
|
||||
ws.setViewportBounds({top: 0, left: 0, right: 200, bottom: 200});
|
||||
|
||||
checkRect(ws, "b", -20, -20);
|
||||
|
||||
ws.panBy(20, 20);
|
||||
checkRect(ws, "b", 0, 0);
|
||||
checkRect(ws, "vp", 20, 20);
|
||||
|
||||
ws.panBy(50, 50);
|
||||
checkRect(ws, "b", 0, 0);
|
||||
checkRect(ws, "vp", 20, 20);
|
||||
|
||||
ws.panBy(-20, -20);
|
||||
ws.panBy(50, 50);
|
||||
checkRect(ws, "b", 0, 0);
|
||||
checkRect(ws, "vp", 20, 20);
|
||||
|
||||
ws.panBy(-200, -200);
|
||||
|
||||
checkRect(ws, "vp", 0, 0);
|
||||
checkInnerBounds(ws, 150, 150, 50, 50);
|
||||
|
||||
ws.panBy(500, 500);
|
||||
|
||||
checkRect(ws, "vp", 20, 20);
|
||||
checkInnerBounds(ws, 0, 0, 50, 50);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function vp2() {
|
||||
CleanDocument(20, 20);
|
||||
|
||||
var s = SetupEightBoxesAndViewport(0, 0);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
var ws = new WidgetStack(s, 20, 20);
|
||||
|
||||
// b5 is the bottom-right; the initial setup has a 20x20 viewport in the middle
|
||||
checkRect(ws, "b5", 20, 20);
|
||||
|
||||
// explicitly use this form of svb
|
||||
ws.setViewportBounds(0, 0, 200, 200);
|
||||
|
||||
// after resizing the viewport bounds, the rect should get pushed out
|
||||
checkRect(ws, "b5", 200, 200);
|
||||
|
||||
ws.panBy(-500, -500);
|
||||
|
||||
checkRect(ws, "b5", 0, 0);
|
||||
checkInnerBounds(ws, 180, 180, 20, 20);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function vp3() {
|
||||
CleanDocument(20, 20);
|
||||
|
||||
var s = SetupEightBoxesAndViewport(0, 0);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
attr("b2", "constraint", "vp-relative,ignore-x");
|
||||
attr("b4", "constraint", "vp-relative,ignore-y");
|
||||
|
||||
var ws = new WidgetStack(s, 20, 20);
|
||||
|
||||
// b2 is the top-middle
|
||||
checkRect(ws, "b2", 0, -20);
|
||||
// b4 is the right-middle
|
||||
checkRect(ws, "b4", 20, 0);
|
||||
|
||||
ws.setViewportBounds(200, 200);
|
||||
|
||||
checkRect(ws, "b2", 0, -20);
|
||||
checkRect(ws, "b4", 200, 0);
|
||||
|
||||
// x pans shouldn't affect ignore-x widgets
|
||||
ws.panBy(-20, 0);
|
||||
checkRect(ws, "b2", 0, -20);
|
||||
ws.panBy( 20, 0);
|
||||
|
||||
// y pans shouldn't affect ignore-y widgets
|
||||
ws.panBy(0, -20);
|
||||
checkRect(ws, "b4", 200, 0);
|
||||
ws.panBy(0, 20);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// test whether the right things happen when the viewport size changes
|
||||
function vp4() {
|
||||
CleanDocument(20, 20);
|
||||
|
||||
var s = SetupEightBoxesAndViewport(0, 0);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
attr("b2", "constraint", "vp-relative,ignore-x");
|
||||
attr("b4", "constraint", "vp-relative,ignore-y");
|
||||
|
||||
var ws = new WidgetStack(s, 20, 20);
|
||||
|
||||
ws.setViewportBounds(200, 200);
|
||||
|
||||
// after resizing the viewport bounds, the rect should get pushed out
|
||||
checkRect(ws, "b4", 200, 0);
|
||||
checkRect(ws, "b5", 200, 200);
|
||||
|
||||
checkInnerBounds(ws, 0, 0, 20, 20);
|
||||
|
||||
ws.panBy(-50, -50);
|
||||
|
||||
checkInnerBounds(ws, 50, 50, 20, 20);
|
||||
|
||||
// the viewport is now going to grow
|
||||
ws.setViewportBounds(400, 400);
|
||||
|
||||
// ... and the inner bounds should remain the same
|
||||
checkInnerBounds(ws, 50, 50, 20, 20);
|
||||
|
||||
// ... but b4 and b5 should be pushed out
|
||||
checkRect(ws, "b4", 400-50, 0);
|
||||
checkRect(ws, "b5", 400-50, 400-50);
|
||||
|
||||
// now move to the far corner
|
||||
ws.panBy(-500, -500);
|
||||
|
||||
// now shrink again
|
||||
ws.setViewportBounds(100, 100);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function vp5() {
|
||||
CleanDocument(20, 20);
|
||||
|
||||
var s = SetupEightBoxesAndViewport(0, 0);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
Barrier(0, undefined, "vertical", true);
|
||||
Barrier(20, undefined, "vertical", true);
|
||||
Barrier(undefined, 0, "horizontal", true);
|
||||
Barrier(undefined, 100, "horizontal", true);
|
||||
|
||||
var ws = new WidgetStack(s, 20, 20);
|
||||
|
||||
ws.setViewportBounds(200, 200);
|
||||
|
||||
ws.panBy(-20, 0);
|
||||
checkRect(ws, "b1", -30, -20);
|
||||
|
||||
ws.panBy(10, 0);
|
||||
checkRect(ws, "b1", -20, -20);
|
||||
|
||||
ws.panBy(-5, 0);
|
||||
checkRect(ws, "b1", -20, -20);
|
||||
|
||||
|
||||
// check the horizontal -- we should hit the first bumper,
|
||||
// but the other one should always be forever out of range of the vp
|
||||
ws.panBy(0, -110);
|
||||
checkRect(ws, "b1", -20, -120);
|
||||
|
||||
ws.panBy(0, -300);
|
||||
checkRect(ws, "b1", -20, -220);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function vp6() {
|
||||
CleanDocument(20, 20);
|
||||
|
||||
var s = SetupEightBoxesAndViewport(0, 0);
|
||||
for (var i = 1; i <= 8; i++) {
|
||||
attr("b"+i, "constraint", "vp-relative");
|
||||
}
|
||||
|
||||
var ws = new WidgetStack(s, 20, 20);
|
||||
|
||||
ws.setViewportBounds(200, 200);
|
||||
checkInnerBounds(ws, 0, 0, 20, 20);
|
||||
|
||||
ws.panTo(-75, -75);
|
||||
checkInnerBounds(ws, 75, 75, 20, 20);
|
||||
|
||||
// scale up
|
||||
ws.setViewportBounds(500, 500);
|
||||
checkInnerBounds(ws, 75, 75, 20, 20);
|
||||
|
||||
ws.panTo(-300, -300);
|
||||
checkInnerBounds(ws, 300, 300, 20, 20);
|
||||
|
||||
// scale down
|
||||
ws.setViewportBounds(200, 200);
|
||||
checkInnerBounds(ws, 75, 75, 20, 20);// XXX?
|
||||
|
||||
ws.panTo(-75, -75);
|
||||
checkInnerBounds(ws, 75, 75, 20, 20);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function run(s) {
|
||||
var r = false;
|
||||
try {
|
||||
r = window[s].apply(window);
|
||||
if (r) {
|
||||
logbase("PASSED: " + s);
|
||||
} else {
|
||||
logbase("FAILED: " + s + " (no exception?)");
|
||||
}
|
||||
} catch (e) {
|
||||
logbase("FAILED: " + s + ": " + e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
run("simple1");
|
||||
run("simple2");
|
||||
run("simple3");
|
||||
|
||||
run("vp1");
|
||||
run("vp2");
|
||||
run("vp3");
|
||||
run("vp4");
|
||||
run("vp5");
|
||||
run("vp6");
|
||||
}
|
||||
|
||||
function handleLoad() {
|
||||
gWsDoLog = true;
|
||||
gWsLogDiv = document.getElementById("logdiv");
|
||||
|
||||
runTests();
|
||||
}
|
25
mobile/tests/wsTests.xul
Normal file
25
mobile/tests/wsTests.xul
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<window id="w"
|
||||
width="800" height="800"
|
||||
onload="handleLoad();"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<script type="text/javascript" src="../chrome/content/WidgetStack.js"/>
|
||||
<script type="text/javascript" src="wsTests.js"/>
|
||||
|
||||
<vbox>
|
||||
<spacer style="height: 50px;"/>
|
||||
<hbox>
|
||||
<spacer style="width: 50px;"/>
|
||||
<box id="testroot" style="width: 50px; height: 50px; background: gray; -moz-stack-sizing: ignore;"/>
|
||||
<spacer flex="1"/>
|
||||
</hbox>
|
||||
<spacer style="height: 400px;" flex="1"/>
|
||||
<hbox>
|
||||
<spacer style="width: 400px;"/>
|
||||
<html:div style="font: 9px sans-serif;" id="logdiv"></html:div>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</window>
|
Loading…
x
Reference in New Issue
Block a user