From 473b0033733f089020d2be43635ca99326dd2514 Mon Sep 17 00:00:00 2001 From: Roy Frostig Date: Fri, 17 Jul 2009 20:08:47 -0700 Subject: [PATCH 001/369] Added Util with deprecated and new (ws)Rect --- toolkit/content/Geometry.jsm | 519 +++++++++++++++++++++++++++++++++++ 1 file changed, 519 insertions(+) create mode 100644 toolkit/content/Geometry.jsm diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm new file mode 100644 index 000000000000..ef036a72b989 --- /dev/null +++ b/toolkit/content/Geometry.jsm @@ -0,0 +1,519 @@ +// -*- 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) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Roy Frostig + * + * 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 ***** */ + +let Ci = Components.interfaces; + +// ----------------------------------------------------------- +// General util/convenience tools +// + +let Util = { + + bind: function bind(f, thisObj) { + return function() { + return f.apply(thisObj, arguments); + }; + }, + + bindAll: function bindAll(instance) { + for (let key in instance) + if (instance[key] instanceof Function) + instance[key] = bind(instance[key], instance); + } + +}; + + +// ----------------------------------------------------------- +// Util.Rect is a simple data structure for representation of a rectangle supporting +// many basic geometric operations. +// + +Util.Rect = function Rect(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x+w; + this.bottom = y+h; +}; + +Util.Rect.prototype = { + get x() { return this.left; }, + get y() { return this.top; }, + get width() { return this.right - this.left; }, + get height() { return this.bottom - this.top; }, + set x(v) { + let diff = this.left - v; + this.left = v; + this.right -= diff; + }, + set y(v) { + let diff = this.top - v; + this.top = v; + this.bottom -= diff; + }, + set width(v) { this.right = this.left + v; }, + set height(v) { this.bottom = this.top + v; }, + + setRect: function(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x+w; + this.bottom = y+h; + + return this; + }, + + setBounds: function(t, l, b, r) { + this.top = t; + this.left = l; + this.bottom = b; + this.right = r; + + return this; + }, + + equals: function equals(r) { + return (r != null && + this.top == r.top && + this.left == r.left && + this.bottom == r.bottom && + this.right == r.right); + }, + + clone: function clone() { + return new wsRect(this.left, this.top, this.right - this.left, this.bottom - this.top); + }, + + center: function center() { + return [this.left + (this.right - this.left) / 2, + this.top + (this.bottom - this.top) / 2]; + }, + + centerRounded: function centerRounded() { + return this.center().map(Math.round); + }, + + copyFrom: function(r) { + this.top = r.top; + this.left = r.left; + this.bottom = r.bottom; + this.right = r.right; + + return this; + }, + + copyFromTLBR: function(r) { + this.left = r.left; + this.top = r.top; + this.right = r.right; + this.bottom = r.bottom; + + return this; + }, + + translate: function(x, y) { + this.left += x; + this.right += x; + this.top += y; + this.bottom += y; + + return this; + }, + + // return a new wsRect that is the union of that one and this one + union: function(rect) { + let l = Math.min(this.left, rect.left); + let r = Math.max(this.right, rect.right); + let t = Math.min(this.top, rect.top); + let b = Math.max(this.bottom, rect.bottom); + + return new wsRect(l, t, r-l, b-t); + }, + + toString: function() { + return "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]"; + }, + + expandBy: function(b) { + this.left += b.left; + this.right += b.right; + this.top += b.top; + this.bottom += b.bottom; + return this; + }, + + contains: function(other) { + return !!(other.left >= this.left && + other.right <= this.right && + other.top >= this.top && + other.bottom <= this.bottom); + }, + + intersect: function(r2) { + let xmost1 = this.right; + let xmost2 = r2.right; + + let x = Math.max(this.left, r2.left); + + let temp = Math.min(xmost1, xmost2); + if (temp <= x) + return null; + + let width = temp - x; + + let ymost1 = this.bottom; + let ymost2 = r2.bottom; + let y = Math.max(this.top, r2.top); + + temp = Math.min(ymost1, ymost2); + if (temp <= y) + return null; + + let height = temp - y; + + return new wsRect(x, y, width, height); + }, + + intersects: function(other) { + let xok = (other.left > this.left && other.left < this.right) || + (other.right > this.left && other.right < this.right) || + (other.left <= this.left && other.right >= this.right); + let yok = (other.top > this.top && other.top < this.bottom) || + (other.bottom > this.top && other.bottom < this.bottom) || + (other.top <= this.top && other.bottom >= this.bottom); + return xok && yok; + }, + + /** + * Similar to (and most code stolen from) intersect(). A restriction + * is an intersection, but this modifies the receiving object instead + * of returning a new rect. + */ + restrictTo: function restrictTo(r2) { + let xmost1 = this.right; + let xmost2 = r2.right; + + let x = Math.max(this.left, r2.left); + + let temp = Math.min(xmost1, xmost2); + if (temp <= x) + throw "Intersection is empty but rects cannot be empty"; + + let width = temp - x; + + let ymost1 = this.bottom; + let ymost2 = r2.bottom; + let y = Math.max(this.top, r2.top); + + temp = Math.min(ymost1, ymost2); + if (temp <= y) + throw "Intersection is empty but rects cannot be empty"; + + let height = temp - y; + + return this.setRect(x, y, width, height); + }, + + /** + * Similar to (and most code stolen from) union(). An extension is a + * union (in our sense of the term, not the common set-theoretic sense), + * but this modifies the receiving object instead of returning a new rect. + * Effectively, this rectangle is expanded minimally to contain all of the + * other rect. "Expanded minimally" means that the rect may shrink if + * given a strict subset rect as the argument. + */ + expandToContain: function extendTo(rect) { + let l = Math.min(this.left, rect.left); + let r = Math.max(this.right, rect.right); + let t = Math.min(this.top, rect.top); + let b = Math.max(this.bottom, rect.bottom); + + return this.setRect(l, t, r-l, b-t); + }, + + round: function round(scale) { + if (!scale) scale = 1; + + this.left = Math.floor(this.left * scale) / scale; + this.top = Math.floor(this.top * scale) / scale; + this.right = Math.ceil(this.right * scale) / scale; + this.bottom = Math.ceil(this.bottom * scale) / scale; + + return this; + }, + + scale: function scale(xscl, yscl) { + this.left *= xscl; + this.right *= xscl; + this.top *= yscl; + this.bottom *= yscl; + + return this; + } +}; + + +// ----------------------------------------------------------- +// Deprecated, global version of wsRect +// + +function wsRect(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x+w; + this.bottom = y+h; +} + +wsRect.prototype = { + + get x() { return this.left; }, + get y() { return this.top; }, + get width() { return this.right - this.left; }, + get height() { return this.bottom - this.top; }, + set x(v) { + let diff = this.left - v; + this.left = v; + this.right -= diff; + }, + set y(v) { + let diff = this.top - v; + this.top = v; + this.bottom -= diff; + }, + set width(v) { this.right = this.left + v; }, + set height(v) { this.bottom = this.top + v; }, + + setRect: function(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x+w; + this.bottom = y+h; + + return this; + }, + + setBounds: function(t, l, b, r) { + this.top = t; + this.left = l; + this.bottom = b; + this.right = r; + + return this; + }, + + equals: function equals(r) { + return (r != null && + this.top == r.top && + this.left == r.left && + this.bottom == r.bottom && + this.right == r.right); + }, + + clone: function clone() { + return new wsRect(this.left, this.top, this.right - this.left, this.bottom - this.top); + }, + + center: function center() { + return [this.left + (this.right - this.left) / 2, + this.top + (this.bottom - this.top) / 2]; + }, + + centerRounded: function centerRounded() { + return this.center().map(Math.round); + }, + + copyFrom: function(r) { + this.top = r.top; + this.left = r.left; + this.bottom = r.bottom; + this.right = r.right; + + return this; + }, + + copyFromTLBR: function(r) { + this.left = r.left; + this.top = r.top; + this.right = r.right; + this.bottom = r.bottom; + + return this; + }, + + translate: function(x, y) { + this.left += x; + this.right += x; + this.top += y; + this.bottom += y; + + return this; + }, + + // return a new wsRect that is the union of that one and this one + union: function(rect) { + let l = Math.min(this.left, rect.left); + let r = Math.max(this.right, rect.right); + let t = Math.min(this.top, rect.top); + let b = Math.max(this.bottom, rect.bottom); + + return new wsRect(l, t, r-l, b-t); + }, + + toString: function() { + return "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]"; + }, + + expandBy: function(b) { + this.left += b.left; + this.right += b.right; + this.top += b.top; + this.bottom += b.bottom; + return this; + }, + + contains: function(other) { + return !!(other.left >= this.left && + other.right <= this.right && + other.top >= this.top && + other.bottom <= this.bottom); + }, + + intersect: function(r2) { + let xmost1 = this.right; + let xmost2 = r2.right; + + let x = Math.max(this.left, r2.left); + + let temp = Math.min(xmost1, xmost2); + if (temp <= x) + return null; + + let width = temp - x; + + let ymost1 = this.bottom; + let ymost2 = r2.bottom; + let y = Math.max(this.top, r2.top); + + temp = Math.min(ymost1, ymost2); + if (temp <= y) + return null; + + let height = temp - y; + + return new wsRect(x, y, width, height); + }, + + intersects: function(other) { + let xok = (other.left > this.left && other.left < this.right) || + (other.right > this.left && other.right < this.right) || + (other.left <= this.left && other.right >= this.right); + let yok = (other.top > this.top && other.top < this.bottom) || + (other.bottom > this.top && other.bottom < this.bottom) || + (other.top <= this.top && other.bottom >= this.bottom); + return xok && yok; + }, + + /** + * Similar to (and most code stolen from) intersect(). A restriction + * is an intersection, but this modifies the receiving object instead + * of returning a new rect. + */ + restrictTo: function restrictTo(r2) { + let xmost1 = this.right; + let xmost2 = r2.right; + + let x = Math.max(this.left, r2.left); + + let temp = Math.min(xmost1, xmost2); + if (temp <= x) + throw "Intersection is empty but rects cannot be empty"; + + let width = temp - x; + + let ymost1 = this.bottom; + let ymost2 = r2.bottom; + let y = Math.max(this.top, r2.top); + + temp = Math.min(ymost1, ymost2); + if (temp <= y) + throw "Intersection is empty but rects cannot be empty"; + + let height = temp - y; + + return this.setRect(x, y, width, height); + }, + + /** + * Similar to (and most code stolen from) union(). An extension is a + * union (in our sense of the term, not the common set-theoretic sense), + * but this modifies the receiving object instead of returning a new rect. + * Effectively, this rectangle is expanded minimally to contain all of the + * other rect. "Expanded minimally" means that the rect may shrink if + * given a strict subset rect as the argument. + */ + expandToContain: function extendTo(rect) { + let l = Math.min(this.left, rect.left); + let r = Math.max(this.right, rect.right); + let t = Math.min(this.top, rect.top); + let b = Math.max(this.bottom, rect.bottom); + + return this.setRect(l, t, r-l, b-t); + }, + + round: function round(scale) { + if (!scale) scale = 1; + + this.left = Math.floor(this.left * scale) / scale; + this.top = Math.floor(this.top * scale) / scale; + this.right = Math.ceil(this.right * scale) / scale; + this.bottom = Math.ceil(this.bottom * scale) / scale; + + return this; + }, + + scale: function scale(xscl, yscl) { + this.left *= xscl; + this.right *= xscl; + this.top *= yscl; + this.bottom *= yscl; + + return this; + } +}; + From 555777814bbbc948beefc55bdb2c9e105a065dd7 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Fri, 17 Jul 2009 20:12:10 -0700 Subject: [PATCH 002/369] adding Util.js to the script --- toolkit/content/Geometry.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index ef036a72b989..a49c27ebd52c 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -52,6 +52,7 @@ let Util = { }, bindAll: function bindAll(instance) { + let bind = Util.bind; for (let key in instance) if (instance[key] instanceof Function) instance[key] = bind(instance[key], instance); From 7b9d28aea7dbb6ce2f15aef499b6b8ffb9d31568 Mon Sep 17 00:00:00 2001 From: Stuart Parmenter Date: Sat, 18 Jul 2009 02:59:24 -0700 Subject: [PATCH 003/369] use pause/resume rendering when panning, resize/repaint immediately when page is done loading, misc other fixes. --- toolkit/content/Geometry.jsm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index a49c27ebd52c..a4cd1bf23c59 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -212,6 +212,7 @@ Util.Rect.prototype = { }, intersects: function(other) { + if (!other) debugger; let xok = (other.left > this.left && other.left < this.right) || (other.right > this.left && other.right < this.right) || (other.left <= this.left && other.right >= this.right); @@ -441,6 +442,7 @@ wsRect.prototype = { }, intersects: function(other) { + if (!other) debugger; let xok = (other.left > this.left && other.left < this.right) || (other.right > this.left && other.right < this.right) || (other.left <= this.left && other.right >= this.right); From 84d299c5e469523a6d5be438ef3238f612e13eb8 Mon Sep 17 00:00:00 2001 From: Roy Frostig Date: Mon, 20 Jul 2009 18:32:52 -0700 Subject: [PATCH 004/369] ForEach macro definition and Dragger input code. --- toolkit/content/Geometry.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index a4cd1bf23c59..5bccbd09bcd6 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -57,7 +57,7 @@ let Util = { if (instance[key] instanceof Function) instance[key] = bind(instance[key], instance); } - + }; From dbd00fb9aec91f794b887fbc4522df83e2cd7f35 Mon Sep 17 00:00:00 2001 From: Roy Frostig Date: Thu, 30 Jul 2009 17:31:09 -0700 Subject: [PATCH 005/369] Fixed Makefile.in WinNT/CE preprocessing command. Tile manager macros done in BEGIN, END form. Tile manager macros put to better use. Small optimizations on begin and endCriticalRect. Fixed tile pool sorting and eviction so that no extra evict guard is needed on every FORCREATE. Fixed assorted small bugs throughout (maybe introduced some new ones ;p). Added sweet sweet tilecache debug to browser.js. --- toolkit/content/Geometry.jsm | 65 +++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 15 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 5bccbd09bcd6..17a1bd8d4d73 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -57,7 +57,7 @@ let Util = { if (instance[key] instanceof Function) instance[key] = bind(instance[key], instance); } - + }; @@ -213,15 +213,33 @@ Util.Rect.prototype = { intersects: function(other) { if (!other) debugger; - let xok = (other.left > this.left && other.left < this.right) || - (other.right > this.left && other.right < this.right) || - (other.left <= this.left && other.right >= this.right); - let yok = (other.top > this.top && other.top < this.bottom) || - (other.bottom > this.top && other.bottom < this.bottom) || - (other.top <= this.top && other.bottom >= this.bottom); - return xok && yok; + + let left = this.left; + let right = this.right; + let otherleft = other.left; + let otherright = otherright; + + let xok = (otherleft > left && otherleft < right) || + (otherright > left && otherright < right) || + (otherleft <= left && otherright >= right); + + if (!xok) return false; + + let top = this.top; + let bottom = this.bottom; + let othertop = other.top; + let otherbottom = other.bottom; + + let yok = (othertop > top && othertop < bottom) || + (otherbottom > top && otherbottom < bottom) || + (othertop <= top && otherbottom >= bottom); + + if (!yok) return false; + + return true; }, + /** * Similar to (and most code stolen from) intersect(). A restriction * is an intersection, but this modifies the receiving object instead @@ -443,13 +461,30 @@ wsRect.prototype = { intersects: function(other) { if (!other) debugger; - let xok = (other.left > this.left && other.left < this.right) || - (other.right > this.left && other.right < this.right) || - (other.left <= this.left && other.right >= this.right); - let yok = (other.top > this.top && other.top < this.bottom) || - (other.bottom > this.top && other.bottom < this.bottom) || - (other.top <= this.top && other.bottom >= this.bottom); - return xok && yok; + + let left = this.left; + let right = this.right; + let otherleft = other.left; + let otherright = otherright; + + let xok = (otherleft > left && otherleft < right) || + (otherright > left && otherright < right) || + (otherleft <= left && otherright >= right); + + if (!xok) return false; + + let top = this.top; + let bottom = this.bottom; + let othertop = other.top; + let otherbottom = other.bottom; + + let yok = (othertop > top && othertop < bottom) || + (otherbottom > top && otherbottom < bottom) || + (othertop <= top && otherbottom >= bottom); + + if (!yok) return false; + + return true; }, /** From b87ca238faed910ce9787ab1eca574d22e74baf9 Mon Sep 17 00:00:00 2001 From: Roy Frostig Date: Thu, 30 Jul 2009 18:17:15 -0700 Subject: [PATCH 006/369] Oopsie, small bugfix for bug I introduced to wsRect::intersects in my last commit. --- toolkit/content/Geometry.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 17a1bd8d4d73..e3608121b391 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -217,7 +217,7 @@ Util.Rect.prototype = { let left = this.left; let right = this.right; let otherleft = other.left; - let otherright = otherright; + let otherright = other.right; let xok = (otherleft > left && otherleft < right) || (otherright > left && otherright < right) || @@ -465,7 +465,7 @@ wsRect.prototype = { let left = this.left; let right = this.right; let otherleft = other.left; - let otherright = otherright; + let otherright = other.right; let xok = (otherleft > left && otherleft < right) || (otherright > left && otherright < right) || From 35fa3ffaa8fc96fc0910738ceac14c29722e77f9 Mon Sep 17 00:00:00 2001 From: Ben Combee Date: Fri, 7 Aug 2009 18:08:06 -0700 Subject: [PATCH 007/369] Merge of iframe scrolling patch to tiles. r=stuart --- toolkit/content/Geometry.jsm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index e3608121b391..cd2b961ee6c3 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -22,6 +22,7 @@ * * Contributor(s): * Roy Frostig + * Ben Combee * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -56,8 +57,13 @@ let Util = { for (let key in instance) if (instance[key] instanceof Function) instance[key] = bind(instance[key], instance); - } + }, + dumpLn: function dumpLn() { + // like dump, but each arg is handled and there's an automatic newline + for (var i = 0; i < arguments.length; i++) { dump(arguments[i]); } + dump("\n"); + } }; @@ -554,4 +560,3 @@ wsRect.prototype = { return this; } }; - From 7e25f14be735abf1346c82c38554bc0ff638fa47 Mon Sep 17 00:00:00 2001 From: Roy Frostig Date: Wed, 12 Aug 2009 17:40:43 -0400 Subject: [PATCH 008/369] Bug 509451: Fix horizontal chrome scrolling when viewport is wider than window, r=stuart --- toolkit/content/Geometry.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index cd2b961ee6c3..df7862ab9f4c 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -59,9 +59,9 @@ let Util = { instance[key] = bind(instance[key], instance); }, + /** Like dump, but each arg is handled and there's an automatic newline */ dumpLn: function dumpLn() { - // like dump, but each arg is handled and there's an automatic newline - for (var i = 0; i < arguments.length; i++) { dump(arguments[i]); } + for (var i = 0; i < arguments.length; i++) { dump(arguments[i] + ' '); } dump("\n"); } }; From f661465a29653d0de5bc76bb6b4e1e277fc825ea Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Tue, 1 Sep 2009 23:39:39 -0400 Subject: [PATCH 009/369] Bug 501566: It takes too long to highlight a selected link, r=froystig, r=mfinkle --- toolkit/content/Geometry.jsm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index df7862ab9f4c..82bf247d765d 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -63,7 +63,18 @@ let Util = { dumpLn: function dumpLn() { for (var i = 0; i < arguments.length; i++) { dump(arguments[i] + ' '); } dump("\n"); + }, + + /** Executes aFunc after other events have been processed. */ + executeSoon: function executeSoon(aFunc) { + let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); + tm.mainThread.dispatch({ + run: function() { + aFunc(); + } + }, Ci.nsIThread.DISPATCH_NORMAL); } + }; From b3029f644b8a35c15c95ec24cca78c417ec251bc Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Sat, 5 Sep 2009 01:14:59 -0400 Subject: [PATCH 010/369] Bug 451670: Discard tab data when low on memory, r=mfinkle, r=stuart --- toolkit/content/Geometry.jsm | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 82bf247d765d..c76e41967135 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -59,9 +59,21 @@ let Util = { instance[key] = bind(instance[key], instance); }, + /** printf-like dump function */ + dumpf: function dumpf(str) { + var args = arguments; + var i = 1; + dump(str.replace(/%s/g, function() { + if (i >= args.length) { + throw "dumps received too many placeholders and not enough arguments"; + } + return args[i++].toString(); + })); + }, + /** Like dump, but each arg is handled and there's an automatic newline */ dumpLn: function dumpLn() { - for (var i = 0; i < arguments.length; i++) { dump(arguments[i] + ' '); } + for (var i = 0; i < arguments.length; i++) { dump(arguments[i] + " "); } dump("\n"); }, From 18763254a9696a94d2cc168fa80c842cc1c15448 Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Thu, 15 Oct 2009 15:50:32 -0400 Subject: [PATCH 011/369] Bug 520910: Refactor Rect and add Point class, r=gavin --- toolkit/content/Geometry.jsm | 562 +++++------------- .../content/tests/browser/browser_Geometry.js | 73 +++ 2 files changed, 235 insertions(+), 400 deletions(-) create mode 100644 toolkit/content/tests/browser/browser_Geometry.js diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index c76e41967135..2ad4ce197de0 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -90,267 +90,98 @@ let Util = { }; -// ----------------------------------------------------------- -// Util.Rect is a simple data structure for representation of a rectangle supporting -// many basic geometric operations. -// - -Util.Rect = function Rect(x, y, w, h) { - this.left = x; - this.top = y; - this.right = x+w; - this.bottom = y+h; -}; - -Util.Rect.prototype = { - get x() { return this.left; }, - get y() { return this.top; }, - get width() { return this.right - this.left; }, - get height() { return this.bottom - this.top; }, - set x(v) { - let diff = this.left - v; - this.left = v; - this.right -= diff; - }, - set y(v) { - let diff = this.top - v; - this.top = v; - this.bottom -= diff; - }, - set width(v) { this.right = this.left + v; }, - set height(v) { this.bottom = this.top + v; }, - - setRect: function(x, y, w, h) { - this.left = x; - this.top = y; - this.right = x+w; - this.bottom = y+h; - - return this; - }, - - setBounds: function(t, l, b, r) { - this.top = t; - this.left = l; - this.bottom = b; - this.right = r; - - return this; - }, - - equals: function equals(r) { - return (r != null && - this.top == r.top && - this.left == r.left && - this.bottom == r.bottom && - this.right == r.right); - }, - - clone: function clone() { - return new wsRect(this.left, this.top, this.right - this.left, this.bottom - this.top); - }, - - center: function center() { - return [this.left + (this.right - this.left) / 2, - this.top + (this.bottom - this.top) / 2]; - }, - - centerRounded: function centerRounded() { - return this.center().map(Math.round); - }, - - copyFrom: function(r) { - this.top = r.top; - this.left = r.left; - this.bottom = r.bottom; - this.right = r.right; - - return this; - }, - - copyFromTLBR: function(r) { - this.left = r.left; - this.top = r.top; - this.right = r.right; - this.bottom = r.bottom; - - return this; - }, - - translate: function(x, y) { - this.left += x; - this.right += x; - this.top += y; - this.bottom += y; - - return this; - }, - - // return a new wsRect that is the union of that one and this one - union: function(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); - - return new wsRect(l, t, r-l, b-t); - }, - - toString: function() { - return "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]"; - }, - - expandBy: function(b) { - this.left += b.left; - this.right += b.right; - this.top += b.top; - this.bottom += b.bottom; - return this; - }, - - contains: function(other) { - return !!(other.left >= this.left && - other.right <= this.right && - other.top >= this.top && - other.bottom <= this.bottom); - }, - - intersect: function(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; - - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - return null; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - return null; - - let height = temp - y; - - return new wsRect(x, y, width, height); - }, - - intersects: function(other) { - if (!other) debugger; - - let left = this.left; - let right = this.right; - let otherleft = other.left; - let otherright = other.right; - - let xok = (otherleft > left && otherleft < right) || - (otherright > left && otherright < right) || - (otherleft <= left && otherright >= right); - - if (!xok) return false; - - let top = this.top; - let bottom = this.bottom; - let othertop = other.top; - let otherbottom = other.bottom; - - let yok = (othertop > top && othertop < bottom) || - (otherbottom > top && otherbottom < bottom) || - (othertop <= top && otherbottom >= bottom); - - if (!yok) return false; - - return true; - }, - - - /** - * Similar to (and most code stolen from) intersect(). A restriction - * is an intersection, but this modifies the receiving object instead - * of returning a new rect. - */ - restrictTo: function restrictTo(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; - - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - throw "Intersection is empty but rects cannot be empty"; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - throw "Intersection is empty but rects cannot be empty"; - - let height = temp - y; - - return this.setRect(x, y, width, height); - }, - - /** - * Similar to (and most code stolen from) union(). An extension is a - * union (in our sense of the term, not the common set-theoretic sense), - * but this modifies the receiving object instead of returning a new rect. - * Effectively, this rectangle is expanded minimally to contain all of the - * other rect. "Expanded minimally" means that the rect may shrink if - * given a strict subset rect as the argument. - */ - expandToContain: function extendTo(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); - - return this.setRect(l, t, r-l, b-t); - }, - - round: function round(scale) { - if (!scale) scale = 1; - - this.left = Math.floor(this.left * scale) / scale; - this.top = Math.floor(this.top * scale) / scale; - this.right = Math.ceil(this.right * scale) / scale; - this.bottom = Math.ceil(this.bottom * scale) / scale; - - return this; - }, - - scale: function scale(xscl, yscl) { - this.left *= xscl; - this.right *= xscl; - this.top *= yscl; - this.bottom *= yscl; - - return this; - } -}; - - -// ----------------------------------------------------------- -// Deprecated, global version of wsRect -// - -function wsRect(x, y, w, h) { - this.left = x; - this.top = y; - this.right = x+w; - this.bottom = y+h; +/** + * Simple Point class. + * + * Any method that takes an x and y may also take a point. + */ +Point = function Point(x, y) { + this.set(x, y); } -wsRect.prototype = { +Point.prototype = { + clone: function clone() { + return new Point(this.x, this.y); + }, + set: function set(x, y) { + this.x = x; + this.y = y; + return this; + }, + + equals: function equals(x, y) { + return this.x == x && this.y == y; + }, + + toString: function toString() { + return "(" + this.x + "," + this.y + ")"; + }, + + map: function map(f) { + this.x = f.call(this, this.x); + this.y = f.call(this, this.y); + return this; + }, + + add: function add(x, y) { + this.x += x; + this.y += y; + return this; + }, + + subtract: function subtract(x, y) { + this.x -= x; + this.y -= y; + return this; + }, + + scale: function scale(s) { + this.x *= s; + this.y *= s; + return this; + }, + + isZero: function() { + return this.x == 0 && this.y == 0; + } +}; + +(function() { + function takePointOrArgs(f) { + return function(arg1, arg2) { + if (arg2 === undefined) + return f.call(this, arg1.x, arg1.y); + else + return f.call(this, arg1, arg2); + }; + } + + for each (let f in ['add', 'subtract', 'equals', 'set']) + Point.prototype[f] = takePointOrArgs(Point.prototype[f]); +})(); + + +/** + * Rect is a simple data structure for representation of a rectangle supporting + * many basic geometric operations. + * + * NOTE: Since its operations are closed, rectangles may be empty and will report + * non-positive widths and heights in that case. + */ + +function Rect(x, y, w, h) { + this.left = x; + this.top = y; + this.right = x+w; + this.bottom = y+h; +}; + +Rect.fromRect = function fromRect(r) { + return new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top); +} + +Rect.prototype = { get x() { return this.left; }, get y() { return this.top; }, get width() { return this.right - this.left; }, @@ -368,6 +199,10 @@ wsRect.prototype = { set width(v) { this.right = this.left + v; }, set height(v) { this.bottom = this.top + v; }, + isEmpty: function isEmpty() { + return this.left >= this.right || this.top >= this.bottom; + }, + setRect: function(x, y, w, h) { this.left = x; this.top = y; @@ -386,41 +221,30 @@ wsRect.prototype = { return this; }, - equals: function equals(r) { - return (r != null && - this.top == r.top && - this.left == r.left && - this.bottom == r.bottom && - this.right == r.right); + equals: function equals(other) { + return (other != null && + this.top == other.top && + this.left == other.left && + this.bottom == other.bottom && + this.right == other.right); }, clone: function clone() { - return new wsRect(this.left, this.top, this.right - this.left, this.bottom - this.top); + return new Rect(this.left, this.top, this.right - this.left, this.bottom - this.top); }, center: function center() { - return [this.left + (this.right - this.left) / 2, - this.top + (this.bottom - this.top) / 2]; + if (this.isEmpty()) + throw "Empty rectangles do not have centers"; + return new Point(this.left + (this.right - this.left) / 2, + this.top + (this.bottom - this.top) / 2); }, - centerRounded: function centerRounded() { - return this.center().map(Math.round); - }, - - copyFrom: function(r) { - this.top = r.top; - this.left = r.left; - this.bottom = r.bottom; - this.right = r.right; - - return this; - }, - - copyFromTLBR: function(r) { - this.left = r.left; - this.top = r.top; - this.right = r.right; - this.bottom = r.bottom; + copyFrom: function(other) { + this.top = other.top; + this.left = other.left; + this.bottom = other.bottom; + this.right = other.right; return this; }, @@ -434,143 +258,74 @@ wsRect.prototype = { return this; }, - // return a new wsRect that is the union of that one and this one - union: function(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); - - return new wsRect(l, t, r-l, b-t); - }, - toString: function() { return "[" + this.x + "," + this.y + "," + this.width + "," + this.height + "]"; }, - expandBy: function(b) { - this.left += b.left; - this.right += b.right; - this.top += b.top; - this.bottom += b.bottom; - return this; + /** return a new rect that is the union of that one and this one */ + union: function(other) { + return this.clone().expandToContain(other); }, contains: function(other) { - return !!(other.left >= this.left && - other.right <= this.right && - other.top >= this.top && - other.bottom <= this.bottom); + if (other.isEmpty()) return true; + if (this.isEmpty()) return false; + + return (other.left >= this.left && + other.right <= this.right && + other.top >= this.top && + other.bottom <= this.bottom); }, - intersect: function(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; - - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - return null; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - return null; - - let height = temp - y; - - return new wsRect(x, y, width, height); + intersect: function(other) { + return this.clone().restrictTo(other); }, intersects: function(other) { - if (!other) debugger; + if (this.isEmpty() || other.isEmpty()) + return false; - let left = this.left; - let right = this.right; - let otherleft = other.left; - let otherright = other.right; - - let xok = (otherleft > left && otherleft < right) || - (otherright > left && otherright < right) || - (otherleft <= left && otherright >= right); - - if (!xok) return false; - - let top = this.top; - let bottom = this.bottom; - let othertop = other.top; - let otherbottom = other.bottom; - - let yok = (othertop > top && othertop < bottom) || - (otherbottom > top && otherbottom < bottom) || - (othertop <= top && otherbottom >= bottom); - - if (!yok) return false; - - return true; + let x1 = Math.max(this.left, other.left); + let x2 = Math.min(this.right, other.right); + let y1 = Math.max(this.top, other.top); + let y2 = Math.min(this.bottom, other.bottom); + return x1 < x2 && y1 < y2; }, - /** - * Similar to (and most code stolen from) intersect(). A restriction - * is an intersection, but this modifies the receiving object instead - * of returning a new rect. - */ - restrictTo: function restrictTo(r2) { - let xmost1 = this.right; - let xmost2 = r2.right; + /** Restrict area of this rectangle to the intersection of both rectangles. */ + restrictTo: function restrictTo(other) { + if (this.isEmpty() || other.isEmpty()) + return this.setRect(0, 0, 0, 0); - let x = Math.max(this.left, r2.left); - - let temp = Math.min(xmost1, xmost2); - if (temp <= x) - throw "Intersection is empty but rects cannot be empty"; - - let width = temp - x; - - let ymost1 = this.bottom; - let ymost2 = r2.bottom; - let y = Math.max(this.top, r2.top); - - temp = Math.min(ymost1, ymost2); - if (temp <= y) - throw "Intersection is empty but rects cannot be empty"; - - let height = temp - y; - - return this.setRect(x, y, width, height); + let x1 = Math.max(this.left, other.left); + let x2 = Math.min(this.right, other.right); + let y1 = Math.max(this.top, other.top); + let y2 = Math.min(this.bottom, other.bottom); + // If width or height is 0, the intersection was empty. + return this.setRect(x1, y1, Math.max(0, x2 - x1), Math.max(0, y2 - y1)); }, - /** - * Similar to (and most code stolen from) union(). An extension is a - * union (in our sense of the term, not the common set-theoretic sense), - * but this modifies the receiving object instead of returning a new rect. - * Effectively, this rectangle is expanded minimally to contain all of the - * other rect. "Expanded minimally" means that the rect may shrink if - * given a strict subset rect as the argument. - */ - expandToContain: function extendTo(rect) { - let l = Math.min(this.left, rect.left); - let r = Math.max(this.right, rect.right); - let t = Math.min(this.top, rect.top); - let b = Math.max(this.bottom, rect.bottom); + /** Expand this rectangle to the union of both rectangles. */ + expandToContain: function expandToContain(other) { + if (this.isEmpty()) return this.copyFrom(other); + if (other.isEmpty()) return this; + let l = Math.min(this.left, other.left); + let r = Math.max(this.right, other.right); + let t = Math.min(this.top, other.top); + let b = Math.max(this.bottom, other.bottom); return this.setRect(l, t, r-l, b-t); }, - round: function round(scale) { - if (!scale) scale = 1; - - this.left = Math.floor(this.left * scale) / scale; - this.top = Math.floor(this.top * scale) / scale; - this.right = Math.ceil(this.right * scale) / scale; - this.bottom = Math.ceil(this.bottom * scale) / scale; - + /** + * Expands to the smallest rectangle that contains original rectangle and is bounded + * by lines with integer coefficients. + */ + expandToIntegers: function round() { + this.left = Math.floor(this.left); + this.top = Math.floor(this.top); + this.right = Math.ceil(this.right); + this.bottom = Math.ceil(this.bottom); return this; }, @@ -579,7 +334,14 @@ wsRect.prototype = { this.right *= xscl; this.top *= yscl; this.bottom *= yscl; + return this; + }, + map: function map(f) { + this.left = f.call(this, this.left); + this.top = f.call(this, this.top); + this.right = f.call(this, this.right); + this.bottom = f.call(this, this.bottom); return this; } }; diff --git a/toolkit/content/tests/browser/browser_Geometry.js b/toolkit/content/tests/browser/browser_Geometry.js new file mode 100644 index 000000000000..1c7afc37d589 --- /dev/null +++ b/toolkit/content/tests/browser/browser_Geometry.js @@ -0,0 +1,73 @@ +function test() { + ok(Rect, "Rect class exists"); + for (var fname in tests) { + tests[fname](); + } +} + +let tests = { + testGetDimensions: function() { + let r = new Rect(5, 10, 100, 50); + ok(r.left == 5, "rect has correct left value"); + ok(r.top == 10, "rect has correct top value"); + ok(r.right == 105, "rect has correct right value"); + ok(r.bottom == 60, "rect has correct bottom value"); + ok(r.width == 100, "rect has correct width value"); + ok(r.height == 50, "rect has correct height value"); + ok(r.x == 5, "rect has correct x value"); + ok(r.y == 10, "rect has correct y value"); + }, + + testIsEmpty: function() { + let r = new Rect(0, 0, 0, 10); + ok(r.isEmpty(), "rect with nonpositive width is empty"); + let r = new Rect(0, 0, 10, 0); + ok(r.isEmpty(), "rect with nonpositive height is empty"); + let r = new Rect(0, 0, 10, 10); + ok(!r.isEmpty(), "rect with positive dimensions is not empty"); + }, + + testRestrictTo: function() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.restrictTo(r2); + ok(r1.equals(new Rect(50, 50, 60, 60)), "intersection is non-empty"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(120, 120, 100, 100); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection is empty"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of rect and empty is empty"); + + let r1 = new Rect(0, 0, 0, 0); + let r2 = new Rect(0, 0, 0, 0); + r1.restrictTo(r2); + ok(r1.isEmpty(), "intersection of empty and empty is empty"); + }, + + testExpandToContain: function() { + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(50, 50, 100, 100); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 140, 140)), "correct expandToContain on intersecting rectangles"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(120, 120, 100, 100); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 210, 210)), "correct expandToContain on non-intersecting rectangles"); + + let r1 = new Rect(10, 10, 100, 100); + let r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok(r1.equals(new Rect(10, 10, 100, 100)), "expandToContain of rect and empty is rect"); + + let r1 = new Rect(10, 10, 0, 0); + let r2 = new Rect(0, 0, 0, 0); + r1.expandToContain(r2); + ok(r1.isEmpty(), "expandToContain of empty and empty is empty"); + } +}; From 2a9176c01063dba5c85f634c2066bc9239fe5fbc Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Mon, 19 Oct 2009 17:21:23 -0400 Subject: [PATCH 012/369] [mq]: findbaroff --- toolkit/content/Geometry.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 2ad4ce197de0..0bac5e7a2d26 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -95,7 +95,7 @@ let Util = { * * Any method that takes an x and y may also take a point. */ -Point = function Point(x, y) { +function Point(x, y) { this.set(x, y); } From 738beba85bd8ff2245425663485a1500fecf6e69 Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Wed, 21 Oct 2009 19:11:38 -0400 Subject: [PATCH 013/369] Bug 520872: Make zooming transition better [r=mark.finkle] --- toolkit/content/Geometry.jsm | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 0bac5e7a2d26..1c15ff7060a0 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -343,5 +343,14 @@ Rect.prototype = { this.right = f.call(this, this.right); this.bottom = f.call(this, this.bottom); return this; + }, + + /** Ensure this rectangle is inside the other, if possible. Preserves w, h. */ + translateInside: function translateInside(other) { + let offsetX = (this.left < other.left ? other.left - this.left : + (this.right > other.right ? this.right - other.right : 0)); + let offsetY = (this.top < other.top ? other.top - this.top : + (this.bottom > other.bottom ? this.bottom - other.bottom : 0)); + return this.translate(offsetX, offsetY); } }; From 2fb58358108e16fb7e9296bf92842a98b4474a45 Mon Sep 17 00:00:00 2001 From: Ben Combee Date: Fri, 23 Oct 2009 12:02:40 -0400 Subject: [PATCH 014/369] Bug 465284: Open link in new tab [r=mark.finkle] --- toolkit/content/Geometry.jsm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 1c15ff7060a0..40f6431ffce4 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -85,6 +85,28 @@ let Util = { aFunc(); } }, Ci.nsIThread.DISPATCH_NORMAL); + }, + + getHrefForElement: function getHrefForElement(target) { + // XXX: This is kind of a hack to work around a Gecko bug (see bug 266932) + // We're going to walk up the DOM looking for a parent link node. + // This shouldn't be necessary, but we're matching the existing behaviour for left click + + let link = null; + while (target) { + if (target instanceof HTMLAnchorElement || + target instanceof HTMLAreaElement || + target instanceof HTMLLinkElement) { + if (target.hasAttribute("href")) + link = target; + } + target = target.parentNode; + } + + if (link && link.hasAttribute("href")) + return link.href; + else + return null; } }; From 7ad93c7e5efa44d5032d9001fccaf4244d504498 Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Thu, 29 Oct 2009 00:37:58 -0400 Subject: [PATCH 015/369] Bug 525088: clean up getZoomForPage and consolidate code, r=blassey --- toolkit/content/Geometry.jsm | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 40f6431ffce4..80db3230fa71 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -107,8 +107,22 @@ let Util = { return link.href; else return null; - } + }, + contentIsHandheld: function contentIsHandheld(browser) { + let doctype = browser.contentDocument.doctype; + if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) + return true; + + let windowUtils = browser.contentWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); + if (handheldFriendly == "true") + return true; + + return false; + } }; From 58259a41978532a7f71a2d8f12793997cbbf5d83 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Tue, 3 Nov 2009 16:20:03 -0500 Subject: [PATCH 016/369] Bug 436083: Viewport meta tag [r=mark.finkle r=gavin.sharp] --- toolkit/content/Geometry.jsm | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 80db3230fa71..9da0a1416ce4 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -112,16 +112,27 @@ let Util = { contentIsHandheld: function contentIsHandheld(browser) { let doctype = browser.contentDocument.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) - return true; + return {reason: "doctype", result: true}; let windowUtils = browser.contentWindow .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); if (handheldFriendly == "true") - return true; + return {reason: "handheld", result: true}; - return false; + let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); + if (viewportScale > 0) { + return { + reason: "viewport", + result: true, + scale: viewportScale, + width: parseInt(windowUtils.getDocumentMetadata("viewport-width")), + height: parseInt(windowUtils.getDocumentMetadata("viewport-height")) + } + } + + return {reason: "", result: false}; } }; From 26628d072702513a12ab87df2eaa325ffb6cf4c6 Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Fri, 13 Nov 2009 14:36:39 -0500 Subject: [PATCH 017/369] Bug 523740: only show about:firstrun on real "first runs", show autocomplete popup otherwise, r=mfinkle --- toolkit/content/Geometry.jsm | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 9da0a1416ce4..5ae658f0a245 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -133,6 +133,34 @@ let Util = { } return {reason: "", result: false}; + }, + + /** + * Determines whether a home page override is needed. + * Returns: + * "new profile" if this is the first run with a new profile. + * "new version" if this is the first run with a build with a different + * Gecko milestone (i.e. right after an upgrade). + * "none" otherwise. + */ + needHomepageOverride: function needHomepageOverride() { + let savedmstone = null; + try { + savedmstone = gPrefService.getCharPref("browser.startup.homepage_override.mstone"); + } catch (e) {} + + if (savedmstone == "ignore") + return "none"; + +#expand let ourmstone = "__MOZ_APP_VERSION__"; + + if (ourmstone != savedmstone) { + gPrefService.setCharPref("browser.startup.homepage_override.mstone", ourmstone); + + return (savedmstone ? "new version" : "new profile"); + } + + return "none"; } }; From b979bdd4d347305f0c03a545826b00d78512d38a Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Thu, 19 Nov 2009 16:40:13 -0500 Subject: [PATCH 018/369] Bug 529935: add infrastructure for memoizing getters for commonly referenced elements (and add one for the browser stringbundle), r=mfinkle --- toolkit/content/Geometry.jsm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 5ae658f0a245..111ebfed125f 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -164,6 +164,20 @@ let Util = { } }; +let Elements = {}; + +[ + ["browserBundle", "bundle_browser"], +].forEach(function (elementGlobal) { + let [name, id] = elementGlobal; + Elements.__defineGetter__(name, function () { + let element = document.getElementById(id); + if (!element) + return null; + delete Elements[name]; + return Elements[name] = element; + }); +}); /** * Simple Point class. From 7e20ea53ae642939dfd9ece1a62947534c324399 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Mon, 23 Nov 2009 12:23:59 -0500 Subject: [PATCH 019/369] Bug 524479: Secondary zoom mechanism works when in Preferences [r=combee] --- toolkit/content/Geometry.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 111ebfed125f..6bf70ff263b1 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -168,6 +168,7 @@ let Elements = {}; [ ["browserBundle", "bundle_browser"], + ["contentShowing", "bcast_contentShowing"], ].forEach(function (elementGlobal) { let [name, id] = elementGlobal; Elements.__defineGetter__(name, function () { From cb9bc32ff7ad18bf26232cc37097cbc81ca370bd Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Mon, 23 Nov 2009 14:24:05 -0500 Subject: [PATCH 020/369] Bug 527384: implement device-width and device-height keywords for viewport meta tag [r=mark.finkle] --- toolkit/content/Geometry.jsm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 6bf70ff263b1..ce3e823d7769 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -122,13 +122,18 @@ let Util = { return {reason: "handheld", result: true}; let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); - if (viewportScale > 0) { + let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); + let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); + let viewportWidth = viewportWidthStr == "device-width" ? window.innerWidth : parseInt(viewportWidthStr); + let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); + + if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0) { return { reason: "viewport", result: true, scale: viewportScale, - width: parseInt(windowUtils.getDocumentMetadata("viewport-width")), - height: parseInt(windowUtils.getDocumentMetadata("viewport-height")) + width: viewportWidth, + height: viewportHeight } } From 00fe89d213d9f4d39ccdd97bfdbc9a0772a3e160 Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Tue, 24 Nov 2009 02:46:01 -0500 Subject: [PATCH 021/369] Bug 529098: Inform embedded Flash objects where to render [r=mark.finkle] --- toolkit/content/Geometry.jsm | 1 - 1 file changed, 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index ce3e823d7769..5da7c4aebdd8 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -45,7 +45,6 @@ let Ci = Components.interfaces; // let Util = { - bind: function bind(f, thisObj) { return function() { return f.apply(thisObj, arguments); From ea8da81ae4dfe881b9d37e8b70c9f8354a2df070 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Tue, 24 Nov 2009 13:23:41 -0500 Subject: [PATCH 022/369] Bug 530013: Fennec should take steps to auto-connect network connections [r=gavin.sharp] --- toolkit/content/Geometry.jsm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 5da7c4aebdd8..ff4ab6235f4c 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -165,6 +165,14 @@ let Util = { } return "none"; + }, + + // Put the Mozilla networking code into a state that will kick the auto-connection + // process. + forceOnline: function forceOnline() { +#ifdef MOZ_PLATFORM_HILDON + gIOService.offline = false; +#endif } }; From 17da625f902cbdd268b5cf695f776520133c3c29 Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Tue, 24 Nov 2009 13:59:35 -0800 Subject: [PATCH 023/369] Bug 526904 - Tabs loading in background are not pannable --- toolkit/content/Geometry.jsm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index ff4ab6235f4c..25e93a056b0f 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -324,7 +324,8 @@ Rect.prototype = { }, equals: function equals(other) { - return (other != null && + return other != null && + (this.isEmpty() && other.isEmpty() || this.top == other.top && this.left == other.left && this.bottom == other.bottom && From 82fe8b26eb41c3d18e1cc19cd23bc8d4fc0c70cb Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Fri, 4 Dec 2009 12:03:56 -0800 Subject: [PATCH 024/369] Bug 530835 - Flash: Chrome UI shows up on background of flash videos [r=gavin.sharp] --- toolkit/content/Geometry.jsm | 37 +++++++++++++++++-- .../content/tests/browser/browser_Geometry.js | 31 +++++++++++++++- 2 files changed, 64 insertions(+), 4 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 25e93a056b0f..400bac0295f5 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -176,11 +176,15 @@ let Util = { } }; +/** + * Cache of commonly used elements. + */ let Elements = {}; [ ["browserBundle", "bundle_browser"], ["contentShowing", "bcast_contentShowing"], + ["stack", "stack"], ].forEach(function (elementGlobal) { let [name, id] = elementGlobal; Elements.__defineGetter__(name, function () { @@ -314,7 +318,7 @@ Rect.prototype = { return this; }, - setBounds: function(t, l, b, r) { + setBounds: function(l, t, r, b) { this.top = t; this.left = l; this.bottom = b; @@ -452,8 +456,35 @@ Rect.prototype = { translateInside: function translateInside(other) { let offsetX = (this.left < other.left ? other.left - this.left : (this.right > other.right ? this.right - other.right : 0)); - let offsetY = (this.top < other.top ? other.top - this.top : + let offsetY = (this.top < other.top ? other.top - this.top : (this.bottom > other.bottom ? this.bottom - other.bottom : 0)); return this.translate(offsetX, offsetY); - } + }, + + /** Subtract other area from this. Returns array of rects whose union is this-other. */ + subtract: function subtract(other) { + let r = new Rect(0, 0, 0, 0); + let result = []; + other = other.intersect(this); + if (other.isEmpty()) + return [this.clone()]; + + // left strip + r.setBounds(this.left, this.top, other.left, this.bottom); + if (!r.isEmpty()) + result.push(r.clone()); + // inside strip + r.setBounds(other.left, this.top, other.right, other.top); + if (!r.isEmpty()) + result.push(r.clone()); + r.setBounds(other.left, other.bottom, other.right, this.bottom); + if (!r.isEmpty()) + result.push(r.clone()); + // right strip + r.setBounds(other.right, this.top, this.right, this.bottom); + if (!r.isEmpty()) + result.push(r.clone()); + + return result; + }, }; diff --git a/toolkit/content/tests/browser/browser_Geometry.js b/toolkit/content/tests/browser/browser_Geometry.js index 1c7afc37d589..c422e6998c7a 100644 --- a/toolkit/content/tests/browser/browser_Geometry.js +++ b/toolkit/content/tests/browser/browser_Geometry.js @@ -69,5 +69,34 @@ let tests = { let r2 = new Rect(0, 0, 0, 0); r1.expandToContain(r2); ok(r1.isEmpty(), "expandToContain of empty and empty is empty"); - } + }, + + testSubtract: function testSubtract() { + function equals(rects1, rects2) { + return rects1.length == rects2.length && rects1.every(function(r, i) { + return r.equals(rects2[i]); + }); + } + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(500, 500, 100, 100); + ok(equals(r1.subtract(r2), [r1]), "subtract area outside of region yields same region"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(-10, -10, 50, 120); + ok(equals(r1.subtract(r2), [new Rect(40, 0, 60, 100)]), "subtracting vertical bar from edge leaves one rect"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(-10, -10, 120, 50); + ok(equals(r1.subtract(r2), [new Rect(0, 40, 100, 60)]), "subtracting horizontal bar from edge leaves one rect"); + + let r1 = new Rect(0, 0, 100, 100); + let r2 = new Rect(40, 40, 20, 20); + ok(equals(r1.subtract(r2), [ + new Rect(0, 0, 40, 100), + new Rect(40, 0, 20, 40), + new Rect(40, 60, 20, 40), + new Rect(60, 0, 40, 100)]), + "subtracting rect in middle leaves union of rects"); + }, }; From ed92cc59c164d4eb226de150e9e83ef2b9f751f7 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Thu, 17 Dec 2009 17:35:48 -0500 Subject: [PATCH 025/369] Bug 500280 - clicking the preferences button takes a few seconds to switch screens [r=gavin.sharp] --- toolkit/content/Geometry.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 400bac0295f5..fc6a44a81b31 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -185,6 +185,7 @@ let Elements = {}; ["browserBundle", "bundle_browser"], ["contentShowing", "bcast_contentShowing"], ["stack", "stack"], + ["panelUI", "panel-container"], ].forEach(function (elementGlobal) { let [name, id] = elementGlobal; Elements.__defineGetter__(name, function () { From 232304800e05a8539b375b9cd731bbd2ae804dad Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Mon, 11 Jan 2010 15:43:47 -0500 Subject: [PATCH 026/369] bug 537660 - fennec takes too long to respond to zoom command r=mfinkle,stechz,froystig --- toolkit/content/Geometry.jsm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index fc6a44a81b31..18ee199e8a28 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -186,6 +186,7 @@ let Elements = {}; ["contentShowing", "bcast_contentShowing"], ["stack", "stack"], ["panelUI", "panel-container"], + ["viewBuffer", "view-buffer"], ].forEach(function (elementGlobal) { let [name, id] = elementGlobal; Elements.__defineGetter__(name, function () { @@ -456,9 +457,9 @@ Rect.prototype = { /** Ensure this rectangle is inside the other, if possible. Preserves w, h. */ translateInside: function translateInside(other) { let offsetX = (this.left < other.left ? other.left - this.left : - (this.right > other.right ? this.right - other.right : 0)); + (this.right > other.right ? other.right - this.right : 0)); let offsetY = (this.top < other.top ? other.top - this.top : - (this.bottom > other.bottom ? this.bottom - other.bottom : 0)); + (this.bottom > other.bottom ? other.bottom - this.bottom : 0)); return this.translate(offsetX, offsetY); }, From c27622cb248a46053ecebf26da6525402c5ee38f Mon Sep 17 00:00:00 2001 From: Benjamin Stover Date: Fri, 22 Jan 2010 09:52:24 -0800 Subject: [PATCH 027/369] Bug 540937 - [regression] Render plugin content in iframes [r=mfinkle] --- toolkit/content/Geometry.jsm | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 18ee199e8a28..0814b01d9845 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -167,6 +167,26 @@ let Util = { return "none"; }, + /** Recursively find all documents, including root document. */ + getAllDocuments: function getAllDocuments(doc, resultSoFar) { + resultSoFar = resultSoFar || [doc]; + if (!doc.defaultView) + return resultSoFar; + let frames = doc.defaultView.frames; + if (!frames) + return resultSoFar; + + let i; + let currentDoc; + for (i = 0; i < frames.length; i++) { + currentDoc = frames[i].document; + resultSoFar.push(currentDoc); + this.getAllDocuments(currentDoc, resultSoFar); + } + + return resultSoFar; + }, + // Put the Mozilla networking code into a state that will kick the auto-connection // process. forceOnline: function forceOnline() { From f78da5bd7c8f96db2c59d1b7308f49b3b8316dbf Mon Sep 17 00:00:00 2001 From: Doug Turner Date: Tue, 9 Feb 2010 17:00:40 -0800 Subject: [PATCH 028/369] Bug 543976 - Clean up Maemo defines. r=mfinkle --- toolkit/content/Geometry.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 0814b01d9845..093ed54d39a2 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -190,7 +190,7 @@ let Util = { // Put the Mozilla networking code into a state that will kick the auto-connection // process. forceOnline: function forceOnline() { -#ifdef MOZ_PLATFORM_HILDON +#ifdef MOZ_PLATFORM_MAEMO gIOService.offline = false; #endif } From 0ccd80e599efd878a0a3dfb0e3cf2fc25304955b Mon Sep 17 00:00:00 2001 From: "aza@70-7-150-98.pools.spcsdns.net" Date: Wed, 24 Feb 2010 01:47:55 -0800 Subject: [PATCH 029/369] First commit. --- .../base/content/tabview/modules/utils.jsm | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 browser/base/content/tabview/modules/utils.jsm diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm new file mode 100644 index 000000000000..defa51aeb3e1 --- /dev/null +++ b/browser/base/content/tabview/modules/utils.jsm @@ -0,0 +1,48 @@ +(function(){ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +// Get this in a way where we can load the page automatically +// where it doesn't need to be focused... +var homeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Ci.nsIWindowWatcher) + .activeWindow; + +var Utils = { + get activeWindow(){ + var win = Cc["@mozilla.org/embedcomp/window-watcher;1"] + .getService(Ci.nsIWindowWatcher) + .activeWindow; + + if( win != null ) return win; + else return homeWindow; + }, + + get activeTab(){ + var tabBrowser = this.activeWindow.gBrowser; + return tabBrowser.selectedTab; + }, + + + get homeTab(){ + for( var i=0; i Date: Tue, 2 Mar 2010 20:14:09 +0100 Subject: [PATCH 030/369] Bug 548219 - autodial prefs should depend on libconic instead of maemo. r=dougt --- toolkit/content/Geometry.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 093ed54d39a2..42807099dff4 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -190,7 +190,7 @@ let Util = { // Put the Mozilla networking code into a state that will kick the auto-connection // process. forceOnline: function forceOnline() { -#ifdef MOZ_PLATFORM_MAEMO +#ifdef MOZ_ENABLE_LIBCONIC gIOService.offline = false; #endif } From 89087198c60127f949190d421981c03ed07cf657 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 3 Mar 2010 17:21:44 -0800 Subject: [PATCH 031/369] + Got rid of Utils.logger and replaced it with Utils.log, Utils.trace and Utils.error + Fixed "new tabs create 2 thumbnails" bug --- .../base/content/tabview/modules/utils.jsm | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index defa51aeb3e1..437807754581 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -8,8 +8,11 @@ const Cr = Components.results; // Get this in a way where we can load the page automatically // where it doesn't need to be focused... var homeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher) - .activeWindow; + .getService(Ci.nsIWindowWatcher) + .activeWindow; + +var consoleService = Cc["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService); var Utils = { get activeWindow(){ @@ -26,7 +29,6 @@ var Utils = { return tabBrowser.selectedTab; }, - get homeTab(){ for( var i=0; i Date: Fri, 5 Mar 2010 15:14:10 -0800 Subject: [PATCH 032/369] + Major reorg: moved all visualizations into "candies" folder, moved core js files into "core" folder, and js files shared by a couple of visualizations into "shared" folder + the top level index.html now automatically grabs all the different visualizations and lists them + Added a visualization switcher that can be dropped into any visualization; drop down in the upper left of the window + The Utils routines log(), trace() and error() now take in any number of parameters. If a parameter is an object, its first level is automatically expanded + Fixed lasso in the "original" visualization --- .../base/content/tabview/modules/utils.jsm | 87 ++++++++++++++++--- 1 file changed, 77 insertions(+), 10 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 437807754581..c9280f8c909b 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -14,7 +14,11 @@ var homeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"] var consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); +var extensionManager = Cc["@mozilla.org/extensions/manager;1"] + .getService(Ci.nsIExtensionManager); + var Utils = { + // ___ Windows and Tabs get activeWindow(){ var win = Cc["@mozilla.org/embedcomp/window-watcher;1"] .getService(Ci.nsIWindowWatcher) @@ -38,19 +42,57 @@ var Utils = { return null; }, - - log: function(value) { - if(typeof(value) == 'object') - value = this.expandObject(value); - - consoleService.logStringMessage(value); + + // ___ Files + getInstallDirectory: function(id) { + var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); + return file.parent; }, - error: function(text) { + getFiles: function(dir) { + var files = []; + if(dir.isReadable() && dir.isDirectory) { + var entries = dir.directoryEntries; + while(entries.hasMoreElements()) { + var entry = entries.getNext(); + entry.QueryInterface(Ci.nsIFile); + files.push(entry); + } + } + + return files; + }, + + getVisualizationNames: function() { + var names = []; + var dir = this.getInstallDirectory('tabcandy@aza.raskin'); + dir.append('content'); + dir.append('candies'); + var files = this.getFiles(dir); + var count = files.length; + var a; + for(a = 0; a < count; a++) { + var file = files[a]; + if(file.isDirectory()) + names.push(file.leafName); + } + + return names; + }, + + // ___ Logging + log: function() { // pass as many arguments as you want, it'll print them all + var text = this.expandArgumentsForLog(arguments); + consoleService.logStringMessage(text); + }, + + error: function(text) { // pass as many arguments as you want, it'll print them all + var text = this.expandArgumentsForLog(arguments); Components.utils.reportError(text); }, - trace: function(text) { + trace: function(text) { // pass as many arguments as you want, it'll print them all + var text = this.expandArgumentsForLog(arguments); if(typeof(printStackTrace) != 'function') this.log(text + ' trace: you need to include stacktrace.js'); else { @@ -71,8 +113,33 @@ var Utils = { + ", "; } return s + '}'; - } -} + }, + + expandArgumentsForLog: function(args) { + var s = ''; + var count = args.length; + var a; + for(a = 0; a < count; a++) { + var arg = args[a]; + if(typeof(arg) == 'object') + arg = this.expandObject(arg); + + s += arg; + if(a < count - 1) + s += '; '; + } + + return s; + }, + + testLogging: function() { + this.log('beginning logging test'); + this.error('this is an error'); + this.trace('this is a trace'); + this.log(1, null, {'foo': 'hello', 'bar': 2}, 'whatever'); + this.log('ending logging test'); + } +}; window.Utils = Utils; From 27e6789de93782ab97094769c9f82c091a54f5a7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 5 Mar 2010 17:21:38 -0800 Subject: [PATCH 033/369] + Just debugging (adding logging) --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index c9280f8c909b..d88ecf365bed 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -98,7 +98,7 @@ var Utils = { else { var calls = printStackTrace(); calls.splice(0, 3); // Remove this call and the printStackTrace calls - this.log(text + ' trace:\n' + calls.join('\n')); + this.log('trace: ' + text + '\n' + calls.join('\n')); } }, From 8bb1b6e08ef90c0655d4b6af2480c524ffdb3abf Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 9 Mar 2010 14:21:08 -0800 Subject: [PATCH 034/369] + Added a "close" feature to the group menu in the stacks candy + Added a tab() feature to window.Tabs, to get a tab object from one of the tab's elements + Stacks now pile up directly on top of each other in the stacks candy (rather than being offset a little) + Added Utils.isRightClick() for weeding out right clicks in mousedown + The Lasso now only starts for left clicks, not both left or right --- browser/base/content/tabview/modules/utils.jsm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index d88ecf365bed..af0a337b6f45 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -138,7 +138,17 @@ var Utils = { this.trace('this is a trace'); this.log(1, null, {'foo': 'hello', 'bar': 2}, 'whatever'); this.log('ending logging test'); - } + }, + + // ___ Event + isRightClick: function(event) { + if(event.which) + return (event.which == 3); + else if(event.button) + return (event.button == 2); + + return false; + } }; window.Utils = Utils; From 6d293513c23a54455e0e5f715d683497256a6b7d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 11 Mar 2010 16:14:50 -0800 Subject: [PATCH 035/369] + Added getMilliseconds() to Utils + Utils.error() now identifies the error as coming from tabcandy + Added a getMilliseconds() to mirror.js (for some reason, it wasn't liking the one in Utils) to replace the use of new Date(), which doesn't work in all situations + Added a heartbeat to TabMirror, where all painting happens now. This allows us to control when and how frequently painting happens, throttling when there are a lot of items on the screen. + Additional clean-up in TabMirror --- browser/base/content/tabview/modules/utils.jsm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index af0a337b6f45..910eebb9f050 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -88,7 +88,7 @@ var Utils = { error: function(text) { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); - Components.utils.reportError(text); + Components.utils.reportError('tabcandy error: ' + text); }, trace: function(text) { // pass as many arguments as you want, it'll print them all @@ -148,6 +148,12 @@ var Utils = { return (event.button == 2); return false; + }, + + // ___ Time + getMilliseconds: function() { + var date = new Date(); + return date.getTime(); } }; From aee81cbba80e55f35d3f126210b149ce055e1af5 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 15 Mar 2010 17:15:27 -0700 Subject: [PATCH 036/369] + More major cleanup in mirror.js (especially in the painting code) + New tabs in the stacks candy now find an empty hole rather than piling up at the top + The stacks candy now has a "site" feature (upper right) that groups the tabs by domain + TabMirror now has a feature to allow pausing paint updates. This allows you to fly things around the screen without getting stutter from paint calls. The stacks candy is currently the only candy that takes advantage of this. + The stacks candy was not visually deselecting a group that had been lassoed once the menu went away; it is now. + Added assert() to Utils --- .../base/content/tabview/modules/utils.jsm | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 910eebb9f050..15fa671ea236 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -86,12 +86,12 @@ var Utils = { consoleService.logStringMessage(text); }, - error: function(text) { // pass as many arguments as you want, it'll print them all + error: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); - Components.utils.reportError('tabcandy error: ' + text); + Cu.reportError('tabcandy error: ' + text); }, - trace: function(text) { // pass as many arguments as you want, it'll print them all + trace: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); if(typeof(printStackTrace) != 'function') this.log(text + ' trace: you need to include stacktrace.js'); @@ -101,7 +101,19 @@ var Utils = { this.log('trace: ' + text + '\n' + calls.join('\n')); } }, - + + assert: function(label, condition) { + if(!condition) { + var text = 'tabcandy assert: ' + label; + if(typeof(printStackTrace) == 'function') { + var calls = printStackTrace(); + text += '\n' + calls[3]; + } + + Cu.reportError(text); + } + }, + expandObject: function(obj) { var s = obj + ' = {'; for(prop in obj) { From e1f9a47f4f7017634296ac75faf8f3b938db8bcd Mon Sep 17 00:00:00 2001 From: "aza@More-Better-Internet.local" Date: Tue, 16 Mar 2010 22:25:00 -0700 Subject: [PATCH 037/369] Adds Utils.ilog(...) for "interactive log". The first time you use the function, it embeds Firebug Lite and then uses that console. This makes it easy to introspect objects, etc. All-around good stuff. --- .../base/content/tabview/modules/utils.jsm | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index af0a337b6f45..ecb6fbb243e8 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -81,6 +81,35 @@ var Utils = { }, // ___ Logging + + // Interactive logging! + ilog: function(){ // pass as many arguments as you want, it'll print them all + // If Firebug lite already exists, print to the console. + if( window.firebug ){ + window.firebug.d.console.cmd.log.apply(null, arguments); + return; + } + + // Else, embed it. + $('') + .appendTo("head"); + + $('') + .appendTo("body"); + + var args = arguments; + + (function(){ + var fb = window.firebug; + if(fb && fb.version){ + fb.init(); + fb.win.setHeight(100); + fb.d.console.cmd.log.apply(null, args); + } + else{setTimeout(arguments.callee);} + })(); + }, + log: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); consoleService.logStringMessage(text); From 90e4fcc4e2f02785a1ba8b17672a65c68d0f8f9f Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Wed, 24 Mar 2010 14:22:18 -0400 Subject: [PATCH 038/369] Bug 551711 - Open Link in New Tab via content panel [r=vingtetun] --- toolkit/content/Geometry.jsm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 42807099dff4..663324849f64 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -108,6 +108,11 @@ let Util = { return null; }, + makeURLAbsolute: function makeURLAbsolute(base, url) { + // Note: makeURI() will throw if url is not a valid URI + return makeURI(url, null, makeURI(base)).spec; + }, + contentIsHandheld: function contentIsHandheld(browser) { let doctype = browser.contentDocument.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) From 09c99ac05868fec7e462279c3fbf4a954d6c7f3d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 24 Mar 2010 14:38:23 -0700 Subject: [PATCH 039/369] + Removed search box + It's no longer possible to accidentally launch into a tab by dragging the mouse and ending up on a tab. Same for close boxes + It's no longer possible to add a tab to a group twice (by dragging it around inside the group) + When a tab inside a group is closed, it's removed from the group. If it's the last in the group, the group goes away + Refactored mirror code to make mirror objects first-class citizens + Added a utility routine for determining if an object is a jQuery object (couldn't find one in jQuery itself) --- browser/base/content/tabview/modules/utils.jsm | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index f6653f800dce..00178f46dfe9 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -195,7 +195,13 @@ var Utils = { getMilliseconds: function() { var date = new Date(); return date.getTime(); - } + }, + + // ___ Misc + isJQuery: function(object) { + // TODO: need more robust way + return (object && typeof(object.fadeIn) == 'function' ? true : false); + } }; window.Utils = Utils; From ba6b54c5538d28953ad0282d2ec95c9496c37b0f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 24 Mar 2010 16:54:48 -0700 Subject: [PATCH 040/369] + Removed the margin from the tabs; we need to handle buffers more explicitly + You can no longer launch into a tab by right clicking it + A group's close box now works + A group's title bar now works again (silly css misunderstanding) + Added a basic Rect object to utils.js, and a getBounds routine for retrieving the bounds of a DOM element + The Utils logging routines no longer expand functions (instead just saying that it's a function) --- .../base/content/tabview/modules/utils.jsm | 59 +++++++++++++++++-- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 00178f46dfe9..6571b5b29666 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -17,6 +17,40 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] var extensionManager = Cc["@mozilla.org/extensions/manager;1"] .getService(Ci.nsIExtensionManager); +// ---------- +function Rect(left, top, width, height) { + this.left = left; + this.top = top; + this.width = width; + this.height = height; +} + +Rect.prototype = { + get right() { + return this.left + this.width; + }, + + set right(value) { + this.width = value - this.left; + }, + + get bottom() { + return this.top + this.height; + }, + + set bottom(value) { + this.height = value - this.top; + }, + + intersects: function(rect) { + return (rect.right > this.left + && rect.left < this.right + && rect.bottom > this.top + && rect.top < this.bottom); + } +}; + +// ---------- var Utils = { // ___ Windows and Tabs get activeWindow(){ @@ -147,11 +181,15 @@ var Utils = { var s = obj + ' = {'; for(prop in obj) { var value = obj[prop]; - s += prop + ": " - + (typeof(value) == 'string' ? '\'' : '') - + value - + (typeof(value) == 'string' ? '\'' : '') - + ", "; + s += prop + ': '; + if(typeof(value) == 'string') + s += '\'' + value + '\''; + else if(typeof(value) == 'function') + s += 'function'; + else + s += value; + + s += ", "; } return s + '}'; }, @@ -197,6 +235,17 @@ var Utils = { return date.getTime(); }, + // ___ Geometry + getBounds: function(el) { + var $el = $(el); + return new Rect( + parseInt($el.css('left')), + parseInt($el.css('top')), + $el.width(), + $el.height() + ); + }, + // ___ Misc isJQuery: function(object) { // TODO: need more robust way From 5f7dadf815bd9139a61b0cce17d7e4448a6369ec Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 25 Mar 2010 17:22:45 -0700 Subject: [PATCH 041/369] + moved groups.js and resizer.png to shared locations + more work on groups pushing things out of the way + some steps toward having groups and tabs share an interface + more geometry: we now have a point class, and the rect class now has center and intersect routines --- .../base/content/tabview/modules/utils.jsm | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6571b5b29666..c4b205cee4eb 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -18,14 +18,20 @@ var extensionManager = Cc["@mozilla.org/extensions/manager;1"] .getService(Ci.nsIExtensionManager); // ---------- -function Rect(left, top, width, height) { +window.Point = function(x, y) { + this.x = (typeof(x) == 'undefined' ? 0 : x); + this.y = (typeof(y) == 'undefined' ? 0 : y); +} + +// ---------- +window.Rect = function(left, top, width, height) { this.left = left; this.top = top; this.width = width; this.height = height; } -Rect.prototype = { +window.Rect.prototype = { get right() { return this.left + this.width; }, @@ -47,6 +53,17 @@ Rect.prototype = { && rect.left < this.right && rect.bottom > this.top && rect.top < this.bottom); + }, + + center: function() { + return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); + }, + + inset: function(x, y) { + this.left += x; + this.width -= x * 2; + this.top += y; + this.height -= y * 2; } }; From 0bdae470edfb925847ecf2bb3e3de3b7437c15ef Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 26 Mar 2010 11:34:09 -0700 Subject: [PATCH 042/369] + First working draft of tabs and groups getting pushed out of the way by the creation of a new group + The grid feature now removes all the tabs from their groups beforehand; it doesn't restore tab sizes properly yet + You can now rearrange tabs inside a group + More geometry: ++ The Rect constructor now accepts either coordinates or another Rect ++ Rect.inset now accepts either coordinates or a Point ++ New functions: Rect.offset and Rect.equals --- .../base/content/tabview/modules/utils.jsm | 65 +++++++++++++++---- 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index c4b205cee4eb..bc3290fa1149 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -17,37 +17,50 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] var extensionManager = Cc["@mozilla.org/extensions/manager;1"] .getService(Ci.nsIExtensionManager); -// ---------- +// ########## window.Point = function(x, y) { this.x = (typeof(x) == 'undefined' ? 0 : x); this.y = (typeof(y) == 'undefined' ? 0 : y); } -// ---------- -window.Rect = function(left, top, width, height) { - this.left = left; - this.top = top; - this.width = width; - this.height = height; +// ########## +window.Rect = function(a, top, width, height) { + if(typeof(a.left) != 'undefined' && typeof(a.top) != 'undefined' + && typeof(a.right) != 'undefined' && typeof(a.bottom) != 'undefined') { + this.left = a.left; + this.top = a.top; + this.width = a.width; + this.height = a.height; + } else { + this.left = a; + this.top = top; + this.width = width; + this.height = height; + } } window.Rect.prototype = { + // ---------- get right() { return this.left + this.width; }, + // ---------- set right(value) { this.width = value - this.left; }, + // ---------- get bottom() { return this.top + this.height; }, + // ---------- set bottom(value) { this.height = value - this.top; }, + // ---------- intersects: function(rect) { return (rect.right > this.left && rect.left < this.right @@ -55,19 +68,45 @@ window.Rect.prototype = { && rect.top < this.bottom); }, + // ---------- center: function() { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, - inset: function(x, y) { - this.left += x; - this.width -= x * 2; - this.top += y; - this.height -= y * 2; + // ---------- + inset: function(a, b) { + if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + b = a.y; + a = a.x; + } + + this.left += a; + this.width -= a * 2; + this.top += b; + this.height -= b * 2; + }, + + // ---------- + offset: function(a, b) { + if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + this.left += a.x; + this.top += a.y; + } else { + this.left += a; + this.top += b; + } + }, + + // ---------- + equals: function(a) { + return (a.left == this.left + && a.top == this.top + && a.right == this.right + && a.bottom == this.bottom); } }; -// ---------- +// ########## var Utils = { // ___ Windows and Tabs get activeWindow(){ From 039a032e58461c08dbd1039cbf7d6cd34c3957bf Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 31 Mar 2010 17:24:16 -0700 Subject: [PATCH 043/369] + font size for tab names now scales properly + tabs now return to their proper size when you pull them out of a group + you can now nest groups inside of other groups (though all of the funky repercussions haven't been sorted out yet) --- .../base/content/tabview/modules/utils.jsm | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index bc3290fa1149..a2cf4c3b3202 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -106,6 +106,45 @@ window.Rect.prototype = { } }; +// ########## +// TODO generalize for any number of events +window.Subscribable = function() { + this.onCloseSubscribers = []; +}; + +window.Subscribable.prototype = { + // ---------- + addOnClose: function(referenceElement, callback) { + var existing = jQuery.grep(this.onCloseSubscribers, function(element) { + return element.referenceElement == referenceElement; + }); + + if(existing.size) { + Utils.assert('should only ever be one', existing.size == 1); + existing[0].callback = callback; + } else { + this.onCloseSubscribers.push({ + referenceElement: referenceElement, + callback: callback + }); + } + }, + + // ---------- + removeOnClose: function(referenceElement) { + this.onCloseSubscribers = jQuery.grep(this.onCloseSubscribers, function(element) { + return element.referenceElement == referenceElement; + }, true); + }, + + // ---------- + _sendOnClose: function() { + jQuery.each(this.onCloseSubscribers, function(index, object) { + object.callback(this); + }); + } +}; + // ########## var Utils = { // ___ Windows and Tabs From 971f4d720839cb844014729551f48dabddede107 Mon Sep 17 00:00:00 2001 From: Dan Walkowski Date: Thu, 1 Apr 2010 17:19:21 -0700 Subject: [PATCH 044/369] added drop feedback when creating a new group, which looks like a group, and then animates into the final arranged shape. --- browser/base/content/tabview/modules/utils.jsm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index bc3290fa1149..378732614083 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -23,7 +23,7 @@ window.Point = function(x, y) { this.y = (typeof(y) == 'undefined' ? 0 : y); } -// ########## +// ########## perhaps 'a' should really be called 'rectOrLeft' window.Rect = function(a, top, width, height) { if(typeof(a.left) != 'undefined' && typeof(a.top) != 'undefined' && typeof(a.right) != 'undefined' && typeof(a.bottom) != 'undefined') { @@ -103,6 +103,16 @@ window.Rect.prototype = { && a.top == this.top && a.right == this.right && a.bottom == this.bottom); + }, + + union: function(a){ + var newLeft = Math.min(a.left, this.left); + var newTop = Math.min(a.top, this.top); + var newWidth = Math.max(a.right, this.right) - newLeft; + var newHeight = Math.max(a.bottom, this.bottom) - newTop; + var newRect = new Rect(newLeft, newTop, newWidth, newHeight); + + return newRect; } }; From 214896b871eabae141df1c7fef713932fbdf62df Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 1 Apr 2010 17:20:59 -0700 Subject: [PATCH 045/369] + We're now keeping track of item locations independently of their divs + Renamed Group._container to .container (which it now inherits from Item) + Added Item.debug flag which turns on rectangles for debugging item locations + further unified dragging tabs vs dragging groups + Further unified the Group and TabItem objects under Item --- browser/base/content/tabview/modules/utils.jsm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index a2cf4c3b3202..3a3f9ede908f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -103,6 +103,14 @@ window.Rect.prototype = { && a.top == this.top && a.right == this.right && a.bottom == this.bottom); + }, + + // ---------- + copy: function(a) { + this.left = a.left; + this.top = a.top; + this.width = a.width; + this.height = a.height; } }; From 2bc75c557b3e0093f2fcff13c139f4d0942df0e8 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Fri, 2 Apr 2010 20:00:15 -0400 Subject: [PATCH 046/369] Bug 556618 - portrait mode - viewport device-width pages do not resize when changing orientation [r=blassey] --- toolkit/content/Geometry.jsm | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 663324849f64..5d43932947eb 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -125,19 +125,29 @@ let Util = { if (handheldFriendly == "true") return {reason: "handheld", result: true}; + // viewport details found here + // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html + // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html + let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); + + // If initial scale is 1.0 and width is not set, assume width=device-width + if (viewportScale == 1.0 && !viewportWidthStr) + viewportWidthStr = "device-width"; + let viewportWidth = viewportWidthStr == "device-width" ? window.innerWidth : parseInt(viewportWidthStr); let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); - + if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0) { return { reason: "viewport", result: true, scale: viewportScale, width: viewportWidth, - height: viewportHeight + height: viewportHeight, + autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height" } } From 3313e3210f8bf83262558535b2748912c5e821b4 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 2 Apr 2010 17:33:06 -0700 Subject: [PATCH 047/369] + Groups lose their resize handle when they get dropped into a group (and get it back when they get pulled out) + Now properly handling z order for dragging and nesting groups + If a group is the last item inside a group, closing it now closes the outer group as well + Fixed bug causing the closing of unrelated tabs to destroy groups --- browser/base/content/tabview/modules/utils.jsm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 0e3469940d9a..a5190ce6970f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -127,12 +127,15 @@ window.Rect.prototype = { // ########## // TODO generalize for any number of events window.Subscribable = function() { - this.onCloseSubscribers = []; + this.onCloseSubscribers = null; }; window.Subscribable.prototype = { // ---------- addOnClose: function(referenceElement, callback) { + if(!this.onCloseSubscribers) + this.onCloseSubscribers = []; + var existing = jQuery.grep(this.onCloseSubscribers, function(element) { return element.referenceElement == referenceElement; }); @@ -150,6 +153,9 @@ window.Subscribable.prototype = { // ---------- removeOnClose: function(referenceElement) { + if(!this.onCloseSubscribers) + return; + this.onCloseSubscribers = jQuery.grep(this.onCloseSubscribers, function(element) { return element.referenceElement == referenceElement; }, true); @@ -157,6 +163,9 @@ window.Subscribable.prototype = { // ---------- _sendOnClose: function() { + if(!this.onCloseSubscribers) + return; + jQuery.each(this.onCloseSubscribers, function(index, object) { object.callback(this); }); From deeb523419f178900d1e374f36b98c58dde073cc Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 8 Apr 2010 16:36:11 -0700 Subject: [PATCH 048/369] + Put Natural Docs (http://www.naturaldocs.org/) -friendly comments in items.js + Added a doc folder that contains generated documentation + Now pointing to the doc folder from the Tab Candy home page + The Tab Candy home page is now accessible from the Candy drop-down --- browser/base/content/tabview/modules/utils.jsm | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index a5190ce6970f..86b201da6011 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -23,8 +23,15 @@ window.Point = function(x, y) { this.y = (typeof(y) == 'undefined' ? 0 : y); } -// ########## perhaps 'a' should really be called 'rectOrLeft' +// ########## +// Class: Rect +// A simple rectangle. +// +// Constructor: Rect +// If a is a Rect, creates a copy of it. Otherwise, expects a to be left, +// and creates a Rect with it along with top, width, and height. window.Rect = function(a, top, width, height) { + // Note: perhaps 'a' should really be called 'rectOrLeft' if(typeof(a.left) != 'undefined' && typeof(a.top) != 'undefined' && typeof(a.right) != 'undefined' && typeof(a.bottom) != 'undefined') { this.left = a.left; From 430ff0b3509acf18dc8a290c1c53035dabbe4e3e Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 9 Apr 2010 01:08:53 -0400 Subject: [PATCH 049/369] Bug 557238 - Hide urlbar contents on about:home [r=mfinkle] --- toolkit/content/Geometry.jsm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 5d43932947eb..cc8d79e70342 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -182,6 +182,11 @@ let Util = { return "none"; }, + /** Don't display anything in the urlbar for these special URIs. */ + isURLEmpty: function isURLEmpty(aURL) { + return (!aURL || aURL == "about:blank" || aURL == "about:home"); + }, + /** Recursively find all documents, including root document. */ getAllDocuments: function getAllDocuments(doc, resultSoFar) { resultSoFar = resultSoFar || [doc]; From 4d1893df9edf680efb63ffdfa6809b919a240a22 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 12 Apr 2010 17:20:35 -0700 Subject: [PATCH 050/369] + Removed unnecessary margin and padding from tabs and groups (was messing up layout) + Now compensating for padding in tabs when laying them out + Tab titles are now a single line, cut at the width of the tab (but no ellipsis yet) + Groups now have the move cursor + Tab resizing now works, and is disabled when a tab is in a group + We now have a "new tabs" group that new tabs go into (created as needed) + New layout algorithm for tabs inside a group. It's not as space efficient as the last one, but the unused space is at the bottom rather than the right side, and in general I think it behaves more like people expect. + Groups now have a minimum width when resizing + If you drag the second to last tab out of a group and don't return it to that group, the group now dissolves. + When a tab gets below a certain size, its thumbnail and close box are hidden + When a tab's title gets below a certain size, it is hidden. --- browser/base/content/tabview/modules/utils.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 86b201da6011..06c52ab8a5b5 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -147,8 +147,8 @@ window.Subscribable.prototype = { return element.referenceElement == referenceElement; }); - if(existing.size) { - Utils.assert('should only ever be one', existing.size == 1); + if(existing.length) { + Utils.assert('should only ever be one', existing.length == 1); existing[0].callback = callback; } else { this.onCloseSubscribers.push({ From 99c2ca9cb10c4bee0d139c3dc512da35826db77b Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Mon, 12 Apr 2010 23:34:54 -0400 Subject: [PATCH 051/369] Bug 556619 - portrait mode - about:config rows shorter than a touchheight [r=vingtetun] --- toolkit/content/Geometry.jsm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index cc8d79e70342..8eda867618cd 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -113,17 +113,20 @@ let Util = { return makeURI(url, null, makeURI(base)).spec; }, - contentIsHandheld: function contentIsHandheld(browser) { + getViewportMetadata: function getViewportMetadata(browser) { let doctype = browser.contentDocument.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) - return {reason: "doctype", result: true}; + return { reason: "doctype", result: true, scale: 1.0 }; let windowUtils = browser.contentWindow .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); if (handheldFriendly == "true") - return {reason: "handheld", result: true}; + return { reason: "handheld", result: true, scale: 1.0 }; + + if (browser.contentDocument instanceof XULDocument) + return { reason: "chrome", result: true, scale: 1.0, autoSize: true, allowZoom: false }; // viewport details found here // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html @@ -147,11 +150,12 @@ let Util = { scale: viewportScale, width: viewportWidth, height: viewportHeight, - autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height" + autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height", + allowZoom: windowUtils.getDocumentMetadata("viewport-user-scalable") != "no" } } - return {reason: "", result: false}; + return { reason: "", result: false, allowZoom: true }; }, /** From ffd33c59ba40cde39a9879c89fc353c032e07e0e Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 16 Apr 2010 15:09:23 -0700 Subject: [PATCH 052/369] + The tab bar toggle now has a down arrow when clicking on it will pull the tab bar down and an up arrow when it will push the tab bar up (was reversed) + The tab bar toggle and decorative strip now reside above the tab candy content in Z + Group titles now have prompt text/icon + Additional documentation (especially Mirror) + Fixed the dev mode features (were broken by the tab bar toggle changes) + The Firefox nav bar now correctly shows again when you create and go to a new tab --- browser/base/content/tabview/modules/utils.jsm | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 06c52ab8a5b5..016ba38cfcec 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -1,3 +1,4 @@ +// Title: utils.js (function(){ const Cc = Components.classes; @@ -132,6 +133,9 @@ window.Rect.prototype = { }; // ########## +// Class: Subscribable +// A mix-in for allowing objects to collect subscribers for custom events. +// Currently supports only onClose. // TODO generalize for any number of events window.Subscribable = function() { this.onCloseSubscribers = null; @@ -139,6 +143,9 @@ window.Subscribable = function() { window.Subscribable.prototype = { // ---------- + // Function: addOnClose + // The given callback will be called when the Subscribable fires its onClose. + // The referenceElement is used to facilitate removal if necessary. addOnClose: function(referenceElement, callback) { if(!this.onCloseSubscribers) this.onCloseSubscribers = []; @@ -159,6 +166,8 @@ window.Subscribable.prototype = { }, // ---------- + // Function: removeOnClose + // Removes the callback associated with referenceElement for onClose notification. removeOnClose: function(referenceElement) { if(!this.onCloseSubscribers) return; @@ -169,6 +178,8 @@ window.Subscribable.prototype = { }, // ---------- + // Function: _sendOnClose + // Internal routine. Used by the Subscribable to fire onClose events. _sendOnClose: function() { if(!this.onCloseSubscribers) return; From da0140727451fcb80076a4310913d4599dd4a264 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 16 Apr 2010 17:21:03 -0700 Subject: [PATCH 053/369] =?UTF-8?q?+=20The=20"new=20tab"=20group=20no=20lo?= =?UTF-8?q?nger=20gets=20pushed=20away=20by=20the=20other=20items=20+=20St?= =?UTF-8?q?arted=20work=20on=20Squish!=E2=84=A2=20code,=20not=20ready=20ye?= =?UTF-8?q?t,=20so=20disabled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/base/content/tabview/modules/utils.jsm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 016ba38cfcec..899ec7204d93 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -24,6 +24,13 @@ window.Point = function(x, y) { this.y = (typeof(y) == 'undefined' ? 0 : y); } +window.Point.prototype = { + // ---------- + plus: function(point) { + return new Point(this.x + point.x, this.y + point.y); + } +}; + // ########## // Class: Rect // A simple rectangle. From c402d1ebd77f838ce3746e521639539f63c1da03 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 22 Apr 2010 14:26:57 -0700 Subject: [PATCH 054/369] =?UTF-8?q?+=20Updated=20Utils=20logging=20to=20de?= =?UTF-8?q?al=20gracefully=20with=20object=20properties=20you=20can't=20ac?= =?UTF-8?q?cess=20(for=20permissions=20reasons,=20for=20instance)=20+=20Ad?= =?UTF-8?q?ded=20storage.js=E2=80=A6=20starting=20serialization=20+=20Disa?= =?UTF-8?q?bled=20autoscroll=20on=20drag;=20the=20window=20should=20be=20l?= =?UTF-8?q?ocked=20down=20now=20+=20Fixed=20Aza's=20"cute=20little=20bug";?= =?UTF-8?q?=20small=20tabs=20used=20to=20scoot=20up=20and=20left?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/base/content/tabview/modules/utils.jsm | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 899ec7204d93..3eb566f43f9c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -226,7 +226,7 @@ var Utils = { // ___ Files getInstallDirectory: function(id) { - var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); + var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); return file.parent; }, @@ -327,7 +327,13 @@ var Utils = { expandObject: function(obj) { var s = obj + ' = {'; for(prop in obj) { - var value = obj[prop]; + var value; + try { + value = obj[prop]; + } catch(e) { + value = '[!!error retrieving property]'; + } + s += prop + ': '; if(typeof(value) == 'string') s += '\'' + value + '\''; From 0c2b12a9433eaba6787f0a36f3eb024f78104b50 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 23 Apr 2010 17:11:06 -0700 Subject: [PATCH 055/369] + Fixed an unfortunate bug in Rect copying + Now reconstituting groups from saved file + Now saving and reconstituting tabs + Fixed start up problem when there's only one tab --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 3eb566f43f9c..d6537f8d9a73 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -41,7 +41,7 @@ window.Point.prototype = { window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' if(typeof(a.left) != 'undefined' && typeof(a.top) != 'undefined' - && typeof(a.right) != 'undefined' && typeof(a.bottom) != 'undefined') { + && typeof(a.width) != 'undefined' && typeof(a.height) != 'undefined') { this.left = a.left; this.top = a.top; this.width = a.width; From 8a4ec46e48c467ff3d059e1baaa9b3fdc10982b1 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 26 Apr 2010 16:48:46 -0700 Subject: [PATCH 056/369] + Point's constructer now accepts a point to copy + Added the close box back to the "new tab" group; it doesn't close the group, but it closes all the tabs. Seems like a useful feature, though maybe it shouldn't look like all the other close boxes + Groups no longer auto-close when all items are removed, if they're named + You can now close an empty group + Fixed bug with dragging tabs out of a group + Resizing the window now works, even if you've closed tab candy + When unsquishing, groups aren't forced into tab aspect ratio --- browser/base/content/tabview/modules/utils.jsm | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index d6537f8d9a73..6a62dedb4b73 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -19,9 +19,21 @@ var extensionManager = Cc["@mozilla.org/extensions/manager;1"] .getService(Ci.nsIExtensionManager); // ########## -window.Point = function(x, y) { - this.x = (typeof(x) == 'undefined' ? 0 : x); - this.y = (typeof(y) == 'undefined' ? 0 : y); +// Class: Point +// A simple point. +// +// Constructor: Point +// If a is a Point, creates a copy of it. Otherwise, expects a to be x, +// and creates a Point with it along with y. If either a or y are omitted, +// 0 is used in their place. +window.Point = function(a, y) { + if(a && typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + this.x = a.x; + this.y = a.y; + } else { + this.x = (typeof(a) == 'undefined' ? 0 : a); + this.y = (typeof(y) == 'undefined' ? 0 : y); + } } window.Point.prototype = { From ba2a6ea40147aca521d43c25368bf94e8ef1fde4 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 27 Apr 2010 14:22:36 -0400 Subject: [PATCH 057/369] Bug 561413 - Allow meta viewport scale settings to override viewport width [r=mfinkle] --- toolkit/content/Geometry.jsm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 8eda867618cd..07c6cba51d7f 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -142,6 +142,11 @@ let Util = { let viewportWidth = viewportWidthStr == "device-width" ? window.innerWidth : parseInt(viewportWidthStr); let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); + + // If (scale * width) < device-width, increase the width (bug 561413). + let maxInitialScale = viewportScale; + if (maxInitialScale && viewportWidth) + viewportWidth = Math.max(viewportWidth, window.innerWidth / maxInitialScale); if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0) { return { From 5207c4e3cd63cbc24b86ec5201786e9489f24225 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 27 Apr 2010 14:22:38 -0400 Subject: [PATCH 058/369] Bug 561497 - Respect minimum-scale and maximum-scale viewport properties [r=mfinkle] --- toolkit/content/Geometry.jsm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 07c6cba51d7f..0db6a12e1396 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -23,6 +23,7 @@ * Contributor(s): * Roy Frostig * Ben Combee + * Matt Brubeck * * 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 @@ -133,6 +134,8 @@ let Util = { // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); + let viewportMinScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale")); + let viewportMaxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale")); let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); @@ -144,20 +147,22 @@ let Util = { let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); // If (scale * width) < device-width, increase the width (bug 561413). - let maxInitialScale = viewportScale; + let maxInitialScale = viewportScale || viewportMaxScale; if (maxInitialScale && viewportWidth) viewportWidth = Math.max(viewportWidth, window.innerWidth / maxInitialScale); - if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0) { + if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0 || viewportMinScale > 0 || viewportMaxScale > 0) { return { reason: "viewport", result: true, scale: viewportScale, + minScale: viewportMinScale, + maxScale: viewportMaxScale, width: viewportWidth, height: viewportHeight, autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height", allowZoom: windowUtils.getDocumentMetadata("viewport-user-scalable") != "no" - } + }; } return { reason: "", result: false, allowZoom: true }; From d38c5a2d65d093b34be3b643f1dc0e68aa1e0d75 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Wed, 28 Apr 2010 13:17:43 -0700 Subject: [PATCH 059/369] + Added Math.tanh by way of utils + Added a minimum and maximum scale --- browser/base/content/tabview/modules/utils.jsm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6a62dedb4b73..38d27a48dd54 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -420,4 +420,9 @@ var Utils = { window.Utils = Utils; +window.Math.tanh = function tanh(x){ + var e = Math.exp(x); + return (e - 1/e) / (e + 1/e); +} + })(); From 7945514078072beab13d0693773f3891c6d5fdbd Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 29 Apr 2010 15:29:01 -0400 Subject: [PATCH 060/369] Bug 561870 - Enforce min/max limits on viewport values [r=mfinkle] --- toolkit/content/Geometry.jsm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 0db6a12e1396..85a0367c8a41 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -41,6 +41,14 @@ let Ci = Components.interfaces; +// Blindly copied from Safari documentation for now. +const kViewportMinScale = 0; +const kViewportMaxScale = 10; +const kViewportMinWidth = 200; +const kViewportMaxWidth = 10000; +const kViewportMinHeight = 223; +const kViewportMaxHeight = 10000; + // ----------------------------------------------------------- // General util/convenience tools // @@ -133,18 +141,27 @@ let Util = { // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html + // Note: These values will be NaN if parseFloat or parseInt doesn't find a number. + // Remember that NaN is contagious: Math.max(1, NaN) == Math.min(1, NaN) == NaN. let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); let viewportMinScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale")); let viewportMaxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale")); let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); + viewportScale = Util.clamp(viewportScale, kViewportMinScale, kViewportMaxScale); + viewportMinScale = Util.clamp(viewportMinScale, kViewportMinScale, kViewportMaxScale); + viewportMaxScale = Util.clamp(viewportMaxScale, kViewportMinScale, kViewportMaxScale); + // If initial scale is 1.0 and width is not set, assume width=device-width if (viewportScale == 1.0 && !viewportWidthStr) viewportWidthStr = "device-width"; let viewportWidth = viewportWidthStr == "device-width" ? window.innerWidth : parseInt(viewportWidthStr); let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); + + viewportWidth = Util.clamp(viewportWidth, kViewportMinWidth, kViewportMaxWidth); + viewportHeight = Util.clamp(viewportHeight, kViewportMinHeight, kViewportMaxHeight); // If (scale * width) < device-width, increase the width (bug 561413). let maxInitialScale = viewportScale || viewportMaxScale; @@ -168,6 +185,10 @@ let Util = { return { reason: "", result: false, allowZoom: true }; }, + clamp: function(num, min, max) { + return Math.max(min, Math.min(max, num)); + }, + /** * Determines whether a home page override is needed. * Returns: From 0925c85c12fe66318f5e4d58de6c888f19d6cdbc Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 30 Apr 2010 15:30:28 -0400 Subject: [PATCH 061/369] Bug 562849 - Reverse meanings of N900 volume buttons in portrait mode [r=mfinkle] --- toolkit/content/Geometry.jsm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 85a0367c8a41..d860efacf685 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -248,6 +248,10 @@ let Util = { #ifdef MOZ_ENABLE_LIBCONIC gIOService.offline = false; #endif + }, + + isPortrait: function isPortrait() { + return (window.innerWidth < 500); } }; From 6e0360c552fb44b66c450d3d1b58d6d5b4e956be Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 30 Apr 2010 17:24:03 -0700 Subject: [PATCH 062/369] + Fixed a bug making it so you couldn't close tabs that were just .mov + Commented out the _render routine in toolbar-button.js, as it was throwing exceptions and didn't seem to work anyway + Added some asserts to Item and cleaned up its documentation + Made Utils.assert more robust + Made tabs prettier when stacked + Commented out js lint, which doesn't seem to work in this context + Fixed it so you can't resize locked groups + Made it so the "new tabs" group is no longer locked (for the time being at least) + Tabs no longer change order when you drag their groups around + The tray now goes away more beautifully + You now have to drag at least 100 pixels and at least 500 milliseconds before a tab is pulled out its tray + No longer reserving space at the bottom of the screen for the "new tabs" group + Tab titles now go away when stacked and come back when not (there was a problem with them coming back) + No longer explicitly positioning the "new tabs" group --- .../base/content/tabview/modules/utils.jsm | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 38d27a48dd54..4a213a7170c6 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -37,6 +37,13 @@ window.Point = function(a, y) { } window.Point.prototype = { + // ---------- + distance: function(point) { + var ax = Math.abs(this.x - point.x); + var ay = Math.abs(this.y - point.y); + return Math.sqrt((ax * ax) + (ay * ay)); + }, + // ---------- plus: function(point) { return new Point(this.x + point.x, this.y + point.y); @@ -326,7 +333,12 @@ var Utils = { assert: function(label, condition) { if(!condition) { - var text = 'tabcandy assert: ' + label; + var text; + if(typeof(label) == 'undefined') + text = 'badly formed assert'; + else + text = 'tabcandy assert: ' + label; + if(typeof(printStackTrace) == 'function') { var calls = printStackTrace(); text += '\n' + calls[3]; @@ -415,6 +427,11 @@ var Utils = { isJQuery: function(object) { // TODO: need more robust way return (object && typeof(object.fadeIn) == 'function' ? true : false); + }, + + isDOMElement: function(object) { + // TODO: need more robust way + return (object && typeof(object.tagName) != 'undefined' ? true : false); } }; From 3ecb8494304e160a9988acb9c6b7ca9f830f869b Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Fri, 30 Apr 2010 23:50:43 -0700 Subject: [PATCH 063/369] + We use to show tabcandy whenever we closed any tab. We now only focus tabcandy when the last tab in a group is closed. + TODO: Ideally, we should have an animation of the last tab be zoomed out into the tabcandy interface, and then that tab fading away along with the group (if the group is unnamed). --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 4a213a7170c6..649871bbba3c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -242,7 +242,7 @@ var Utils = { return null; }, - + // ___ Files getInstallDirectory: function(id) { var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); From ba3d5a4442d3a61c7c23e790f5c88a0a6141dffe Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 3 May 2010 15:32:11 -0700 Subject: [PATCH 064/369] + Tabs dropped on stacks now appear on top + Misc fixes + We now show chrome tabs (but not the tab candy tab) + When the browser first starts up, it takes it a while for the tabs to all get their correct URLs; we now properly deal with this when reconnecting them to their groups + Added a "dev" menu in the upper left for various debugging features. Nothing much there so far --- .../base/content/tabview/modules/utils.jsm | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 649871bbba3c..fe165deb04c5 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -164,10 +164,61 @@ window.Rect.prototype = { // Currently supports only onClose. // TODO generalize for any number of events window.Subscribable = function() { + this.subscribers = {}; this.onCloseSubscribers = null; }; window.Subscribable.prototype = { + // ---------- + // Function: addSubscriber + // The given callback will be called when the Subscribable fires the given event. + // The refObject is used to facilitate removal if necessary. + addSubscriber: function(refObject, eventName, callback) { + if(!this.subscribers[eventName]) + this.subscribers[eventName] = []; + + var subs = this.subscribers[eventName]; + var existing = jQuery.grep(subs, function(element) { + return element.refObject == refObject; + }); + + if(existing.length) { + Utils.assert('should only ever be one', existing.length == 1); + existing[0].callback = callback; + } else { + subs.push({ + refObject: refObject, + callback: callback + }); + } + }, + + // ---------- + // Function: removeSubscriber + // Removes the callback associated with refObject for the given event. + removeSubscriber: function(refObject, eventName) { + if(!this.subscribers[eventName]) + return; + + this.subscribers[eventName] = jQuery.grep(this.subscribers[eventName], function(element) { + return element.refObject == refObject; + }, true); + }, + + // ---------- + // Function: _sendToSubscribers + // Internal routine. Used by the Subscribable to fire events. + _sendToSubscribers: function(eventName, eventInfo) { + if(!this.subscribers[eventName]) + return; + + var self = this; + var subsCopy = $.merge([], this.subscribers[eventName]); + $.each(subsCopy, function(index, object) { + object.callback(self, eventInfo); + }); + }, + // ---------- // Function: addOnClose // The given callback will be called when the Subscribable fires its onClose. From 87c9c63a2beb135722eb4815618d639901a06ef4 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 5 May 2010 15:51:55 -0400 Subject: [PATCH 065/369] Bug 561455 - Change the ratio of CSS/viewport px to physical pixels on high density screens [r=mfinkle r=stechz] --- toolkit/content/Geometry.jsm | 47 ++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index d860efacf685..fd7ff0bc3d68 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -123,19 +123,25 @@ let Util = { }, getViewportMetadata: function getViewportMetadata(browser) { + let dpiScale = gPrefService.getIntPref("zoom.dpiScale") / 100; + + // Device size in CSS pixels: + let deviceWidth = window.innerWidth / dpiScale; + let deviceHeight = window.innerHeight / dpiScale; + let doctype = browser.contentDocument.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) - return { reason: "doctype", result: true, scale: 1.0 }; + return { reason: "doctype", result: true, defaultZoom: dpiScale, width: deviceWidth }; let windowUtils = browser.contentWindow .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); if (handheldFriendly == "true") - return { reason: "handheld", result: true, scale: 1.0 }; + return { reason: "handheld", defaultZoom: dpiScale, width: deviceWidth }; if (browser.contentDocument instanceof XULDocument) - return { reason: "chrome", result: true, scale: 1.0, autoSize: true, allowZoom: false }; + return { reason: "chrome", defaultZoom: 1.0, autoSize: true, allowZoom: false }; // viewport details found here // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html @@ -157,24 +163,33 @@ let Util = { if (viewportScale == 1.0 && !viewportWidthStr) viewportWidthStr = "device-width"; - let viewportWidth = viewportWidthStr == "device-width" ? window.innerWidth : parseInt(viewportWidthStr); - let viewportHeight = viewportHeightStr == "device-height" ? window.innerHeight : parseInt(viewportHeightStr); + let viewportWidth = viewportWidthStr == "device-width" ? deviceWidth + : Util.clamp(parseInt(viewportWidthStr), kViewportMinWidth, kViewportMaxWidth); + let viewportHeight = viewportHeightStr == "device-height" ? deviceHeight + : Util.clamp(parseInt(viewportHeightStr), kViewportMinHeight, kViewportMaxHeight); + + // Zoom level is the final (device pixel : CSS pixel) ratio for content. + // Since web content specifies scale as (reference pixel : CSS pixel) ratio, + // multiply the requested scale by a constant (device pixel : reference pixel) + // factor to account for high DPI devices. + // + // See bug 561445 or any of the examples of chrome/tests/browser_viewport_XX.html + // for more information and examples. + let defaultZoom = viewportScale * dpiScale; + let minZoom = viewportMinScale * dpiScale; + let maxZoom = viewportMaxScale * dpiScale; - viewportWidth = Util.clamp(viewportWidth, kViewportMinWidth, kViewportMaxWidth); - viewportHeight = Util.clamp(viewportHeight, kViewportMinHeight, kViewportMaxHeight); - // If (scale * width) < device-width, increase the width (bug 561413). - let maxInitialScale = viewportScale || viewportMaxScale; - if (maxInitialScale && viewportWidth) - viewportWidth = Math.max(viewportWidth, window.innerWidth / maxInitialScale); + let maxInitialZoom = defaultZoom || maxZoom; + if (maxInitialZoom && viewportWidth) + viewportWidth = Math.max(viewportWidth, window.innerWidth / maxInitialZoom); if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0 || viewportMinScale > 0 || viewportMaxScale > 0) { return { reason: "viewport", - result: true, - scale: viewportScale, - minScale: viewportMinScale, - maxScale: viewportMaxScale, + defaultZoom: defaultZoom, + minZoom: minZoom, + maxZoom: maxZoom, width: viewportWidth, height: viewportHeight, autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height", @@ -182,7 +197,7 @@ let Util = { }; } - return { reason: "", result: false, allowZoom: true }; + return { reason: null, allowZoom: true }; }, clamp: function(num, min, max) { From 70803a902306314cd16ea0234e8c86754e843fb2 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 5 May 2010 15:51:58 -0400 Subject: [PATCH 066/369] Bug 562773 - Viewport is not resized on window resize / orientation change [r=mfinkle r=stechz] --- toolkit/content/Geometry.jsm | 57 +++++++++++++----------------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index fd7ff0bc3d68..602f1bb07cd7 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -125,23 +125,19 @@ let Util = { getViewportMetadata: function getViewportMetadata(browser) { let dpiScale = gPrefService.getIntPref("zoom.dpiScale") / 100; - // Device size in CSS pixels: - let deviceWidth = window.innerWidth / dpiScale; - let deviceHeight = window.innerHeight / dpiScale; - let doctype = browser.contentDocument.doctype; if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) - return { reason: "doctype", result: true, defaultZoom: dpiScale, width: deviceWidth }; + return { defaultZoom: dpiScale, autoSize: true }; let windowUtils = browser.contentWindow .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); if (handheldFriendly == "true") - return { reason: "handheld", defaultZoom: dpiScale, width: deviceWidth }; + return { defaultZoom: dpiScale, autoSize: true }; if (browser.contentDocument instanceof XULDocument) - return { reason: "chrome", defaultZoom: 1.0, autoSize: true, allowZoom: false }; + return { defaultZoom: 1.0, autoSize: true, allowZoom: false }; // viewport details found here // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html @@ -155,18 +151,17 @@ let Util = { let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); - viewportScale = Util.clamp(viewportScale, kViewportMinScale, kViewportMaxScale); + viewportScale = Util.clamp(viewportScale, kViewportMinScale, kViewportMaxScale); viewportMinScale = Util.clamp(viewportMinScale, kViewportMinScale, kViewportMaxScale); viewportMaxScale = Util.clamp(viewportMaxScale, kViewportMinScale, kViewportMaxScale); // If initial scale is 1.0 and width is not set, assume width=device-width - if (viewportScale == 1.0 && !viewportWidthStr) - viewportWidthStr = "device-width"; + let autoSize = (viewportWidthStr == "device-width" || + viewportHeightStr == "device-height" || + (viewportScale == 1.0 && !viewportWidthStr)); - let viewportWidth = viewportWidthStr == "device-width" ? deviceWidth - : Util.clamp(parseInt(viewportWidthStr), kViewportMinWidth, kViewportMaxWidth); - let viewportHeight = viewportHeightStr == "device-height" ? deviceHeight - : Util.clamp(parseInt(viewportHeightStr), kViewportMinHeight, kViewportMaxHeight); + let viewportWidth = Util.clamp(parseInt(viewportWidthStr), kViewportMinWidth, kViewportMaxWidth); + let viewportHeight = Util.clamp(parseInt(viewportHeightStr), kViewportMinHeight, kViewportMaxHeight); // Zoom level is the final (device pixel : CSS pixel) ratio for content. // Since web content specifies scale as (reference pixel : CSS pixel) ratio, @@ -175,29 +170,19 @@ let Util = { // // See bug 561445 or any of the examples of chrome/tests/browser_viewport_XX.html // for more information and examples. - let defaultZoom = viewportScale * dpiScale; - let minZoom = viewportMinScale * dpiScale; - let maxZoom = viewportMaxScale * dpiScale; + let defaultZoom = viewportScale * dpiScale; + let minZoom = viewportMinScale * dpiScale; + let maxZoom = viewportMaxScale * dpiScale; - // If (scale * width) < device-width, increase the width (bug 561413). - let maxInitialZoom = defaultZoom || maxZoom; - if (maxInitialZoom && viewportWidth) - viewportWidth = Math.max(viewportWidth, window.innerWidth / maxInitialZoom); - - if (viewportScale > 0 || viewportWidth > 0 || viewportHeight > 0 || viewportMinScale > 0 || viewportMaxScale > 0) { - return { - reason: "viewport", - defaultZoom: defaultZoom, - minZoom: minZoom, - maxZoom: maxZoom, - width: viewportWidth, - height: viewportHeight, - autoSize: viewportWidthStr == "device-width" || viewportHeightStr == "device-height", - allowZoom: windowUtils.getDocumentMetadata("viewport-user-scalable") != "no" - }; - } - - return { reason: null, allowZoom: true }; + return { + defaultZoom: defaultZoom, + minZoom: minZoom, + maxZoom: maxZoom, + width: viewportWidth, + height: viewportHeight, + autoSize: autoSize, + allowZoom: windowUtils.getDocumentMetadata("viewport-user-scalable") != "no" + }; }, clamp: function(num, min, max) { From b54a6af9890e242f757a0f690b0f8461ce22ad7b Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Thu, 6 May 2010 16:28:10 -0700 Subject: [PATCH 067/369] + You can now click-n-drag in empty space to create a group. If the group is too small, it will animate away (the cancel action) and if they group contains orphaned tabs, lo', it pulls those tabs into itself. + Changed the behavior of removing the penultimate tab from a group. The group no longer goes away until the very last tab is removed. + Added some new rect utilities for finding if a point/rect is inside another rect --- .../base/content/tabview/modules/utils.jsm | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index fe165deb04c5..16572aa123b0 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -102,6 +102,34 @@ window.Rect.prototype = { && rect.top < this.bottom); }, + // ---------- + // Function: containsPoint + // Returns a boolean denoting if the is inside of + // the bounding rect. + // + // Paramaters + // - A point + containsPoint: function(point){ + return( point.x > this.left + && point.x < this.right + && point.y > this.top + && point.y < this.bottom ) + }, + + // ---------- + // Function: contains + // Returns a boolean denoting if the is contained inside + // of the bounding rect. + // + // Paramaters + // - A rect + contains: function(rect){ + return( rect.left > this.left + && rect.right < this.right + && rect.top > this.top + && rect.bottom < this.bottom ) + }, + // ---------- center: function() { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); From ce008121e79295a7ff9fb6d67314233a8d997ee3 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 7 May 2010 15:49:54 -0700 Subject: [PATCH 068/369] + The "new tabs" group can now not be renamed or closed, but it can be moved and resized. Updated how item locking works to accommodate this. + Groups now have some extra space at the bottom to accommodate for the "new tab" button + Items now unsquish after another item is closed, and on resize. This should cut down on random-seeming unsquishes later. --- browser/base/content/tabview/modules/utils.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 16572aa123b0..7fc9c1bd119d 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -167,6 +167,7 @@ window.Rect.prototype = { && a.bottom == this.bottom); }, + // ---------- union: function(a){ var newLeft = Math.min(a.left, this.left); var newTop = Math.min(a.top, this.top); From 2f4c6d407ff7e1b11aabee2d4a52a0d1aef9ed45 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 11 May 2010 12:03:31 -0700 Subject: [PATCH 069/369] + Fixed an extremely subtle bug that would cause groups that you hadn't manually resized to start falling apart after a refresh. + Added sanity checks on data going to and from storage (still some work to be done there); if the check doesn't pass, the data isn't loaded/saved + Added a manual save option to the dev menu + Added Utils.isNumber, as well as standalone isRect and isPoint routines + Miscellaneous double checks and assertions --- .../base/content/tabview/modules/utils.jsm | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 7fc9c1bd119d..1eb313a4591c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -27,14 +27,19 @@ var extensionManager = Cc["@mozilla.org/extensions/manager;1"] // and creates a Point with it along with y. If either a or y are omitted, // 0 is used in their place. window.Point = function(a, y) { - if(a && typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + if(isPoint(a)) { this.x = a.x; this.y = a.y; } else { - this.x = (typeof(a) == 'undefined' ? 0 : a); - this.y = (typeof(y) == 'undefined' ? 0 : y); + this.x = (Utils.isNumber(a) ? a : 0); + this.y = (Utils.isNumber(y) ? y : 0); } -} +}; + +// ---------- +window.isPoint = function(p) { + return (p && Utils.isNumber(p.x) && Utils.isNumber(p.y)); +}; window.Point.prototype = { // ---------- @@ -59,8 +64,7 @@ window.Point.prototype = { // and creates a Rect with it along with top, width, and height. window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' - if(typeof(a.left) != 'undefined' && typeof(a.top) != 'undefined' - && typeof(a.width) != 'undefined' && typeof(a.height) != 'undefined') { + if(isRect(a)) { this.left = a.left; this.top = a.top; this.width = a.width; @@ -71,7 +75,15 @@ window.Rect = function(a, top, width, height) { this.width = width; this.height = height; } -} +}; + +// ---------- +window.isRect = function(r) { + return (Utils.isNumber(r.left) + && Utils.isNumber(r.top) + && Utils.isNumber(r.width) + && Utils.isNumber(r.height)); +}; window.Rect.prototype = { // ---------- @@ -512,7 +524,12 @@ var Utils = { isDOMElement: function(object) { // TODO: need more robust way return (object && typeof(object.tagName) != 'undefined' ? true : false); - } + }, + + // ---------- + isNumber: function(n) { + return (typeof(n) == 'number' && !isNaN(n)); + } }; window.Utils = Utils; From f0b26c0e620de9ca72ef19955ef6364b2b4d7db7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 11 May 2010 17:22:06 -0700 Subject: [PATCH 070/369] =?UTF-8?q?+=20Trying=20-moz-crisp-edges=E2=80=A6?= =?UTF-8?q?=20doesn't=20seem=20to=20improve=20speed=20much=20on=203.6,=20a?= =?UTF-8?q?nd=20looks=20worse=20+=20Created=20Utils.copy=20to=20make=20dup?= =?UTF-8?q?licating=20objects=20cleaner=20+=20Fixed=20a=20number=20of=20is?= =?UTF-8?q?sues=20with=20pulling=20tabs=20out=20of=20a=20tray.=20Temporari?= =?UTF-8?q?ly=20disabled=20the=20delay=20on=20drag=20as=20well.=20+=20Impr?= =?UTF-8?q?oved=20the=20threshold=20for=20switching=20from=20grid=20to=20s?= =?UTF-8?q?tack;=20only=20happens=20when=20the=20grid=20fills=20up.=20+=20?= =?UTF-8?q?No=20longer=20announcing=20"bad=20storage=20data"=20when=20ther?= =?UTF-8?q?e=20is=20in=20fact=20no=20storage=20data=20(fresh=20start)=20+?= =?UTF-8?q?=20A=20couple=20more=20asserts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/base/content/tabview/modules/utils.jsm | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 1eb313a4591c..6b4f0f411f07 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -309,6 +309,8 @@ window.Subscribable.prototype = { }; // ########## +// Class: Utils +// Singelton with common utility functions. var Utils = { // ___ Windows and Tabs get activeWindow(){ @@ -527,8 +529,21 @@ var Utils = { }, // ---------- + // Function: isNumber + // Returns true if the argument is a valid number. isNumber: function(n) { return (typeof(n) == 'number' && !isNaN(n)); + }, + + // ---------- + // Function: copy + // Returns a copy of the argument. Note that this is a shallow copy; if the argument + // has properties that are themselves objects, those properties will be copied by reference. + copy: function(value) { + if(value && typeof(value) == 'object') + return $.extend({}, value); + + return value; } }; From 7bda5a2eb1c00e999cebb8f3508001acd235c759 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 13 May 2010 10:37:50 -0700 Subject: [PATCH 071/369] + patch from Dietrich to get Tab Candy running in Minefield (disables listing of candies) --- browser/base/content/tabview/modules/utils.jsm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6b4f0f411f07..7b7212aeaf37 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -15,9 +15,6 @@ var homeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"] var consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); -var extensionManager = Cc["@mozilla.org/extensions/manager;1"] - .getService(Ci.nsIExtensionManager); - // ########## // Class: Point // A simple point. @@ -339,6 +336,8 @@ var Utils = { // ___ Files getInstallDirectory: function(id) { + var extensionManager = Cc["@mozilla.org/extensions/manager;1"] + .getService(Ci.nsIExtensionManager); var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); return file.parent; }, @@ -359,6 +358,7 @@ var Utils = { getVisualizationNames: function() { var names = []; + /* var dir = this.getInstallDirectory('tabcandy@aza.raskin'); dir.append('content'); dir.append('candies'); @@ -371,6 +371,7 @@ var Utils = { names.push(file.leafName); } + */ return names; }, From e5edc89fe359296065a03799daf9760bbcff82a9 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 13 May 2010 15:14:08 -0700 Subject: [PATCH 072/369] + Fixed: Quit Firefox with TabCandy open then open Firefox again. Your tabs and and navbar get hidden. + Fixed: If you quit tab candy (but not the browser), all of the tabs need to be reshown. + Starting firefox with tab candy selected now hides the tab bar + Utils.activeTab now returns a Tabs tab, rather than a raw tab --- .../base/content/tabview/modules/utils.jsm | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 7b7212aeaf37..15002bc1741e 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -310,20 +310,45 @@ window.Subscribable.prototype = { // Singelton with common utility functions. var Utils = { // ___ Windows and Tabs + + // ---------- + // Variable: activeWindow get activeWindow(){ var win = Cc["@mozilla.org/embedcomp/window-watcher;1"] .getService(Ci.nsIWindowWatcher) .activeWindow; - if( win != null ) return win; - else return homeWindow; + if( win != null ) + return win; + + if(homeWindow != null) + return homeWindow; + + win = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Components.interfaces.nsIWindowMediator) + .getMostRecentWindow("navigator:browser"); + + return win; }, + // ---------- + // Variable: activeTab + // The tab that represents the active tab in the active window. get activeTab(){ var tabBrowser = this.activeWindow.gBrowser; - return tabBrowser.selectedTab; + var rawTab = tabBrowser.selectedTab; + for( var i=0; i tab that represents the tab candy tab. + // TODO: what if there are multiple tab candy tabs? get homeTab(){ for( var i=0; i Date: Thu, 13 May 2010 15:38:10 -0700 Subject: [PATCH 073/369] + applied Dietrich's second patch; Listing candies (on the home page) now works in both 3.6 and minefield --- .../base/content/tabview/modules/utils.jsm | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 15002bc1741e..07218a34a96e 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -360,11 +360,22 @@ var Utils = { }, // ___ Files - getInstallDirectory: function(id) { - var extensionManager = Cc["@mozilla.org/extensions/manager;1"] - .getService(Ci.nsIExtensionManager); - var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); - return file.parent; + getInstallDirectory: function(id, callback) { + if (Cc["@mozilla.org/extensions/manager;1"]) { + var extensionManager = Cc["@mozilla.org/extensions/manager;1"] + .getService(Ci.nsIExtensionManager); + var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); + callback(file.parent); + } + else { + Components.utils.import("resource://gre/modules/AddonManager.jsm"); + AddonManager.getAddonByID(id, function(addon) { + var fileStr = addon.getResourceURL("install.rdf"); + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var url = ios.newURI(fileStr, null, null); + callback(url.QueryInterface(Ci.nsIFileURL).file.parent); + }); + } }, getFiles: function(dir) { @@ -381,23 +392,23 @@ var Utils = { return files; }, - getVisualizationNames: function() { - var names = []; - /* - var dir = this.getInstallDirectory('tabcandy@aza.raskin'); - dir.append('content'); - dir.append('candies'); - var files = this.getFiles(dir); - var count = files.length; - var a; - for(a = 0; a < count; a++) { - var file = files[a]; - if(file.isDirectory()) - names.push(file.leafName); - } - - */ - return names; + getVisualizationNames: function(callback) { + var self = this; + this.getInstallDirectory("tabcandy@aza.raskin", function(dir) { + var names = []; + dir.append('content'); + dir.append('candies'); + var files = self.getFiles(dir); + var count = files.length; + var a; + for(a = 0; a < count; a++) { + var file = files[a]; + if(file.isDirectory()) + names.push(file.leafName); + } + + callback(names); + }); }, // ___ Logging From 6f3159781164cd7a4f78a004062add096eb2adc7 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 18 May 2010 17:08:45 -0700 Subject: [PATCH 074/369] + Feature: When returning from a tab that is in a stack, arrange it at the top of the stack (but don't change its order in the children list) + TabItems now keeps track of all TabItem objects manually; we were using $('.tab:visible'), but this was causing bizarre behavior in certain cases --- browser/base/content/tabview/modules/utils.jsm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 07218a34a96e..28b0d024054a 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -577,8 +577,12 @@ var Utils = { // Returns a copy of the argument. Note that this is a shallow copy; if the argument // has properties that are themselves objects, those properties will be copied by reference. copy: function(value) { - if(value && typeof(value) == 'object') + if(value && typeof(value) == 'object') { + if($.isArray(value)) + return $.extend([], value); + return $.extend({}, value); + } return value; } From b0b5b85b38a9fe9c777708d30e5d392537f98949 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 20 May 2010 16:56:19 -0700 Subject: [PATCH 075/369] + this marks the checkin where Ian has switched to Minefield and is no longer paying attention to 3.6 + the TabCandy button/hot key now works to make a separate TabCandy for each window + Fixed a number of startup issues relating to multiple tab candies running at the same time --- .../base/content/tabview/modules/utils.jsm | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 28b0d024054a..7cc5c8c1dfc0 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -358,7 +358,31 @@ var Utils = { return null; }, + + // ---------- + // Function: getCurrentWindow + // Returns the nsIDOMWindowInternal for the currently active window, + // i.e. the window belonging to the active page's DOM "window" object. + getCurrentWindow: function() { + var wm = Cc["@mozilla.org/appshell/window-mediator;1"] + .getService(Ci.nsIWindowMediator); + var browserEnumerator = wm.getEnumerator("navigator:browser"); + while (browserEnumerator.hasMoreElements()) { + var browserWin = browserEnumerator.getNext(); + var tabbrowser = browserWin.gBrowser; + + // Check each tab of this browser instance + var numTabs = tabbrowser.browsers.length; + for (var index = 0; index < numTabs; index++) { + var currentBrowser = tabbrowser.getBrowserAtIndex(index); + if(currentBrowser.contentWindow == window) + return browserWin; + } + } + return null; + }, + // ___ Files getInstallDirectory: function(id, callback) { if (Cc["@mozilla.org/extensions/manager;1"]) { @@ -443,6 +467,7 @@ var Utils = { log: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); +/* $('body').prepend(text + '
'); */ consoleService.logStringMessage(text); }, From a1e14885b891d4cbaf692e3772fcb1b2d66fed50 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 21 May 2010 13:05:46 -0400 Subject: [PATCH 076/369] Bug 567044 - Tapping in urlbar doesn't work after pressing Android menu button [r=mfinkle] --- toolkit/content/Geometry.jsm | 1 + 1 file changed, 1 insertion(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 602f1bb07cd7..21253914f791 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -266,6 +266,7 @@ let Elements = {}; ["stack", "stack"], ["panelUI", "panel-container"], ["viewBuffer", "view-buffer"], + ["toolbarContainer", "toolbar-container"], ].forEach(function (elementGlobal) { let [name, id] = elementGlobal; Elements.__defineGetter__(name, function () { From 8b598da3fa4509399feedcdf56ffd28761bbe8f3 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 21 May 2010 15:44:15 -0700 Subject: [PATCH 077/369] + Applied Ehsan's sessionstore patch + Various related fixes + Added Utils.log2, which logs directly to the window --- browser/base/content/tabview/modules/utils.jsm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 7cc5c8c1dfc0..aabb66cff59c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -471,6 +471,16 @@ var Utils = { consoleService.logStringMessage(text); }, + log2: function() { // pass as many arguments as you want, it'll print them all + var text = this.expandArgumentsForLog(arguments); + var html = + '
' + + text + + '
'; + + $(html).prependTo('body'); + }, + error: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); Cu.reportError('tabcandy error: ' + text); From 48696fd4affeea24bf1bba51fc1bfa24b05be29e Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 24 May 2010 17:14:10 -0700 Subject: [PATCH 078/369] + New "new tabs" group behavior and position + New "new tab" button position for the "new tabs" group + Misc bug fixes --- browser/base/content/tabview/modules/utils.jsm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index aabb66cff59c..70f3c1fa2caa 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -76,7 +76,8 @@ window.Rect = function(a, top, width, height) { // ---------- window.isRect = function(r) { - return (Utils.isNumber(r.left) + return (r + && Utils.isNumber(r.left) && Utils.isNumber(r.top) && Utils.isNumber(r.width) && Utils.isNumber(r.height)); From 4c42deb59cdee07c112f0959502d65f1f21af6dc Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 25 May 2010 17:04:59 -0700 Subject: [PATCH 079/369] + Started iQ, replacement for jQuery + Converted Utils.js to use iQ instead of jQuery + Removed the use of Firebug lite + Removed reference to jQuery.lint --- browser/base/content/tabview/iq.js | 420 ++++++++++++++++++ .../base/content/tabview/modules/utils.jsm | 82 +--- 2 files changed, 440 insertions(+), 62 deletions(-) create mode 100644 browser/base/content/tabview/iq.js diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js new file mode 100644 index 000000000000..59bc1d39739c --- /dev/null +++ b/browser/base/content/tabview/iq.js @@ -0,0 +1,420 @@ +/* ***** 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 iQ. + * + * The Initial Developer of the Original Code is + * Ian Gilman . + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Some portions copied from: + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * ... which includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * 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 ***** */ + +// ########## +// Title: iq.js +// jQuery, hacked down to just the bits we need. +(function( window, undefined ) { + +var iQ = function(selector, context) { + // The iQ object is actually just the init constructor 'enhanced' + return new iQ.fn.init( selector, context ); + }, + + // Map over iQ in case of overwrite + _iQ = window.iQ, + + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + // A central reference to the root iQ(document) + rootiQ, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, + + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwnProperty = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + indexOf = Array.prototype.indexOf; + +// ########## +// Class: iQ.fn +// An individual element or group of elements. +iQ.fn = iQ.prototype = { + // ---------- + // Function: init + init: function( selector, context ) { + return null; // TODO: this routine not ready yet + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + doc = (context ? context.ownerDocument || context : document); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( iQ.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; +/* iQ.fn.attr.call( selector, context, true ); */ + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { +/* ret = doc.createDocumentFragment([match */ + ret = buildFragment( [ match[1] ], [ doc ] ); + selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; + } + + return iQ.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + if ( elem ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootiQ.find( selector ); + } + + // Otherwise, we inject the element directly into the iQ object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + return iQ.merge( this, selector ); + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.iq ) { + return (context || rootiQ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return iQ( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( iQ.isFunction( selector ) ) { + Utils.log('iQ does not support ready functions'); + return null; + } + + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } + + return iQ.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of iQ being used + iq: "1.4.2", + + // The default length of a iQ object is 0 + length: 0 +}; + +// ---------- +// Give the init function the iQ prototype for later instantiation +iQ.fn.init.prototype = iQ.fn; + +// ---------- +// Function: extend +iQ.extend = iQ.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !iQ.isFunction(target) ) { + target = {}; + } + + // extend iQ itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( iQ.isPlainObject(copy) || iQ.isArray(copy) ) ) { + var clone = src && ( iQ.isPlainObject(src) || iQ.isArray(src) ) ? src + : iQ.isArray(copy) ? [] : {}; + + // Never move original objects, clone them + target[ name ] = iQ.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +// ########## +// Class: iQ +// Singleton +iQ.extend({ + // ----------- + // Function: isFunction + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + // ---------- + // Function: isArray + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + // ---------- + // Function: isPlainObject + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); + }, + + // ---------- + // Function: isEmptyObject + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + // ---------- + // Function: merge + merge: function( first, second ) { + var i = first.length, j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + // ---------- + // Function: grep + grep: function( elems, callback, inv ) { + var ret = []; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + if ( !inv !== !callback( elems[ i ], i ) ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // ---------- + // Function: each + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || iQ.isFunction(object); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } + + return object; + } +}); + +// ---------- +// All iQ objects should point back to these +rootiQ = iQ(document); + +// ---------- +// Expose iQ to the global object +window.iQ = iQ; + +})(window); diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 70f3c1fa2caa..ba29365f0578 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -173,8 +173,8 @@ window.Rect.prototype = { equals: function(a) { return (a.left == this.left && a.top == this.top - && a.right == this.right - && a.bottom == this.bottom); + && a.width == this.width + && a.height == this.height); }, // ---------- @@ -217,7 +217,7 @@ window.Subscribable.prototype = { this.subscribers[eventName] = []; var subs = this.subscribers[eventName]; - var existing = jQuery.grep(subs, function(element) { + var existing = iQ.grep(subs, function(element) { return element.refObject == refObject; }); @@ -239,7 +239,7 @@ window.Subscribable.prototype = { if(!this.subscribers[eventName]) return; - this.subscribers[eventName] = jQuery.grep(this.subscribers[eventName], function(element) { + this.subscribers[eventName] = iQ.grep(this.subscribers[eventName], function(element) { return element.refObject == refObject; }, true); }, @@ -252,8 +252,8 @@ window.Subscribable.prototype = { return; var self = this; - var subsCopy = $.merge([], this.subscribers[eventName]); - $.each(subsCopy, function(index, object) { + var subsCopy = iQ.merge([], this.subscribers[eventName]); + iQ.each(subsCopy, function(index, object) { object.callback(self, eventInfo); }); }, @@ -266,7 +266,7 @@ window.Subscribable.prototype = { if(!this.onCloseSubscribers) this.onCloseSubscribers = []; - var existing = jQuery.grep(this.onCloseSubscribers, function(element) { + var existing = iQ.grep(this.onCloseSubscribers, function(element) { return element.referenceElement == referenceElement; }); @@ -288,7 +288,7 @@ window.Subscribable.prototype = { if(!this.onCloseSubscribers) return; - this.onCloseSubscribers = jQuery.grep(this.onCloseSubscribers, function(element) { + this.onCloseSubscribers = iQ.grep(this.onCloseSubscribers, function(element) { return element.referenceElement == referenceElement; }, true); }, @@ -300,7 +300,7 @@ window.Subscribable.prototype = { if(!this.onCloseSubscribers) return; - jQuery.each(this.onCloseSubscribers, function(index, object) { + iQ.each(this.onCloseSubscribers, function(index, object) { object.callback(this); }); } @@ -438,50 +438,15 @@ var Utils = { // ___ Logging - // Interactive logging! - ilog: function(){ // pass as many arguments as you want, it'll print them all - // If Firebug lite already exists, print to the console. - if( window.firebug ){ - window.firebug.d.console.cmd.log.apply(null, arguments); - return; - } - - // Else, embed it. - $('') - .appendTo("head"); - - $('') - .appendTo("body"); - - var args = arguments; - - (function(){ - var fb = window.firebug; - if(fb && fb.version){ - fb.init(); - fb.win.setHeight(100); - fb.d.console.cmd.log.apply(null, args); - } - else{setTimeout(arguments.callee);} - })(); + ilog: function(){ + Utils.log('!!ilog is no longer supported!!'); }, log: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); -/* $('body').prepend(text + '
'); */ consoleService.logStringMessage(text); }, - log2: function() { // pass as many arguments as you want, it'll print them all - var text = this.expandArgumentsForLog(arguments); - var html = - '
' - + text - + '
'; - - $(html).prependTo('body'); - }, - error: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); Cu.reportError('tabcandy error: ' + text); @@ -511,7 +476,7 @@ var Utils = { text += '\n' + calls[3]; } - Cu.reportError(text); + this.log(text); } }, @@ -581,24 +546,17 @@ var Utils = { // ___ Geometry getBounds: function(el) { - var $el = $(el); return new Rect( - parseInt($el.css('left')), - parseInt($el.css('top')), - $el.width(), - $el.height() + parseInt(el.style.left) || el.offsetLeft, + parseInt(el.style.top) || el.offsetTop, + el.clientWidth, + el.clientHeight ); }, // ___ Misc - isJQuery: function(object) { - // TODO: need more robust way - return (object && typeof(object.fadeIn) == 'function' ? true : false); - }, - isDOMElement: function(object) { - // TODO: need more robust way - return (object && typeof(object.tagName) != 'undefined' ? true : false); + return (object && typeof(object.nodeType) != 'undefined' ? true : false); }, // ---------- @@ -614,10 +572,10 @@ var Utils = { // has properties that are themselves objects, those properties will be copied by reference. copy: function(value) { if(value && typeof(value) == 'object') { - if($.isArray(value)) - return $.extend([], value); + if(iQ.isArray(value)) + return iQ.extend([], value); - return $.extend({}, value); + return iQ.extend({}, value); } return value; From 79769aab22924b5a26c70bdd445c9a925a65310b Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 26 May 2010 17:04:56 -0700 Subject: [PATCH 080/369] + jQuery is completely out of the content/js files we're using in revision-a! + The function you give to TabMirror.customize now takes a Mirror rather than a jQuery object + iQ is coming along. I tried to put asserts in for all the standard functionality that's missing (and that we might not need, but that someone used to jQuery might expect). I've also been adding Natural Docs comments, to give a sense of the scope+ Got rid of TabCanvas.animate(), which we haven't used in a while. + No longer including switch.js, which we also haven't used in a while --- browser/base/content/tabview/iq.js | 411 +++++++++++++++++++++++++---- 1 file changed, 358 insertions(+), 53 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 59bc1d39739c..045582919a9b 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -88,14 +88,26 @@ var iQ = function(selector, context) { slice = Array.prototype.slice, indexOf = Array.prototype.indexOf; +var rclass = /[\n\t]/g, + rspace = /\s+/, + rreturn = /\r/g, + rspecialurl = /href|src|style/, + rtype = /(button|input)/i, + rfocusable = /(button|input|object|select|textarea)/i, + rclickable = /^(a|area)$/i, + rradiocheck = /radio|checkbox/; + // ########## // Class: iQ.fn // An individual element or group of elements. iQ.fn = iQ.prototype = { // ---------- // Function: init + // You don't call this directly; this is what's called by iQ(). + // It works pretty much like jQuery(), with a few exceptions, + // most notably that you can't use strings with complex html, + // just simple tags like '
'. init: function( selector, context ) { - return null; // TODO: this routine not ready yet var match, elem, ret, doc; // Handle $(""), $(null), or $(undefined) @@ -127,7 +139,7 @@ iQ.fn = iQ.prototype = { // Verify a match, and that no context was specified for #id if ( match && (match[1] || !context) ) { - // HANDLE: $(html) -> $(array) + // HANDLE $(html) -> $(array) if ( match[1] ) { doc = (context ? context.ownerDocument || context : document); @@ -137,6 +149,7 @@ iQ.fn = iQ.prototype = { if ( ret ) { if ( iQ.isPlainObject( context ) ) { + Utils.assert('does not support HTML creation with context', false); selector = [ document.createElement( ret[1] ) ]; /* iQ.fn.attr.call( selector, context, true ); */ @@ -145,6 +158,7 @@ iQ.fn = iQ.prototype = { } } else { + Utils.assert('does not support complex HTML creation', false); /* ret = doc.createDocumentFragment([match */ ret = buildFragment( [ match[1] ], [ doc ] ); selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; @@ -152,18 +166,11 @@ iQ.fn = iQ.prototype = { return iQ.merge( this, selector ); - // HANDLE: $("#id") + // HANDLE $("#id") } else { elem = document.getElementById( match[2] ); if ( elem ) { - // Handle the case where IE and Opera return items - // by name instead of ID - if ( elem.id !== match[2] ) { - return rootiQ.find( selector ); - } - - // Otherwise, we inject the element directly into the iQ object this.length = 1; this[0] = elem; } @@ -173,24 +180,24 @@ iQ.fn = iQ.prototype = { return this; } - // HANDLE: $("TAG") + // HANDLE $("TAG") } else if ( !context && /^\w+$/.test( selector ) ) { this.selector = selector; this.context = document; selector = document.getElementsByTagName( selector ); return iQ.merge( this, selector ); - // HANDLE: $(expr, $(...)) + // HANDLE $(expr, $(...)) } else if ( !context || context.iq ) { return (context || rootiQ).find( selector ); - // HANDLE: $(expr, context) + // HANDLE $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { return iQ( context ).find( selector ); } - // HANDLE: $(function) + // HANDLE $(function) // Shortcut for document ready } else if ( iQ.isFunction( selector ) ) { Utils.log('iQ does not support ready functions'); @@ -212,7 +219,279 @@ iQ.fn = iQ.prototype = { iq: "1.4.2", // The default length of a iQ object is 0 - length: 0 + length: 0, + + // ---------- + // Function: toArray + toArray: function() { + return slice.call( this, 0 ); + }, + + // ---------- + // Function: get + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); + }, + + // ---------- + // Function: pushStack + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new iQ matched element set + var ret = iQ(); + + if ( iQ.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + iQ.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // ---------- + // Function: each + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return iQ.each( this, callback, args ); + }, + + // ---------- + // Function: slice + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + // ---------- + // Function: addClass + addClass: function( value ) { + if ( iQ.isFunction(value) ) { + Utils.assert('does not support function argument', false); + return null; + } + + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspace ); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; + + } else { + var className = " " + elem.className + " ", setClass = elem.className; + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = iQ.trim( setClass ); + } + } + } + } + + return this; + }, + + // ---------- + // Function: removeClass + removeClass: function( value ) { + if ( iQ.isFunction(value) ) { + Utils.assert('does not support function argument', false); + return null; + } + + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split(rspace); + + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = iQ.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + // ---------- + // Function: find + find: function( selector ) { + var ret = [], length = 0; + + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + try { + iQ.merge(ret, this[i].querySelectorAll( selector ) ); + } catch(e) { + Utils.log('iQ.find error (bad selector)', e); + } + + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return iQ(ret); + }, + + // ---------- + // Function: remove + remove: function(unused) { + Utils.assert('does not accept a selector', unused === undefined); + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + // ---------- + // Function: empty + empty: function() { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + // ---------- + // Function: width + width: function(unused) { + Utils.assert('does not yet support setting', unused === undefined); + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + return this[0].clientWidth; + }, + + // ---------- + // Function: height + height: function(unused) { + Utils.assert('does not yet support setting', unused === undefined); + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + return this[0].clientHeight; + }, + + // ---------- + // Function: bounds + bounds: function(unused) { + Utils.assert('does not yet support setting', unused === undefined); + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + var el = this[0]; + return new Rect( + parseInt(el.style.left) || el.offsetLeft, + parseInt(el.style.top) || el.offsetTop, + el.clientWidth, + el.clientHeight + ); + }, + + // ---------- + // Function: data + data: function(key, value) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + var data = this[0].iQData; + if(value === undefined) + return (data ? data[key] : null); + + if(!data) + data = this[0].iQData = {}; + + data[key] = value; + return this; + }, + + // ---------- + // Function: html + // TODO: security + html: function(value) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + if(value === undefined) + return this[0].innerHTML; + + this[0].innerHTML = value; + return this; + }, + + // ---------- + // Function: text + text: function(value) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + if(value === undefined) { + return this[0].textContent; + } + + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); + }, + + // ---------- + // Function: appendTo + appendTo: function(selector) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + iQ(selector).append(this); + return this; + }, + + // ---------- + // Function: append + append: function(selector) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + var object = iQ(selector); + Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1); + this[0].appendChild(object[0]); + return this; + } }; // ---------- @@ -331,43 +610,6 @@ iQ.extend({ return true; }, - // ---------- - // Function: merge - merge: function( first, second ) { - var i = first.length, j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - // ---------- - // Function: grep - grep: function( elems, callback, inv ) { - var ret = []; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - if ( !inv !== !callback( elems[ i ], i ) ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - // ---------- // Function: each // args is for internal usage only @@ -406,7 +648,70 @@ iQ.extend({ } return object; - } + }, + + // ---------- + // Function: trim + trim: function( text ) { + return (text || "").replace( rtrim, "" ); + }, + + // ---------- + // Function: makeArray + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + if ( array.length == null || typeof array === "string" || iQ.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { + push.call( ret, array ); + } else { + iQ.merge( ret, array ); + } + } + + return ret; + }, + + // ---------- + // Function: merge + merge: function( first, second ) { + var i = first.length, j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + // ---------- + // Function: grep + grep: function( elems, callback, inv ) { + var ret = []; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + if ( !inv !== !callback( elems[ i ], i ) ) { + ret.push( elems[ i ] ); + } + } + + return ret; + } }); // ---------- From 75d639f51d9cc192e2153f8385cddf229338723a Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 27 May 2010 17:25:14 -0700 Subject: [PATCH 081/369] + Items.js is now jQuery-free + Just a little refactor for the "reset" code (also removed from the dev menu) + Started on event handling for iQ --- browser/base/content/tabview/iq.js | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 045582919a9b..2ef915cedeab 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -353,6 +353,19 @@ iQ.fn = iQ.prototype = { return this; }, + // ---------- + // Function: hasClass + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + // ---------- // Function: find find: function( selector ) { @@ -491,6 +504,50 @@ iQ.fn = iQ.prototype = { Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1); this[0].appendChild(object[0]); return this; + }, + + // ---------- + // Function: css + css: function(a, b) { + var properties = null; + if(typeof a === 'string') { + if(b === undefined) { + Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + return window.getComputedStyle(this[0], null).getPropertyValue(a); + } else { + properties = {}; + properties[a] = b; + } + } else + properties = a; + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + iQ.each(properties, function(key, value) { + if(key == 'left' || key == 'top' || key == 'width' || key == 'height') { + if(typeof(value) != 'string') + value += 'px'; + } + + elem.style[key] = value; + }); + } + }, + + // ---------- + // Function: bind + bind: function(type, func) { + Utils.assert('does not support eventData argument', iQ.isFunction(func)); + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + elem.addEventListener(type, func, false); + } + }, + + // ---------- + // Function: unbind + unbind: function(type, func) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + elem.removeEventListener(type, func, false); + } } }; From f90b12b086172c6ca6f549abcee3306f8dcb827e Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Thu, 27 May 2010 19:17:01 -0700 Subject: [PATCH 082/369] + Fixed a bug in iQ where CSS of type -moz-transform wasn't getting set. + Made stacks a bit prettier. --- browser/base/content/tabview/iq.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 2ef915cedeab..0db1b2515ccd 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -19,6 +19,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Aza Raskin * * Some portions copied from: * jQuery JavaScript Library v1.4.2 @@ -527,7 +528,12 @@ iQ.fn = iQ.prototype = { if(typeof(value) != 'string') value += 'px'; } - + + // -moz-transform is a special case. To set it doing elem.style["-moz-transform"] + // doesn't work. You have to use elem.style["MozTransform"]. There are probably + // other key values like this. + // TODO: Generalize. + if( key == "-moz-transform" ) key = "MozTransform"; elem.style[key] = value; }); } From 08a5e5117be6242335173ad5f08e90835fc21e48 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 28 May 2010 12:20:02 -0700 Subject: [PATCH 083/369] + removed sizzle reference from iQ.js license block, as it doesn't look like we'll be using any portion of jQuery that has sizzle (also, testing my ability to push to mozilla hg) --- browser/base/content/tabview/iq.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 0db1b2515ccd..9f5c3c56d81c 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -28,11 +28,6 @@ * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * - * ... which includes Sizzle.js - * http://sizzlejs.com/ - * Copyright 2010, The Dojo Foundation - * Released under the MIT, BSD, and GPL Licenses. - * * 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"), From 59c1535d0883d734d5ac9fe7a0cfa0e62a178370 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 28 May 2010 13:54:29 -0700 Subject: [PATCH 084/369] + changed more uses of Utils.activeWindow to Utils.getCurrentWindow() + Generalized Aza's iQ.css('-moz-transform') fix --- browser/base/content/tabview/iq.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9f5c3c56d81c..c635a8461ac0 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -517,6 +517,11 @@ iQ.fn = iQ.prototype = { } else properties = a; + var subsitutions = { + '-moz-transform': 'MozTransform', + 'z-index': 'zIndex' + }; + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { iQ.each(properties, function(key, value) { if(key == 'left' || key == 'top' || key == 'width' || key == 'height') { @@ -524,12 +529,7 @@ iQ.fn = iQ.prototype = { value += 'px'; } - // -moz-transform is a special case. To set it doing elem.style["-moz-transform"] - // doesn't work. You have to use elem.style["MozTransform"]. There are probably - // other key values like this. - // TODO: Generalize. - if( key == "-moz-transform" ) key = "MozTransform"; - elem.style[key] = value; + elem.style[subsitutions[key] || key] = value; }); } }, From ba8db3fb1ff6fbc7b485c1577f0af045d0f8bf4f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 28 May 2010 15:56:00 -0700 Subject: [PATCH 085/369] + Started work on iQ animations with CSS transitions; not ready yet + iQ event binding works now; converted over some of the usage in ui.js + Additional fixes to iQ's CSS function + Added position() and one() to iQ, as well as a number of bind() aliases --- browser/base/content/tabview/iq.js | 114 +++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 14 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index c635a8461ac0..8ef764a4105b 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -432,6 +432,18 @@ iQ.fn = iQ.prototype = { return this[0].clientHeight; }, + // ---------- + // Function: position + position: function(unused) { + Utils.assert('does not yet support setting', unused === undefined); + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + var el = this[0]; + return { + left: (parseInt(el.style.left) || el.offsetLeft), + top: (parseInt(el.style.top) || el.offsetTop) + }; + }, + // ---------- // Function: bounds bounds: function(unused) { @@ -506,22 +518,23 @@ iQ.fn = iQ.prototype = { // Function: css css: function(a, b) { var properties = null; - if(typeof a === 'string') { - if(b === undefined) { - Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); - return window.getComputedStyle(this[0], null).getPropertyValue(a); - } else { - properties = {}; - properties[a] = b; - } - } else - properties = a; - var subsitutions = { '-moz-transform': 'MozTransform', 'z-index': 'zIndex' }; + if(typeof a === 'string') { + var key = (subsitutions[a] || a); + if(b === undefined) { + Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + return window.getComputedStyle(this[0], null).getPropertyValue(key); + } else { + properties = {}; + properties[key] = b; + } + } else + properties = a; + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { iQ.each(properties, function(key, value) { if(key == 'left' || key == 'top' || key == 'width' || key == 'height') { @@ -531,24 +544,78 @@ iQ.fn = iQ.prototype = { elem.style[subsitutions[key] || key] = value; }); - } + } + + return this; }, - + + // ---------- + // Function: animate + animate: function(css, duration, callback) { + try { + this.addClass(duration); + + var self = this; + var cleanedUp = false; + this.one('transitionend', function() { + if(!cleanedUp) { + self.removeClass(duration); + cleanedUp = true; + if(iQ.isFunction(callback)) + callback(); + } + }); + + this.css(css); + } catch(e) { + Utils.log('iQ.fn.animate error', e); + } + + }, + // ---------- // Function: bind bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); + + var handler = function(e) { + try { + return func(e); + } catch(e) { + Utils.log(e); + } + }; + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.addEventListener(type, func, false); + elem.addEventListener(type, handler, false); } + + return this; + }, + + // ---------- + // Function: one + one: function(type, func) { + Utils.assert('does not support eventData argument', iQ.isFunction(func)); + + var handler = function(e) { + iQ(this).unbind(type, handler); + return func(e); + }; + + return this.bind(type, handler); }, // ---------- // Function: unbind unbind: function(type, func) { + Utils.assert('Must provide a function', iQ.isFunction(func)); + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { elem.removeEventListener(type, func, false); } + + return this; } }; @@ -772,6 +839,25 @@ iQ.extend({ } }); +// ---------- +// Create various event aliases +(function() { + var events = [ + 'keyup', + 'keydown', + 'mouseup', + 'mousedown', + 'mousemove', + 'click' + ]; + + iQ.each(events, function(index, event) { + iQ.fn[event] = function(func) { + return this.bind(event, func); + }; + }); +})(); + // ---------- // All iQ objects should point back to these rootiQ = iQ(document); From b38b65212597712f62c04f896bcebc60c0f37c0d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 3 Jun 2010 15:31:18 -0700 Subject: [PATCH 086/369] =?UTF-8?q?+=20Turned=20off=20-moz-crisp-edges=20+?= =?UTF-8?q?=20First=20use=20of=20iQ.animate(),=20which=20uses=20CSS=20tran?= =?UTF-8?q?sitions:=20tab=20zoom=20up=20and=20down,=20as=20well=20as=20pha?= =?UTF-8?q?ntom=20group=20disappearing=20when=20you=20drag=20in=20empty=20?= =?UTF-8?q?space=20but=20don't=20make=20it=20big=20enough=20+=20Consolidat?= =?UTF-8?q?ed=20onFocus=20call=20of=20UIClass=20into=20onFocus=20call=20of?= =?UTF-8?q?=20Page=20+=20ui.js=20is=20jQuery-free=20+=20The=20zoom=20down?= =?UTF-8?q?=20animation=20happens=20after=20a=20setTimeout(=E2=80=A6,=201)?= =?UTF-8?q?,=20which=20causes=20an=20unfortunate=20delay,=20but=20without?= =?UTF-8?q?=20it,=20the=20animation=20doesn't=20happen=20at=20all=20+=20Ad?= =?UTF-8?q?ded=20val,=20resize,=20change=20to=20iQ.fn=20and=20isAnimating?= =?UTF-8?q?=20to=20iQ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/base/content/tabview/iq.js | 57 +++++++++++++++++++++++++----- 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 8ef764a4105b..f6bddd09ad5e 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -496,6 +496,18 @@ iQ.fn = iQ.prototype = { return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); }, + // ---------- + // Function: val + val: function(value) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + if(value === undefined) { + return this[0].value; + } + + this[0].value = value; + return this; + }, + // ---------- // Function: appendTo appendTo: function(selector) { @@ -518,13 +530,15 @@ iQ.fn = iQ.prototype = { // Function: css css: function(a, b) { var properties = null; +/* var subsitutions = { '-moz-transform': 'MozTransform', 'z-index': 'zIndex' }; +*/ if(typeof a === 'string') { - var key = (subsitutions[a] || a); + var key = a; //(subsitutions[a] || a); if(b === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return window.getComputedStyle(this[0], null).getPropertyValue(key); @@ -535,14 +549,24 @@ iQ.fn = iQ.prototype = { } else properties = a; + var pixels = { + 'left': true, + 'top': true, + 'right': true, + 'bottom': true, + 'width': true, + 'height': true + }; + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { iQ.each(properties, function(key, value) { - if(key == 'left' || key == 'top' || key == 'width' || key == 'height') { - if(typeof(value) != 'string') - value += 'px'; - } + if(pixels[key] && typeof(value) != 'string') + value += 'px'; - elem.style[subsitutions[key] || key] = value; + if(key.indexOf('-') != -1) + elem.style.setProperty(key, value, ''); + else + elem.style[key] = value; }); } @@ -554,11 +578,13 @@ iQ.fn = iQ.prototype = { animate: function(css, duration, callback) { try { this.addClass(duration); + iQ.animationCount++; var self = this; var cleanedUp = false; this.one('transitionend', function() { if(!cleanedUp) { + iQ.animationCount--; self.removeClass(duration); cleanedUp = true; if(iQ.isFunction(callback)) @@ -578,6 +604,7 @@ iQ.fn = iQ.prototype = { bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); +/* var handler = function(e) { try { return func(e); @@ -585,9 +612,10 @@ iQ.fn = iQ.prototype = { Utils.log(e); } }; +*/ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.addEventListener(type, handler, false); + elem.addEventListener(type, func, false); } return this; @@ -685,6 +713,17 @@ iQ.extend = iQ.fn.extend = function() { // Class: iQ // Singleton iQ.extend({ + // ---------- + // Variable: animationCount + // For internal use only + animationCount: 0, + + // ---------- + // Function: isAnimating + isAnimating: function() { + return (this.animationCount != 0); + }, + // ----------- // Function: isFunction // See test/unit/core.js for details concerning isFunction. @@ -848,7 +887,9 @@ iQ.extend({ 'mouseup', 'mousedown', 'mousemove', - 'click' + 'click', + 'resize', + 'change' ]; iQ.each(events, function(index, event) { From 0cf14120d40263e2d0783f4b7ea2eab3fb89cf10 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 3 Jun 2010 17:11:13 -0700 Subject: [PATCH 087/369] =?UTF-8?q?+=20Started=20on=20iQ=20for=20tabitems.?= =?UTF-8?q?js;=20now=20using=20iQ=20for=20TabItem.setBounds.=20We're=20not?= =?UTF-8?q?=20yet=20supporting=20the=20tabcandy=20bounce=20animation,=20so?= =?UTF-8?q?=20the=20tab=20movement=20boring=20now=E2=80=A6=20to=20be=20fix?= =?UTF-8?q?ed=20soon.=20+=20Added=20fadeIn=20and=20fadeOut=20to=20iQ=20+?= =?UTF-8?q?=20Added=20a=20simple=20test=20framework=20to=20stand=20in=20fo?= =?UTF-8?q?r=20mochitest,=20and=20gave=20it=20a=20couple=20of=20iQ=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- browser/base/content/tabview/iq.js | 31 +++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f6bddd09ad5e..9634f696db0b 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -576,6 +576,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: animate animate: function(css, duration, callback) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); try { this.addClass(duration); iQ.animationCount++; @@ -588,7 +589,7 @@ iQ.fn = iQ.prototype = { self.removeClass(duration); cleanedUp = true; if(iQ.isFunction(callback)) - callback(); + callback.apply(this); } }); @@ -597,6 +598,34 @@ iQ.fn = iQ.prototype = { Utils.log('iQ.fn.animate error', e); } + return this; + }, + + // ---------- + // Function: fadeOut + fadeOut: function() { + try { + this.animate({opacity: 0}, 'animate350', function() { + iQ(this).css({display: 'none'}); + }); + } catch(e) { + Utils.log(e); + } + + return this; + }, + + // ---------- + // Function: fadeIn + fadeIn: function() { + try { + this.css({display: ''}); + this.animate({opacity: 1}, 'animate350'); + } catch(e) { + Utils.log(e); + } + + return this; }, // ---------- From ff165ad580486c32480449376216d4ffd93807db Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 4 Jun 2010 12:34:03 -0700 Subject: [PATCH 088/369] + Added hide and show to iQ + tabitems.js is now jQuery-free except for drag/drop/resize + Cleaned up usage of TabItems.getItemByTab and renamed it to getItemByTabElement to make it clearer --- browser/base/content/tabview/iq.js | 40 ++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9634f696db0b..b1630d1be6a0 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -628,6 +628,30 @@ iQ.fn = iQ.prototype = { return this; }, + // ---------- + // Function: hide + hide: function() { + try { + this.css({display: 'none', opacity: 0}); + } catch(e) { + Utils.log(e); + } + + return this; + }, + + // ---------- + // Function: show + show: function() { + try { + this.css({display: '', opacity: 1}); + } catch(e) { + Utils.log(e); + } + + return this; + }, + // ---------- // Function: bind bind: function(type, func) { @@ -869,6 +893,22 @@ iQ.extend({ return ret; }, + // ---------- + // Function: inArray + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; + }, + // ---------- // Function: merge merge: function( first, second ) { From ff909a029eb93384f1e5a52b4828163a779609b3 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 4 Jun 2010 15:08:24 -0700 Subject: [PATCH 089/369] + groups.js is now jQuery-free except for drag/drop/resize and the newTab routine + Added blur and focus event handlers to iQ + iQ.fn.data() now supports multi-objects + iQ.fn.fadeOut() now accepts a callback --- browser/base/content/tabview/iq.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index b1630d1be6a0..3a804a1111e5 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -461,15 +461,21 @@ iQ.fn = iQ.prototype = { // ---------- // Function: data data: function(key, value) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - var data = this[0].iQData; - if(value === undefined) + if(value === undefined) { + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + var data = this[0].iQData; return (data ? data[key] : null); + } + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + var data = elem.iQData; + + if(!data) + data = elem.iQData = {}; + + data[key] = value; + } - if(!data) - data = this[0].iQData = {}; - - data[key] = value; return this; }, @@ -603,10 +609,13 @@ iQ.fn = iQ.prototype = { // ---------- // Function: fadeOut - fadeOut: function() { + fadeOut: function(callback) { try { + Utils.assert('does not yet support duration', iQ.isFunction(callback) || callback === undefined); this.animate({opacity: 0}, 'animate350', function() { iQ(this).css({display: 'none'}); + if(iQ.isFunction(callback)) + callback.apply(this); }); } catch(e) { Utils.log(e); @@ -958,7 +967,9 @@ iQ.extend({ 'mousemove', 'click', 'resize', - 'change' + 'change', + 'blur', + 'focus' ]; iQ.each(events, function(index, event) { From da98d05805d409e7eafb3ea58ab5bb70f56fe24c Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 4 Jun 2010 16:39:33 -0700 Subject: [PATCH 090/369] + Temporarily added a bunch of logging for debugging purposes (commented out) + Fixed a bug with iQ.fn.one that caused it to not properly set "this" + Temporarily disabled TabMirror.pausePainting, which is causing problems at the moment (will fix soon) --- browser/base/content/tabview/iq.js | 36 ++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 3a804a1111e5..53fc587c681d 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -584,18 +584,40 @@ iQ.fn = iQ.prototype = { animate: function(css, duration, callback) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); try { +/* + this.css({ + '-moz-transition-property': 'all', + '-moz-transition-duration': '0.3s', + '-moz-transition-timing-function': 'cubic-bezier(0.0, 0.35, .6, 1.4)' + }); +*/ + this.addClass(duration); iQ.animationCount++; var self = this; var cleanedUp = false; this.one('transitionend', function() { - if(!cleanedUp) { - iQ.animationCount--; - self.removeClass(duration); - cleanedUp = true; - if(iQ.isFunction(callback)) - callback.apply(this); + try { +/* Utils.log('transition ended'); */ + if(!cleanedUp) { + iQ.animationCount--; + +/* + iQ(this).css({ + '-moz-transition-property': '', + '-moz-transition-duration': '', + '-moz-transition-timing-function': '' + }); +*/ + + self.removeClass(duration); + cleanedUp = true; + if(iQ.isFunction(callback)) + callback.apply(this); + } + } catch(e) { + Utils.log(e); } }); @@ -690,7 +712,7 @@ iQ.fn = iQ.prototype = { var handler = function(e) { iQ(this).unbind(type, handler); - return func(e); + return func.apply(this, [e]); }; return this.bind(type, handler); From 266229c78ded23dfd0f3893d86b62a93bea4cc69 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 7 Jun 2010 16:16:55 -0700 Subject: [PATCH 091/369] + We're no longer using a style sheet for our CSS transitions (doing it directly in JavaScript instead) + Changed the iQ.fn.animate call signature: it now takes two arguments, a CSS object and an options object (with possible parameters: duration, easing and complete) + iQ.fn.animate now reliably calls back the completion routine (this was causing all sorts of trouble) + Reenabled TabMirror.pausePainting + Added animate.html, a test bed for playing with CSS transitions --- browser/base/content/tabview/iq.js | 77 +++++++++++++++++------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 53fc587c681d..3f73144ea746 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -581,49 +581,58 @@ iQ.fn = iQ.prototype = { // ---------- // Function: animate - animate: function(css, duration, callback) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + // Uses CSS transitions to animate the element. + // + // Parameters: + // css - an object map of the CSS properties to change + // options - an object with various properites (see below) + // + // Possible "options" properties: + // duration - how long to animate, in milliseconds + // easing - easing function to use. Possibilities include 'tabcandyBounce', 'easeInQuad'. + // Default is 'ease'. + // complete - function to call once the animation is done, takes nothing in, but "this" + // is set to the element that was animated. + animate: function(css, options) { try { -/* - this.css({ - '-moz-transition-property': 'all', - '-moz-transition-duration': '0.3s', - '-moz-transition-timing-function': 'cubic-bezier(0.0, 0.35, .6, 1.4)' - }); -*/ + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - this.addClass(duration); - iQ.animationCount++; + if(!options) + options = {}; + + var easings = { + tabcandyBounce: 'cubic-bezier(0.0, 0.35, .6, 1.4)', + easeInQuad: 'ease-in' // TODO: make it a real easeInQuad, or decide we don't care + }; + + var duration = (options.duration || 400); + var easing = (easings[options.easing] || 'ease'); + + this.css({ + '-moz-transition-property': 'all', // TODO: just animate the properties we're changing + '-moz-transition-duration': (duration / 1000) + 's', + '-moz-transition-timing-function': easing + }); + + this.css(css); var self = this; - var cleanedUp = false; - this.one('transitionend', function() { + setTimeout(function() { try { -/* Utils.log('transition ended'); */ - if(!cleanedUp) { - iQ.animationCount--; + self.css({ + '-moz-transition-property': 'none', + '-moz-transition-duration': '', + '-moz-transition-timing-function': '' + }); -/* - iQ(this).css({ - '-moz-transition-property': '', - '-moz-transition-duration': '', - '-moz-transition-timing-function': '' - }); -*/ - - self.removeClass(duration); - cleanedUp = true; - if(iQ.isFunction(callback)) - callback.apply(this); - } + if(iQ.isFunction(options.complete)) + options.complete.apply(self); } catch(e) { Utils.log(e); - } - }); - - this.css(css); + } + }, duration); } catch(e) { - Utils.log('iQ.fn.animate error', e); + Utils.log(e); } return this; From 74e39016c51a9bde30a5608ba7d9658f0a0371f1 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 7 Jun 2010 17:20:15 -0700 Subject: [PATCH 092/369] + All event handlers sent into iQ.fn.bind and its aliases are now automatically wrapped in a try/catch (the catch simply does a Utils.log with the error) + Fixed a couple of issues found with the new event try/catch + Converted Group.newTab over to iQ (was the last jQuery bit besides drag/drop/resize) --- browser/base/content/tabview/iq.js | 32 +++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 3f73144ea746..9e76020075dd 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -697,18 +697,27 @@ iQ.fn = iQ.prototype = { bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); -/* var handler = function(e) { try { - return func(e); + return func.apply(this, [e]); } catch(e) { Utils.log(e); } }; -*/ for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.addEventListener(type, func, false); + if(!elem.iQEventData) + elem.iQEventData = {}; + + if(!elem.iQEventData[type]) + elem.iQEventData[type] = []; + + elem.iQEventData[type].push({ + original: func, + modified: handler + }); + + elem.addEventListener(type, handler, false); } return this; @@ -733,7 +742,19 @@ iQ.fn = iQ.prototype = { Utils.assert('Must provide a function', iQ.isFunction(func)); for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.removeEventListener(type, func, false); + var handler = func; + if(elem.iQEventData && elem.iQEventData[type]) { + for(var a = 0, count = elem.iQEventData[type].length; a < count; a++) { + var pair = elem.iQEventData[type][a]; + if(pair.original == func) { + handler = pair.modified; + elem.iQEventData[type].splice(a, 1); + break; + } + } + } + + elem.removeEventListener(type, handler, false); } return this; @@ -995,6 +1016,7 @@ iQ.extend({ 'keydown', 'mouseup', 'mousedown', + 'mouseover', 'mousemove', 'click', 'resize', From 726d15b41d23f3e1d301c906e23ab8b4301c95ad Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 7 Jun 2010 17:24:55 -0700 Subject: [PATCH 093/369] + documentation+ documentation+ documentation+ documentation+ documentation+ documentation+ documentation+ documentation+ documentation --- browser/base/content/tabview/iq.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9e76020075dd..f5525f30f4b8 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -694,6 +694,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: bind + // Binds the given function to the given event type. Also wraps the function + // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); From d45cf7f793b73b2c760b242d25394e6cbae480a9 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Tue, 8 Jun 2010 13:42:27 -0700 Subject: [PATCH 094/369] + Tweaked the timing function to look prettier. --- browser/base/content/tabview/iq.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f5525f30f4b8..03e1e0dd4d51 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -601,7 +601,7 @@ iQ.fn = iQ.prototype = { options = {}; var easings = { - tabcandyBounce: 'cubic-bezier(0.0, 0.35, .6, 1.4)', + tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', easeInQuad: 'ease-in' // TODO: make it a real easeInQuad, or decide we don't care }; From ffc61066d515de589fe76a6c1905d4314627c660 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 8 Jun 2010 17:13:19 -0700 Subject: [PATCH 095/369] + All dragging is now done with iQ + You can drop tabs onto groups (but no tabs on tabs yet) + Disabled resize for now + Fixed a bug with groups looking like they were deleted but not really being --- browser/base/content/tabview/iq.js | 176 ++++++++++++++++++++++++++++- 1 file changed, 171 insertions(+), 5 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f5525f30f4b8..a81027b6b07c 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -643,10 +643,15 @@ iQ.fn = iQ.prototype = { fadeOut: function(callback) { try { Utils.assert('does not yet support duration', iQ.isFunction(callback) || callback === undefined); - this.animate({opacity: 0}, 'animate350', function() { - iQ(this).css({display: 'none'}); - if(iQ.isFunction(callback)) - callback.apply(this); + this.animate({ + opacity: 0 + }, { + duration: 400, + complete: function() { + iQ(this).css({display: 'none'}); + if(iQ.isFunction(callback)) + callback.apply(this); + } }); } catch(e) { Utils.log(e); @@ -660,7 +665,11 @@ iQ.fn = iQ.prototype = { fadeIn: function() { try { this.css({display: ''}); - this.animate({opacity: 1}, 'animate350'); + this.animate({ + opacity: 1 + }, { + duration: 400 + }); } catch(e) { Utils.log(e); } @@ -760,6 +769,163 @@ iQ.fn = iQ.prototype = { } return this; + }, + + // ---------- + // Function: draggable + draggable: function(options) { + try { + if(!options) + options = {}; + + var cancelClasses = []; + if(typeof(options.cancelClass) == 'string') + cancelClasses = options.cancelClass.split(' '); + + var startMouse; + var startPos; + var elem; + var $elem; + var startSent; + var startEvent; + var droppables; + var $dropTarget; + + // ___ mousemove + var handleMouseMove = function(e) { + var mouse = new Point(e.pageX, e.pageY); + var newPos = { + left: startPos.x + (mouse.x - startMouse.x), + top: startPos.y + (mouse.y - startMouse.y) + }; + + $elem.css(newPos); + var bounds = $elem.bounds(); + var $newDropTarget = null; + iQ.each(droppables, function(index, droppable) { + if(bounds.intersects(droppable.bounds)) { + var $possibleDropTarget = iQ(droppable.element); + var accept = true; + if($possibleDropTarget != $dropTarget) { + var dropOptions = $possibleDropTarget.data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.accept)) + accept = dropOptions.accept.apply($possibleDropTarget.get(0), [elem]); + } + + if(accept) { + $newDropTarget = $possibleDropTarget; + return false; + } + } + }); + + if($newDropTarget != $dropTarget) { + var dropOptions; + if($dropTarget) { + dropOptions = $dropTarget.data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.out)) + dropOptions.out.apply($dropTarget.get(0), [e]); + } + + $dropTarget = $newDropTarget; + + if($dropTarget) { + dropOptions = $dropTarget.data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.over)) + dropOptions.over.apply($dropTarget.get(0), [e]); + } + } + + if(!startSent) { + if(iQ.isFunction(options.start)) + options.start.apply(elem, [startEvent, {position: {left: startPos.x, top: startPos.y}}]); + + startSent = true; + } + + if(iQ.isFunction(options.drag)) + options.drag.apply(elem, [e, {position: newPos}]); + + e.preventDefault(); + }; + + // ___ mouseup + var handleMouseUp = function(e) { + iQ(window) + .unbind('mousemove', handleMouseMove) + .unbind('mouseup', handleMouseUp); + + if($dropTarget) { + var dropOptions = $dropTarget.data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.drop)) + dropOptions.drop.apply($dropTarget.get(0), [e]); + } + + if(startSent && iQ.isFunction(options.stop)) + options.stop.apply(elem, [e]); + + e.preventDefault(); + }; + + // ___ mousedown + this.mousedown(function(e) { + var cancel = false; + var $target = iQ(e.target); + iQ.each(cancelClasses, function(index, class) { + if($target.hasClass(class)) { + cancel = true; + return false; + } + }); + + if(cancel) { + e.preventDefault(); + return; + } + + elem = this; + $elem = iQ(this); + var pos = $elem.position(); + startMouse = new Point(e.pageX, e.pageY); + startPos = new Point(pos.left, pos.top); + startEvent = e; + startSent = false; + $dropTarget = null; + + droppables = []; + iQ('.iq-droppable').each(function() { + droppables.push({ + element: this, + bounds: iQ(this).bounds() + }); + }); + + iQ(window) + .mousemove(handleMouseMove) + .mouseup(handleMouseUp); + + e.preventDefault(); + }); + } catch(e) { + Utils.log(e); + } + }, + + // ---------- + // Function: droppable + droppable: function(options) { + try { + if(options == 'enable') + this.addClass('iq-droppable'); + else if(options == 'disable') + this.removeClass('iq-droppable'); + else { + this.addClass('iq-droppable'); + this.data('iq-droppable', options); + } + } catch(e) { + Utils.log(e); + } } }; From 0d0b212d006eaae5336233dfa8c371dbec382535 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 9 Jun 2010 14:45:48 -0700 Subject: [PATCH 096/369] + We are entirely jQuery-free in revision-a! + You can now drop tabs on tabs + Resize works once again, but with iQ --- browser/base/content/tabview/iq.js | 184 +++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 46 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index c39d2a23b7d4..f922826d09f5 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -44,7 +44,7 @@ // ########## // Title: iq.js -// jQuery, hacked down to just the bits we need. +// jQuery, hacked down to just the bits we need, with a bunch of other stuff added. (function( window, undefined ) { var iQ = function(selector, context) { @@ -789,10 +789,11 @@ iQ.fn = iQ.prototype = { var startSent; var startEvent; var droppables; - var $dropTarget; + var dropTarget; // ___ mousemove var handleMouseMove = function(e) { + // positioning var mouse = new Point(e.pageX, e.pageY); var newPos = { left: startPos.x + (mouse.x - startMouse.x), @@ -800,42 +801,8 @@ iQ.fn = iQ.prototype = { }; $elem.css(newPos); - var bounds = $elem.bounds(); - var $newDropTarget = null; - iQ.each(droppables, function(index, droppable) { - if(bounds.intersects(droppable.bounds)) { - var $possibleDropTarget = iQ(droppable.element); - var accept = true; - if($possibleDropTarget != $dropTarget) { - var dropOptions = $possibleDropTarget.data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.accept)) - accept = dropOptions.accept.apply($possibleDropTarget.get(0), [elem]); - } - - if(accept) { - $newDropTarget = $possibleDropTarget; - return false; - } - } - }); - if($newDropTarget != $dropTarget) { - var dropOptions; - if($dropTarget) { - dropOptions = $dropTarget.data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.out)) - dropOptions.out.apply($dropTarget.get(0), [e]); - } - - $dropTarget = $newDropTarget; - - if($dropTarget) { - dropOptions = $dropTarget.data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.over)) - dropOptions.over.apply($dropTarget.get(0), [e]); - } - } - + // drag events if(!startSent) { if(iQ.isFunction(options.start)) options.start.apply(elem, [startEvent, {position: {left: startPos.x, top: startPos.y}}]); @@ -846,6 +813,43 @@ iQ.fn = iQ.prototype = { if(iQ.isFunction(options.drag)) options.drag.apply(elem, [e, {position: newPos}]); + // drop events + var bounds = $elem.bounds(); + var newDropTarget = null; + iQ.each(droppables, function(index, droppable) { + if(bounds.intersects(droppable.bounds)) { + var possibleDropTarget = droppable.element; + var accept = true; + if(possibleDropTarget != dropTarget) { + var dropOptions = iQ(possibleDropTarget).data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.accept)) + accept = dropOptions.accept.apply(possibleDropTarget, [elem]); + } + + if(accept) { + newDropTarget = possibleDropTarget; + return false; + } + } + }); + + if(newDropTarget != dropTarget) { + var dropOptions; + if(dropTarget) { + dropOptions = iQ(dropTarget).data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.out)) + dropOptions.out.apply(dropTarget, [e]); + } + + dropTarget = newDropTarget; + + if(dropTarget) { + dropOptions = iQ(dropTarget).data('iq-droppable'); + if(dropOptions && iQ.isFunction(dropOptions.over)) + dropOptions.over.apply(dropTarget, [e]); + } + } + e.preventDefault(); }; @@ -855,10 +859,10 @@ iQ.fn = iQ.prototype = { .unbind('mousemove', handleMouseMove) .unbind('mouseup', handleMouseUp); - if($dropTarget) { - var dropOptions = $dropTarget.data('iq-droppable'); + if(dropTarget) { + var dropOptions = iQ(dropTarget).data('iq-droppable'); if(dropOptions && iQ.isFunction(dropOptions.drop)) - dropOptions.drop.apply($dropTarget.get(0), [e]); + dropOptions.drop.apply(dropTarget, [e]); } if(startSent && iQ.isFunction(options.stop)) @@ -869,6 +873,9 @@ iQ.fn = iQ.prototype = { // ___ mousedown this.mousedown(function(e) { + if(Utils.isRightClick(e)) + return; + var cancel = false; var $target = iQ(e.target); iQ.each(cancelClasses, function(index, class) { @@ -890,14 +897,16 @@ iQ.fn = iQ.prototype = { startPos = new Point(pos.left, pos.top); startEvent = e; startSent = false; - $dropTarget = null; + dropTarget = null; droppables = []; iQ('.iq-droppable').each(function() { - droppables.push({ - element: this, - bounds: iQ(this).bounds() - }); + if(this != elem) { + droppables.push({ + element: this, + bounds: iQ(this).bounds() + }); + } }); iQ(window) @@ -921,11 +930,94 @@ iQ.fn = iQ.prototype = { this.removeClass('iq-droppable'); else { this.addClass('iq-droppable'); - this.data('iq-droppable', options); + this.data('iq-droppable', options || {}); } } catch(e) { Utils.log(e); } + }, + + // ---------- + // Function: resizable + resizable: function(options) { + try { + iQ('.iq-resizable-handle', this).remove(); + + if(options == 'destroy') { + this.removeClass('iq-resizable'); + } else { + if(!options) + options = {}; + + this.addClass('iq-resizable'); + + var startMouse; + var startSize; + var elem; + var $elem; + + // ___ mousemove + var handleMouseMove = function(e) { + var mouse = new Point(e.pageX, e.pageY); + var newSize = { + width: Math.max(options.minWidth || 0, startSize.x + (mouse.x - startMouse.x)), + height: Math.max(options.minHeight || 0, startSize.y + (mouse.y - startMouse.y)) + }; + + if(options.aspectRatio) { + if(startAspect < 1) + newSize.height = newSize.width * startAspect; + else + newSize.width = newSize.height / startAspect; + } + + $elem.css(newSize); + + if(iQ.isFunction(options.resize)) + options.resize.apply(elem, [e]); + + e.preventDefault(); + e.stopPropagation(); + }; + + // ___ mouseup + var handleMouseUp = function(e) { + iQ(window) + .unbind('mousemove', handleMouseMove) + .unbind('mouseup', handleMouseUp); + + if(iQ.isFunction(options.stop)) + options.stop.apply(elem, [e]); + + e.preventDefault(); + e.stopPropagation(); + }; + + // ___ handle + mousedown + iQ('
') + .addClass('iq-resizable-handle iq-resizable-se') + .appendTo(this) + .mousedown(function(e) { + if(Utils.isRightClick(e)) + return; + + elem = this.parentNode; + $elem = iQ(elem); + startMouse = new Point(e.pageX, e.pageY); + startSize = new Point($elem.width(), $elem.height()); + startAspect = startSize.y / startSize.x; + + iQ(window) + .mousemove(handleMouseMove) + .mouseup(handleMouseUp); + + e.preventDefault(); + e.stopPropagation(); + }); + } + } catch(e) { + Utils.log(e); + } } }; From bd7acd124adc8d236b71fdbe07e4db3b8f8e0a23 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 9 Jun 2010 19:07:12 -0400 Subject: [PATCH 097/369] Bug 566500 - e10s: Move viewport metadata support into the browser XBL binding [r=mfinkle] --- toolkit/content/Geometry.jsm | 145 ++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 63 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 21253914f791..ea15a58fe5ff 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -85,6 +85,18 @@ let Util = { dump("\n"); }, + getWindowUtils: function getWindowUtils(aWindow) { + return aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + }, + + getScrollOffset: function getScrollOffset(aWindow) { + var cwu = Util.getWindowUtils(aWindow); + var scrollX = {}; + var scrollY = {}; + cwu.getScrollXY(false, scrollX, scrollY); + return new Point(scrollX.value, scrollY.value); + }, + /** Executes aFunc after other events have been processed. */ executeSoon: function executeSoon(aFunc) { let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); @@ -122,69 +134,6 @@ let Util = { return makeURI(url, null, makeURI(base)).spec; }, - getViewportMetadata: function getViewportMetadata(browser) { - let dpiScale = gPrefService.getIntPref("zoom.dpiScale") / 100; - - let doctype = browser.contentDocument.doctype; - if (doctype && /(WAP|WML|Mobile)/.test(doctype.publicId)) - return { defaultZoom: dpiScale, autoSize: true }; - - let windowUtils = browser.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - let handheldFriendly = windowUtils.getDocumentMetadata("HandheldFriendly"); - if (handheldFriendly == "true") - return { defaultZoom: dpiScale, autoSize: true }; - - if (browser.contentDocument instanceof XULDocument) - return { defaultZoom: 1.0, autoSize: true, allowZoom: false }; - - // viewport details found here - // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html - // http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariWebContent/UsingtheViewport/UsingtheViewport.html - - // Note: These values will be NaN if parseFloat or parseInt doesn't find a number. - // Remember that NaN is contagious: Math.max(1, NaN) == Math.min(1, NaN) == NaN. - let viewportScale = parseFloat(windowUtils.getDocumentMetadata("viewport-initial-scale")); - let viewportMinScale = parseFloat(windowUtils.getDocumentMetadata("viewport-minimum-scale")); - let viewportMaxScale = parseFloat(windowUtils.getDocumentMetadata("viewport-maximum-scale")); - let viewportWidthStr = windowUtils.getDocumentMetadata("viewport-width"); - let viewportHeightStr = windowUtils.getDocumentMetadata("viewport-height"); - - viewportScale = Util.clamp(viewportScale, kViewportMinScale, kViewportMaxScale); - viewportMinScale = Util.clamp(viewportMinScale, kViewportMinScale, kViewportMaxScale); - viewportMaxScale = Util.clamp(viewportMaxScale, kViewportMinScale, kViewportMaxScale); - - // If initial scale is 1.0 and width is not set, assume width=device-width - let autoSize = (viewportWidthStr == "device-width" || - viewportHeightStr == "device-height" || - (viewportScale == 1.0 && !viewportWidthStr)); - - let viewportWidth = Util.clamp(parseInt(viewportWidthStr), kViewportMinWidth, kViewportMaxWidth); - let viewportHeight = Util.clamp(parseInt(viewportHeightStr), kViewportMinHeight, kViewportMaxHeight); - - // Zoom level is the final (device pixel : CSS pixel) ratio for content. - // Since web content specifies scale as (reference pixel : CSS pixel) ratio, - // multiply the requested scale by a constant (device pixel : reference pixel) - // factor to account for high DPI devices. - // - // See bug 561445 or any of the examples of chrome/tests/browser_viewport_XX.html - // for more information and examples. - let defaultZoom = viewportScale * dpiScale; - let minZoom = viewportMinScale * dpiScale; - let maxZoom = viewportMaxScale * dpiScale; - - return { - defaultZoom: defaultZoom, - minZoom: minZoom, - maxZoom: maxZoom, - width: viewportWidth, - height: viewportHeight, - autoSize: autoSize, - allowZoom: windowUtils.getDocumentMetadata("viewport-user-scalable") != "no" - }; - }, - clamp: function(num, min, max) { return Math.max(min, Math.min(max, num)); }, @@ -249,12 +198,82 @@ let Util = { gIOService.offline = false; #endif }, + + /** Capitalize first letter of a string. */ + capitalize: function(str) { + return str.charAt(0).toUpperCase() + str.substring(1); + }, isPortrait: function isPortrait() { return (window.innerWidth < 500); } }; + +/** + * Helper class to nsITimer that adds a little more pizazz. Callback can be an + * object with a notify method or a function. + */ +Util.Timeout = function(aCallback) { + this._callback = aCallback; + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._active = false; +} + +Util.Timeout.prototype = { + /** Timer callback. Don't call this manually. */ + notify: function notify() { + this._active = false; + if (this._callback.notify) + this._callback.notify(); + else + this._callback.apply(null); + }, + + /** Do the callback once. Cancels other timeouts on this object. */ + once: function once(aDelay, aCallback) { + if (aCallback) + this._callback = aCallback; + this.clear(); + this._timer.initWithCallback(this, aDelay, this._timer.TYPE_ONE_SHOT); + this._active = true; + return this; + }, + + /** Do the callback every aDelay msecs. Cancels other timeouts on this object. */ + interval: function interval(aDelay, aCallback) { + if (aCallback) + this._callback = aCallback; + this.clear(); + this._timer.initWithCallback(this, aDelay, this._timer.TYPE_REPEATING_SLACK); + this._active = true; + return this; + }, + + /** Clear any pending timeouts. */ + clear: function clear() { + if (this._active) { + this._timer.cancel(); + this._active = false; + } + return this; + }, + + /** If there is a pending timeout, call it and cancel the timeout. */ + flush: function flush() { + if (this._active) { + this.clear(); + this.notify(); + } + return this; + }, + + /** Return true iff we are waiting for a callback. */ + isPending: function isPending() { + return this._active; + } +}; + /** * Cache of commonly used elements. */ From 0ae6da1b158c21446ab46037bf1f45d3011edfbd Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 9 Jun 2010 17:30:02 -0700 Subject: [PATCH 098/369] + Fixed z-index issue (and added a unit test for it) + Fixed some of the tab sizing issues (like when returning from a tab) --- browser/base/content/tabview/iq.js | 32 +++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f922826d09f5..e7a7930314c3 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -420,27 +420,31 @@ iQ.fn = iQ.prototype = { // Function: width width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - return this[0].clientWidth; +/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ + return parseInt(this.css('width')); //this[0].clientWidth; }, // ---------- // Function: height height: function(unused) { Utils.assert('does not yet support setting', unused === undefined); - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - return this[0].clientHeight; +/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ + return parseInt(this.css('height')); //this[0].clientHeight; }, // ---------- // Function: position position: function(unused) { Utils.assert('does not yet support setting', unused === undefined); - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - var el = this[0]; +/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ +/* var el = this[0]; */ return { + left: parseInt(this.css('left')), + top: parseInt(this.css('top')) +/* left: (parseInt(el.style.left) || el.offsetLeft), top: (parseInt(el.style.top) || el.offsetTop) +*/ }; }, @@ -536,18 +540,18 @@ iQ.fn = iQ.prototype = { // Function: css css: function(a, b) { var properties = null; -/* - var subsitutions = { - '-moz-transform': 'MozTransform', - 'z-index': 'zIndex' - }; -*/ if(typeof a === 'string') { - var key = a; //(subsitutions[a] || a); + var key = a; if(b === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); - return window.getComputedStyle(this[0], null).getPropertyValue(key); + + var substitutions = { + 'MozTransform': '-moz-transform', + 'zIndex': 'z-index' + }; + + return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key); } else { properties = {}; properties[key] = b; From a0507cee7401907ee8d1d243bee4560103ca0e3d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 10 Jun 2010 12:04:39 -0700 Subject: [PATCH 099/369] + Added iQ.timeout, which wraps setTimeout in try/catch. Everything uses this now --- browser/base/content/tabview/iq.js | 51 +++++++++++++++--------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index e7a7930314c3..cc4cde8c4386 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -420,31 +420,23 @@ iQ.fn = iQ.prototype = { // Function: width width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); -/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ - return parseInt(this.css('width')); //this[0].clientWidth; + return parseInt(this.css('width')); }, // ---------- // Function: height height: function(unused) { Utils.assert('does not yet support setting', unused === undefined); -/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ - return parseInt(this.css('height')); //this[0].clientHeight; + return parseInt(this.css('height')); }, // ---------- // Function: position position: function(unused) { Utils.assert('does not yet support setting', unused === undefined); -/* Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); */ -/* var el = this[0]; */ return { left: parseInt(this.css('left')), top: parseInt(this.css('top')) -/* - left: (parseInt(el.style.left) || el.offsetLeft), - top: (parseInt(el.style.top) || el.offsetTop) -*/ }; }, @@ -621,19 +613,15 @@ iQ.fn = iQ.prototype = { this.css(css); var self = this; - setTimeout(function() { - try { - self.css({ - '-moz-transition-property': 'none', - '-moz-transition-duration': '', - '-moz-transition-timing-function': '' - }); - - if(iQ.isFunction(options.complete)) - options.complete.apply(self); - } catch(e) { - Utils.log(e); - } + iQ.timeout(function() { + self.css({ + '-moz-transition-property': 'none', + '-moz-transition-duration': '', + '-moz-transition-timing-function': '' + }); + + if(iQ.isFunction(options.complete)) + options.complete.apply(self); }, duration); } catch(e) { Utils.log(e); @@ -774,7 +762,7 @@ iQ.fn = iQ.prototype = { return this; }, - + // ---------- // Function: draggable draggable: function(options) { @@ -1269,7 +1257,20 @@ iQ.extend({ } return ret; - } + }, + + // ---------- + // Function: timeout + // wraps setTimeout with try/catch + timeout: function(func, delay) { + setTimeout(function() { + try { + func(); + } catch(e) { + Utils.log(e); + } + }, delay); + } }); // ---------- From 5ba54d4fdf0745222ac86ed526ccc6d933ac2cfd Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Thu, 10 Jun 2010 14:35:15 -0700 Subject: [PATCH 100/369] + A work-around for supporting transitions to and from 'auto' values of left, top, width, height, etc. + For more details see: https://bugzilla.mozilla.org/show_bug.cgi?id=571344 --- browser/base/content/tabview/iq.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index e7a7930314c3..b74e54fc5a9f 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -612,6 +612,19 @@ iQ.fn = iQ.prototype = { var duration = (options.duration || 400); var easing = (easings[options.easing] || 'ease'); + // The latest versions of Firefox do not animate from a non-explicitly set + // css properties. So for each element to be animated, go through and + // explicitly define 'em. + rupper = /([A-Z])/g; + this.each(function(){ + var cStyle = window.getComputedStyle(this, null); + for(var prop in css){ + prop = prop.replace( rupper, "-$1" ).toLowerCase(); + iQ(this).css(prop, cStyle.getPropertyValue(prop)); + } + }); + + this.css({ '-moz-transition-property': 'all', // TODO: just animate the properties we're changing '-moz-transition-duration': (duration / 1000) + 's', From 1f42327eb959d4bc9a74a9ac78e7152272e33ad7 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Fri, 11 Jun 2010 17:57:02 -0700 Subject: [PATCH 101/369] + Added code for making zoom-in faster too + Made the code for the speed-ups on zoom-in/zoom-out more general --- browser/base/content/tabview/iq.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 74cee2d9d611..309501bca90d 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -598,7 +598,8 @@ iQ.fn = iQ.prototype = { var easings = { tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', - easeInQuad: 'ease-in' // TODO: make it a real easeInQuad, or decide we don't care + easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care + fast: 'cubic-bezier(0.7,0,1,1)' }; var duration = (options.duration || 400); From f5531e7c7b152865fb97917c8cbc2a5f096202bd Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 15 Jun 2010 11:53:53 -0700 Subject: [PATCH 102/369] + mitcho's first pass at group snapping --- browser/base/content/tabview/iq.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 309501bca90d..f8575f25ef67 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -1013,6 +1013,9 @@ iQ.fn = iQ.prototype = { startSize = new Point($elem.width(), $elem.height()); startAspect = startSize.y / startSize.x; + if(iQ.isFunction(options.start)) + options.start.apply(elem, [e]); + iQ(window) .mousemove(handleMouseMove) .mouseup(handleMouseUp); From 555ae49cadafba1c157d0b1f393967a1360577f5 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 15 Jun 2010 15:55:47 -0700 Subject: [PATCH 103/369] + Cleaned up TabItem bounds getting and setting (the translation between our coordinates and the div style needs to be done carefully, in particular due to the padding in the tab style), and added a unit test for it --- browser/base/content/tabview/iq.js | 10 ++-------- browser/base/content/tabview/modules/utils.jsm | 10 ---------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f8575f25ef67..5235acd33b90 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -444,14 +444,8 @@ iQ.fn = iQ.prototype = { // Function: bounds bounds: function(unused) { Utils.assert('does not yet support setting', unused === undefined); - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - var el = this[0]; - return new Rect( - parseInt(el.style.left) || el.offsetLeft, - parseInt(el.style.top) || el.offsetTop, - el.clientWidth, - el.clientHeight - ); + var p = this.position(); + return new Rect(p.left, p.top, this.width(), this.height()); }, // ---------- diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index ba29365f0578..dec440de7555 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -544,16 +544,6 @@ var Utils = { return date.getTime(); }, - // ___ Geometry - getBounds: function(el) { - return new Rect( - parseInt(el.style.left) || el.offsetLeft, - parseInt(el.style.top) || el.offsetTop, - el.clientWidth, - el.clientHeight - ); - }, - // ___ Misc isDOMElement: function(object) { return (object && typeof(object.nodeType) != 'undefined' ? true : false); From 6c31429006ee8a56cf4c15a58d64d42bad7a87a5 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 16 Jun 2010 15:46:05 -0700 Subject: [PATCH 104/369] + removed unused code and reorged remainder --- .../base/content/tabview/modules/utils.jsm | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index dec440de7555..b67026ebd9f4 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -417,25 +417,6 @@ var Utils = { return files; }, - getVisualizationNames: function(callback) { - var self = this; - this.getInstallDirectory("tabcandy@aza.raskin", function(dir) { - var names = []; - dir.append('content'); - dir.append('candies'); - var files = self.getFiles(dir); - var count = files.length; - var a; - for(a = 0; a < count; a++) { - var file = files[a]; - if(file.isDirectory()) - names.push(file.leafName); - } - - callback(names); - }); - }, - // ___ Logging ilog: function(){ From e0dc68f5f2283428957e5dd4bf8fa7de7e859b09 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 16 Jun 2010 17:19:11 -0700 Subject: [PATCH 105/369] + updated/added license blocks to all JavaScript files --- browser/base/content/tabview/iq.js | 6 ++- .../base/content/tabview/modules/utils.jsm | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 5235acd33b90..9992ea9254b5 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -11,7 +11,7 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is iQ. + * The Original Code is iq.js. * * The Initial Developer of the Original Code is * Ian Gilman . @@ -20,6 +20,7 @@ * * Contributor(s): * Aza Raskin + * Michael Yoshitaka Erlewine * * Some portions copied from: * jQuery JavaScript Library v1.4.2 @@ -42,9 +43,10 @@ * * ***** END LICENSE BLOCK ***** */ -// ########## +// ********** // Title: iq.js // jQuery, hacked down to just the bits we need, with a bunch of other stuff added. + (function( window, undefined ) { var iQ = function(selector, context) { diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index b67026ebd9f4..9bdc3b4934f0 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -1,4 +1,43 @@ +/* ***** 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 utils.js. + * + * The Initial Developer of the Original Code is + * Aza Raskin + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ian Gilman + * + * 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 ***** */ + +// ********** // Title: utils.js + (function(){ const Cc = Components.classes; From 42b6ab9a7e02bb62848554ddade2026d8efffeed Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 18 Jun 2010 14:42:06 -0700 Subject: [PATCH 106/369] + Fixed: Bug 570089 - Dragging northwest in tabcandy area creates zero-area tab group --- browser/base/content/tabview/modules/utils.jsm | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 9bdc3b4934f0..3f5350395351 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -233,6 +233,19 @@ window.Rect.prototype = { this.top = a.top; this.width = a.width; this.height = a.height; + }, + + // ---------- + // Function: css + // Returns an object with the dimensions of this rectangle, suitable for passing into iQ.fn.css. + // You could of course just pass the rectangle straight in, but this is cleaner. + css: function() { + return { + left: this.left, + top: this.top, + width: this.width, + height: this.height + }; } }; From f9f1a26791a6c189f19f7a7df3e85a276f7199c3 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sat, 19 Jun 2010 16:58:51 -0400 Subject: [PATCH 107/369] class Range --- .../base/content/tabview/modules/utils.jsm | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 3f5350395351..066407a7344b 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -20,6 +20,7 @@ * * Contributor(s): * Ian Gilman + * Michael Yoshitaka Erlewine * * 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 @@ -249,6 +250,46 @@ window.Rect.prototype = { } }; +// ########## +// Class: Range +// A physical interval, with a min and max. +// +// Constructor: Range +// Creates a Range with the given min and max +window.Range = function(min, max) { + this.min = min; + this.max = max; +}; + +// ---------- +window.isRange = function(r) { + return (r + && Utils.isNumber(r.min) + && Utils.isNumber(r.max)); +}; + +window.Range.prototype = { + // ---------- + // Function: contains + // Whether the contains the given value or not + // + // Paramaters + // - a number + contains: function(value) { + return( value >= this.min && value <= this.max ); + }, + // ---------- + // Function: containsWithin + // Whether the 's interior contains the given value or not + // + // Paramaters + // - a number + containsWithin: function(value) { + return( value > this.min && value < this.max ); + }, + +}; + // ########## // Class: Subscribable // A mix-in for allowing objects to collect subscribers for custom events. From 7f4935a73cab1538cdc89308137ffbc1298e1df1 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sat, 19 Jun 2010 17:37:36 -0400 Subject: [PATCH 108/369] Range update --- .../base/content/tabview/modules/utils.jsm | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 066407a7344b..e5eb1948c018 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -257,8 +257,8 @@ window.Rect.prototype = { // Constructor: Range // Creates a Range with the given min and max window.Range = function(min, max) { - this.min = min; - this.max = max; + this.min = min || 0; + this.max = max || 0; }; // ---------- @@ -269,23 +269,39 @@ window.isRange = function(r) { }; window.Range.prototype = { + // Variable: extent + // Equivalent to max-min + get extent() { + return (this.max - this.min); + }, + + set extent(extent) { + this.max = extent - this.min; + }, + // ---------- // Function: contains // Whether the contains the given value or not // // Paramaters - // - a number + // - a number or contains: function(value) { - return( value >= this.min && value <= this.max ); + if (Utils.isNumber(value)) + return ( value >= this.min && value <= this.max ); + else if (isRange(value)) + return ( value.min >= this.min && value.max <= this.max ); }, // ---------- // Function: containsWithin // Whether the 's interior contains the given value or not // // Paramaters - // - a number + // - a number or containsWithin: function(value) { - return( value > this.min && value < this.max ); + if (Utils.isNumber(value)) + return ( value > this.min && value < this.max ); + else if (isRange(value)) + return ( value.min > this.min && value.max < this.max ); }, }; From 70bb1f6e7a9c00f085e2d7cd6c9ac186d416bb1a Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sat, 19 Jun 2010 18:47:26 -0400 Subject: [PATCH 109/369] Rect now computes its own xRange and yRange --- .../base/content/tabview/modules/utils.jsm | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index e5eb1948c018..4c7bd59c6809 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -131,7 +131,7 @@ window.Rect.prototype = { // ---------- set right(value) { - this.width = value - this.left; + this.width = value - this.left; }, // ---------- @@ -141,9 +141,17 @@ window.Rect.prototype = { // ---------- set bottom(value) { - this.height = value - this.top; + this.height = value - this.top; + }, + + get xRange() { + return new Range(this.left,this.right); }, + get yRange() { + return new Range(this.top,this.bottom); + }, + // ---------- intersects: function(rect) { return (rect.right > this.left @@ -257,8 +265,13 @@ window.Rect.prototype = { // Constructor: Range // Creates a Range with the given min and max window.Range = function(min, max) { - this.min = min || 0; - this.max = max || 0; + if (isRange(min) && !max) { // if the one variable given is a range, copy it. + this.min = min.min; + this.max = min.max; + } else { + this.min = min || 0; + this.max = max || 0; + } }; // ---------- From 0cb1b4b3bd2de42074e2c8ebc608b22f7582fd38 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Sat, 19 Jun 2010 18:46:39 -0700 Subject: [PATCH 110/369] -- Stacked Group Behavior -- + Removed the command-click quasimode for showing the tray version of stacked groups. + Added a new expand icon for showing the tray version of stacked groups + Refactored the Group.js code so that we now have both group.collapse() and group.expand() -- Website Behavior -- + Updated the website to pull the documentation section from the Etherpad + Some general DRY-based cleanup -- Install.rdf -- + Fiddled with the min/max version --- browser/base/content/tabview/iq.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9992ea9254b5..8c2c0c2e463d 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -1295,6 +1295,7 @@ iQ.extend({ 'mouseup', 'mousedown', 'mouseover', + 'mouseout', 'mousemove', 'click', 'resize', From 3e3caa9c0045437f64b69103a8d1cd522eb762c4 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Mon, 21 Jun 2010 22:04:59 +0200 Subject: [PATCH 111/369] Bug 573443 - Remove Util.Timeout in Util.js [r=mfinkle] --- toolkit/content/Geometry.jsm | 65 +----------------------------------- 1 file changed, 1 insertion(+), 64 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index ea15a58fe5ff..fc905bc9a1d7 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -210,70 +210,6 @@ let Util = { }; -/** - * Helper class to nsITimer that adds a little more pizazz. Callback can be an - * object with a notify method or a function. - */ -Util.Timeout = function(aCallback) { - this._callback = aCallback; - this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._active = false; -} - -Util.Timeout.prototype = { - /** Timer callback. Don't call this manually. */ - notify: function notify() { - this._active = false; - if (this._callback.notify) - this._callback.notify(); - else - this._callback.apply(null); - }, - - /** Do the callback once. Cancels other timeouts on this object. */ - once: function once(aDelay, aCallback) { - if (aCallback) - this._callback = aCallback; - this.clear(); - this._timer.initWithCallback(this, aDelay, this._timer.TYPE_ONE_SHOT); - this._active = true; - return this; - }, - - /** Do the callback every aDelay msecs. Cancels other timeouts on this object. */ - interval: function interval(aDelay, aCallback) { - if (aCallback) - this._callback = aCallback; - this.clear(); - this._timer.initWithCallback(this, aDelay, this._timer.TYPE_REPEATING_SLACK); - this._active = true; - return this; - }, - - /** Clear any pending timeouts. */ - clear: function clear() { - if (this._active) { - this._timer.cancel(); - this._active = false; - } - return this; - }, - - /** If there is a pending timeout, call it and cancel the timeout. */ - flush: function flush() { - if (this._active) { - this.clear(); - this.notify(); - } - return this; - }, - - /** Return true iff we are waiting for a callback. */ - isPending: function isPending() { - return this._active; - } -}; - /** * Cache of commonly used elements. */ @@ -297,6 +233,7 @@ let Elements = {}; }); }); + /** * Simple Point class. * From 4900562688e811cb12fe8bf8b09e1bf3f15ebbad Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Mon, 21 Jun 2010 22:16:37 +0200 Subject: [PATCH 112/369] Bug 573041 - Clicker code should happen on content side [r=mfinkle] --- toolkit/content/Geometry.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index fc905bc9a1d7..74fd16546359 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -317,8 +317,8 @@ Point.prototype = { function Rect(x, y, w, h) { this.left = x; this.top = y; - this.right = x+w; - this.bottom = y+h; + this.right = x + w; + this.bottom = y + h; }; Rect.fromRect = function fromRect(r) { From f3d7fae183c610cd74eb46bdc07ec37aff46e94f Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 21 Jun 2010 17:27:12 -0700 Subject: [PATCH 113/369] + Added attr to iQ, plus a unit test for it + Fixed click handler for expand button (it was allowing drag, and also it was on mousedown) + Added Ehsan and Raymond to the install.rdf contributors list --- browser/base/content/tabview/iq.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 8c2c0c2e463d..1fda415fce11 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -524,6 +524,27 @@ iQ.fn = iQ.prototype = { return this; }, + // ---------- + // Function: attr + // Sets or gets an attribute on the element(s). + attr: function(key, value) { + try { + Utils.assert('string key', typeof key === 'string'); + if(value === undefined) { + Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + return this[0].getAttribute(key); + } else { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + elem.setAttribute(key, value); + } + } + } catch(e) { + Utils.log(e); + } + + return this; + }, + // ---------- // Function: css css: function(a, b) { From 5a2c9050c09793f7b343dfb6de01b33094faf8a5 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Mon, 21 Jun 2010 17:34:34 -0700 Subject: [PATCH 114/369] + Fixed a bug in iQ whereby unadorend numbers in animations wouldn't get a "px" suffix. + This fixes the title's strange placement. --- browser/base/content/tabview/iq.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 8c2c0c2e463d..59fe21b1069f 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -610,6 +610,10 @@ iQ.fn = iQ.prototype = { for(var prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); iQ(this).css(prop, cStyle.getPropertyValue(prop)); + + // While we are looping through all of the CSS properties, it makes + // sense to add the default unit of "px" to any unadorned number. + if( typeof css[prop] == "number" ) css[prop] = css[prop] + "px" } }); From 1ecd755252916f999c9b077706e8f0cc13654ec7 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Mon, 21 Jun 2010 18:05:46 -0700 Subject: [PATCH 115/369] + Opacity was effected, so backed out my iQ change. --- browser/base/content/tabview/iq.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 01d87483b813..1fda415fce11 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -631,10 +631,6 @@ iQ.fn = iQ.prototype = { for(var prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); iQ(this).css(prop, cStyle.getPropertyValue(prop)); - - // While we are looping through all of the CSS properties, it makes - // sense to add the default unit of "px" to any unadorned number. - if( typeof css[prop] == "number" ) css[prop] = css[prop] + "px" } }); From 0449a02ad5fb4a118108d1b69b37d79a00582d02 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Mon, 21 Jun 2010 22:32:07 -0400 Subject: [PATCH 116/369] + Range.overlaps; bugfix: border trenches unfortunately had an inappropriately large activeRange... fixed. This fixes the long-distance border-snapping which unfortunately shipped with 0.4 --- browser/base/content/tabview/modules/utils.jsm | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 4c7bd59c6809..976b92b08534 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -316,7 +316,18 @@ window.Range.prototype = { else if (isRange(value)) return ( value.min > this.min && value.max < this.max ); }, - + // ---------- + // Function: overlaps + // Whether the overlaps with the given or not. + // + // Paramaters + // - a number or + overlaps: function(value) { + if (Utils.isNumber(value)) + return this.contains(value); + else if (isRange(value)) + return ( value.min <= this.max && this.min <= value.max ); + }, }; // ########## From f27b76876bf9fcef4491e05d1dcb39221806d288 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 22 Jun 2010 16:42:06 -0700 Subject: [PATCH 117/369] + In the quest to abolish reloadBounds(), I've rewritten draggable, droppable and resizable to operate on an item's bounds rather than directly on the div. I've also removed those routines from iQ, and integrated them into Item. Additional related cleanup and documentation. + Removed a little bit of aza debug code + Added size and position routines to Rect --- browser/base/content/tabview/iq.js | 252 ------------------ .../base/content/tabview/modules/utils.jsm | 10 + 2 files changed, 10 insertions(+), 252 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 1fda415fce11..e4eefd4cfc64 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -792,258 +792,6 @@ iQ.fn = iQ.prototype = { } return this; - }, - - // ---------- - // Function: draggable - draggable: function(options) { - try { - if(!options) - options = {}; - - var cancelClasses = []; - if(typeof(options.cancelClass) == 'string') - cancelClasses = options.cancelClass.split(' '); - - var startMouse; - var startPos; - var elem; - var $elem; - var startSent; - var startEvent; - var droppables; - var dropTarget; - - // ___ mousemove - var handleMouseMove = function(e) { - // positioning - var mouse = new Point(e.pageX, e.pageY); - var newPos = { - left: startPos.x + (mouse.x - startMouse.x), - top: startPos.y + (mouse.y - startMouse.y) - }; - - $elem.css(newPos); - - // drag events - if(!startSent) { - if(iQ.isFunction(options.start)) - options.start.apply(elem, [startEvent, {position: {left: startPos.x, top: startPos.y}}]); - - startSent = true; - } - - if(iQ.isFunction(options.drag)) - options.drag.apply(elem, [e, {position: newPos}]); - - // drop events - var bounds = $elem.bounds(); - var newDropTarget = null; - iQ.each(droppables, function(index, droppable) { - if(bounds.intersects(droppable.bounds)) { - var possibleDropTarget = droppable.element; - var accept = true; - if(possibleDropTarget != dropTarget) { - var dropOptions = iQ(possibleDropTarget).data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.accept)) - accept = dropOptions.accept.apply(possibleDropTarget, [elem]); - } - - if(accept) { - newDropTarget = possibleDropTarget; - return false; - } - } - }); - - if(newDropTarget != dropTarget) { - var dropOptions; - if(dropTarget) { - dropOptions = iQ(dropTarget).data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.out)) - dropOptions.out.apply(dropTarget, [e]); - } - - dropTarget = newDropTarget; - - if(dropTarget) { - dropOptions = iQ(dropTarget).data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.over)) - dropOptions.over.apply(dropTarget, [e]); - } - } - - e.preventDefault(); - }; - - // ___ mouseup - var handleMouseUp = function(e) { - iQ(window) - .unbind('mousemove', handleMouseMove) - .unbind('mouseup', handleMouseUp); - - if(dropTarget) { - var dropOptions = iQ(dropTarget).data('iq-droppable'); - if(dropOptions && iQ.isFunction(dropOptions.drop)) - dropOptions.drop.apply(dropTarget, [e]); - } - - if(startSent && iQ.isFunction(options.stop)) - options.stop.apply(elem, [e]); - - e.preventDefault(); - }; - - // ___ mousedown - this.mousedown(function(e) { - if(Utils.isRightClick(e)) - return; - - var cancel = false; - var $target = iQ(e.target); - iQ.each(cancelClasses, function(index, class) { - if($target.hasClass(class)) { - cancel = true; - return false; - } - }); - - if(cancel) { - e.preventDefault(); - return; - } - - elem = this; - $elem = iQ(this); - var pos = $elem.position(); - startMouse = new Point(e.pageX, e.pageY); - startPos = new Point(pos.left, pos.top); - startEvent = e; - startSent = false; - dropTarget = null; - - droppables = []; - iQ('.iq-droppable').each(function() { - if(this != elem) { - droppables.push({ - element: this, - bounds: iQ(this).bounds() - }); - } - }); - - iQ(window) - .mousemove(handleMouseMove) - .mouseup(handleMouseUp); - - e.preventDefault(); - }); - } catch(e) { - Utils.log(e); - } - }, - - // ---------- - // Function: droppable - droppable: function(options) { - try { - if(options == 'enable') - this.addClass('iq-droppable'); - else if(options == 'disable') - this.removeClass('iq-droppable'); - else { - this.addClass('iq-droppable'); - this.data('iq-droppable', options || {}); - } - } catch(e) { - Utils.log(e); - } - }, - - // ---------- - // Function: resizable - resizable: function(options) { - try { - iQ('.iq-resizable-handle', this).remove(); - - if(options == 'destroy') { - this.removeClass('iq-resizable'); - } else { - if(!options) - options = {}; - - this.addClass('iq-resizable'); - - var startMouse; - var startSize; - var elem; - var $elem; - - // ___ mousemove - var handleMouseMove = function(e) { - var mouse = new Point(e.pageX, e.pageY); - var newSize = { - width: Math.max(options.minWidth || 0, startSize.x + (mouse.x - startMouse.x)), - height: Math.max(options.minHeight || 0, startSize.y + (mouse.y - startMouse.y)) - }; - - if(options.aspectRatio) { - if(startAspect < 1) - newSize.height = newSize.width * startAspect; - else - newSize.width = newSize.height / startAspect; - } - - $elem.css(newSize); - - if(iQ.isFunction(options.resize)) - options.resize.apply(elem, [e]); - - e.preventDefault(); - e.stopPropagation(); - }; - - // ___ mouseup - var handleMouseUp = function(e) { - iQ(window) - .unbind('mousemove', handleMouseMove) - .unbind('mouseup', handleMouseUp); - - if(iQ.isFunction(options.stop)) - options.stop.apply(elem, [e]); - - e.preventDefault(); - e.stopPropagation(); - }; - - // ___ handle + mousedown - iQ('
') - .addClass('iq-resizable-handle iq-resizable-se') - .appendTo(this) - .mousedown(function(e) { - if(Utils.isRightClick(e)) - return; - - elem = this.parentNode; - $elem = iQ(elem); - startMouse = new Point(e.pageX, e.pageY); - startSize = new Point($elem.width(), $elem.height()); - startAspect = startSize.y / startSize.x; - - if(iQ.isFunction(options.start)) - options.start.apply(elem, [e]); - - iQ(window) - .mousemove(handleMouseMove) - .mouseup(handleMouseUp); - - e.preventDefault(); - e.stopPropagation(); - }); - } - } catch(e) { - Utils.log(e); - } } }; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 976b92b08534..7429ac6b6f1f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -193,6 +193,16 @@ window.Rect.prototype = { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, + // ---------- + size: function() { + return new Point(this.width, this.height); + }, + + // ---------- + position: function() { + return new Point(this.left, this.top); + }, + // ---------- inset: function(a, b) { if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { From 4b8db5704fe8a81aa24aefdb4aeefeb985834b33 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 22 Jun 2010 19:50:37 -0400 Subject: [PATCH 118/369] just some comments to pushAway and related functions; rm legacy squishModes; pushAway now uses Trenches.gutter for margins, and respects safe window bounds --- browser/base/content/tabview/modules/utils.jsm | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 7429ac6b6f1f..6b9d38c80283 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -166,7 +166,7 @@ window.Rect.prototype = { // the bounding rect. // // Paramaters - // - A point + // - A containsPoint: function(point){ return( point.x > this.left && point.x < this.right @@ -180,7 +180,7 @@ window.Rect.prototype = { // of the bounding rect. // // Paramaters - // - A rect + // - A contains: function(rect){ return( rect.left > this.left && rect.right < this.right @@ -193,7 +193,6 @@ window.Rect.prototype = { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, - // ---------- size: function() { return new Point(this.width, this.height); }, @@ -204,6 +203,12 @@ window.Rect.prototype = { }, // ---------- + // Function: inset + // Makes the rect smaller (if the arguments are positive) as if a margin is added all around + // the initial rect, with the margin widths (symmetric) being specified by the arguments. + // + // Paramaters + // - A or two arguments: x and y inset: function(a, b) { if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { b = a.y; @@ -217,6 +222,11 @@ window.Rect.prototype = { }, // ---------- + // Function: offset + // Moves (translates) the rect by the given vector. + // + // Paramaters + // - A or two arguments: x and y offset: function(a, b) { if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { this.left += a.x; From db039df3ccbb9a21875e8b6d0d0b661d9d05cf36 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 23 Jun 2010 17:10:16 -0700 Subject: [PATCH 119/369] + If a dragged item overlaps more than one droppable, the largest intersection wins + Added intersection and area to Rect + Cleaned out a little debug code --- .../base/content/tabview/modules/utils.jsm | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6b9d38c80283..2dc24720c744 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -144,10 +144,12 @@ window.Rect.prototype = { this.height = value - this.top; }, + // ---------- get xRange() { return new Range(this.left,this.right); }, + // ---------- get yRange() { return new Range(this.top,this.bottom); }, @@ -160,6 +162,17 @@ window.Rect.prototype = { && rect.top < this.bottom); }, + // ---------- + intersection: function(rect) { + var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0); + box.right = Math.min(rect.right, this.right); + box.bottom = Math.min(rect.bottom, this.bottom); + if(box.width > 0 && box.height > 0) + return box; + + return null; + }, + // ---------- // Function: containsPoint // Returns a boolean denoting if the is inside of @@ -193,6 +206,7 @@ window.Rect.prototype = { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, + // ---------- size: function() { return new Point(this.width, this.height); }, @@ -202,6 +216,11 @@ window.Rect.prototype = { return new Point(this.left, this.top); }, + // ---------- + area: function() { + return this.width * this.height; + }, + // ---------- // Function: inset // Makes the rect smaller (if the arguments are positive) as if a margin is added all around From 59ca7177c1b1b93d4111279419fb30beeda2a8ff Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 25 Jun 2010 16:00:51 -0700 Subject: [PATCH 120/369] + Removed icons that exist elsewhere in the browser; now using them instead + Our toolbar button now uses the standard toolbar button styling; we just apply a glyph to it + Went through the code with http://caja.appspot.com/tools/index and fixed a number of minor issues it found + Added "refresh" to the dev menu (since you won't be able to just refresh the tabcandy page once it's in the xul:deck) --- browser/base/content/tabview/iq.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index e4eefd4cfc64..a5d548cfb5b4 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -148,18 +148,12 @@ iQ.fn = iQ.prototype = { if ( ret ) { if ( iQ.isPlainObject( context ) ) { Utils.assert('does not support HTML creation with context', false); - selector = [ document.createElement( ret[1] ) ]; -/* iQ.fn.attr.call( selector, context, true ); */ - } else { selector = [ doc.createElement( ret[1] ) ]; } } else { Utils.assert('does not support complex HTML creation', false); -/* ret = doc.createDocumentFragment([match */ - ret = buildFragment( [ match[1] ], [ doc ] ); - selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; } return iQ.merge( this, selector ); @@ -453,14 +447,15 @@ iQ.fn = iQ.prototype = { // ---------- // Function: data data: function(key, value) { + var data = null; if(value === undefined) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - var data = this[0].iQData; + data = this[0].iQData; return (data ? data[key] : null); } for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - var data = elem.iQData; + data = elem.iQData; if(!data) data = elem.iQData = {}; @@ -625,7 +620,7 @@ iQ.fn = iQ.prototype = { // The latest versions of Firefox do not animate from a non-explicitly set // css properties. So for each element to be animated, go through and // explicitly define 'em. - rupper = /([A-Z])/g; + var rupper = /([A-Z])/g; this.each(function(){ var cStyle = window.getComputedStyle(this, null); for(var prop in css){ @@ -731,9 +726,9 @@ iQ.fn = iQ.prototype = { bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); - var handler = function(e) { + var handler = function(event) { try { - return func.apply(this, [e]); + return func.apply(this, [event]); } catch(e) { Utils.log(e); } From cb3ec79a4c44271d7855252b45054c7efb0e07ff Mon Sep 17 00:00:00 2001 From: Raymond Lee Date: Wed, 23 Jun 2010 17:25:34 +0800 Subject: [PATCH 121/369] Bug 572889 - Move TabCandy out of a tab and into a per-window xul:deck [r=iangilman] Update code for tabCandy in xul:deck to not assume it's living in a tab and update various events and callbacks. --HG-- extra : rebase_source : 7007f13f4e9e3ebd0eb38f91d41bccfa68b3099d --- .../base/content/tabview/modules/utils.jsm | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 2dc24720c744..53ef36dac422 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -518,20 +518,6 @@ var Utils = { return null; }, - // ---------- - // Variable: homeTab - // The tab that represents the tab candy tab. - // TODO: what if there are multiple tab candy tabs? - get homeTab(){ - for( var i=0; i Date: Wed, 23 Jun 2010 15:16:01 -0700 Subject: [PATCH 122/369] Bug 574117 - Page content never updates [r=mfinkle] --- toolkit/content/Geometry.jsm | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 74fd16546359..2d2dafa46586 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -210,6 +210,72 @@ let Util = { }; +/** + * Helper class to nsITimer that adds a little more pizazz. Callback can be an + * object with a notify method or a function. + */ +Util.Timeout = function(aCallback) { + this._callback = aCallback; + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + this._active = false; +} + +Util.Timeout.prototype = { + /** Timer callback. Don't call this manually. */ + notify: function notify() { + this._active = false; + if (this._callback.notify) + this._callback.notify(); + else + this._callback.apply(null); + }, + + /** Do the callback once. Cancels other timeouts on this object. */ + once: function once(aDelay, aCallback) { + if (aCallback) + this._callback = aCallback; + this.clear(); + this._timer.initWithCallback(this, aDelay, this._timer.TYPE_ONE_SHOT); + this._active = true; + return this; + }, + + /** Do the callback every aDelay msecs. Cancels other timeouts on this object. */ + interval: function interval(aDelay, aCallback) { + if (aCallback) + this._callback = aCallback; + this.clear(); + this._timer.initWithCallback(this, aDelay, this._timer.TYPE_REPEATING_SLACK); + this._active = true; + return this; + }, + + /** Clear any pending timeouts. */ + clear: function clear() { + if (this._active) { + this._timer.cancel(); + this._active = false; + } + return this; + }, + + /** If there is a pending timeout, call it and cancel the timeout. */ + flush: function flush() { + if (this._active) { + this.clear(); + this.notify(); + } + return this; + }, + + /** Return true iff we are waiting for a callback. */ + isPending: function isPending() { + return this._active; + } +}; + + + /** * Cache of commonly used elements. */ From f7bccb3b8ba60572c2fed8b42f126a510991a438 Mon Sep 17 00:00:00 2001 From: Jaakko Kiviluoto Date: Fri, 25 Jun 2010 17:16:01 -0400 Subject: [PATCH 123/369] Bug 437957 - Animate zoom [r=mfinkle] --- toolkit/content/Geometry.jsm | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 2d2dafa46586..3752fe7a2ccd 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -591,4 +591,33 @@ Rect.prototype = { return result; }, + + /** + * Blends two rectangles together. + * @param rect Rectangle to blend this one with + * @param scalar Ratio from 0 (returns a clone of this rect) to 1 (clone of rect). + * @return New blended rectangle. + */ + blend: function blend(rect, scalar) { + return new Rect( + this.left + (rect.left - this.left ) * scalar, + this.top + (rect.top - this.top ) * scalar, + this.width + (rect.width - this.width ) * scalar, + this.height + (rect.height - this.height) * scalar); + }, + + /** + * Grows or shrinks the rectangle while keeping the center point. + * Accepts single multipler, or separate for both axes. + */ + inflate: function inflate(xscl, yscl) { + let xAdj = (this.width * xscl - this.width) / 2; + let s = (arguments.length > 1) ? yscl : xscl; + let yAdj = (this.height * s - this.height) / 2; + this.left -= xAdj; + this.right += xAdj; + this.top -= yAdj; + this.bottom += yAdj; + return this; + } }; From 0f85e469fa4644d5f0519a8240dc96ba4a8e5d94 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sun, 27 Jun 2010 12:44:15 -0400 Subject: [PATCH 124/369] Bug 572980 - e10s: HttpChannelChild incorrectly refcounted. r=cjones, jduell. This version adds checks to make sure child doesn't try to send any IPDL msgs to parent after IPDL has been shut down. --- netwerk/ipc/NeckoChild.cpp | 4 +- netwerk/protocol/http/HttpChannelChild.cpp | 56 +++++++++---------- netwerk/protocol/http/HttpChannelChild.h | 8 ++- netwerk/protocol/http/HttpChannelParent.cpp | 7 --- netwerk/protocol/http/HttpChannelParent.h | 1 - netwerk/protocol/http/PHttpChannel.ipdl | 2 - netwerk/test/unit/test_channel_close.js | 55 ++++++++++++++++++ .../test/unit_ipc/test_channel_close_wrap.js | 3 + 8 files changed, 95 insertions(+), 41 deletions(-) create mode 100644 netwerk/test/unit/test_channel_close.js create mode 100644 netwerk/test/unit_ipc/test_channel_close_wrap.js diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index 0da3b9e0a1da..1b88f69ab32d 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -99,8 +99,8 @@ NeckoChild::DeallocPHttpChannel(PHttpChannelChild* channel) { NS_ABORT_IF_FALSE(IsNeckoChild(), "DeallocPHttpChannel called by non-child!"); - // Delete channel (HttpChannelChild's refcnt must already hit 0 to get here) - delete channel; + HttpChannelChild* child = static_cast(channel); + child->ReleaseIPDLReference(); return true; } diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 68a769d3a328..1061318735cc 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -58,6 +58,7 @@ HttpChannelChild::HttpChannelChild() , mCacheEntryAvailable(PR_FALSE) , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) , mState(HCC_NEW) + , mIPCOpen(false) { LOG(("Creating HttpChannelChild @%x\n", this)); } @@ -73,18 +74,7 @@ HttpChannelChild::~HttpChannelChild() // Override nsHashPropertyBag's AddRef: we don't need thread-safe refcnt NS_IMPL_ADDREF(HttpChannelChild) -NS_IMPL_RELEASE_WITH_DESTROY(HttpChannelChild, RefcountHitZero()) - -void -HttpChannelChild::RefcountHitZero() -{ - if (mWasOpened) { - // NeckoChild::DeallocPHttpChannel will delete this - PHttpChannelChild::Send__delete__(this); - } else { - delete this; // we never opened IPDL channel - } -} +NS_IMPL_RELEASE(HttpChannelChild) NS_INTERFACE_MAP_BEGIN(HttpChannelChild) NS_INTERFACE_MAP_ENTRY(nsIRequest) @@ -105,6 +95,22 @@ NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) // HttpChannelChild::PHttpChannelChild //----------------------------------------------------------------------------- +void +HttpChannelChild::AddIPDLReference() +{ + NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference"); + mIPCOpen = true; + AddRef(); +} + +void +HttpChannelChild::ReleaseIPDLReference() +{ + NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference"); + mIPCOpen = false; + Release(); +} + bool HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& useResponseHead, @@ -186,7 +192,7 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) mIsPending = PR_FALSE; mStatus = statusCode; - nsresult rv = mListener->OnStopRequest(this, mListenerContext, statusCode); + mListener->OnStopRequest(this, mListenerContext, statusCode); mListener = 0; mListenerContext = 0; mCacheEntryAvailable = PR_FALSE; @@ -194,15 +200,9 @@ HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) if (mLoadGroup) mLoadGroup->RemoveRequest(this, nsnull, statusCode); - SendOnStopRequestCompleted(); - - // Corresponding AddRef in AsyncOpen(). - this->Release(); - - if (NS_FAILED(rv)) { - // TODO: Cancel request: see OnStartRequest (bug 536317) - return false; - } + // This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL + // holds the last reference. Don't rely on |this| existing after here. + PHttpChannelChild::Send__delete__(this); return true; } @@ -377,6 +377,10 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) tabChild = static_cast(iTabChild.get()); } + // The socket transport layer in the chrome process now has a logical ref to + // us, until either OnStopRequest or OnRedirect is called. + AddIPDLReference(); + gNeckoChild->SendPHttpChannelConstructor(this, tabChild); SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI), IPC::URI(mDocumentURI), @@ -385,10 +389,6 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) uploadStreamInfo, mPriority, mRedirectionLimit, mAllowPipelining, mForceAllowThirdPartyCookie); - // The socket transport layer in the chrome process now has a logical ref to - // us, until either OnStopRequest or OnRedirect is called. - this->AddRef(); - mState = HCC_OPENED; return NS_OK; } @@ -453,7 +453,7 @@ HttpChannelChild::GetCacheTokenCachedCharset(nsACString &_retval) NS_IMETHODIMP HttpChannelChild::SetCacheTokenCachedCharset(const nsACString &aCharset) { - if (!mCacheEntryAvailable) + if (!mCacheEntryAvailable || !mIPCOpen) return NS_ERROR_NOT_AVAILABLE; mCachedCharset = aCharset; @@ -523,7 +523,7 @@ HttpChannelChild::SetPriority(PRInt32 aPriority) if (mPriority == newValue) return NS_OK; mPriority = newValue; - if (mWasOpened) + if (mIPCOpen) SendSetPriority(mPriority); return NS_OK; } diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index a9914353b721..16ebca79acfc 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -112,8 +112,13 @@ public: // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); + // IPDL holds a reference while the PHttpChannel protocol is live (starting at + // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of + // which call NeckoChild::DeallocPHttpChannel()). + void AddIPDLReference(); + void ReleaseIPDLReference(); + protected: - void RefcountHitZero(); bool RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& useResponseHead, const PRBool& isFromCache, @@ -137,6 +142,7 @@ private: // FIXME: replace with IPDL states (bug 536319) enum HttpChannelChildState mState; + bool mIPCOpen; }; } // namespace net diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 9baeb018a7d6..ddc1571f1095 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -191,13 +191,6 @@ HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset) return true; } -bool -HttpChannelParent::RecvOnStopRequestCompleted() -{ - mCacheDescriptor = nsnull; - return true; -} - //----------------------------------------------------------------------------- // HttpChannelParent::nsIRequestObserver //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index bc4ca954c32a..d64277a9464d 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -90,7 +90,6 @@ protected: virtual bool RecvSetPriority(const PRUint16& priority); virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset); - virtual bool RecvOnStopRequestCompleted(); virtual void ActorDestroy(ActorDestroyReason why); diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index dfbc5fe7fe98..77ebf1827243 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -80,8 +80,6 @@ parent: SetCacheTokenCachedCharset(nsCString charset); - OnStopRequestCompleted(); - child: OnStartRequest(nsHttpResponseHead responseHead, PRBool useResponseHead, diff --git a/netwerk/test/unit/test_channel_close.js b/netwerk/test/unit/test_channel_close.js new file mode 100644 index 000000000000..56ea5787db35 --- /dev/null +++ b/netwerk/test/unit/test_channel_close.js @@ -0,0 +1,55 @@ +do_load_httpd_js(); + +var httpserver = new nsHttpServer(); +var testpath = "/simple"; +var httpbody = "0123456789"; + +var live_channels = []; + +function run_test() { + httpserver.registerPathHandler(testpath, serverHandler); + httpserver.start(4444); + + var local_channel; + + // Opened channel that has no remaining references on shutdown + local_channel = setupChannel(testpath); + local_channel.asyncOpen( + new ChannelListener(checkRequest, local_channel), null); + + // Opened channel that has no remaining references after being opened + setupChannel(testpath).asyncOpen( + new ChannelListener(function() {}, null), null); + + // Unopened channel that has remaining references on shutdown + live_channels.push(setupChannel(testpath)); + + // Opened channel that has remaining references on shutdown + live_channels.push(setupChannel(testpath)); + live_channels[1].asyncOpen( + new ChannelListener(checkRequestFinish, live_channels[1]), null); + + do_test_pending(); +} + +function setupChannel(path) { + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var chan = ios.newChannel("http://localhost:4444" + path, "", null); + chan.QueryInterface(Ci.nsIHttpChannel); + chan.requestMethod = "GET"; + return chan; +} + +function serverHandler(metadata, response) { + response.setHeader("Content-Type", "text/plain", false); + response.bodyOutputStream.write(httpbody, httpbody.length); +} + +function checkRequest(request, data, context) { + do_check_eq(data, httpbody); +} + +function checkRequestFinish(request, data, context) { + checkRequest(request, data, context); + httpserver.stop(do_test_finished); +} diff --git a/netwerk/test/unit_ipc/test_channel_close_wrap.js b/netwerk/test/unit_ipc/test_channel_close_wrap.js new file mode 100644 index 000000000000..ead99995793c --- /dev/null +++ b/netwerk/test/unit_ipc/test_channel_close_wrap.js @@ -0,0 +1,3 @@ +function run_test() { + run_test_in_child("../unit/test_channel_close.js"); +} From cf06601b9206c83592f7262a49726dafeff8f00b Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Tue, 29 Jun 2010 14:15:07 -0400 Subject: [PATCH 125/369] Bug 574006 - Support context menu and open-in-new-tab behavior [r=vingtetun] --- toolkit/content/Geometry.jsm | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 3752fe7a2ccd..46408f3a9b7c 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -114,9 +114,9 @@ let Util = { let link = null; while (target) { - if (target instanceof HTMLAnchorElement || - target instanceof HTMLAreaElement || - target instanceof HTMLLinkElement) { + if (target instanceof Ci.nsIDOMHTMLAnchorElement || + target instanceof Ci.nsIDOMHTMLAreaElement || + target instanceof Ci.nsIDOMHTMLLinkElement) { if (target.hasAttribute("href")) link = target; } @@ -129,9 +129,13 @@ let Util = { return null; }, + makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) { + return gIOService.newURI(aURL, aOriginCharset, aBaseURI); + }, + makeURLAbsolute: function makeURLAbsolute(base, url) { // Note: makeURI() will throw if url is not a valid URI - return makeURI(url, null, makeURI(base)).spec; + return this.makeURI(url, null, this.makeURI(base)).spec; }, clamp: function(num, min, max) { From 4c5dd290a46db3adc0e57a9deab11a4d671b7e35 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 1 Jul 2010 17:05:46 -0700 Subject: [PATCH 126/369] + Removed placebo hack from Storage.wipe() + Removed TabItems.reconstitute(), which we haven't actually needed for a while, but now it's also causing trouble + Removed Utils.activeWindow, which doesn't seem to work, and replaced its use in Utils.activeTab (the only remaining place it was being used) with Utils.getCurrentWindow(), which does seem to work + Utils.assert() now dumps a trace + Fixed paths to the edit pencil and new tab button images; they should now show up properly --- .../base/content/tabview/modules/utils.jsm | 40 ++++++------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 53ef36dac422..5322fb63348e 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -484,35 +484,21 @@ window.Subscribable.prototype = { var Utils = { // ___ Windows and Tabs - // ---------- - // Variable: activeWindow - get activeWindow(){ - var win = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher) - .activeWindow; - - if( win != null ) - return win; - - if(homeWindow != null) - return homeWindow; - - win = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Components.interfaces.nsIWindowMediator) - .getMostRecentWindow("navigator:browser"); - - return win; - }, - // ---------- // Variable: activeTab // The tab that represents the active tab in the active window. get activeTab(){ - var tabBrowser = this.activeWindow.gBrowser; - var rawTab = tabBrowser.selectedTab; - for( var i=0; i Date: Fri, 2 Jul 2010 13:48:46 -0400 Subject: [PATCH 127/369] Bug 576343 - Make sure search provider loads are in a remote browser [r=mbrubeck] --- toolkit/content/Geometry.jsm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 46408f3a9b7c..9c79cc8b3a48 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -138,6 +138,10 @@ let Util = { return this.makeURI(url, null, this.makeURI(base)).spec; }, + isLocalScheme: function isLocalScheme(aURL) { + return (aURL.indexOf("about:") == 0 && aURL != "about:blank") || aURL.indexOf("chrome:") == 0; + }, + clamp: function(num, min, max) { return Math.max(min, Math.min(max, num)); }, From d1e2e54e2217525603518ccc1e4fcadb53a972d4 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 2 Jul 2010 16:33:33 -0700 Subject: [PATCH 128/369] + refactored Raymond's fix to Bug 576424 --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 5322fb63348e..7a40d28d1ac3 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -481,7 +481,7 @@ window.Subscribable.prototype = { // ########## // Class: Utils // Singelton with common utility functions. -var Utils = { +var Utils = { // ___ Windows and Tabs // ---------- From f074bb3090ed98161cb86d815ae6e22f406caf45 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 7 Jul 2010 17:04:01 -0700 Subject: [PATCH 129/369] + started work on the first run experience: everything goes into a single new group, and there is an info item as well + Added "reset" to the dev menu so we can test first run (since the reset button is currently AWOL) + Added Utils.assertThrow(), an assert that throws an exception --- .../base/content/tabview/modules/utils.jsm | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 7a40d28d1ac3..6d9e8e9ce923 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -601,6 +601,26 @@ var Utils = { } }, + // Function: assertThrow + // Throws label as an exception if condition is false. + assertThrow: function(label, condition) { + if(!condition) { + var text; + if(typeof(label) == 'undefined') + text = 'badly formed assert'; + else + text = 'tabcandy assert: ' + label; + + if(typeof(printStackTrace) == 'function') { + var calls = printStackTrace(); + calls.splice(0, 3); // Remove this call and the printStackTrace calls + text += '\n' + calls.join('\n'); + } + + throw text; + } + }, + expandObject: function(obj) { var s = obj + ' = {'; for(prop in obj) { From 7c68df1b199a60ca141326b63d0f5fa9dd0d944d Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sun, 11 Jul 2010 19:48:05 -0400 Subject: [PATCH 130/369] bug 577968: removing elses preceded by returns; replacing tabs --- browser/base/content/tabview/iq.js | 927 +++++++++--------- .../base/content/tabview/modules/utils.jsm | 67 +- 2 files changed, 495 insertions(+), 499 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index a5d548cfb5b4..9de671ce5242 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -50,50 +50,50 @@ (function( window, undefined ) { var iQ = function(selector, context) { - // The iQ object is actually just the init constructor 'enhanced' - return new iQ.fn.init( selector, context ); - }, + // The iQ object is actually just the init constructor 'enhanced' + return new iQ.fn.init( selector, context ); + }, - // Map over iQ in case of overwrite - _iQ = window.iQ, + // Map over iQ in case of overwrite + _iQ = window.iQ, - // Use the correct document accordingly with window argument (sandbox) - document = window.document, + // Use the correct document accordingly with window argument (sandbox) + document = window.document, - // A central reference to the root iQ(document) - rootiQ, + // A central reference to the root iQ(document) + rootiQ, - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/, - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, - // Used for trimming whitespace - rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, + // Used for trimming whitespace + rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwnProperty = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - indexOf = Array.prototype.indexOf; + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwnProperty = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + indexOf = Array.prototype.indexOf; var rclass = /[\n\t]/g, - rspace = /\s+/, - rreturn = /\r/g, - rspecialurl = /href|src|style/, - rtype = /(button|input)/i, - rfocusable = /(button|input|object|select|textarea)/i, - rclickable = /^(a|area)$/i, - rradiocheck = /radio|checkbox/; + rspace = /\s+/, + rreturn = /\r/g, + rspecialurl = /href|src|style/, + rtype = /(button|input)/i, + rfocusable = /(button|input|object|select|textarea)/i, + rclickable = /^(a|area)$/i, + rradiocheck = /radio|checkbox/; // ########## // Class: iQ.fn @@ -105,323 +105,323 @@ iQ.fn = iQ.prototype = { // It works pretty much like jQuery(), with a few exceptions, // most notably that you can't use strings with complex html, // just simple tags like '
'. - init: function( selector, context ) { - var match, elem, ret, doc; + init: function( selector, context ) { + var match, elem, ret, doc; - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } - - // The body element only exists once, optimize finding it - if ( selector === "body" && !context ) { - this.context = document; - this[0] = document.body; - this.selector = "body"; - this.length = 1; - return this; - } + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - match = quickExpr.exec( selector ); + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + match = quickExpr.exec( selector ); - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { - // HANDLE $(html) -> $(array) - if ( match[1] ) { - doc = (context ? context.ownerDocument || context : document); + // HANDLE $(html) -> $(array) + if ( match[1] ) { + doc = (context ? context.ownerDocument || context : document); - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); - if ( ret ) { - if ( iQ.isPlainObject( context ) ) { - Utils.assert('does not support HTML creation with context', false); - } else { - selector = [ doc.createElement( ret[1] ) ]; - } + if ( ret ) { + if ( iQ.isPlainObject( context ) ) { + Utils.assert('does not support HTML creation with context', false); + } else { + selector = [ doc.createElement( ret[1] ) ]; + } - } else { - Utils.assert('does not support complex HTML creation', false); - } - - return iQ.merge( this, selector ); - - // HANDLE $("#id") - } else { - elem = document.getElementById( match[2] ); + } else { + Utils.assert('does not support complex HTML creation', false); + } + + return iQ.merge( this, selector ); + + // HANDLE $("#id") + } else { + elem = document.getElementById( match[2] ); - if ( elem ) { - this.length = 1; - this[0] = elem; - } + if ( elem ) { + this.length = 1; + this[0] = elem; + } - this.context = document; - this.selector = selector; - return this; - } + this.context = document; + this.selector = selector; + return this; + } - // HANDLE $("TAG") - } else if ( !context && /^\w+$/.test( selector ) ) { - this.selector = selector; - this.context = document; - selector = document.getElementsByTagName( selector ); - return iQ.merge( this, selector ); + // HANDLE $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + return iQ.merge( this, selector ); - // HANDLE $(expr, $(...)) - } else if ( !context || context.iq ) { - return (context || rootiQ).find( selector ); + // HANDLE $(expr, $(...)) + } else if ( !context || context.iq ) { + return (context || rootiQ).find( selector ); - // HANDLE $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return iQ( context ).find( selector ); - } + // HANDLE $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return iQ( context ).find( selector ); + } - // HANDLE $(function) - // Shortcut for document ready - } else if ( iQ.isFunction( selector ) ) { - Utils.log('iQ does not support ready functions'); - return null; - } + // HANDLE $(function) + // Shortcut for document ready + } else if ( iQ.isFunction( selector ) ) { + Utils.log('iQ does not support ready functions'); + return null; + } - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } - return iQ.makeArray( selector, this ); - }, - - // Start with an empty selector - selector: "", + return iQ.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", - // The current version of iQ being used - iq: "1.4.2", + // The current version of iQ being used + iq: "1.4.2", - // The default length of a iQ object is 0 - length: 0, - + // The default length of a iQ object is 0 + length: 0, + // ---------- // Function: toArray - toArray: function() { - return slice.call( this, 0 ); - }, + toArray: function() { + return slice.call( this, 0 ); + }, // ---------- // Function: get - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function( num ) { - return num == null ? + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? - // Return a 'clean' array - this.toArray() : + // Return a 'clean' array + this.toArray() : - // Return just the object - ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); - }, + // Return just the object + ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); + }, // ---------- // Function: pushStack - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new iQ matched element set - var ret = iQ(); + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new iQ matched element set + var ret = iQ(); - if ( iQ.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - iQ.merge( ret, elems ); - } + if ( iQ.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + iQ.merge( ret, elems ); + } - // Add the old object onto the stack (as a reference) - ret.prevObject = this; + // Add the old object onto the stack (as a reference) + ret.prevObject = this; - ret.context = this.context; + ret.context = this.context; - if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } + if ( name === "find" ) { + ret.selector = this.selector + (this.selector ? " " : "") + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } - // Return the newly-formed element set - return ret; - }, + // Return the newly-formed element set + return ret; + }, // ---------- // Function: each - // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return iQ.each( this, callback, args ); - }, - + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return iQ.each( this, callback, args ); + }, + // ---------- // Function: slice - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, // ---------- // Function: addClass - addClass: function( value ) { - if ( iQ.isFunction(value) ) { - Utils.assert('does not support function argument', false); - return null; - } + addClass: function( value ) { + if ( iQ.isFunction(value) ) { + Utils.assert('does not support function argument', false); + return null; + } - if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspace ); + if ( value && typeof value === "string" ) { + var classNames = (value || "").split( rspace ); - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; - if ( elem.nodeType === 1 ) { - if ( !elem.className ) { - elem.className = value; + if ( elem.nodeType === 1 ) { + if ( !elem.className ) { + elem.className = value; - } else { - var className = " " + elem.className + " ", setClass = elem.className; - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { - setClass += " " + classNames[c]; - } - } - elem.className = iQ.trim( setClass ); - } - } - } - } + } else { + var className = " " + elem.className + " ", setClass = elem.className; + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { + setClass += " " + classNames[c]; + } + } + elem.className = iQ.trim( setClass ); + } + } + } + } - return this; - }, + return this; + }, // ---------- // Function: removeClass - removeClass: function( value ) { - if ( iQ.isFunction(value) ) { - Utils.assert('does not support function argument', false); - return null; - } + removeClass: function( value ) { + if ( iQ.isFunction(value) ) { + Utils.assert('does not support function argument', false); + return null; + } - if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split(rspace); + if ( (value && typeof value === "string") || value === undefined ) { + var classNames = (value || "").split(rspace); - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; + for ( var i = 0, l = this.length; i < l; i++ ) { + var elem = this[i]; - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - var className = (" " + elem.className + " ").replace(rclass, " "); - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[c] + " ", " "); - } - elem.className = iQ.trim( className ); + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + var className = (" " + elem.className + " ").replace(rclass, " "); + for ( var c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[c] + " ", " "); + } + elem.className = iQ.trim( className ); - } else { - elem.className = ""; - } - } - } - } + } else { + elem.className = ""; + } + } + } + } - return this; - }, + return this; + }, // ---------- // Function: hasClass - hasClass: function( selector ) { - var className = " " + selector + " "; - for ( var i = 0, l = this.length; i < l; i++ ) { - if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { - return true; - } - } + hasClass: function( selector ) { + var className = " " + selector + " "; + for ( var i = 0, l = this.length; i < l; i++ ) { + if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } - return false; - }, + return false; + }, // ---------- // Function: find - find: function( selector ) { - var ret = [], length = 0; + find: function( selector ) { + var ret = [], length = 0; - for ( var i = 0, l = this.length; i < l; i++ ) { - length = ret.length; + for ( var i = 0, l = this.length; i < l; i++ ) { + length = ret.length; try { iQ.merge(ret, this[i].querySelectorAll( selector ) ); } catch(e) { Utils.log('iQ.find error (bad selector)', e); } - if ( i > 0 ) { - // Make sure that the results are unique - for ( var n = length; n < ret.length; n++ ) { - for ( var r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { - ret.splice(n--, 1); - break; - } - } - } - } - } + if ( i > 0 ) { + // Make sure that the results are unique + for ( var n = length; n < ret.length; n++ ) { + for ( var r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } - return iQ(ret); - }, + return iQ(ret); + }, // ---------- // Function: remove - remove: function(unused) { - Utils.assert('does not accept a selector', unused === undefined); - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, + remove: function(unused) { + Utils.assert('does not accept a selector', unused === undefined); + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, // ---------- // Function: empty - empty: function() { - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); - } - } - - return this; - }, + empty: function() { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, // ---------- // Function: width - width: function(unused) { + width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return parseInt(this.css('width')); }, // ---------- // Function: height - height: function(unused) { + height: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return parseInt(this.css('height')); }, @@ -454,7 +454,7 @@ iQ.fn = iQ.prototype = { return (data ? data[key] : null); } - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { data = elem.iQData; if(!data) @@ -486,7 +486,7 @@ iQ.fn = iQ.prototype = { return this[0].textContent; } - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); }, // ---------- @@ -498,7 +498,7 @@ iQ.fn = iQ.prototype = { } this[0].value = value; - return this; + return this; }, // ---------- @@ -528,10 +528,9 @@ iQ.fn = iQ.prototype = { if(value === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); - } else { - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.setAttribute(key, value); - } + } + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + elem.setAttribute(key, value); } } catch(e) { Utils.log(e); @@ -556,23 +555,23 @@ iQ.fn = iQ.prototype = { }; return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key); - } else { - properties = {}; - properties[key] = b; } - } else + properties = {}; + properties[key] = b; + } else { properties = a; + } - var pixels = { - 'left': true, - 'top': true, - 'right': true, - 'bottom': true, - 'width': true, - 'height': true - }; - - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + var pixels = { + 'left': true, + 'top': true, + 'right': true, + 'bottom': true, + 'width': true, + 'height': true + }; + + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { iQ.each(properties, function(key, value) { if(pixels[key] && typeof(value) != 'string') value += 'px'; @@ -734,7 +733,7 @@ iQ.fn = iQ.prototype = { } }; - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { if(!elem.iQEventData) elem.iQEventData = {}; @@ -770,7 +769,7 @@ iQ.fn = iQ.prototype = { unbind: function(type, func) { Utils.assert('Must provide a function', iQ.isFunction(func)); - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { var handler = func; if(elem.iQEventData && elem.iQEventData[type]) { for(var a = 0, count = elem.iQEventData[type].length; a < count; a++) { @@ -797,59 +796,59 @@ iQ.fn.init.prototype = iQ.fn; // ---------- // Function: extend iQ.extend = iQ.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !iQ.isFunction(target) ) { - target = {}; - } + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !iQ.isFunction(target) ) { + target = {}; + } - // extend iQ itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } + // extend iQ itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; - // Prevent never-ending loop - if ( target === copy ) { - continue; - } + // Prevent never-ending loop + if ( target === copy ) { + continue; + } - // Recurse if we're merging object literal values or arrays - if ( deep && copy && ( iQ.isPlainObject(copy) || iQ.isArray(copy) ) ) { - var clone = src && ( iQ.isPlainObject(src) || iQ.isArray(src) ) ? src - : iQ.isArray(copy) ? [] : {}; + // Recurse if we're merging object literal values or arrays + if ( deep && copy && ( iQ.isPlainObject(copy) || iQ.isArray(copy) ) ) { + var clone = src && ( iQ.isPlainObject(src) || iQ.isArray(src) ) ? src + : iQ.isArray(copy) ? [] : {}; - // Never move original objects, clone them - target[ name ] = iQ.extend( deep, clone, copy ); + // Never move original objects, clone them + target[ name ] = iQ.extend( deep, clone, copy ); - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } - // Return the modified object - return target; + // Return the modified object + return target; }; // ########## @@ -867,174 +866,174 @@ iQ.extend({ return (this.animationCount != 0); }, - // ----------- - // Function: isFunction - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, + // ----------- + // Function: isFunction + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, // ---------- // Function: isArray - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, // ---------- // Function: isPlainObject - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { - return false; - } - - // Not own constructor property must be Object - if ( obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwnProperty.call( obj, key ); - }, + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); + }, // ---------- // Function: isEmptyObject - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, - // ---------- + // ---------- // Function: each - // args is for internal usage only - each: function( object, callback, args ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || iQ.isFunction(object); + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || iQ.isFunction(object); - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } - } - } + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } - // A special, fast, case for the most common use of each - } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} - } - } + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} + } + } - return object; - }, - + return object; + }, + // ---------- // Function: trim - trim: function( text ) { - return (text || "").replace( rtrim, "" ); - }, + trim: function( text ) { + return (text || "").replace( rtrim, "" ); + }, // ---------- // Function: makeArray - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - if ( array.length == null || typeof array === "string" || iQ.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { - push.call( ret, array ); - } else { - iQ.merge( ret, array ); - } - } + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + if ( array.length == null || typeof array === "string" || iQ.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { + push.call( ret, array ); + } else { + iQ.merge( ret, array ); + } + } - return ret; - }, + return ret; + }, // ---------- // Function: inArray - inArray: function( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } + inArray: function( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } - return -1; - }, + return -1; + }, // ---------- // Function: merge - merge: function( first, second ) { - var i = first.length, j = 0; + merge: function( first, second ) { + var i = first.length, j = 0; - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } - first.length = i; + first.length = i; - return first; - }, + return first; + }, // ---------- // Function: grep - grep: function( elems, callback, inv ) { - var ret = []; + grep: function( elems, callback, inv ) { + var ret = []; - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - if ( !inv !== !callback( elems[ i ], i ) ) { - ret.push( elems[ i ] ); - } - } + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + if ( !inv !== !callback( elems[ i ], i ) ) { + ret.push( elems[ i ] ); + } + } - return ret; - }, + return ret; + }, // ---------- // Function: timeout diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6d9e8e9ce923..a3b3235b1c16 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -131,7 +131,7 @@ window.Rect.prototype = { // ---------- set right(value) { - this.width = value - this.left; + this.width = value - this.left; }, // ---------- @@ -141,17 +141,17 @@ window.Rect.prototype = { // ---------- set bottom(value) { - this.height = value - this.top; + this.height = value - this.top; }, // ---------- get xRange() { - return new Range(this.left,this.right); + return new Range(this.left,this.right); }, // ---------- get yRange() { - return new Range(this.top,this.bottom); + return new Range(this.top,this.bottom); }, // ---------- @@ -304,13 +304,13 @@ window.Rect.prototype = { // Constructor: Range // Creates a Range with the given min and max window.Range = function(min, max) { - if (isRange(min) && !max) { // if the one variable given is a range, copy it. - this.min = min.min; - this.max = min.max; - } else { - this.min = min || 0; - this.max = max || 0; - } + if (isRange(min) && !max) { // if the one variable given is a range, copy it. + this.min = min.min; + this.max = min.max; + } else { + this.min = min || 0; + this.max = max || 0; + } }; // ---------- @@ -321,15 +321,15 @@ window.isRange = function(r) { }; window.Range.prototype = { - // Variable: extent - // Equivalent to max-min - get extent() { - return (this.max - this.min); - }, + // Variable: extent + // Equivalent to max-min + get extent() { + return (this.max - this.min); + }, - set extent(extent) { - this.max = extent - this.min; - }, + set extent(extent) { + this.max = extent - this.min; + }, // ---------- // Function: contains @@ -338,10 +338,9 @@ window.Range.prototype = { // Paramaters // - a number or contains: function(value) { - if (Utils.isNumber(value)) - return ( value >= this.min && value <= this.max ); - else if (isRange(value)) - return ( value.min >= this.min && value.max <= this.max ); + return Utils.isNumber(value) ? + ( value >= this.min && value <= this.max ) : + ( value.min >= this.min && value.max <= this.max ); }, // ---------- // Function: containsWithin @@ -350,10 +349,9 @@ window.Range.prototype = { // Paramaters // - a number or containsWithin: function(value) { - if (Utils.isNumber(value)) - return ( value > this.min && value < this.max ); - else if (isRange(value)) - return ( value.min > this.min && value.max < this.max ); + return Utils.isNumber(value) ? + ( value > this.min && value < this.max ) : + ( value.min > this.min && value.max < this.max ); }, // ---------- // Function: overlaps @@ -362,10 +360,9 @@ window.Range.prototype = { // Paramaters // - a number or overlaps: function(value) { - if (Utils.isNumber(value)) - return this.contains(value); - else if (isRange(value)) - return ( value.min <= this.max && this.min <= value.max ); + return Utils.isNumber(value) ? + this.contains(value) : + ( value.min <= this.max && this.min <= value.max ); }, }; @@ -517,7 +514,7 @@ var Utils = { var tabbrowser = browserWin.gBrowser; let tabCandyContainer = browserWin.document.getElementById("tab-candy"); if (tabCandyContainer && tabCandyContainer.contentWindow == window) { - return browserWin; + return browserWin; } } @@ -673,7 +670,7 @@ var Utils = { isRightClick: function(event) { if(event.which) return (event.which == 3); - else if(event.button) + if(event.button) return (event.button == 2); return false; @@ -681,8 +678,8 @@ var Utils = { // ___ Time getMilliseconds: function() { - var date = new Date(); - return date.getTime(); + var date = new Date(); + return date.getTime(); }, // ___ Misc From 12fea0e4df6f6081b40576cf5b2a99189c02c0be Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sun, 11 Jul 2010 20:54:42 -0400 Subject: [PATCH 131/369] bug 577968: control words like if, switch, etc. should have a space after them --- browser/base/content/tabview/iq.js | 38 +++++------ .../base/content/tabview/modules/utils.jsm | 68 +++++++++---------- 2 files changed, 53 insertions(+), 53 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9de671ce5242..a9a4a0603cc6 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -448,7 +448,7 @@ iQ.fn = iQ.prototype = { // Function: data data: function(key, value) { var data = null; - if(value === undefined) { + if (value === undefined) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); data = this[0].iQData; return (data ? data[key] : null); @@ -457,7 +457,7 @@ iQ.fn = iQ.prototype = { for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { data = elem.iQData; - if(!data) + if (!data) data = elem.iQData = {}; data[key] = value; @@ -471,7 +471,7 @@ iQ.fn = iQ.prototype = { // TODO: security html: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if(value === undefined) + if (value === undefined) return this[0].innerHTML; this[0].innerHTML = value; @@ -482,7 +482,7 @@ iQ.fn = iQ.prototype = { // Function: text text: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if(value === undefined) { + if (value === undefined) { return this[0].textContent; } @@ -493,7 +493,7 @@ iQ.fn = iQ.prototype = { // Function: val val: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if(value === undefined) { + if (value === undefined) { return this[0].value; } @@ -525,7 +525,7 @@ iQ.fn = iQ.prototype = { attr: function(key, value) { try { Utils.assert('string key', typeof key === 'string'); - if(value === undefined) { + if (value === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); } @@ -544,9 +544,9 @@ iQ.fn = iQ.prototype = { css: function(a, b) { var properties = null; - if(typeof a === 'string') { + if (typeof a === 'string') { var key = a; - if(b === undefined) { + if (b === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); var substitutions = { @@ -573,10 +573,10 @@ iQ.fn = iQ.prototype = { for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { iQ.each(properties, function(key, value) { - if(pixels[key] && typeof(value) != 'string') + if (pixels[key] && typeof(value) != 'string') value += 'px'; - if(key.indexOf('-') != -1) + if (key.indexOf('-') != -1) elem.style.setProperty(key, value, ''); else elem.style[key] = value; @@ -604,7 +604,7 @@ iQ.fn = iQ.prototype = { try { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if(!options) + if (!options) options = {}; var easings = { @@ -622,7 +622,7 @@ iQ.fn = iQ.prototype = { var rupper = /([A-Z])/g; this.each(function(){ var cStyle = window.getComputedStyle(this, null); - for(var prop in css){ + for (var prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); iQ(this).css(prop, cStyle.getPropertyValue(prop)); } @@ -645,7 +645,7 @@ iQ.fn = iQ.prototype = { '-moz-transition-timing-function': '' }); - if(iQ.isFunction(options.complete)) + if (iQ.isFunction(options.complete)) options.complete.apply(self); }, duration); } catch(e) { @@ -666,7 +666,7 @@ iQ.fn = iQ.prototype = { duration: 400, complete: function() { iQ(this).css({display: 'none'}); - if(iQ.isFunction(callback)) + if (iQ.isFunction(callback)) callback.apply(this); } }); @@ -734,10 +734,10 @@ iQ.fn = iQ.prototype = { }; for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - if(!elem.iQEventData) + if (!elem.iQEventData) elem.iQEventData = {}; - if(!elem.iQEventData[type]) + if (!elem.iQEventData[type]) elem.iQEventData[type] = []; elem.iQEventData[type].push({ @@ -771,10 +771,10 @@ iQ.fn = iQ.prototype = { for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { var handler = func; - if(elem.iQEventData && elem.iQEventData[type]) { - for(var a = 0, count = elem.iQEventData[type].length; a < count; a++) { + if (elem.iQEventData && elem.iQEventData[type]) { + for (var a = 0, count = elem.iQEventData[type].length; a < count; a++) { var pair = elem.iQEventData[type][a]; - if(pair.original == func) { + if (pair.original == func) { handler = pair.modified; elem.iQEventData[type].splice(a, 1); break; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index a3b3235b1c16..86a7d975127b 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -64,7 +64,7 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] // and creates a Point with it along with y. If either a or y are omitted, // 0 is used in their place. window.Point = function(a, y) { - if(isPoint(a)) { + if (isPoint(a)) { this.x = a.x; this.y = a.y; } else { @@ -101,7 +101,7 @@ window.Point.prototype = { // and creates a Rect with it along with top, width, and height. window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' - if(isRect(a)) { + if (isRect(a)) { this.left = a.left; this.top = a.top; this.width = a.width; @@ -167,7 +167,7 @@ window.Rect.prototype = { var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0); box.right = Math.min(rect.right, this.right); box.bottom = Math.min(rect.bottom, this.bottom); - if(box.width > 0 && box.height > 0) + if (box.width > 0 && box.height > 0) return box; return null; @@ -229,7 +229,7 @@ window.Rect.prototype = { // Paramaters // - A or two arguments: x and y inset: function(a, b) { - if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + if (typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { b = a.y; a = a.x; } @@ -247,7 +247,7 @@ window.Rect.prototype = { // Paramaters // - A or two arguments: x and y offset: function(a, b) { - if(typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + if (typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { this.left += a.x; this.top += a.y; } else { @@ -382,7 +382,7 @@ window.Subscribable.prototype = { // The given callback will be called when the Subscribable fires the given event. // The refObject is used to facilitate removal if necessary. addSubscriber: function(refObject, eventName, callback) { - if(!this.subscribers[eventName]) + if (!this.subscribers[eventName]) this.subscribers[eventName] = []; var subs = this.subscribers[eventName]; @@ -390,7 +390,7 @@ window.Subscribable.prototype = { return element.refObject == refObject; }); - if(existing.length) { + if (existing.length) { Utils.assert('should only ever be one', existing.length == 1); existing[0].callback = callback; } else { @@ -405,7 +405,7 @@ window.Subscribable.prototype = { // Function: removeSubscriber // Removes the callback associated with refObject for the given event. removeSubscriber: function(refObject, eventName) { - if(!this.subscribers[eventName]) + if (!this.subscribers[eventName]) return; this.subscribers[eventName] = iQ.grep(this.subscribers[eventName], function(element) { @@ -417,7 +417,7 @@ window.Subscribable.prototype = { // Function: _sendToSubscribers // Internal routine. Used by the Subscribable to fire events. _sendToSubscribers: function(eventName, eventInfo) { - if(!this.subscribers[eventName]) + if (!this.subscribers[eventName]) return; var self = this; @@ -432,14 +432,14 @@ window.Subscribable.prototype = { // The given callback will be called when the Subscribable fires its onClose. // The referenceElement is used to facilitate removal if necessary. addOnClose: function(referenceElement, callback) { - if(!this.onCloseSubscribers) + if (!this.onCloseSubscribers) this.onCloseSubscribers = []; var existing = iQ.grep(this.onCloseSubscribers, function(element) { return element.referenceElement == referenceElement; }); - if(existing.length) { + if (existing.length) { Utils.assert('should only ever be one', existing.length == 1); existing[0].callback = callback; } else { @@ -454,7 +454,7 @@ window.Subscribable.prototype = { // Function: removeOnClose // Removes the callback associated with referenceElement for onClose notification. removeOnClose: function(referenceElement) { - if(!this.onCloseSubscribers) + if (!this.onCloseSubscribers) return; this.onCloseSubscribers = iQ.grep(this.onCloseSubscribers, function(element) { @@ -466,7 +466,7 @@ window.Subscribable.prototype = { // Function: _sendOnClose // Internal routine. Used by the Subscribable to fire onClose events. _sendOnClose: function() { - if(!this.onCloseSubscribers) + if (!this.onCloseSubscribers) return; iQ.each(this.onCloseSubscribers, function(index, object) { @@ -490,8 +490,8 @@ var Utils = { Utils.assert('tabBrowser', tabBrowser); var rawTab = tabBrowser.selectedTab; - for( var i=0; i Date: Tue, 13 Jul 2010 12:04:04 +0800 Subject: [PATCH 132/369] Bug 576110: Open tab candy at startup if it was last open --- browser/base/content/tabview/modules/utils.jsm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 86a7d975127b..275598f64dc2 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -478,7 +478,9 @@ window.Subscribable.prototype = { // ########## // Class: Utils // Singelton with common utility functions. -var Utils = { +var Utils = { + _isMac : null, + // ___ Windows and Tabs // ---------- @@ -707,6 +709,14 @@ var Utils = { } return value; + }, + + // ___ Is Mac + isMac: function() { + if (this._isMac == null) + this._isMac = (navigator.platform.search(/mac/i) > -1); + + return this._isMac; } }; From 9aef5342f2ac0836f347fb7888e29c3aa838d1ec Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 13 Jul 2010 10:36:09 -0400 Subject: [PATCH 133/369] Bug 578122 - Use Services.jsm in Fennec [r=mfinkle] --- toolkit/content/Geometry.jsm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 9c79cc8b3a48..65f0ebca1296 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -99,8 +99,7 @@ let Util = { /** Executes aFunc after other events have been processed. */ executeSoon: function executeSoon(aFunc) { - let tm = Cc["@mozilla.org/thread-manager;1"].getService(Ci.nsIThreadManager); - tm.mainThread.dispatch({ + Services.tm.mainThread.dispatch({ run: function() { aFunc(); } @@ -130,7 +129,7 @@ let Util = { }, makeURI: function makeURI(aURL, aOriginCharset, aBaseURI) { - return gIOService.newURI(aURL, aOriginCharset, aBaseURI); + return Services.io.newURI(aURL, aOriginCharset, aBaseURI); }, makeURLAbsolute: function makeURLAbsolute(base, url) { @@ -157,7 +156,7 @@ let Util = { needHomepageOverride: function needHomepageOverride() { let savedmstone = null; try { - savedmstone = gPrefService.getCharPref("browser.startup.homepage_override.mstone"); + savedmstone = Services.prefs.getCharPref("browser.startup.homepage_override.mstone"); } catch (e) {} if (savedmstone == "ignore") @@ -166,7 +165,7 @@ let Util = { #expand let ourmstone = "__MOZ_APP_VERSION__"; if (ourmstone != savedmstone) { - gPrefService.setCharPref("browser.startup.homepage_override.mstone", ourmstone); + Services.prefs.setCharPref("browser.startup.homepage_override.mstone", ourmstone); return (savedmstone ? "new version" : "new profile"); } @@ -203,7 +202,7 @@ let Util = { // process. forceOnline: function forceOnline() { #ifdef MOZ_ENABLE_LIBCONIC - gIOService.offline = false; + Services.io.offline = false; #endif }, From 2518a2912dc00123a6cbc817dd491cf67bc3f175 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 17:39:46 -0400 Subject: [PATCH 134/369] cleanup: rm iQ.makeArray --- browser/base/content/tabview/iq.js | 35 ++++++++++++------------------ 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index a9a4a0603cc6..8136e77e4f13 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -201,7 +201,20 @@ iQ.fn = iQ.prototype = { this.context = selector.context; } - return iQ.makeArray( selector, this ); + // this used to be makeArray: + var ret = this || []; + if ( selector != null ) { + // The window, strings (and functions) also have 'length' + // The extra typeof function check is to prevent crashes + // in Safari 2 (See: #3039) + if ( selector.length == null || typeof selector === "string" || iQ.isFunction(selector) || (typeof selector !== "function" && selector.setInterval) ) { + push.call( ret, selector ); + } else { + iQ.merge( ret, selector ); + } + } + return ret; + }, // Start with an empty selector @@ -962,26 +975,6 @@ iQ.extend({ return (text || "").replace( rtrim, "" ); }, - // ---------- - // Function: makeArray - // results is for internal usage only - makeArray: function( array, results ) { - var ret = results || []; - - if ( array != null ) { - // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - if ( array.length == null || typeof array === "string" || iQ.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { - push.call( ret, array ); - } else { - iQ.merge( ret, array ); - } - } - - return ret; - }, - // ---------- // Function: inArray inArray: function( elem, array ) { From 115a5ceee4a6c9428e397d5b549f94a0bb99eecf Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 19:38:51 -0400 Subject: [PATCH 135/369] cleanup: rm various instances of iQ.each where it's an array, so we can use .forEach --HG-- extra : rebase_source : f41f088641f71aba6796092766b0645fca925ad1 --- browser/base/content/tabview/iq.js | 2 +- browser/base/content/tabview/modules/utils.jsm | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 8136e77e4f13..59fa6144673c 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -1060,7 +1060,7 @@ iQ.extend({ 'focus' ]; - iQ.each(events, function(index, event) { + events.forEach(function(event) { iQ.fn[event] = function(func) { return this.bind(event, func); }; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 275598f64dc2..f0fc9cabcd4e 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -422,7 +422,7 @@ window.Subscribable.prototype = { var self = this; var subsCopy = iQ.merge([], this.subscribers[eventName]); - iQ.each(subsCopy, function(index, object) { + subsCopy.forEach(function(object) { object.callback(self, eventInfo); }); }, @@ -435,7 +435,7 @@ window.Subscribable.prototype = { if (!this.onCloseSubscribers) this.onCloseSubscribers = []; - var existing = iQ.grep(this.onCloseSubscribers, function(element) { + var existing = this.onCloseSubscribers.filter(function(element) { return element.referenceElement == referenceElement; }); @@ -457,7 +457,7 @@ window.Subscribable.prototype = { if (!this.onCloseSubscribers) return; - this.onCloseSubscribers = iQ.grep(this.onCloseSubscribers, function(element) { + this.onCloseSubscribers = this.onCloseSubscribers.filter(function(element) { return element.referenceElement == referenceElement; }, true); }, @@ -469,8 +469,9 @@ window.Subscribable.prototype = { if (!this.onCloseSubscribers) return; - iQ.each(this.onCloseSubscribers, function(index, object) { - object.callback(this); + var self = this; + this.onCloseSubscribers.forEach(function(object) { + object.callback(self); }); } }; From 66fbb972b6a802e65937a16902dc7d1bc976dd0a Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 20:10:53 -0400 Subject: [PATCH 136/369] cleanup: rm iQ.inArray --- browser/base/content/tabview/iq.js | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 59fa6144673c..05941207823e 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -975,22 +975,6 @@ iQ.extend({ return (text || "").replace( rtrim, "" ); }, - // ---------- - // Function: inArray - inArray: function( elem, array ) { - if ( array.indexOf ) { - return array.indexOf( elem ); - } - - for ( var i = 0, length = array.length; i < length; i++ ) { - if ( array[ i ] === elem ) { - return i; - } - } - - return -1; - }, - // ---------- // Function: merge merge: function( first, second ) { From 45ae6c43ea90c9f2fa47fd05dcf80acc3c557fd6 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 20:23:04 -0400 Subject: [PATCH 137/369] cleanup: rm iQ.grep; using .filter instead. --- browser/base/content/tabview/iq.js | 16 ---------------- browser/base/content/tabview/modules/utils.jsm | 12 ++++++------ 2 files changed, 6 insertions(+), 22 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 05941207823e..2e4e79dc881f 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -996,22 +996,6 @@ iQ.extend({ return first; }, - // ---------- - // Function: grep - grep: function( elems, callback, inv ) { - var ret = []; - - // Go through the array, only saving the items - // that pass the validator function - for ( var i = 0, length = elems.length; i < length; i++ ) { - if ( !inv !== !callback( elems[ i ], i ) ) { - ret.push( elems[ i ] ); - } - } - - return ret; - }, - // ---------- // Function: timeout // wraps setTimeout with try/catch diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index f0fc9cabcd4e..eb5d8391ba54 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -386,7 +386,7 @@ window.Subscribable.prototype = { this.subscribers[eventName] = []; var subs = this.subscribers[eventName]; - var existing = iQ.grep(subs, function(element) { + var existing = subs.filter(function(element) { return element.refObject == refObject; }); @@ -408,9 +408,9 @@ window.Subscribable.prototype = { if (!this.subscribers[eventName]) return; - this.subscribers[eventName] = iQ.grep(this.subscribers[eventName], function(element) { - return element.refObject == refObject; - }, true); + this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { + return element.refObject != refObject; + }); }, // ---------- @@ -458,8 +458,8 @@ window.Subscribable.prototype = { return; this.onCloseSubscribers = this.onCloseSubscribers.filter(function(element) { - return element.referenceElement == referenceElement; - }, true); + return element.referenceElement != referenceElement; + }); }, // ---------- From 2f32f45b95e397ad9a887740d3e023afde1bda1d Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 20:28:36 -0400 Subject: [PATCH 138/369] cleanup: iQ.each: never used with args, so that code is removed --- browser/base/content/tabview/iq.js | 32 +++++++----------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 2e4e79dc881f..f47e94d9d450 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -932,38 +932,20 @@ iQ.extend({ // ---------- // Function: each // args is for internal usage only - each: function( object, callback, args ) { + each: function( object, callback ) { var name, i = 0, length = object.length, isObj = length === undefined || iQ.isFunction(object); - if ( args ) { - if ( isObj ) { - for ( name in object ) { - if ( callback.apply( object[ name ], args ) === false ) { - break; - } - } - } else { - for ( ; i < length; ) { - if ( callback.apply( object[ i++ ], args ) === false ) { - break; - } + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; } } - - // A special, fast, case for the most common use of each } else { - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} - } + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} } return object; From 5b86879af2cb96e9d30126e5444072dd079ea814 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 22:02:11 -0400 Subject: [PATCH 139/369] iQ cleanup: rm unused regexps and other constants; rm iQ.toArray, iQ.pushStack, iQ.slice, iQ.trim --- browser/base/content/tabview/iq.js | 86 +++--------------------------- 1 file changed, 8 insertions(+), 78 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f47e94d9d450..67a5ec42818f 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -67,33 +67,15 @@ var iQ = function(selector, context) { // (both of which we optimize for) quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, - // Is it a simple selector - isSimple = /^.[^:#\[\.,]*$/, - - // Check if a string has a non-whitespace character in it - rnotwhite = /\S/, - - // Used for trimming whitespace - rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g, - // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, // Save a reference to some core methods toString = Object.prototype.toString, hasOwnProperty = Object.prototype.hasOwnProperty, - push = Array.prototype.push, - slice = Array.prototype.slice, - indexOf = Array.prototype.indexOf; -var rclass = /[\n\t]/g, - rspace = /\s+/, - rreturn = /\r/g, - rspecialurl = /href|src|style/, - rtype = /(button|input)/i, - rfocusable = /(button|input|object|select|textarea)/i, - rclickable = /^(a|area)$/i, - rradiocheck = /radio|checkbox/; + rclass = /[\n\t]/g, + rspace = /\s+/; // ########## // Class: iQ.fn @@ -208,7 +190,7 @@ iQ.fn = iQ.prototype = { // The extra typeof function check is to prevent crashes // in Safari 2 (See: #3039) if ( selector.length == null || typeof selector === "string" || iQ.isFunction(selector) || (typeof selector !== "function" && selector.setInterval) ) { - push.call( ret, selector ); + Array.prototype.push.call( ret, selector ); } else { iQ.merge( ret, selector ); } @@ -226,12 +208,6 @@ iQ.fn = iQ.prototype = { // The default length of a iQ object is 0 length: 0, - // ---------- - // Function: toArray - toArray: function() { - return slice.call( this, 0 ); - }, - // ---------- // Function: get // Get the Nth element in the matched element set OR @@ -240,40 +216,11 @@ iQ.fn = iQ.prototype = { return num == null ? // Return a 'clean' array - this.toArray() : + // was toArray + Array.prototype.slice.call( this, 0 ) : // Return just the object - ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] ); - }, - - // ---------- - // Function: pushStack - // Take an array of elements and push it onto the stack - // (returning the new matched element set) - pushStack: function( elems, name, selector ) { - // Build a new iQ matched element set - var ret = iQ(); - - if ( iQ.isArray( elems ) ) { - push.apply( ret, elems ); - - } else { - iQ.merge( ret, elems ); - } - - // Add the old object onto the stack (as a reference) - ret.prevObject = this; - - ret.context = this.context; - - if ( name === "find" ) { - ret.selector = this.selector + (this.selector ? " " : "") + selector; - } else if ( name ) { - ret.selector = this.selector + "." + name + "(" + selector + ")"; - } - - // Return the newly-formed element set - return ret; + ( num < 0 ? this[ num + this.length ] : this[ num ] ); }, // ---------- @@ -284,13 +231,6 @@ iQ.fn = iQ.prototype = { each: function( callback, args ) { return iQ.each( this, callback, args ); }, - - // ---------- - // Function: slice - slice: function() { - return this.pushStack( slice.apply( this, arguments ), - "slice", slice.call(arguments).join(",") ); - }, // ---------- // Function: addClass @@ -309,7 +249,6 @@ iQ.fn = iQ.prototype = { if ( elem.nodeType === 1 ) { if ( !elem.className ) { elem.className = value; - } else { var className = " " + elem.className + " ", setClass = elem.className; for ( var c = 0, cl = classNames.length; c < cl; c++ ) { @@ -317,7 +256,7 @@ iQ.fn = iQ.prototype = { setClass += " " + classNames[c]; } } - elem.className = iQ.trim( setClass ); + elem.className = String.trim( setClass ); } } } @@ -346,7 +285,7 @@ iQ.fn = iQ.prototype = { for ( var c = 0, cl = classNames.length; c < cl; c++ ) { className = className.replace(" " + classNames[c] + " ", " "); } - elem.className = iQ.trim( className ); + elem.className = String.trim( className ); } else { elem.className = ""; @@ -881,9 +820,6 @@ iQ.extend({ // ----------- // Function: isFunction - // See test/unit/core.js for details concerning isFunction. - // Since version 1.3, DOM methods and functions like alert - // aren't supported. They return false on IE (#2968). isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, @@ -951,12 +887,6 @@ iQ.extend({ return object; }, - // ---------- - // Function: trim - trim: function( text ) { - return (text || "").replace( rtrim, "" ); - }, - // ---------- // Function: merge merge: function( first, second ) { From fe182c97dc61f17c76dfcbe4f29d7a80fbb60975 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 22:17:35 -0400 Subject: [PATCH 140/369] iQ cleanup: of course we're Gecko > 1.9.2 so we have classList! use it in .{add,remove,has}Class --- browser/base/content/tabview/iq.js | 33 +++++++----------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 67a5ec42818f..819ffc00015f 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -241,23 +241,12 @@ iQ.fn = iQ.prototype = { } if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspace ); - for ( var i = 0, l = this.length; i < l; i++ ) { var elem = this[i]; - if ( elem.nodeType === 1 ) { - if ( !elem.className ) { - elem.className = value; - } else { - var className = " " + elem.className + " ", setClass = elem.className; - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { - setClass += " " + classNames[c]; - } - } - elem.className = String.trim( setClass ); - } + (value || "").split( rspace ).forEach(function(className) { + elem.classList.add(className); + }); } } } @@ -274,19 +263,13 @@ iQ.fn = iQ.prototype = { } if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split(rspace); - for ( var i = 0, l = this.length; i < l; i++ ) { var elem = this[i]; - if ( elem.nodeType === 1 && elem.className ) { if ( value ) { - var className = (" " + elem.className + " ").replace(rclass, " "); - for ( var c = 0, cl = classNames.length; c < cl; c++ ) { - className = className.replace(" " + classNames[c] + " ", " "); - } - elem.className = String.trim( className ); - + (value || "").split(rspace).forEach(function(className) { + elem.classList.remove(className); + }); } else { elem.className = ""; } @@ -300,13 +283,11 @@ iQ.fn = iQ.prototype = { // ---------- // Function: hasClass hasClass: function( selector ) { - var className = " " + selector + " "; for ( var i = 0, l = this.length; i < l; i++ ) { - if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + if ( this[i].classList.contains( selector ) ) { return true; } } - return false; }, From 091ba94b69bf95d9ea82452959b90877f7bf959e Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 13 Jul 2010 22:48:55 -0400 Subject: [PATCH 141/369] cleanup: rm unused functions, constants from Utils + Mirror --- .../base/content/tabview/modules/utils.jsm | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index eb5d8391ba54..ace41d5726d3 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -46,12 +46,6 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -// Get this in a way where we can load the page automatically -// where it doesn't need to be focused... -var homeWindow = Cc["@mozilla.org/embedcomp/window-watcher;1"] - .getService(Ci.nsIWindowWatcher) - .activeWindow; - var consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); @@ -523,46 +517,9 @@ var Utils = { return null; }, - - // ___ Files - getInstallDirectory: function(id, callback) { - if (Cc["@mozilla.org/extensions/manager;1"]) { - var extensionManager = Cc["@mozilla.org/extensions/manager;1"] - .getService(Ci.nsIExtensionManager); - var file = extensionManager.getInstallLocation(id).getItemFile(id, "install.rdf"); - callback(file.parent); - } - else { - Components.utils.import("resource://gre/modules/AddonManager.jsm"); - AddonManager.getAddonByID(id, function(addon) { - var fileStr = addon.getResourceURL("install.rdf"); - var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); - var url = ios.newURI(fileStr, null, null); - callback(url.QueryInterface(Ci.nsIFileURL).file.parent); - }); - } - }, - getFiles: function(dir) { - var files = []; - if (dir.isReadable() && dir.isDirectory) { - var entries = dir.directoryEntries; - while (entries.hasMoreElements()) { - var entry = entries.getNext(); - entry.QueryInterface(Ci.nsIFile); - files.push(entry); - } - } - - return files; - }, - // ___ Logging - - ilog: function(){ - Utils.log('!!ilog is no longer supported!!'); - }, - + log: function() { // pass as many arguments as you want, it'll print them all var text = this.expandArgumentsForLog(arguments); consoleService.logStringMessage(text); From 9a60acf90533ce601268d3ef0e1712b262f42fb9 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Wed, 14 Jul 2010 00:03:47 -0400 Subject: [PATCH 142/369] Bug 577387: iQ.each is gone! iQ(...).each still exists, and its callback must now explicitly take the element as its argument --- browser/base/content/tabview/iq.js | 41 ++++++++---------------------- 1 file changed, 10 insertions(+), 31 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 819ffc00015f..ddaf0e82ba89 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -226,10 +226,10 @@ iQ.fn = iQ.prototype = { // ---------- // Function: each // Execute a callback for every element in the matched set. - // (You can seed the arguments with an array of args, but this is - // only used internally.) - each: function( callback, args ) { - return iQ.each( this, callback, args ); + each: function( callback ) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + callback(elem); + } }, // ---------- @@ -505,7 +505,8 @@ iQ.fn = iQ.prototype = { }; for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - iQ.each(properties, function(key, value) { + for (var key in properties) { + var value = properties[key]; if (pixels[key] && typeof(value) != 'string') value += 'px'; @@ -513,7 +514,7 @@ iQ.fn = iQ.prototype = { elem.style.setProperty(key, value, ''); else elem.style[key] = value; - }); + } } return this; @@ -553,11 +554,11 @@ iQ.fn = iQ.prototype = { // css properties. So for each element to be animated, go through and // explicitly define 'em. var rupper = /([A-Z])/g; - this.each(function(){ - var cStyle = window.getComputedStyle(this, null); + this.each(function(elem){ + var cStyle = window.getComputedStyle(elem, null); for (var prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); - iQ(this).css(prop, cStyle.getPropertyValue(prop)); + iQ(elem).css(prop, cStyle.getPropertyValue(prop)); } }); @@ -845,28 +846,6 @@ iQ.extend({ } return true; }, - - // ---------- - // Function: each - // args is for internal usage only - each: function( object, callback ) { - var name, i = 0, - length = object.length, - isObj = length === undefined || iQ.isFunction(object); - - if ( isObj ) { - for ( name in object ) { - if ( callback.call( object[ name ], name, object[ name ] ) === false ) { - break; - } - } - } else { - for ( var value = object[0]; - i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {} - } - - return object; - }, // ---------- // Function: merge From b2e717a708926965af79f7855cc9e1329310b68e Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Wed, 14 Jul 2010 00:08:45 -0400 Subject: [PATCH 143/369] Bug 577387: cleanup of the new iQ(...).each --- browser/base/content/tabview/iq.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index ddaf0e82ba89..29807ccfb210 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -227,9 +227,14 @@ iQ.fn = iQ.prototype = { // Function: each // Execute a callback for every element in the matched set. each: function( callback ) { + if ( !iQ.isFunction(value) ) { + Utils.assert("each's argument must be a function", false); + return null; + } for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { callback(elem); } + return this; }, // ---------- From f7219087854647e064d00ee487a49eee490f6cd9 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 14 Jul 2010 11:21:07 -0700 Subject: [PATCH 144/369] + Fixed a typo in iQ.each that was breaking everything + Rearranged the UIClass initialization sequence a little, to make sure everything happens in the right sequence --- browser/base/content/tabview/iq.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 29807ccfb210..c1610c840371 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -227,7 +227,7 @@ iQ.fn = iQ.prototype = { // Function: each // Execute a callback for every element in the matched set. each: function( callback ) { - if ( !iQ.isFunction(value) ) { + if ( !iQ.isFunction(callback) ) { Utils.assert("each's argument must be a function", false); return null; } From 1672b8823be9a1043e0dd7e7683697acb8959299 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 14 Jul 2010 16:59:37 -0700 Subject: [PATCH 145/369] + Subscribable (in utils.js) no longer has a separate onClose path + My recent addition of a "content" div broke dragging to make a new group; fixed --- .../base/content/tabview/modules/utils.jsm | 51 ------------------- 1 file changed, 51 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index ace41d5726d3..b7384ca9fe5a 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -363,11 +363,8 @@ window.Range.prototype = { // ########## // Class: Subscribable // A mix-in for allowing objects to collect subscribers for custom events. -// Currently supports only onClose. -// TODO generalize for any number of events window.Subscribable = function() { this.subscribers = {}; - this.onCloseSubscribers = null; }; window.Subscribable.prototype = { @@ -419,54 +416,6 @@ window.Subscribable.prototype = { subsCopy.forEach(function(object) { object.callback(self, eventInfo); }); - }, - - // ---------- - // Function: addOnClose - // The given callback will be called when the Subscribable fires its onClose. - // The referenceElement is used to facilitate removal if necessary. - addOnClose: function(referenceElement, callback) { - if (!this.onCloseSubscribers) - this.onCloseSubscribers = []; - - var existing = this.onCloseSubscribers.filter(function(element) { - return element.referenceElement == referenceElement; - }); - - if (existing.length) { - Utils.assert('should only ever be one', existing.length == 1); - existing[0].callback = callback; - } else { - this.onCloseSubscribers.push({ - referenceElement: referenceElement, - callback: callback - }); - } - }, - - // ---------- - // Function: removeOnClose - // Removes the callback associated with referenceElement for onClose notification. - removeOnClose: function(referenceElement) { - if (!this.onCloseSubscribers) - return; - - this.onCloseSubscribers = this.onCloseSubscribers.filter(function(element) { - return element.referenceElement != referenceElement; - }); - }, - - // ---------- - // Function: _sendOnClose - // Internal routine. Used by the Subscribable to fire onClose events. - _sendOnClose: function() { - if (!this.onCloseSubscribers) - return; - - var self = this; - this.onCloseSubscribers.forEach(function(object) { - object.callback(self); - }); } }; From 177cd1241417e4d7087fba3adcb59cba869d6831 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Wed, 14 Jul 2010 17:24:03 -0700 Subject: [PATCH 146/369] + Cleaned up a scope issue with Subscribable and added some guards --- .../base/content/tabview/modules/utils.jsm | 87 ++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index b7384ca9fe5a..6b3254123dd7 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -364,7 +364,7 @@ window.Range.prototype = { // Class: Subscribable // A mix-in for allowing objects to collect subscribers for custom events. window.Subscribable = function() { - this.subscribers = {}; + this.subscribers = null; }; window.Subscribable.prototype = { @@ -373,22 +373,34 @@ window.Subscribable.prototype = { // The given callback will be called when the Subscribable fires the given event. // The refObject is used to facilitate removal if necessary. addSubscriber: function(refObject, eventName, callback) { - if (!this.subscribers[eventName]) - this.subscribers[eventName] = []; - - var subs = this.subscribers[eventName]; - var existing = subs.filter(function(element) { - return element.refObject == refObject; - }); - - if (existing.length) { - Utils.assert('should only ever be one', existing.length == 1); - existing[0].callback = callback; - } else { - subs.push({ - refObject: refObject, - callback: callback + try { + Utils.assertThrow("refObject", refObject); + Utils.assertThrow("callback must be a function", iQ.isFunction(callback)); + Utils.assertThrow("eventName must be a non-empty string", + eventName && typeof(eventName) == "string"); + + if (!this.subscribers) + this.subscribers = {}; + + if (!this.subscribers[eventName]) + this.subscribers[eventName] = []; + + var subs = this.subscribers[eventName]; + var existing = subs.filter(function(element) { + return element.refObject == refObject; }); + + if (existing.length) { + Utils.assert('should only ever be one', existing.length == 1); + existing[0].callback = callback; + } else { + subs.push({ + refObject: refObject, + callback: callback + }); + } + } catch(e) { + Utils.log(e); } }, @@ -396,26 +408,41 @@ window.Subscribable.prototype = { // Function: removeSubscriber // Removes the callback associated with refObject for the given event. removeSubscriber: function(refObject, eventName) { - if (!this.subscribers[eventName]) - return; - - this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { - return element.refObject != refObject; - }); + try { + Utils.assertThrow("refObject", refObject); + Utils.assertThrow("eventName must be a non-empty string", + eventName && typeof(eventName) == "string"); + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { + return element.refObject != refObject; + }); + } catch(e) { + Utils.log(e); + } }, // ---------- // Function: _sendToSubscribers // Internal routine. Used by the Subscribable to fire events. _sendToSubscribers: function(eventName, eventInfo) { - if (!this.subscribers[eventName]) - return; - - var self = this; - var subsCopy = iQ.merge([], this.subscribers[eventName]); - subsCopy.forEach(function(object) { - object.callback(self, eventInfo); - }); + try { + Utils.assertThrow("eventName must be a non-empty string", + eventName && typeof(eventName) == "string"); + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + var self = this; + var subsCopy = iQ.merge([], this.subscribers[eventName]); + subsCopy.forEach(function(object) { + object.callback(self, eventInfo); + }); + } catch(e) { + Utils.log(e); + } } }; From 29bffe418155879a19697eb278a190b1e908104f Mon Sep 17 00:00:00 2001 From: Raymond Lee Date: Thu, 15 Jul 2010 10:40:46 +0800 Subject: [PATCH 147/369] Bug 576110: Show saved thumbnails at browser startup if tab candy was last open --- browser/base/content/tabview/modules/utils.jsm | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6b3254123dd7..11306386ed3c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -647,8 +647,12 @@ var Utils = { // ___ Is Mac isMac: function() { - if (this._isMac == null) - this._isMac = (navigator.platform.search(/mac/i) > -1); + if (this._isMac == null) { + var xulRuntime = + Components.classes["@mozilla.org/xre/app-info;1"]. + getService(Components.interfaces.nsIXULRuntime); + this._isMac = (xulRuntime.OS == "Darwin"); + } return this._isMac; } From f6bf040f296bccfd0735bf53491e8cec46a37495 Mon Sep 17 00:00:00 2001 From: Raymond Lee Date: Thu, 15 Jul 2010 16:41:08 +0800 Subject: [PATCH 148/369] Bug 577445: Show tab candy title in the tab candy interface and change 'let' to 'var' --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 11306386ed3c..5e52c116a11c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -485,7 +485,7 @@ var Utils = { while (browserEnumerator.hasMoreElements()) { var browserWin = browserEnumerator.getNext(); var tabbrowser = browserWin.gBrowser; - let tabCandyContainer = browserWin.document.getElementById("tab-candy"); + var tabCandyContainer = browserWin.document.getElementById("tab-candy"); if (tabCandyContainer && tabCandyContainer.contentWindow == window) { return browserWin; } From 9eca35d1cbc47ebcd05c9111e160c24caa49246a Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Fri, 16 Jul 2010 17:27:01 -0700 Subject: [PATCH 149/369] + Finished documenting all of the app code, plus utils.js. Remaining: iq.js, mirror.js, tabs.js --- .../base/content/tabview/modules/utils.jsm | 105 ++++++++++++++++-- 1 file changed, 96 insertions(+), 9 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 5e52c116a11c..e5a402e00cca 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -59,7 +59,10 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] // 0 is used in their place. window.Point = function(a, y) { if (isPoint(a)) { + // Variable: x this.x = a.x; + + // Variable: y this.y = a.y; } else { this.x = (Utils.isNumber(a) ? a : 0); @@ -68,12 +71,17 @@ window.Point = function(a, y) { }; // ---------- +// Function: isPoint +// Returns true if the given object (p) looks like a . +// Note that this is not an actual method of , but a global routine. window.isPoint = function(p) { return (p && Utils.isNumber(p.x) && Utils.isNumber(p.y)); }; window.Point.prototype = { // ---------- + // Function: distance + // Returns the distance from this point to the given . distance: function(point) { var ax = Math.abs(this.x - point.x); var ay = Math.abs(this.y - point.y); @@ -81,6 +89,8 @@ window.Point.prototype = { }, // ---------- + // Function: plus + // Returns a new point with the result of adding this point to the given . plus: function(point) { return new Point(this.x + point.x, this.y + point.y); } @@ -88,7 +98,9 @@ window.Point.prototype = { // ########## // Class: Rect -// A simple rectangle. +// A simple rectangle. Note that in addition to the left and width, it also has +// a right property; changing one affects the others appropriately. Same for the +// vertical properties. // // Constructor: Rect // If a is a Rect, creates a copy of it. Otherwise, expects a to be left, @@ -96,9 +108,16 @@ window.Point.prototype = { window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' if (isRect(a)) { + // Variable: left this.left = a.left; + + // Variable: top this.top = a.top; + + // Variable: width this.width = a.width; + + // Variable: height this.height = a.height; } else { this.left = a; @@ -109,6 +128,9 @@ window.Rect = function(a, top, width, height) { }; // ---------- +// Function: isRect +// Returns true if the given object (r) looks like a . +// Note that this is not an actual method of , but a global routine. window.isRect = function(r) { return (r && Utils.isNumber(r.left) @@ -119,6 +141,7 @@ window.isRect = function(r) { window.Rect.prototype = { // ---------- + // Variable: right get right() { return this.left + this.width; }, @@ -129,6 +152,7 @@ window.Rect.prototype = { }, // ---------- + // Variable: bottom get bottom() { return this.top + this.height; }, @@ -139,16 +163,22 @@ window.Rect.prototype = { }, // ---------- + // Variable: xRange + // Gives you a new for the horizontal dimension. get xRange() { return new Range(this.left,this.right); }, // ---------- + // Variable: yRange + // Gives you a new for the vertical dimension. get yRange() { return new Range(this.top,this.bottom); }, // ---------- + // Function: intersects + // Returns true if this rectangle intersects the given . intersects: function(rect) { return (rect.right > this.left && rect.left < this.right @@ -157,6 +187,9 @@ window.Rect.prototype = { }, // ---------- + // Function: intersection + // Returns a new with the intersection of this rectangle and the give , + // or null if they don't intersect. intersection: function(rect) { var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0); box.right = Math.min(rect.right, this.right); @@ -196,21 +229,29 @@ window.Rect.prototype = { }, // ---------- + // Function: center + // Returns a new with the center location of this rectangle. center: function() { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, // ---------- + // Function: size + // Returns a new with the dimensions of this rectangle. size: function() { return new Point(this.width, this.height); }, // ---------- + // Function: position + // Returns a new with the top left of this rectangle. position: function() { return new Point(this.left, this.top); }, // ---------- + // Function: area + // Returns the area of this rectangle. area: function() { return this.width * this.height; }, @@ -251,6 +292,8 @@ window.Rect.prototype = { }, // ---------- + // Function: equals + // Returns true if this rectangle is identical to the given . equals: function(a) { return (a.left == this.left && a.top == this.top @@ -259,6 +302,8 @@ window.Rect.prototype = { }, // ---------- + // Function: union + // Returns a new with the union of this rectangle and the given . union: function(a){ var newLeft = Math.min(a.left, this.left); var newTop = Math.min(a.top, this.top); @@ -270,6 +315,8 @@ window.Rect.prototype = { }, // ---------- + // Function: copy + // Copies the values of the given into this rectangle. copy: function(a) { this.left = a.left; this.top = a.top; @@ -308,6 +355,9 @@ window.Range = function(min, max) { }; // ---------- +// Function: isRange +// Returns true if the given object (r) looks like a . +// Note that this is not an actual method of , but a global routine. window.isRange = function(r) { return (r && Utils.isNumber(r.min) @@ -495,18 +545,32 @@ var Utils = { }, // ___ Logging - - log: function() { // pass as many arguments as you want, it'll print them all + + // ---------- + // Function: log + // Prints the given arguments to the JavaScript error console as a message. + // Pass as many arguments as you want, it'll print them all. + log: function() { var text = this.expandArgumentsForLog(arguments); consoleService.logStringMessage(text); }, - error: function() { // pass as many arguments as you want, it'll print them all + // ---------- + // Function: error + // Prints the given arguments to the JavaScript error console as an error. + // Pass as many arguments as you want, it'll print them all. + // TODO: Does this still work? + error: function() { var text = this.expandArgumentsForLog(arguments); Cu.reportError('tabcandy error: ' + text); }, - trace: function() { // pass as many arguments as you want, it'll print them all + // ---------- + // Function: trace + // Prints the given arguments to the JavaScript error console as a message, + // along with a full stack trace. + // Pass as many arguments as you want, it'll print them all. + trace: function() { var text = this.expandArgumentsForLog(arguments); if (typeof(printStackTrace) != 'function') this.log(text + ' trace: you need to include stacktrace.js'); @@ -517,6 +581,9 @@ var Utils = { } }, + // ---------- + // Function: assert + // Prints a stack trace along with label (as a console message) if condition is false. assert: function(label, condition) { if (!condition) { var text; @@ -534,6 +601,7 @@ var Utils = { } }, + // ---------- // Function: assertThrow // Throws label as an exception if condition is false. assertThrow: function(label, condition) { @@ -554,6 +622,9 @@ var Utils = { } }, + // ---------- + // Function: expandObject + // Prints the given object to a string, including all of its properties. expandObject: function(obj) { var s = obj + ' = {'; for (prop in obj) { @@ -577,6 +648,9 @@ var Utils = { return s + '}'; }, + // ---------- + // Function: expandArgumentsForLog + // Expands all of the given args (an array) into a single string. expandArgumentsForLog: function(args) { var s = ''; var count = args.length; @@ -594,6 +668,9 @@ var Utils = { return s; }, + // ---------- + // Funtion: testLogging + // Prints some test messages with the various logging methods. testLogging: function() { this.log('beginning logging test'); this.error('this is an error'); @@ -602,7 +679,11 @@ var Utils = { this.log('ending logging test'); }, - // ___ Event + // ___ Misc + + // ---------- + // Function: isRightClick + // Given a DOM mouse event, returns true if it was for the right mouse button. isRightClick: function(event) { if (event.which) return (event.which == 3); @@ -612,13 +693,17 @@ var Utils = { return false; }, - // ___ Time + // ---------- + // Function: getMilliseconds + // Returns the total milliseconds on the system clock right now. getMilliseconds: function() { var date = new Date(); return date.getTime(); }, - // ___ Misc + // ---------- + // Function: isDOMElement + // Returns true if the given object is a DOM element. isDOMElement: function(object) { return (object && typeof(object.nodeType) != 'undefined' ? true : false); }, @@ -645,7 +730,9 @@ var Utils = { return value; }, - // ___ Is Mac + // ---------- + // Function: isMac + // Returns true if running on a Mac. isMac: function() { if (this._isMac == null) { var xulRuntime = From 6f9844660336abf9fdc20d30d291aaa6e0c3b1fb Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Sun, 18 Jul 2010 11:58:10 -0400 Subject: [PATCH 150/369] rm trailing whitespace --- browser/base/content/tabview/iq.js | 200 ++++++------- .../base/content/tabview/modules/utils.jsm | 270 +++++++++--------- 2 files changed, 235 insertions(+), 235 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index c1610c840371..b57332c855df 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -83,9 +83,9 @@ var iQ = function(selector, context) { iQ.fn = iQ.prototype = { // ---------- // Function: init - // You don't call this directly; this is what's called by iQ(). - // It works pretty much like jQuery(), with a few exceptions, - // most notably that you can't use strings with complex html, + // You don't call this directly; this is what's called by iQ(). + // It works pretty much like jQuery(), with a few exceptions, + // most notably that you can't use strings with complex html, // just simple tags like '
'. init: function( selector, context ) { var match, elem, ret, doc; @@ -101,7 +101,7 @@ iQ.fn = iQ.prototype = { this.length = 1; return this; } - + // The body element only exists once, optimize finding it if ( selector === "body" && !context ) { this.context = document; @@ -137,9 +137,9 @@ iQ.fn = iQ.prototype = { } else { Utils.assert('does not support complex HTML creation', false); } - + return iQ.merge( this, selector ); - + // HANDLE $("#id") } else { elem = document.getElementById( match[2] ); @@ -196,9 +196,9 @@ iQ.fn = iQ.prototype = { } } return ret; - + }, - + // Start with an empty selector selector: "", @@ -206,8 +206,8 @@ iQ.fn = iQ.prototype = { iq: "1.4.2", // The default length of a iQ object is 0 - length: 0, - + length: 0, + // ---------- // Function: get // Get the Nth element in the matched element set OR @@ -334,7 +334,7 @@ iQ.fn = iQ.prototype = { elem.parentNode.removeChild( elem ); } } - + return this; }, @@ -346,7 +346,7 @@ iQ.fn = iQ.prototype = { elem.removeChild( elem.firstChild ); } } - + return this; }, @@ -354,7 +354,7 @@ iQ.fn = iQ.prototype = { // Function: width width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); - return parseInt(this.css('width')); + return parseInt(this.css('width')); }, // ---------- @@ -373,7 +373,7 @@ iQ.fn = iQ.prototype = { top: parseInt(this.css('top')) }; }, - + // ---------- // Function: bounds bounds: function(unused) { @@ -381,7 +381,7 @@ iQ.fn = iQ.prototype = { var p = this.position(); return new Rect(p.left, p.top, this.width(), this.height()); }, - + // ---------- // Function: data data: function(key, value) { @@ -391,19 +391,19 @@ iQ.fn = iQ.prototype = { data = this[0].iQData; return (data ? data[key] : null); } - + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { data = elem.iQData; if (!data) data = elem.iQData = {}; - + data[key] = value; } - - return this; + + return this; }, - + // ---------- // Function: html // TODO: security @@ -411,11 +411,11 @@ iQ.fn = iQ.prototype = { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) return this[0].innerHTML; - + this[0].innerHTML = value; return this; - }, - + }, + // ---------- // Function: text text: function(value) { @@ -423,10 +423,10 @@ iQ.fn = iQ.prototype = { if (value === undefined) { return this[0].textContent; } - + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); - }, - + }, + // ---------- // Function: val val: function(value) { @@ -434,11 +434,11 @@ iQ.fn = iQ.prototype = { if (value === undefined) { return this[0].value; } - - this[0].value = value; + + this[0].value = value; return this; - }, - + }, + // ---------- // Function: appendTo appendTo: function(selector) { @@ -446,7 +446,7 @@ iQ.fn = iQ.prototype = { iQ(selector).append(this); return this; }, - + // ---------- // Function: append append: function(selector) { @@ -456,7 +456,7 @@ iQ.fn = iQ.prototype = { this[0].appendChild(object[0]); return this; }, - + // ---------- // Function: attr // Sets or gets an attribute on the element(s). @@ -464,16 +464,16 @@ iQ.fn = iQ.prototype = { try { Utils.assert('string key', typeof key === 'string'); if (value === undefined) { - Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); } for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { elem.setAttribute(key, value); - } + } } catch(e) { Utils.log(e); } - + return this; }, @@ -481,18 +481,18 @@ iQ.fn = iQ.prototype = { // Function: css css: function(a, b) { var properties = null; - + if (typeof a === 'string') { - var key = a; + var key = a; if (b === undefined) { - Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); var substitutions = { 'MozTransform': '-moz-transform', 'zIndex': 'z-index' }; - return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key); + return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key); } properties = {}; properties[key] = b; @@ -508,79 +508,79 @@ iQ.fn = iQ.prototype = { 'width': true, 'height': true }; - + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { for (var key in properties) { var value = properties[key]; - if (pixels[key] && typeof(value) != 'string') + if (pixels[key] && typeof(value) != 'string') value += 'px'; - + if (key.indexOf('-') != -1) elem.style.setProperty(key, value, ''); else elem.style[key] = value; } } - - return this; + + return this; }, // ---------- // Function: animate - // Uses CSS transitions to animate the element. - // - // Parameters: + // Uses CSS transitions to animate the element. + // + // Parameters: // css - an object map of the CSS properties to change // options - an object with various properites (see below) // - // Possible "options" properties: + // Possible "options" properties: // duration - how long to animate, in milliseconds - // easing - easing function to use. Possibilities include 'tabcandyBounce', 'easeInQuad'. - // Default is 'ease'. + // easing - easing function to use. Possibilities include 'tabcandyBounce', 'easeInQuad'. + // Default is 'ease'. // complete - function to call once the animation is done, takes nothing in, but "this" - // is set to the element that was animated. + // is set to the element that was animated. animate: function(css, options) { try { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (!options) options = {}; - + var easings = { - tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', + tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care fast: 'cubic-bezier(0.7,0,1,1)' }; - + var duration = (options.duration || 400); var easing = (easings[options.easing] || 'ease'); // The latest versions of Firefox do not animate from a non-explicitly set // css properties. So for each element to be animated, go through and // explicitly define 'em. - var rupper = /([A-Z])/g; + var rupper = /([A-Z])/g; this.each(function(elem){ - var cStyle = window.getComputedStyle(elem, null); + var cStyle = window.getComputedStyle(elem, null); for (var prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); iQ(elem).css(prop, cStyle.getPropertyValue(prop)); - } + } }); this.css({ - '-moz-transition-property': 'all', // TODO: just animate the properties we're changing - '-moz-transition-duration': (duration / 1000) + 's', + '-moz-transition-property': 'all', // TODO: just animate the properties we're changing + '-moz-transition-duration': (duration / 1000) + 's', '-moz-transition-timing-function': easing }); this.css(css); - + var self = this; iQ.timeout(function() { self.css({ - '-moz-transition-property': 'none', - '-moz-transition-duration': '', + '-moz-transition-property': 'none', + '-moz-transition-duration': '', '-moz-transition-timing-function': '' }); @@ -590,10 +590,10 @@ iQ.fn = iQ.prototype = { } catch(e) { Utils.log(e); } - + return this; }, - + // ---------- // Function: fadeOut fadeOut: function(callback) { @@ -608,14 +608,14 @@ iQ.fn = iQ.prototype = { if (iQ.isFunction(callback)) callback.apply(this); } - }); + }); } catch(e) { Utils.log(e); } - + return this; }, - + // ---------- // Function: fadeIn fadeIn: function() { @@ -625,14 +625,14 @@ iQ.fn = iQ.prototype = { opacity: 1 }, { duration: 400 - }); + }); } catch(e) { Utils.log(e); } - + return this; }, - + // ---------- // Function: hide hide: function() { @@ -641,10 +641,10 @@ iQ.fn = iQ.prototype = { } catch(e) { Utils.log(e); } - + return this; }, - + // ---------- // Function: show show: function() { @@ -653,13 +653,13 @@ iQ.fn = iQ.prototype = { } catch(e) { Utils.log(e); } - + return this; }, - + // ---------- // Function: bind - // Binds the given function to the given event type. Also wraps the function + // Binds the given function to the given event type. Also wraps the function // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); @@ -675,56 +675,56 @@ iQ.fn = iQ.prototype = { for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { if (!elem.iQEventData) elem.iQEventData = {}; - + if (!elem.iQEventData[type]) elem.iQEventData[type] = []; - + elem.iQEventData[type].push({ - original: func, + original: func, modified: handler }); - + elem.addEventListener(type, handler, false); } - - return this; + + return this; }, - + // ---------- // Function: one one: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); - + var handler = function(e) { iQ(this).unbind(type, handler); return func.apply(this, [e]); }; - + return this.bind(type, handler); }, - + // ---------- // Function: unbind unbind: function(type, func) { Utils.assert('Must provide a function', iQ.isFunction(func)); - + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { var handler = func; if (elem.iQEventData && elem.iQEventData[type]) { for (var a = 0, count = elem.iQEventData[type].length; a < count; a++) { var pair = elem.iQEventData[type][a]; if (pair.original == func) { - handler = pair.modified; + handler = pair.modified; elem.iQEventData[type].splice(a, 1); break; } } } - + elem.removeEventListener(type, handler, false); } - - return this; + + return this; } }; @@ -798,13 +798,13 @@ iQ.extend({ // Variable: animationCount // For internal use only animationCount: 0, - + // ---------- // Function: isAnimating isAnimating: function() { return (this.animationCount != 0); }, - + // ----------- // Function: isFunction isFunction: function( obj ) { @@ -826,20 +826,20 @@ iQ.extend({ if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { return false; } - + // Not own constructor property must be Object if ( obj.constructor && !hasOwnProperty.call(obj, "constructor") && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { return false; } - + // Own properties are enumerated firstly, so to speed up, // if last one is own, then all properties are own. - + var key; for ( key in obj ) {} - + return key === undefined || hasOwnProperty.call( obj, key ); }, @@ -851,7 +851,7 @@ iQ.extend({ } return true; }, - + // ---------- // Function: merge merge: function( first, second ) { @@ -861,7 +861,7 @@ iQ.extend({ for ( var l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } - + } else { while ( second[j] !== undefined ) { first[ i++ ] = second[ j++ ]; @@ -877,7 +877,7 @@ iQ.extend({ // Function: timeout // wraps setTimeout with try/catch timeout: function(func, delay) { - setTimeout(function() { + setTimeout(function() { try { func(); } catch(e) { @@ -904,7 +904,7 @@ iQ.extend({ 'blur', 'focus' ]; - + events.forEach(function(event) { iQ.fn[event] = function(func) { return this.bind(event, func); diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index e5a402e00cca..3b4aae107569 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -51,17 +51,17 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] // ########## // Class: Point -// A simple point. +// A simple point. // // Constructor: Point -// If a is a Point, creates a copy of it. Otherwise, expects a to be x, -// and creates a Point with it along with y. If either a or y are omitted, -// 0 is used in their place. +// If a is a Point, creates a copy of it. Otherwise, expects a to be x, +// and creates a Point with it along with y. If either a or y are omitted, +// 0 is used in their place. window.Point = function(a, y) { if (isPoint(a)) { // Variable: x this.x = a.x; - + // Variable: y this.y = a.y; } else { @@ -72,51 +72,51 @@ window.Point = function(a, y) { // ---------- // Function: isPoint -// Returns true if the given object (p) looks like a . -// Note that this is not an actual method of , but a global routine. +// Returns true if the given object (p) looks like a . +// Note that this is not an actual method of , but a global routine. window.isPoint = function(p) { return (p && Utils.isNumber(p.x) && Utils.isNumber(p.y)); }; -window.Point.prototype = { - // ---------- +window.Point.prototype = { + // ---------- // Function: distance - // Returns the distance from this point to the given . - distance: function(point) { + // Returns the distance from this point to the given . + distance: function(point) { var ax = Math.abs(this.x - point.x); var ay = Math.abs(this.y - point.y); return Math.sqrt((ax * ax) + (ay * ay)); }, - // ---------- + // ---------- // Function: plus - // Returns a new point with the result of adding this point to the given . - plus: function(point) { + // Returns a new point with the result of adding this point to the given . + plus: function(point) { return new Point(this.x + point.x, this.y + point.y); } }; -// ########## +// ########## // Class: Rect -// A simple rectangle. Note that in addition to the left and width, it also has -// a right property; changing one affects the others appropriately. Same for the -// vertical properties. +// A simple rectangle. Note that in addition to the left and width, it also has +// a right property; changing one affects the others appropriately. Same for the +// vertical properties. // // Constructor: Rect -// If a is a Rect, creates a copy of it. Otherwise, expects a to be left, -// and creates a Rect with it along with top, width, and height. +// If a is a Rect, creates a copy of it. Otherwise, expects a to be left, +// and creates a Rect with it along with top, width, and height. window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' if (isRect(a)) { // Variable: left this.left = a.left; - + // Variable: top this.top = a.top; - + // Variable: width this.width = a.width; - + // Variable: height this.height = a.height; } else { @@ -129,10 +129,10 @@ window.Rect = function(a, top, width, height) { // ---------- // Function: isRect -// Returns true if the given object (r) looks like a . -// Note that this is not an actual method of , but a global routine. +// Returns true if the given object (r) looks like a . +// Note that this is not an actual method of , but a global routine. window.isRect = function(r) { - return (r + return (r && Utils.isNumber(r.left) && Utils.isNumber(r.top) && Utils.isNumber(r.width) @@ -145,7 +145,7 @@ window.Rect.prototype = { get right() { return this.left + this.width; }, - + // ---------- set right(value) { this.width = value - this.left; @@ -156,7 +156,7 @@ window.Rect.prototype = { get bottom() { return this.top + this.height; }, - + // ---------- set bottom(value) { this.height = value - this.top; @@ -168,7 +168,7 @@ window.Rect.prototype = { get xRange() { return new Range(this.left,this.right); }, - + // ---------- // Variable: yRange // Gives you a new for the vertical dimension. @@ -183,12 +183,12 @@ window.Rect.prototype = { return (rect.right > this.left && rect.left < this.right && rect.bottom > this.top - && rect.top < this.bottom); + && rect.top < this.bottom); }, - + // ---------- // Function: intersection - // Returns a new with the intersection of this rectangle and the give , + // Returns a new with the intersection of this rectangle and the give , // or null if they don't intersect. intersection: function(rect) { var box = new Rect(Math.max(rect.left, this.left), Math.max(rect.top, this.top), 0, 0); @@ -196,15 +196,15 @@ window.Rect.prototype = { box.bottom = Math.min(rect.bottom, this.bottom); if (box.width > 0 && box.height > 0) return box; - + return null; }, - + // ---------- // Function: containsPoint // Returns a boolean denoting if the is inside of // the bounding rect. - // + // // Paramaters // - A containsPoint: function(point){ @@ -213,7 +213,7 @@ window.Rect.prototype = { && point.y > this.top && point.y < this.bottom ) }, - + // ---------- // Function: contains // Returns a boolean denoting if the is contained inside @@ -227,35 +227,35 @@ window.Rect.prototype = { && rect.top > this.top && rect.bottom < this.bottom ) }, - + // ---------- // Function: center // Returns a new with the center location of this rectangle. center: function() { return new Point(this.left + (this.width / 2), this.top + (this.height / 2)); }, - + // ---------- // Function: size // Returns a new with the dimensions of this rectangle. size: function() { return new Point(this.width, this.height); }, - + // ---------- // Function: position - // Returns a new with the top left of this rectangle. + // Returns a new with the top left of this rectangle. position: function() { return new Point(this.left, this.top); }, - + // ---------- // Function: area - // Returns the area of this rectangle. + // Returns the area of this rectangle. area: function() { return this.width * this.height; }, - + // ---------- // Function: inset // Makes the rect smaller (if the arguments are positive) as if a margin is added all around @@ -265,16 +265,16 @@ window.Rect.prototype = { // - A or two arguments: x and y inset: function(a, b) { if (typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { - b = a.y; + b = a.y; a = a.x; } - + this.left += a; this.width -= a * 2; this.top += b; this.height -= b * 2; }, - + // ---------- // Function: offset // Moves (translates) the rect by the given vector. @@ -290,17 +290,17 @@ window.Rect.prototype = { this.top += b; } }, - + // ---------- // Function: equals - // Returns true if this rectangle is identical to the given . + // Returns true if this rectangle is identical to the given . equals: function(a) { return (a.left == this.left && a.top == this.top && a.width == this.width && a.height == this.height); }, - + // ---------- // Function: union // Returns a new with the union of this rectangle and the given . @@ -309,11 +309,11 @@ window.Rect.prototype = { var newTop = Math.min(a.top, this.top); var newWidth = Math.max(a.right, this.right) - newLeft; var newHeight = Math.max(a.bottom, this.bottom) - newTop; - var newRect = new Rect(newLeft, newTop, newWidth, newHeight); - + var newRect = new Rect(newLeft, newTop, newWidth, newHeight); + return newRect; }, - + // ---------- // Function: copy // Copies the values of the given into this rectangle. @@ -323,7 +323,7 @@ window.Rect.prototype = { this.width = a.width; this.height = a.height; }, - + // ---------- // Function: css // Returns an object with the dimensions of this rectangle, suitable for passing into iQ.fn.css. @@ -338,7 +338,7 @@ window.Rect.prototype = { } }; -// ########## +// ########## // Class: Range // A physical interval, with a min and max. // @@ -356,15 +356,15 @@ window.Range = function(min, max) { // ---------- // Function: isRange -// Returns true if the given object (r) looks like a . -// Note that this is not an actual method of , but a global routine. +// Returns true if the given object (r) looks like a . +// Note that this is not an actual method of , but a global routine. window.isRange = function(r) { - return (r + return (r && Utils.isNumber(r.min) && Utils.isNumber(r.max)); }; -window.Range.prototype = { +window.Range.prototype = { // Variable: extent // Equivalent to max-min get extent() { @@ -404,7 +404,7 @@ window.Range.prototype = { // Paramaters // - a number or overlaps: function(value) { - return Utils.isNumber(value) ? + return Utils.isNumber(value) ? this.contains(value) : ( value.min <= this.max && this.min <= value.max ); }, @@ -412,7 +412,7 @@ window.Range.prototype = { // ########## // Class: Subscribable -// A mix-in for allowing objects to collect subscribers for custom events. +// A mix-in for allowing objects to collect subscribers for custom events. window.Subscribable = function() { this.subscribers = null; }; @@ -421,31 +421,31 @@ window.Subscribable.prototype = { // ---------- // Function: addSubscriber // The given callback will be called when the Subscribable fires the given event. - // The refObject is used to facilitate removal if necessary. + // The refObject is used to facilitate removal if necessary. addSubscriber: function(refObject, eventName, callback) { try { Utils.assertThrow("refObject", refObject); Utils.assertThrow("callback must be a function", iQ.isFunction(callback)); - Utils.assertThrow("eventName must be a non-empty string", + Utils.assertThrow("eventName must be a non-empty string", eventName && typeof(eventName) == "string"); - + if (!this.subscribers) this.subscribers = {}; - + if (!this.subscribers[eventName]) this.subscribers[eventName] = []; - + var subs = this.subscribers[eventName]; var existing = subs.filter(function(element) { return element.refObject == refObject; }); - + if (existing.length) { Utils.assert('should only ever be one', existing.length == 1); existing[0].callback = callback; - } else { + } else { subs.push({ - refObject: refObject, + refObject: refObject, callback: callback }); } @@ -453,19 +453,19 @@ window.Subscribable.prototype = { Utils.log(e); } }, - + // ---------- // Function: removeSubscriber - // Removes the callback associated with refObject for the given event. + // Removes the callback associated with refObject for the given event. removeSubscriber: function(refObject, eventName) { try { Utils.assertThrow("refObject", refObject); - Utils.assertThrow("eventName must be a non-empty string", + Utils.assertThrow("eventName must be a non-empty string", eventName && typeof(eventName) == "string"); - + if (!this.subscribers || !this.subscribers[eventName]) return; - + this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { return element.refObject != refObject; }); @@ -473,21 +473,21 @@ window.Subscribable.prototype = { Utils.log(e); } }, - + // ---------- // Function: _sendToSubscribers // Internal routine. Used by the Subscribable to fire events. _sendToSubscribers: function(eventName, eventInfo) { try { - Utils.assertThrow("eventName must be a non-empty string", + Utils.assertThrow("eventName must be a non-empty string", eventName && typeof(eventName) == "string"); - + if (!this.subscribers || !this.subscribers[eventName]) return; - + var self = this; var subsCopy = iQ.merge([], this.subscribers[eventName]); - subsCopy.forEach(function(object) { + subsCopy.forEach(function(object) { object.callback(self, eventInfo); }); } catch(e) { @@ -501,7 +501,7 @@ window.Subscribable.prototype = { // Singelton with common utility functions. var Utils = { _isMac : null, - + // ___ Windows and Tabs // ---------- @@ -511,7 +511,7 @@ var Utils = { try { var tabBrowser = this.getCurrentWindow().gBrowser; Utils.assert('tabBrowser', tabBrowser); - + var rawTab = tabBrowser.selectedTab; for ( var i=0; i Date: Mon, 19 Jul 2010 17:37:22 +0800 Subject: [PATCH 151/369] Fixed Bug 579814 - Fix behavior of tab-manipulation keyboard shortcuts --- browser/base/content/tabview/modules/utils.jsm | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 3b4aae107569..5f552bb9b54c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -500,8 +500,6 @@ window.Subscribable.prototype = { // Class: Utils // Singelton with common utility functions. var Utils = { - _isMac : null, - // ___ Windows and Tabs // ---------- @@ -728,20 +726,6 @@ var Utils = { } return value; - }, - - // ---------- - // Function: isMac - // Returns true if running on a Mac. - isMac: function() { - if (this._isMac == null) { - var xulRuntime = - Components.classes["@mozilla.org/xre/app-info;1"]. - getService(Components.interfaces.nsIXULRuntime); - this._isMac = (xulRuntime.OS == "Darwin"); - } - - return this._isMac; } }; From 0b1bea18546116b7a0533ee2d421a23fb25e73b5 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Mon, 19 Jul 2010 14:21:09 -0400 Subject: [PATCH 152/369] rm stacktrace.js! --HG-- extra : rebase_source : ee145386d3b3836f0c02005c1f4f7ef517377b7e --- .../base/content/tabview/modules/utils.jsm | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 5f552bb9b54c..373555825dcf 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -570,19 +570,22 @@ var Utils = { // Pass as many arguments as you want, it'll print them all. trace: function() { var text = this.expandArgumentsForLog(arguments); - if (typeof(printStackTrace) != 'function') - this.log(text + ' trace: you need to include stacktrace.js'); - else { - var calls = printStackTrace(); - calls.splice(0, 3); // Remove this call and the printStackTrace calls - this.log('trace: ' + text + '\n' + calls.join('\n')); + try { // coerce an error + throw new Error("error"); + } catch (e) { + // cut off the first two lines of the stack trace, because they're just this function. + var stack = e.stack.replace(/^.*?\n.*?\n/,''); + // if the caller was assert, cut out the line for the assert function as well. + if (this.trace.caller.name == 'Utils_assert') + stack = stack.replace(/^.*?\n/,''); + this.log('trace: ' + text + '\n' + stack); } }, // ---------- // Function: assert // Prints a stack trace along with label (as a console message) if condition is false. - assert: function(label, condition) { + assert: function Utils_assert(label, condition) { if (!condition) { var text; if (typeof(label) == 'undefined') @@ -590,11 +593,6 @@ var Utils = { else text = 'tabcandy assert: ' + label; - if (typeof(printStackTrace) == 'function') { - var calls = printStackTrace(); - text += '\n' + calls[3]; - } - this.trace(text); } }, @@ -610,10 +608,11 @@ var Utils = { else text = 'tabcandy assert: ' + label; - if (typeof(printStackTrace) == 'function') { - var calls = printStackTrace(); - calls.splice(0, 3); // Remove this call and the printStackTrace calls - text += '\n' + calls.join('\n'); + try { // coerce an error + throw new Error("error"); + } catch (e) { + // cut off the first two lines of the stack trace, because they're just this function. + text += e.stack.replace(/^.*?\n.*?\n/,''); } throw text; From 475f1478b145e4d07a225766a9a89967118e0564 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 19 Jul 2010 15:27:19 -0700 Subject: [PATCH 153/369] + Filled out iQ.js comments --- browser/base/content/tabview/iq.js | 58 ++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index b57332c855df..8c773d637723 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -45,7 +45,7 @@ // ********** // Title: iq.js -// jQuery, hacked down to just the bits we need, with a bunch of other stuff added. +// Various helper functions, in the vein of jQuery. (function( window, undefined ) { @@ -239,6 +239,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: addClass + // Adds the given class(es) to the receiver. addClass: function( value ) { if ( iQ.isFunction(value) ) { Utils.assert('does not support function argument', false); @@ -261,6 +262,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: removeClass + // Removes the given class(es) from the receiver. removeClass: function( value ) { if ( iQ.isFunction(value) ) { Utils.assert('does not support function argument', false); @@ -287,6 +289,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: hasClass + // Returns true is the receiver has the given css class. hasClass: function( selector ) { for ( var i = 0, l = this.length; i < l; i++ ) { if ( this[i].classList.contains( selector ) ) { @@ -298,6 +301,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: find + // Searches the receiver and its children, returning a new iQ object with + // elements that match the given selector. find: function( selector ) { var ret = [], length = 0; @@ -327,6 +332,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: remove + // Removes the receiver from the DOM. remove: function(unused) { Utils.assert('does not accept a selector', unused === undefined); for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { @@ -340,6 +346,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: empty + // Removes all of the reciever's children and HTML content from the DOM. empty: function() { for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { while ( elem.firstChild ) { @@ -352,6 +359,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: width + // Returns the width of the receiver. width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return parseInt(this.css('width')); @@ -359,6 +367,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: height + // Returns the height of the receiver. height: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return parseInt(this.css('height')); @@ -366,6 +375,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: position + // Returns an object with the receiver's position in left and top properties. position: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return { @@ -376,6 +386,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: bounds + // Returns a with the receiver's bounds. bounds: function(unused) { Utils.assert('does not yet support setting', unused === undefined); var p = this.position(); @@ -384,6 +395,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: data + // Pass in both key and value to attach some data to the receiver; + // pass in just key to retrieve it. data: function(key, value) { var data = null; if (value === undefined) { @@ -406,6 +419,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: html + // Given a value, sets the receiver's innerHTML to it; otherwise returns what's already there. // TODO: security html: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); @@ -418,6 +432,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: text + // Given a value, sets the receiver's textContent to it; otherwise returns what's already there. text: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) { @@ -429,6 +444,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: val + // Given a value, sets the receiver's value to it; otherwise returns what's already there. val: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) { @@ -441,6 +457,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: appendTo + // Appends the receiver to the result of iQ(selector). appendTo: function(selector) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); iQ(selector).append(this); @@ -449,6 +466,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: append + // Appends the result of iQ(selector) to the receiver. append: function(selector) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); var object = iQ(selector); @@ -479,6 +497,13 @@ iQ.fn = iQ.prototype = { // ---------- // Function: css + // Sets or gets CSS properties on the receiver. When setting certain numerical properties, + // will automatically add "px". + // + // Possible call patterns: + // a: object, b: undefined - sets with properties from a + // a: string, b: undefined - gets property specified by a + // a: string, b: string/number - sets property specified by a to b css: function(a, b) { var properties = null; @@ -596,6 +621,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: fadeOut + // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { try { Utils.assert('does not yet support duration', iQ.isFunction(callback) || callback === undefined); @@ -618,6 +644,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: fadeIn + // Animates the receiver to full opacity. fadeIn: function() { try { this.css({display: ''}); @@ -635,6 +662,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: hide + // Hides the receiver. hide: function() { try { this.css({display: 'none', opacity: 0}); @@ -647,6 +675,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: show + // Shows the receiver. show: function() { try { this.css({display: '', opacity: 1}); @@ -692,6 +721,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: one + // Binds the given function to the given event type, but only for one call; + // automatically unbinds after the event fires once. one: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); @@ -705,6 +736,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: unbind + // Unbinds the given function from the given event type. unbind: function(type, func) { Utils.assert('Must provide a function', iQ.isFunction(func)); @@ -732,8 +764,13 @@ iQ.fn = iQ.prototype = { // Give the init function the iQ prototype for later instantiation iQ.fn.init.prototype = iQ.fn; +// ########## +// Class: iQ +// Additional utility functions. + // ---------- // Function: extend +// Pass several objects in and it will combine them all into the first object and return it. iQ.extend = iQ.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; @@ -790,35 +827,24 @@ iQ.extend = iQ.fn.extend = function() { return target; }; -// ########## -// Class: iQ -// Singleton iQ.extend({ - // ---------- - // Variable: animationCount - // For internal use only - animationCount: 0, - - // ---------- - // Function: isAnimating - isAnimating: function() { - return (this.animationCount != 0); - }, - // ----------- // Function: isFunction + // Returns true if the given object is a function. isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, // ---------- // Function: isArray + // Returns true if the given object is an array. isArray: function( obj ) { return toString.call(obj) === "[object Array]"; }, // ---------- // Function: isPlainObject + // Check to see if an object is a plain object (created using "{}" or "new Object"). isPlainObject: function( obj ) { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. @@ -845,6 +871,7 @@ iQ.extend({ // ---------- // Function: isEmptyObject + // Returns true if the given object has no members. isEmptyObject: function( obj ) { for ( var name in obj ) { return false; @@ -854,6 +881,7 @@ iQ.extend({ // ---------- // Function: merge + // Merge two arrays and return the result. merge: function( first, second ) { var i = first.length, j = 0; From b40ce0f381f24d66f530232753b73522c20f6390 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Tue, 20 Jul 2010 10:17:29 -0700 Subject: [PATCH 154/369] Remove trailing spaces/tabs from tabcandy files. --- browser/base/content/tabview/iq.js | 58 +++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 8c773d637723..9e644777d5ff 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -45,7 +45,7 @@ // ********** // Title: iq.js -// Various helper functions, in the vein of jQuery. +// Various helper functions, in the vein of jQuery. (function( window, undefined ) { @@ -239,7 +239,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: addClass - // Adds the given class(es) to the receiver. + // Adds the given class(es) to the receiver. addClass: function( value ) { if ( iQ.isFunction(value) ) { Utils.assert('does not support function argument', false); @@ -262,7 +262,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: removeClass - // Removes the given class(es) from the receiver. + // Removes the given class(es) from the receiver. removeClass: function( value ) { if ( iQ.isFunction(value) ) { Utils.assert('does not support function argument', false); @@ -289,7 +289,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: hasClass - // Returns true is the receiver has the given css class. + // Returns true is the receiver has the given css class. hasClass: function( selector ) { for ( var i = 0, l = this.length; i < l; i++ ) { if ( this[i].classList.contains( selector ) ) { @@ -301,8 +301,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: find - // Searches the receiver and its children, returning a new iQ object with - // elements that match the given selector. + // Searches the receiver and its children, returning a new iQ object with + // elements that match the given selector. find: function( selector ) { var ret = [], length = 0; @@ -332,7 +332,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: remove - // Removes the receiver from the DOM. + // Removes the receiver from the DOM. remove: function(unused) { Utils.assert('does not accept a selector', unused === undefined); for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { @@ -359,7 +359,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: width - // Returns the width of the receiver. + // Returns the width of the receiver. width: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return parseInt(this.css('width')); @@ -375,7 +375,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: position - // Returns an object with the receiver's position in left and top properties. + // Returns an object with the receiver's position in left and top properties. position: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return { @@ -386,7 +386,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: bounds - // Returns a with the receiver's bounds. + // Returns a with the receiver's bounds. bounds: function(unused) { Utils.assert('does not yet support setting', unused === undefined); var p = this.position(); @@ -395,8 +395,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: data - // Pass in both key and value to attach some data to the receiver; - // pass in just key to retrieve it. + // Pass in both key and value to attach some data to the receiver; + // pass in just key to retrieve it. data: function(key, value) { var data = null; if (value === undefined) { @@ -419,7 +419,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: html - // Given a value, sets the receiver's innerHTML to it; otherwise returns what's already there. + // Given a value, sets the receiver's innerHTML to it; otherwise returns what's already there. // TODO: security html: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); @@ -432,7 +432,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: text - // Given a value, sets the receiver's textContent to it; otherwise returns what's already there. + // Given a value, sets the receiver's textContent to it; otherwise returns what's already there. text: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) { @@ -444,7 +444,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: val - // Given a value, sets the receiver's value to it; otherwise returns what's already there. + // Given a value, sets the receiver's value to it; otherwise returns what's already there. val: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) { @@ -457,7 +457,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: appendTo - // Appends the receiver to the result of iQ(selector). + // Appends the receiver to the result of iQ(selector). appendTo: function(selector) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); iQ(selector).append(this); @@ -466,7 +466,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: append - // Appends the result of iQ(selector) to the receiver. + // Appends the result of iQ(selector) to the receiver. append: function(selector) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); var object = iQ(selector); @@ -497,13 +497,13 @@ iQ.fn = iQ.prototype = { // ---------- // Function: css - // Sets or gets CSS properties on the receiver. When setting certain numerical properties, - // will automatically add "px". + // Sets or gets CSS properties on the receiver. When setting certain numerical properties, + // will automatically add "px". // - // Possible call patterns: + // Possible call patterns: // a: object, b: undefined - sets with properties from a // a: string, b: undefined - gets property specified by a - // a: string, b: string/number - sets property specified by a to b + // a: string, b: string/number - sets property specified by a to b css: function(a, b) { var properties = null; @@ -621,7 +621,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: fadeOut - // Animates the receiver to full transparency. Calls callback on completion. + // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { try { Utils.assert('does not yet support duration', iQ.isFunction(callback) || callback === undefined); @@ -644,7 +644,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: fadeIn - // Animates the receiver to full opacity. + // Animates the receiver to full opacity. fadeIn: function() { try { this.css({display: ''}); @@ -721,7 +721,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: one - // Binds the given function to the given event type, but only for one call; + // Binds the given function to the given event type, but only for one call; // automatically unbinds after the event fires once. one: function(type, func) { Utils.assert('does not support eventData argument', iQ.isFunction(func)); @@ -736,7 +736,7 @@ iQ.fn = iQ.prototype = { // ---------- // Function: unbind - // Unbinds the given function from the given event type. + // Unbinds the given function from the given event type. unbind: function(type, func) { Utils.assert('Must provide a function', iQ.isFunction(func)); @@ -766,11 +766,11 @@ iQ.fn.init.prototype = iQ.fn; // ########## // Class: iQ -// Additional utility functions. +// Additional utility functions. // ---------- // Function: extend -// Pass several objects in and it will combine them all into the first object and return it. +// Pass several objects in and it will combine them all into the first object and return it. iQ.extend = iQ.fn.extend = function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; @@ -830,14 +830,14 @@ iQ.extend = iQ.fn.extend = function() { iQ.extend({ // ----------- // Function: isFunction - // Returns true if the given object is a function. + // Returns true if the given object is a function. isFunction: function( obj ) { return toString.call(obj) === "[object Function]"; }, // ---------- // Function: isArray - // Returns true if the given object is an array. + // Returns true if the given object is an array. isArray: function( obj ) { return toString.call(obj) === "[object Array]"; }, From 1b1134fd1818ca789496cb23e0243e84282a825e Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 20 Jul 2010 16:49:31 -0400 Subject: [PATCH 155/369] Bug 580382 - Typos and misc cleanups in mobile js. r=mfinkle --- toolkit/content/Geometry.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/toolkit/content/Geometry.jsm b/toolkit/content/Geometry.jsm index 65f0ebca1296..f3821d7fa6eb 100644 --- a/toolkit/content/Geometry.jsm +++ b/toolkit/content/Geometry.jsm @@ -225,7 +225,7 @@ Util.Timeout = function(aCallback) { this._callback = aCallback; this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this._active = false; -} +}; Util.Timeout.prototype = { /** Timer callback. Don't call this manually. */ @@ -396,7 +396,7 @@ function Rect(x, y, w, h) { Rect.fromRect = function fromRect(r) { return new Rect(r.left, r.top, r.right - r.left, r.bottom - r.top); -} +}; Rect.prototype = { get x() { return this.left; }, From 5fae71c4f8629457fa6603d8ab03661eec8bbd50 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 13:42:32 -0500 Subject: [PATCH 156/369] Bug 579236: Fix shutdown crash and lesser bugs with remote pref observers. r=dwitte --- dom/ipc/ContentChild.cpp | 190 ++++++++++++++++++++++++++++++++------- dom/ipc/ContentChild.h | 93 ++++--------------- 2 files changed, 173 insertions(+), 110 deletions(-) diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index b301c20391a7..238e4f0eb4b8 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -45,7 +45,14 @@ #include "mozilla/ipc/XPCShellEnvironment.h" #include "mozilla/jsipc/PContextWrapperChild.h" +#include "nsIObserverService.h" +#include "nsTObserverArray.h" +#include "nsIObserver.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsServiceManagerUtils.h" #include "nsXULAppAPI.h" +#include "nsWeakReference.h" #include "base/message_loop.h" #include "base/task.h" @@ -59,9 +66,115 @@ using namespace mozilla::net; namespace mozilla { namespace dom { +class PrefObserver +{ +public: + /** + * Pass |aHoldWeak=true| to force this to hold a weak ref to + * |aObserver|. Otherwise, this holds a strong ref. + * + * XXX/cjones: what do domain and prefRoot mean? + */ + PrefObserver(nsIObserver *aObserver, bool aHoldWeak, + const nsCString& aPrefRoot, const nsCString& aDomain) + : mPrefRoot(aPrefRoot) + , mDomain(aDomain) + { + if (aHoldWeak) { + nsCOMPtr supportsWeakRef = + do_QueryInterface(aObserver); + if (supportsWeakRef) + mWeakObserver = do_GetWeakReference(aObserver); + } else { + mObserver = aObserver; + } + } + + ~PrefObserver() {} + + /** + * Return true if this observer can no longer receive + * notifications. + */ + bool IsDead() const + { + nsCOMPtr observer = GetObserver(); + return !!observer; + } + + /** + * Return true iff a request to remove observers matching + * entails removal of this. + */ + bool ShouldRemoveFrom(nsIObserver* aObserver, + const nsCString& aPrefRoot, + const nsCString& aDomain) const + { + nsCOMPtr observer = GetObserver(); + return (observer == aObserver && + mDomain == aDomain && mPrefRoot == aPrefRoot); + } + + /** + * Return true iff this should be notified of changes to |aPref|. + */ + bool Observes(const nsCString& aPref) const + { + nsCAutoString myPref(mPrefRoot); + myPref += mDomain; + return StringBeginsWith(aPref, myPref); + } + + /** + * Notify this of a pref change that's relevant to our interests + * (see Observes() above). Return false iff this no longer cares + * to observe any more pref changes. + */ + bool Notify() const + { + nsCOMPtr observer = GetObserver(); + if (!observer) { + return false; + } + + nsCOMPtr prefBranch; + nsCOMPtr prefService = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefService) { + prefService->GetBranch(mPrefRoot.get(), + getter_AddRefs(prefBranch)); + observer->Observe(prefBranch, "nsPref:changed", + NS_ConvertASCIItoUTF16(mDomain).get()); + } + return true; + } + +private: + already_AddRefed GetObserver() const + { + nsCOMPtr observer = + mObserver ? mObserver : do_QueryReferent(mWeakObserver); + return observer.forget(); + } + + // We only either hold a strong or a weak reference to the + // observer, so only either mObserver or + // GetReferent(mWeakObserver) is ever non-null. + nsCOMPtr mObserver; + nsWeakPtr mWeakObserver; + nsCString mPrefRoot; + nsCString mDomain; + + // disable these + PrefObserver(const PrefObserver&); + PrefObserver& operator=(const PrefObserver&); +}; + + ContentChild* ContentChild::sSingleton; ContentChild::ContentChild() + : mDead(false) { } @@ -159,60 +272,71 @@ ContentChild::ActorDestroy(ActorDestroyReason why) if (AbnormalShutdown == why) NS_WARNING("shutting down because of crash!"); - ClearPrefObservers(); + // We might be holding the last ref to some of the observers in + // mPrefObserverArray. Some of them try to unregister themselves + // in their dtors (sketchy). To side-step uaf problems and so + // forth, we set this mDead flag. Then, if during a Clear() a + // being-deleted observer tries to unregister itself, it hits the + // |if (mDead)| special case below and we're safe. + mDead = true; + mPrefObservers.Clear(); + XRE_ShutdownChildProcess(); } nsresult -ContentChild::AddRemotePrefObserver(const nsCString &aDomain, - const nsCString &aPrefRoot, - nsIObserver *aObserver, +ContentChild::AddRemotePrefObserver(const nsCString& aDomain, + const nsCString& aPrefRoot, + nsIObserver* aObserver, PRBool aHoldWeak) { - nsPrefObserverStorage* newObserver = - new nsPrefObserverStorage(aObserver, aDomain, aPrefRoot, aHoldWeak); - - mPrefObserverArray.AppendElement(newObserver); + if (aObserver) { + mPrefObservers.AppendElement( + new PrefObserver(aObserver, aHoldWeak, aPrefRoot, aDomain)); + } return NS_OK; } nsresult -ContentChild::RemoveRemotePrefObserver(const nsCString &aDomain, - const nsCString &aPrefRoot, - nsIObserver *aObserver) +ContentChild::RemoveRemotePrefObserver(const nsCString& aDomain, + const nsCString& aPrefRoot, + nsIObserver* aObserver) { - if (mPrefObserverArray.IsEmpty()) + if (mDead) { + // Silently ignore, we're about to exit. See comment in + // ActorDestroy(). return NS_OK; + } - nsPrefObserverStorage *entry; - for (PRUint32 i = 0; i < mPrefObserverArray.Length(); ++i) { - entry = mPrefObserverArray[i]; - if (entry && entry->GetObserver() == aObserver && - entry->GetDomain().Equals(aDomain)) { - // Remove this observer from our array - mPrefObserverArray.RemoveElementAt(i); + for (PRUint32 i = 0; i < mPrefObservers.Length(); + /*we mutate the array during the loop; ++i iff no mutation*/) { + PrefObserver* observer = mPrefObservers[i]; + if (observer->IsDead()) { + mPrefObservers.RemoveElementAt(i); + continue; + } else if (observer->ShouldRemoveFrom(aObserver, aPrefRoot, aDomain)) { + mPrefObservers.RemoveElementAt(i); return NS_OK; } + ++i; } - NS_WARNING("No preference Observer was matched !"); + + NS_WARNING("RemoveRemotePrefObserver(): no observer was matched!"); return NS_ERROR_UNEXPECTED; } bool -ContentChild::RecvNotifyRemotePrefObserver(const nsCString& aDomain) +ContentChild::RecvNotifyRemotePrefObserver(const nsCString& aPref) { - nsPrefObserverStorage *entry; - for (PRUint32 i = 0; i < mPrefObserverArray.Length(); ) { - entry = mPrefObserverArray[i]; - nsCAutoString prefName(entry->GetPrefRoot() + entry->GetDomain()); - // aDomain here is returned from our global pref observer from parent - // so it's really a full pref name (i.e. root + domain) - if (StringBeginsWith(aDomain, prefName)) { - if (!entry->NotifyObserver()) { - // remove the observer from the list - mPrefObserverArray.RemoveElementAt(i); - continue; - } + for (PRUint32 i = 0; i < mPrefObservers.Length(); + /*we mutate the array during the loop; ++i iff no mutation*/) { + PrefObserver* observer = mPrefObservers[i]; + if (observer->Observes(aPref) && + !observer->Notify()) { + // |observer| had a weak ref that went away, so it no + // longer cares about pref changes + mPrefObservers.RemoveElementAt(i); + continue; } ++i; } diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 06250f01ef21..505f9f8e58ec 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -42,85 +42,24 @@ #include "mozilla/dom/PContentChild.h" -#include "nsIObserverService.h" -#include "nsTObserverArray.h" -#include "nsIObserver.h" -#include "nsIPrefService.h" -#include "nsIPrefBranch.h" -#include "nsServiceManagerUtils.h" #include "nsTArray.h" -#include "nsAutoPtr.h" -#include "nsWeakReference.h" struct ChromePackage; +class nsIObserver; struct ResourceMapping; struct OverrideMapping; namespace mozilla { namespace dom { +class PrefObserver; + class ContentChild : public PContentChild { public: ContentChild(); virtual ~ContentChild(); - class nsPrefObserverStorage { - public: - nsPrefObserverStorage(nsIObserver *aObserver, nsCString aDomain, - nsCString aPrefRoot, bool aHoldWeak) { - mDomain = aDomain; - mPrefRoot = aPrefRoot; - mObserver = aObserver; - if (aHoldWeak) { - nsCOMPtr weakRefFactory = - do_QueryInterface(aObserver); - if (weakRefFactory) - mWeakRef = do_GetWeakReference(aObserver); - } else { - mWeakRef = nsnull; - } - } - - ~nsPrefObserverStorage() { - } - - bool NotifyObserver() { - nsCOMPtr observer; - if (mWeakRef) { - observer = do_QueryReferent(mWeakRef); - if (!observer) { - // this weak referenced observer went away, tell - // the caller so he can remove the observer from the list - return false; - } - } else { - observer = mObserver; - } - - nsCOMPtr prefBranch; - nsCOMPtr prefService = - do_GetService(NS_PREFSERVICE_CONTRACTID); - if (prefService) { - prefService->GetBranch(mPrefRoot.get(), - getter_AddRefs(prefBranch)); - observer->Observe(prefBranch, "nsPref:changed", - NS_ConvertASCIItoUTF16(mDomain).get()); - } - return true; - } - - nsIObserver* GetObserver() { return mObserver; } - const nsCString& GetDomain() { return mDomain; } - const nsCString& GetPrefRoot() { return mPrefRoot; } - - private: - nsCOMPtr mObserver; - nsWeakPtr mWeakRef; - nsCString mPrefRoot; - nsCString mDomain; - }; - bool Init(MessageLoop* aIOLoop, base::ProcessHandle aParentHandle, IPC::Channel* aChannel); @@ -149,28 +88,28 @@ public: virtual bool RecvSetOffline(const PRBool& offline); - nsresult AddRemotePrefObserver(const nsCString &aDomain, - const nsCString &aPrefRoot, - nsIObserver *aObserver, PRBool aHoldWeak); - nsresult RemoveRemotePrefObserver(const nsCString &aDomain, - const nsCString &aPrefRoot, - nsIObserver *aObserver); - inline void ClearPrefObservers() { - mPrefObserverArray.Clear(); - } + /** + * Notify |aObserver| of changes to |aPrefRoot|.|aDomain|. If + * |aHoldWeak|, only a weak reference to |aObserver| is held. + */ + nsresult AddRemotePrefObserver(const nsCString& aDomain, + const nsCString& aPrefRoot, + nsIObserver* aObserver, PRBool aHoldWeak); + nsresult RemoveRemotePrefObserver(const nsCString& aDomain, + const nsCString& aPrefRoot, + nsIObserver* aObserver); virtual bool RecvNotifyRemotePrefObserver( const nsCString& aDomain); - - private: NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); - static ContentChild* sSingleton; + nsTArray > mPrefObservers; + bool mDead; - nsTArray< nsAutoPtr > mPrefObserverArray; + static ContentChild* sSingleton; DISALLOW_EVIL_CONSTRUCTORS(ContentChild); }; From 6ecf439706c134bcf35b07ca9ebd49b575fd5b9c Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 157/369] Bug 570294, part b: Add a Mutated() API allowing Layers to notify their managers that their Layer attributes have changed. r=Bas sr=roc --- gfx/layers/Layers.h | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 3c52db39519d..74424ebb984e 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -213,6 +213,12 @@ public: */ Layer* GetRoot() { return mRoot; } + /** + * CONSTRUCTION PHASE ONLY + * Called when a managee has mutated. + */ + virtual void Mutated(Layer* aLayer) { } + /** * CONSTRUCTION PHASE ONLY * Create a ThebesLayer for this manager's layer tree. @@ -332,7 +338,11 @@ public: * content. This enables some internal quality and performance * optimizations. */ - void SetIsOpaqueContent(PRBool aOpaque) { mIsOpaqueContent = aOpaque; } + void SetIsOpaqueContent(PRBool aOpaque) + { + mIsOpaqueContent = aOpaque; + Mutated(); + } /** * CONSTRUCTION PHASE ONLY * Tell this layer which region will be visible. It is the responsibility @@ -340,14 +350,22 @@ public: * contribute to the final visible window. This can be an * overapproximation to the true visible region. */ - virtual void SetVisibleRegion(const nsIntRegion& aRegion) { mVisibleRegion = aRegion; } + virtual void SetVisibleRegion(const nsIntRegion& aRegion) + { + mVisibleRegion = aRegion; + Mutated(); + } /** * CONSTRUCTION PHASE ONLY * Set the opacity which will be applied to this layer as it * is composited to the destination. */ - void SetOpacity(float aOpacity) { mOpacity = aOpacity; } + void SetOpacity(float aOpacity) + { + mOpacity = aOpacity; + Mutated(); + } /** * CONSTRUCTION PHASE ONLY @@ -365,6 +383,7 @@ public: if (aRect) { mClipRect = *aRect; } + Mutated(); } /** * CONSTRUCTION PHASE ONLY @@ -384,6 +403,7 @@ public: mUseClipRect = PR_TRUE; mClipRect = aRect; } + Mutated(); } /** @@ -393,7 +413,11 @@ public: * XXX Currently only transformations corresponding to 2D affine transforms * are supported. */ - void SetTransform(const gfx3DMatrix& aMatrix) { mTransform = aMatrix; } + void SetTransform(const gfx3DMatrix& aMatrix) + { + mTransform = aMatrix; + Mutated(); + } // These getters can be used anytime. float GetOpacity() { return mOpacity; } @@ -478,6 +502,8 @@ protected: mIsOpaqueContent(PR_FALSE) {} + void Mutated() { mManager->Mutated(this); } + // Print interesting information about this into aTo. Internally // used to implement Dump*() and Log*(). If subclasses have // additional interesting properties, they should override this with From 9636238eefb2fbda07c9b4fd43a87ff6f689a41c Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 158/369] Bug 570294, part c: C++ part of Layers IPC interface. r=Bas sr=vlad --- gfx/layers/Layers.h | 12 +- gfx/layers/Makefile.in | 8 + gfx/layers/ipc/ShadowLayers.h | 406 ++++++++++++++++++++++++++++++++++ 3 files changed, 425 insertions(+), 1 deletion(-) create mode 100644 gfx/layers/ipc/ShadowLayers.h diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 74424ebb984e..6b3b73cb505e 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -77,6 +77,7 @@ class ImageLayer; class ColorLayer; class ImageContainer; class CanvasLayer; +class SpecificLayerAttributes; #define MOZ_LAYER_DECL_NAME(n, e) \ virtual const char* Name() const { return n; } \ @@ -320,7 +321,8 @@ public: TYPE_CONTAINER, TYPE_IMAGE, TYPE_COLOR, - TYPE_CANVAS + TYPE_CANVAS, + TYPE_SHADOW }; virtual ~Layer() {} @@ -430,6 +432,14 @@ public: virtual Layer* GetFirstChild() { return nsnull; } const gfx3DMatrix& GetTransform() { return mTransform; } + /** + * DRAWING PHASE ONLY + * + * Write layer-subtype-specific attributes into aAttrs. Used to + * synchronize layer attributes to their shadows'. + */ + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { } + // Returns true if it's OK to save the contents of aLayer in an // opaque surface (a surface without an alpha channel). // If we can use a surface without an alpha channel, we should, because diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 011b78c8d038..8ac2f1c50215 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -47,6 +47,10 @@ VPATH = \ include $(DEPTH)/config/autoconf.mk +ifdef MOZ_IPC +VPATH += $(srcdir)/ipc +endif + MODULE = thebes LIBRARY_NAME = layers LIBXUL_LIBRARY = 1 @@ -102,6 +106,10 @@ endif include $(topsrcdir)/config/rules.mk +ifdef MOZ_IPC +include $(topsrcdir)/ipc/chromium/chromium-config.mk +endif + CXXFLAGS += $(MOZ_CAIRO_CFLAGS) LayerManagerOGLShaders.h: LayerManagerOGLShaders.txt genshaders.py $(GLOBAL_DEPS) diff --git a/gfx/layers/ipc/ShadowLayers.h b/gfx/layers/ipc/ShadowLayers.h new file mode 100644 index 000000000000..447b88a1d3a6 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.h @@ -0,0 +1,406 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#ifndef mozilla_layers_ShadowLayers_h +#define mozilla_layers_ShadowLayers_h 1 + +#include "gfxASurface.h" + +#include "ImageLayers.h" +#include "Layers.h" + +class gfxSharedImageSurface; + +namespace mozilla { +namespace layers { + +struct Edit; +struct EditReply; +class PLayerChild; +class PLayersChild; +class PLayersParent; +class ShadowableLayer; +class ShadowThebesLayer; +class ShadowImageLayer; +class ShadowCanvasLayer; +class Transaction; + +/** + * We want to share layer trees across thread contexts and address + * spaces for several reasons; chief among them + * + * - a parent process can paint a child process's layer tree while + * the child process is blocked, say on content script. This is + * important on mobile devices where UI responsiveness is key. + * + * - a dedicated "compositor" process can asynchronously (wrt the + * browser process) composite and animate layer trees, allowing a + * form of pipeline parallelism between compositor/browser/content + * + * - a dedicated "compositor" process can take all responsibility for + * accessing the GPU, which is desirable on systems with + * buggy/leaky drivers because the compositor process can die while + * browser and content live on (and failover mechanisms can be + * installed to quickly bring up a replacement compositor) + * + * The Layers model has a crisply defined API, which makes it easy to + * safely "share" layer trees. The ShadowLayers API extends Layers to + * allow a remote, parent process to access a child process's layer + * tree. + * + * ShadowLayerForwarder publishes a child context's layer tree to a + * parent context. This comprises recording layer-tree modifications + * into atomic transactions and pushing them over IPC. + * + * ShadowLayerManager grafts layer subtrees published by child-context + * ShadowLayerForwarder(s) into a parent-context layer tree. + * + * (Advanced note: because our process tree may have a height >2, a + * non-leaf subprocess may both receive updates from child processes + * and publish them to parent processes. Put another way, + * LayerManagers may be both ShadowLayerManagers and + * ShadowLayerForwarders.) + * + * There are only shadow types for layers that have different shadow + * vs. not-shadow behavior. ColorLayers and ContainerLayers behave + * the same way in both regimes (so far). + */ + +class ShadowLayerForwarder +{ +public: + virtual ~ShadowLayerForwarder(); + + /** + * Begin recording a transaction to be forwarded atomically to a + * ShadowLayerManager. + */ + void BeginTransaction(); + + /** + * The following methods may only be called after BeginTransaction() + * but before EndTransaction(). They mirror the LayerManager + * interface in Layers.h. + */ + + /** + * Notify the shadow manager that a new, "real" layer has been + * created, and a corresponding shadow layer should be created in + * the compositing process. + */ + void CreatedThebesLayer(ShadowableLayer* aThebes); + void CreatedContainerLayer(ShadowableLayer* aContainer); + void CreatedImageLayer(ShadowableLayer* aImage); + void CreatedColorLayer(ShadowableLayer* aColor); + void CreatedCanvasLayer(ShadowableLayer* aCanvas); + + /** + * Notify the shadow manager that a buffer has been created for the + * specificed layer. |aInitialFrontSurface| is one of the newly + * created, transparent black buffers for the layer; the "real" + * layer holds on to the other as its back buffer. We send it + * across on buffer creation to avoid special cases in the buffer + * swapping logic for Painted*() operations. + * + * It is expected that Created*Buffer() will be followed by a + * Painted*Buffer() in the same transaction, so that + * |aInitialFrontBuffer| is never actually drawn to screen. + */ + /** + * |aBufferRect| is the screen rect covered by |aInitialFrontBuffer|. + */ + void CreatedThebesBuffer(ShadowableLayer* aThebes, + nsIntRect aBufferRect, + gfxSharedImageSurface* aInitialFrontBuffer); + /** + * For the next two methods, |aSize| is the size of + * |aInitialFrontSurface|. + */ + void CreatedImageBuffer(ShadowableLayer* aImage, + nsIntSize aSize, + gfxSharedImageSurface* aInitialFrontSurface); + void CreatedCanvasBuffer(ShadowableLayer* aCanvas, + nsIntSize aSize, + gfxSharedImageSurface* aInitialFrontSurface); + + /** + * At least one attribute of |aMutant| has changed, and |aMutant| + * needs to sync to its shadow layer. This initial implementation + * forwards all attributes when any is mutated. + */ + void Mutated(ShadowableLayer* aMutant); + + void SetRoot(ShadowableLayer* aRoot); + /** + * Insert |aChild| after |aAfter| in |aContainer|. |aAfter| can be + * NULL to indicated that |aChild| should be appended to the end of + * |aContainer|'s child list. + */ + void InsertAfter(ShadowableLayer* aContainer, + ShadowableLayer* aChild, + ShadowableLayer* aAfter=NULL); + void RemoveChild(ShadowableLayer* aContainer, + ShadowableLayer* aChild); + + /** + * Notify the shadow manager that the specified layer's back buffer + * has new pixels and should become the new front buffer, and be + * re-rendered, in the compositing process. The former front buffer + * is swapped for |aNewFrontBuffer| and becomes the new back buffer + * for the "real" layer. + */ + /** + * |aBufferRect| is the screen rect covered as a whole by the + * possibly-toroidally-rotated |aNewFrontBuffer|. |aBufferRotation| + * is buffer's rotation, if any. + */ + void PaintedThebesBuffer(ShadowableLayer* aThebes, + nsIntRect aBufferRect, + nsIntPoint aBufferRotation, + gfxSharedImageSurface* aNewFrontBuffer); + /** + * NB: this initial implementation only forwards RGBA data for + * ImageLayers. This is slow, and will be optimized. + */ + void PaintedImage(ShadowableLayer* aImage, + gfxSharedImageSurface* aNewFrontSurface); + void PaintedCanvas(ShadowableLayer* aCanvas, + gfxSharedImageSurface* aNewFrontSurface); + + /** + * End the current transaction and forward it to ShadowLayerManager. + * |aReplies| are directions from the ShadowLayerManager to the + * caller of EndTransaction(). + */ + PRBool EndTransaction(nsTArray* aReplies); + + /** + * True if this is forwarding to a ShadowLayerManager. + */ + PRBool HasShadowManager() { return !!mShadowManager; } + + PRBool AllocDoubleBuffer(const gfxIntSize& aSize, + gfxASurface::gfxImageFormat aFormat, + gfxSharedImageSurface** aFrontBuffer, + gfxSharedImageSurface** aBackBuffer); + + void DestroySharedSurface(gfxSharedImageSurface* aSurface); + + /** + * Construct a shadow of |aLayer| on the "other side", at the + * ShadowLayerManager. + */ + PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer); + +protected: + ShadowLayerForwarder(); + + PLayersChild* mShadowManager; + +private: + Transaction* mTxn; +}; + + +class ShadowLayerManager : public LayerManager +{ +public: + virtual ~ShadowLayerManager() {} + + PRBool HasForwarder() { return !!mForwarder; } + + void SetForwarder(PLayersParent* aForwarder) + { + NS_ASSERTION(!HasForwarder(), "setting forwarder twice?"); + mForwarder = aForwarder; + } + + void DestroySharedSurface(gfxSharedImageSurface* aSurface); + + /** CONSTRUCTION PHASE ONLY */ + virtual already_AddRefed CreateShadowThebesLayer() = 0; + /** CONSTRUCTION PHASE ONLY */ + virtual already_AddRefed CreateShadowImageLayer() = 0; + /** CONSTRUCTION PHASE ONLY */ + virtual already_AddRefed CreateShadowCanvasLayer() = 0; + +protected: + ShadowLayerManager() : mForwarder(NULL) {} + + PLayersParent* mForwarder; +}; + + +/** + * A ShadowableLayer is a Layer can be shared with a parent context + * through a ShadowLayerForwarder. A ShadowableLayer maps to a + * Shadow*Layer in a parent context. + * + * Note that ShadowLayers can themselves be ShadowableLayers. + */ +class ShadowableLayer +{ +public: + virtual ~ShadowableLayer() {} + + virtual Layer* AsLayer() = 0; + + /** + * True if this layer has a shadow in a parent process. + */ + PRBool HasShadow() { return !!mShadow; } + + /** + * Return the IPC handle to a Shadow*Layer referring to this if one + * exists, NULL if not. + */ + PLayerChild* GetShadow() { return mShadow; } + +protected: + ShadowableLayer() : mShadow(NULL) {} + + PLayerChild* mShadow; +}; + + +class ShadowThebesLayer : public ThebesLayer +{ +public: + virtual void InvalidateRegion(const nsIntRegion& aRegion) + { + NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions"); + } + + /** + * CONSTRUCTION PHASE ONLY + */ + void SetValidRegion(const nsIntRegion& aRegion) + { + mValidRegion = aRegion; + Mutated(); + } + + /** + * CONSTRUCTION PHASE ONLY + * + * Publish the remote layer's back ThebesLayerBuffer to this shadow, + * swapping out the old front ThebesLayerBuffer (the new back buffer + * for the remote layer). + * + * XXX should the receiving process blit updates from the new front + * buffer to the previous front buffer (new back buffer) while it has + * access to the new front buffer? Or is it better to fill the + * updates bits in anew on the new back buffer? + * + * Seems like memcpy()s from new-front to new-back would have to + * always be no slower than any kind of fill from content, so one + * would expect the former to win in terms of total throughput. + * However, that puts the blit on the critical path of + * publishing-process-blocking-on-receiving-process, so + * responsiveness might suffer, pointing to the latter. Experience + * will tell! (Maybe possible to choose between both depending on + * size of blit vs. expense of re-fill?) + */ + virtual already_AddRefed + Swap(gfxSharedImageSurface* aNewFront, + const nsIntRect& aBufferRect, + const nsIntPoint& aRotation) = 0; + + MOZ_LAYER_DECL_NAME("ShadowThebesLayer", TYPE_SHADOW) + +protected: + ShadowThebesLayer(LayerManager* aManager, void* aImplData) : + ThebesLayer(aManager, aImplData) {} +}; + + +class ShadowCanvasLayer : public CanvasLayer +{ +public: + /** + * CONSTRUCTION PHASE ONLY + * + * Publish the remote layer's back surface to this shadow, swapping + * out the old front surface (the new back surface for the remote + * layer). + */ + virtual already_AddRefed + Swap(gfxSharedImageSurface* aNewFront) = 0; + + MOZ_LAYER_DECL_NAME("ShadowCanvasLayer", TYPE_SHADOW) + +protected: + ShadowCanvasLayer(LayerManager* aManager, void* aImplData) : + CanvasLayer(aManager, aImplData) {} +}; + + +class ShadowImageLayer : public ImageLayer +{ +public: + /** + * CONSTRUCTION PHASE ONLY + * + * Initialize this with a (temporary) front surface with the given + * size. This is expected to be followed with a Swap() in the same + * transaction to bring in real pixels. Init() may only be called + * once. + */ + virtual PRBool Init(gfxSharedImageSurface* aFront, const nsIntSize& aSize) = 0; + + /** + * CONSTRUCTION PHASE ONLY + * @see ShadowCanvasLayer::Swap + */ + virtual already_AddRefed + Swap(gfxSharedImageSurface* newFront) = 0; + + MOZ_LAYER_DECL_NAME("ShadowImageLayer", TYPE_SHADOW) + +protected: + ShadowImageLayer(LayerManager* aManager, void* aImplData) : + ImageLayer(aManager, aImplData) {} +}; + + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayers_h From df8456dc042267f4967118bf03d97fb6ac17c4f6 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 159/369] Bug 570294, part d: IPC protocol for Layers. r=jrmuizel sr=vlad --- gfx/layers/ipc/PLayer.ipdl | 72 ++++++++++++ gfx/layers/ipc/PLayers.ipdl | 216 ++++++++++++++++++++++++++++++++++++ gfx/layers/ipc/ipdl.mk | 4 + ipc/ipdl/Makefile.in | 9 +- 4 files changed, 297 insertions(+), 4 deletions(-) create mode 100644 gfx/layers/ipc/PLayer.ipdl create mode 100644 gfx/layers/ipc/PLayers.ipdl create mode 100644 gfx/layers/ipc/ipdl.mk diff --git a/gfx/layers/ipc/PLayer.ipdl b/gfx/layers/ipc/PLayer.ipdl new file mode 100644 index 000000000000..28463492fd6f --- /dev/null +++ b/gfx/layers/ipc/PLayer.ipdl @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +include protocol PLayers; + +namespace mozilla { +namespace layers { + +/** + * PLayer represents a layer shared across thread contexts. + */ + +sync protocol PLayer { + manager PLayers; + + /** + * OWNERSHIP MODEL + * + * Roughly speaking, the child side "actually owns" a Layer. This + * is because the parent side is the "shadow"; when the child + * releases a Layer, the parent's shadow is no longer meaningful. + * + * To implement this model, the concrete PLayerParent keeps a + * strong ref to its Layer, so the Layer's lifetime is bound to + * the PLayerParent's. Then, when the Layer's refcount hits 0 on + * the child side, we send __delete__() from the child to parent. + * The parent then releases its Layer, which results in the Layer + * being deleted "soon" (usually immediately). + */ +parent: + __delete__(); +}; + +} // layers +} // mozilla diff --git a/gfx/layers/ipc/PLayers.ipdl b/gfx/layers/ipc/PLayers.ipdl new file mode 100644 index 000000000000..7f90abff46c9 --- /dev/null +++ b/gfx/layers/ipc/PLayers.ipdl @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +//include protocol PCompositor; +include protocol PLayer; + +using gfx3DMatrix; +using gfxRGBA; +using nsIntPoint; +using nsIntRect; +using nsIntRegion; +using nsIntSize; +using mozilla::GraphicsFilterType; +using mozilla::null_t; + +/** + * The layers protocol is spoken between thread contexts that manage + * layer (sub)trees. The protocol comprises atomically publishing + * layer subtrees to a "shadow" thread context (which grafts the + * subtree into its own tree), and atomically updating a published + * subtree. ("Atomic" in this sense is wrt painting.) + */ + +namespace mozilla { +namespace layers { + +// Create a shadow layer for |layer| +struct OpCreateThebesLayer { PLayer layer; }; +struct OpCreateContainerLayer { PLayer layer; }; +struct OpCreateImageLayer { PLayer layer; }; +struct OpCreateColorLayer { PLayer layer; }; +struct OpCreateCanvasLayer { PLayer layer; }; + +// For the "buffer creation" operations, we send an initial front +// buffer that only contains (transparent) black pixels just so that +// we can swap it back after the first OpPaint without a special case. +struct OpCreateThebesBuffer { + PLayer layer; + nsIntRect bufferRect; + Shmem initialFront; +}; + +struct OpCreateCanvasBuffer { + PLayer layer; + nsIntSize size; + Shmem initialFront; +}; + +struct OpCreateImageBuffer { + PLayer layer; + nsIntSize size; + Shmem initialFront; +}; + +// Change a layer's attributes +struct CommonLayerAttributes { + nsIntRegion visibleRegion; + gfx3DMatrix transform; + bool isOpaqueContent; + float opacity; + bool useClipRect; + nsIntRect clipRect; +}; + +struct ThebesLayerAttributes { nsIntRegion validRegion; }; +struct ColorLayerAttributes { gfxRGBA color; }; +struct CanvasLayerAttributes { GraphicsFilterType filter; }; +struct ImageLayerAttributes { GraphicsFilterType filter; }; + +union SpecificLayerAttributes { + null_t; + ThebesLayerAttributes; + ColorLayerAttributes; + CanvasLayerAttributes; + ImageLayerAttributes; +}; + +struct LayerAttributes { + CommonLayerAttributes common; + SpecificLayerAttributes specific; +}; + +struct OpSetLayerAttributes { + PLayer layer; + LayerAttributes attrs; +}; + + +// Monkey with the tree structure +struct OpSetRoot { PLayer root; }; +struct OpInsertAfter { PLayer container; PLayer childLayer; PLayer after; }; +struct OpAppendChild { PLayer container; PLayer childLayer; }; +struct OpRemoveChild { PLayer container; PLayer childLayer; }; + + +// Paint (buffer update) +struct ThebesBuffer { + Shmem buffer; + nsIntRect rect; + nsIntPoint rotation; +}; +struct OpPaintThebesBuffer { + PLayer layer; + ThebesBuffer newFrontBuffer; +}; + +struct OpPaintCanvas { + PLayer layer; + nsIntRect updated; + Shmem newFrontBuffer; +}; + +struct OpPaintImage { + PLayer layer; + Shmem newFrontBuffer; +}; + + +// A unit of a changeset; a set of these comprise a changeset +union Edit { + OpCreateThebesLayer; + OpCreateContainerLayer; + OpCreateImageLayer; + OpCreateColorLayer; + OpCreateCanvasLayer; + OpCreateCanvasBuffer; + OpCreateThebesBuffer; + OpCreateImageBuffer; + + OpSetLayerAttributes; + + OpSetRoot; + OpInsertAfter; + OpAppendChild; + OpRemoveChild; + + OpPaintThebesBuffer; + OpPaintCanvas; + OpPaintImage; +}; + + +// Replies to operations +struct OpBufferSwap { PLayer layer; Shmem newBackBuffer; }; + +/* + * XXX: if we choose *not* to do a new-front-to-new-back fill in the + * parent process, then we'll need to send back the old buffer + * attributes so that it can be filled anew. + * +struct OpThebesBufferSwap { + PLayer layer; + ThebesBuffer newBackBuffer; +}; +*/ + +// Unit of a "changeset reply". This is a weird abstraction, probably +// only to be used for buffer swapping. +union EditReply { + OpBufferSwap; +// OpThebesBufferSwap; +}; + + +sync protocol PLayers { + //manager PCompositor /*or PContent or PMedia or PPlugin*/; + manages PLayer; + +parent: + async PLayer(); + + sync Update(Edit[] cset) + returns (EditReply[] reply); + + async __delete__(); +}; + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ipdl.mk b/gfx/layers/ipc/ipdl.mk new file mode 100644 index 000000000000..92686e138263 --- /dev/null +++ b/gfx/layers/ipc/ipdl.mk @@ -0,0 +1,4 @@ +IPDLSRCS = \ + PLayer.ipdl \ + PLayers.ipdl \ + $(NULL) diff --git a/ipc/ipdl/Makefile.in b/ipc/ipdl/Makefile.in index c0d28b67fc18..9fa4d1ee787e 100644 --- a/ipc/ipdl/Makefile.in +++ b/ipc/ipdl/Makefile.in @@ -57,13 +57,14 @@ IPDLDIRS = \ dom/src/geolocation \ dom/plugins \ dom/ipc \ - netwerk/ipc \ - netwerk/protocol/http \ - netwerk/cookie \ - js/jetpack \ + gfx/layers/ipc \ ipc/ipdl/test/cxx \ ipc/testshell \ js/ipc \ + js/jetpack \ + netwerk/ipc \ + netwerk/protocol/http \ + netwerk/cookie \ $(NULL) ##----------------------------------------------------------------------------- From 2f286075858c94411fa7fdfe239fbf3bf0b4fdcf Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 160/369] Bug 570294, part e: Implement the "forwarder" side of IPC layers. r=jrmuizel --- gfx/layers/Makefile.in | 6 + gfx/layers/ipc/ShadowLayers.cpp | 297 ++++++++++++++++++++++++++++++++ 2 files changed, 303 insertions(+) create mode 100644 gfx/layers/ipc/ShadowLayers.cpp diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 8ac2f1c50215..682305186465 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -97,6 +97,12 @@ CPPSRCS += \ endif endif +ifdef MOZ_IPC #{ +CPPSRCS += \ + ShadowLayers.cpp \ + $(NULL) +endif #} + # Enable GLES2.0 under maemo ifdef MOZ_X11 ifdef MOZ_PLATFORM_MAEMO diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp new file mode 100644 index 000000000000..278439fc8607 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -0,0 +1,297 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#include +#include + +#include "gfxSharedImageSurface.h" + +#include "mozilla/layers/PLayerChild.h" +#include "mozilla/layers/PLayersChild.h" +#include "ShadowLayers.h" + +namespace mozilla { +namespace layers { + +typedef std::vector EditVector; +typedef std::set ShadowableLayerSet; + +class Transaction +{ +public: + Transaction() : mOpen(PR_FALSE) {} + + void Begin() { mOpen = PR_TRUE; } + + void AddEdit(const Edit& aEdit) + { + NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?"); + mCset.push_back(aEdit); + } + void AddMutant(ShadowableLayer* aLayer) + { + NS_ABORT_IF_FALSE(!Finished(), "forgot BeginTransaction?"); + mMutants.insert(aLayer); + } + + void End() + { + mCset.clear(); + mMutants.clear(); + mOpen = PR_FALSE; + } + + PRBool Empty() const { return mCset.empty() && mMutants.empty(); } + PRBool Finished() const { return !mOpen && Empty(); } + + EditVector mCset; + ShadowableLayerSet mMutants; + +private: + PRBool mOpen; + + // disabled + Transaction(const Transaction&); + Transaction& operator=(const Transaction&); +}; +struct AutoTxnEnd { + AutoTxnEnd(Transaction* aTxn) : mTxn(aTxn) {} + ~AutoTxnEnd() { mTxn->End(); } + Transaction* mTxn; +}; + +ShadowLayerForwarder::ShadowLayerForwarder() : mShadowManager(NULL) +{ + mTxn = new Transaction(); +} + +ShadowLayerForwarder::~ShadowLayerForwarder() +{ + NS_ABORT_IF_FALSE(mTxn->Finished(), "unfinished transaction?"); + delete mTxn; +} + +void +ShadowLayerForwarder::BeginTransaction() +{ + NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); + NS_ABORT_IF_FALSE(mTxn->Finished(), "uncommitted txn?"); + mTxn->Begin(); +} + +static PLayerChild* +Shadow(ShadowableLayer* aLayer) +{ + return aLayer->GetShadow(); +} + +template +static void +CreatedLayer(Transaction* aTxn, ShadowableLayer* aLayer) +{ + aTxn->AddEdit(OpCreateT(NULL, Shadow(aLayer))); +} + +void +ShadowLayerForwarder::CreatedThebesLayer(ShadowableLayer* aThebes) +{ + CreatedLayer(mTxn, aThebes); +} +void +ShadowLayerForwarder::CreatedContainerLayer(ShadowableLayer* aContainer) +{ + CreatedLayer(mTxn, aContainer); +} +void +ShadowLayerForwarder::CreatedImageLayer(ShadowableLayer* aImage) +{ + CreatedLayer(mTxn, aImage); +} +void +ShadowLayerForwarder::CreatedColorLayer(ShadowableLayer* aColor) +{ + CreatedLayer(mTxn, aColor); +} +void +ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas) +{ + CreatedLayer(mTxn, aCanvas); +} + +void +ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes, + nsIntRect aBufferRect, + gfxSharedImageSurface* aTempFrontBuffer) +{ + mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes), + aBufferRect, + aTempFrontBuffer->GetShmem())); +} + +void +ShadowLayerForwarder::CreatedImageBuffer(ShadowableLayer* aImage, + nsIntSize aSize, + gfxSharedImageSurface* aTempFrontSurface) +{ + mTxn->AddEdit(OpCreateImageBuffer(NULL, Shadow(aImage), + aSize, + aTempFrontSurface->GetShmem())); +} + +void +ShadowLayerForwarder::CreatedCanvasBuffer(ShadowableLayer* aCanvas, + nsIntSize aSize, + gfxSharedImageSurface* aTempFrontSurface) +{ + mTxn->AddEdit(OpCreateCanvasBuffer(NULL, Shadow(aCanvas), + aSize, + aTempFrontSurface->GetShmem())); +} + +void +ShadowLayerForwarder::Mutated(ShadowableLayer* aMutant) +{ + mTxn->AddMutant(aMutant); +} + +void +ShadowLayerForwarder::SetRoot(ShadowableLayer* aRoot) +{ + mTxn->AddEdit(OpSetRoot(NULL, Shadow(aRoot))); +} +void +ShadowLayerForwarder::InsertAfter(ShadowableLayer* aContainer, + ShadowableLayer* aChild, + ShadowableLayer* aAfter) +{ + if (aAfter) + mTxn->AddEdit(OpInsertAfter(NULL, Shadow(aContainer), + NULL, Shadow(aChild), + NULL, Shadow(aAfter))); + else + mTxn->AddEdit(OpAppendChild(NULL, Shadow(aContainer), + NULL, Shadow(aChild))); +} +void +ShadowLayerForwarder::RemoveChild(ShadowableLayer* aContainer, + ShadowableLayer* aChild) +{ + mTxn->AddEdit(OpRemoveChild(NULL, Shadow(aContainer), + NULL, Shadow(aChild))); +} + +void +ShadowLayerForwarder::PaintedThebesBuffer(ShadowableLayer* aThebes, + nsIntRect aBufferRect, + nsIntPoint aBufferRotation, + gfxSharedImageSurface* aNewFrontBuffer) +{ + mTxn->AddEdit(OpPaintThebesBuffer(NULL, Shadow(aThebes), + ThebesBuffer(aNewFrontBuffer->GetShmem(), + aBufferRect, + aBufferRotation))); +} +void +ShadowLayerForwarder::PaintedImage(ShadowableLayer* aImage, + gfxSharedImageSurface* aNewFrontSurface) +{ + mTxn->AddEdit(OpPaintImage(NULL, Shadow(aImage), + aNewFrontSurface->GetShmem())); +} +void +ShadowLayerForwarder::PaintedCanvas(ShadowableLayer* aCanvas, + gfxSharedImageSurface* aNewFrontSurface) +{ + mTxn->AddEdit(OpPaintCanvas(NULL, Shadow(aCanvas), + nsIntRect(), + aNewFrontSurface->GetShmem())); +} + +PRBool +ShadowLayerForwarder::EndTransaction(nsTArray* aReplies) +{ + NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); + NS_ABORT_IF_FALSE(!mTxn->Finished(), "forgot BeginTransaction?"); + + AutoTxnEnd _(mTxn); + + if (mTxn->Empty()) { + MOZ_LAYERS_LOG(("[LayersForwarder] 0-length cset (?), skipping Update()")); + return PR_TRUE; + } + + MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction...")); + + for (ShadowableLayerSet::const_iterator it = mTxn->mMutants.begin(); + it != mTxn->mMutants.end(); ++it) { + ShadowableLayer* shadow = *it; + Layer* mutant = shadow->AsLayer(); + NS_ABORT_IF_FALSE(!!mutant, "unshadowable layer?"); + + LayerAttributes attrs; + CommonLayerAttributes& common = attrs.common(); + common.visibleRegion() = mutant->GetVisibleRegion(); + common.transform() = mutant->GetTransform(); + common.isOpaqueContent() = mutant->IsOpaqueContent(); + common.opacity() = mutant->GetOpacity(); + common.useClipRect() = !!mutant->GetClipRect(); + common.clipRect() = (common.useClipRect() ? + *mutant->GetClipRect() : nsIntRect()); + attrs.specific() = null_t(); + mutant->FillSpecificAttributes(attrs.specific()); + + mTxn->AddEdit(OpSetLayerAttributes(NULL, Shadow(shadow), attrs)); + } + + nsAutoTArray cset; + cset.SetCapacity(mTxn->mCset.size()); + cset.AppendElements(mTxn->mCset.data(), mTxn->mCset.size()); + + if (!mShadowManager->SendUpdate(cset, aReplies)) { + MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); + return PR_FALSE; + } + + MOZ_LAYERS_LOG(("[LayersForwarder] ... done")); + return PR_TRUE; +} + +} // namespace layers +} // namespace mozilla From cd6faf2b77c894ebd2c8f41543066d470fbf4541 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 161/369] Bug 570294, part f: Implement the "manager" side of IPC layers. r=vlad --- gfx/layers/Makefile.in | 2 + gfx/layers/ipc/ShadowLayerChild.h | 69 +++++ gfx/layers/ipc/ShadowLayerParent.cpp | 78 ++++++ gfx/layers/ipc/ShadowLayerParent.h | 75 +++++ gfx/layers/ipc/ShadowLayers.cpp | 8 + gfx/layers/ipc/ShadowLayersChild.h | 72 +++++ gfx/layers/ipc/ShadowLayersParent.cpp | 376 ++++++++++++++++++++++++++ gfx/layers/ipc/ShadowLayersParent.h | 83 ++++++ 8 files changed, 763 insertions(+) create mode 100644 gfx/layers/ipc/ShadowLayerChild.h create mode 100644 gfx/layers/ipc/ShadowLayerParent.cpp create mode 100644 gfx/layers/ipc/ShadowLayerParent.h create mode 100644 gfx/layers/ipc/ShadowLayersChild.h create mode 100644 gfx/layers/ipc/ShadowLayersParent.cpp create mode 100644 gfx/layers/ipc/ShadowLayersParent.h diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 682305186465..5a9eed6f11a5 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -100,6 +100,8 @@ endif ifdef MOZ_IPC #{ CPPSRCS += \ ShadowLayers.cpp \ + ShadowLayerParent.cpp \ + ShadowLayersParent.cpp \ $(NULL) endif #} diff --git a/gfx/layers/ipc/ShadowLayerChild.h b/gfx/layers/ipc/ShadowLayerChild.h new file mode 100644 index 000000000000..4f534ec978b1 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerChild.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#ifndef mozilla_layers_ShadowLayerChild_h +#define mozilla_layers_ShadowLayerChild_h + +#include "mozilla/layers/PLayerChild.h" + +namespace mozilla { +namespace layers { + +class ShadowableLayer; + +class ShadowLayerChild : public PLayerChild +{ +public: + ShadowLayerChild(ShadowableLayer* aLayer) : mLayer(aLayer) + { } + + virtual ~ShadowLayerChild() + { } + + ShadowableLayer* layer() const { return mLayer; } + +private: + ShadowableLayer* mLayer; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayerChild_h diff --git a/gfx/layers/ipc/ShadowLayerParent.cpp b/gfx/layers/ipc/ShadowLayerParent.cpp new file mode 100644 index 000000000000..a5ec36ff8aff --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerParent.cpp @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#include "ShadowLayersParent.h" +#include "ShadowLayerParent.h" +#include "ShadowLayers.h" + +#include "BasicLayers.h" + +namespace mozilla { +namespace layers { + +ShadowLayerParent::ShadowLayerParent() : mLayer(NULL) +{ +} + +ShadowLayerParent::~ShadowLayerParent() +{ +} + +void +ShadowLayerParent::Bind(Layer* layer) +{ + mLayer = layer; +} + +ContainerLayer* +ShadowLayerParent::AsContainer() const +{ + return static_cast(AsLayer()); +} + +bool +ShadowLayerParent::Recv__delete__() +{ + mLayer = NULL; + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayerParent.h b/gfx/layers/ipc/ShadowLayerParent.h new file mode 100644 index 000000000000..e089fb8b3383 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayerParent.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#ifndef mozilla_layers_ShadowLayerParent_h +#define mozilla_layers_ShadowLayerParent_h + +#include "mozilla/layers/PLayerParent.h" + +namespace mozilla { +namespace layers { + +class ContainerLayer; +class Layer; +class LayerManager; +class ShadowLayersParent; + +class ShadowLayerParent : public PLayerParent +{ +public: + ShadowLayerParent(); + + virtual ~ShadowLayerParent(); + + void Bind(Layer* layer); + + Layer* AsLayer() const { return mLayer; } + ContainerLayer* AsContainer() const; + +private: + NS_OVERRIDE virtual bool Recv__delete__(); + + nsRefPtr mLayer; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayerParent_h diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 278439fc8607..4d43c514cd89 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -46,6 +46,7 @@ #include "mozilla/layers/PLayerChild.h" #include "mozilla/layers/PLayersChild.h" #include "ShadowLayers.h" +#include "ShadowLayerChild.h" namespace mozilla { namespace layers { @@ -293,5 +294,12 @@ ShadowLayerForwarder::EndTransaction(nsTArray* aReplies) return PR_TRUE; } +PLayerChild* +ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) +{ + NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); + return mShadowManager->SendPLayerConstructor(new ShadowLayerChild(aLayer)); +} + } // namespace layers } // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayersChild.h b/gfx/layers/ipc/ShadowLayersChild.h new file mode 100644 index 000000000000..f9937006bdcd --- /dev/null +++ b/gfx/layers/ipc/ShadowLayersChild.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#ifndef mozilla_layers_ShadowLayersChild_h +#define mozilla_layers_ShadowLayersChild_h + +#include "mozilla/layers/PLayersChild.h" +#include "mozilla/layers/ShadowLayerChild.h" + +namespace mozilla { +namespace layers { + +class ShadowLayersChild : public PLayersChild +{ +public: + ShadowLayersChild() { } + ~ShadowLayersChild() { } + +protected: + NS_OVERRIDE virtual PLayerChild* AllocPLayer() { + // we always use the "power-user" ctor + NS_RUNTIMEABORT("not reached"); + return NULL; + } + + NS_OVERRIDE virtual bool DeallocPLayer(PLayerChild* actor) { + delete actor; + return true; + } +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayersChild_h diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp new file mode 100644 index 000000000000..7af5a7b2e1c9 --- /dev/null +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#include + +#include "ShadowLayersParent.h" +#include "ShadowLayerParent.h" +#include "ShadowLayers.h" + +#include "mozilla/unused.h" + +#include "gfxSharedImageSurface.h" + +#include "ImageLayers.h" + +typedef std::vector EditReplyVector; + +namespace mozilla { +namespace layers { + +//-------------------------------------------------- +// Convenience accessors +static ShadowLayerParent* +cast(const PLayerParent* in) +{ + return const_cast( + static_cast(in)); +} + +template +static ShadowLayerParent* +AsShadowLayer(const OpCreateT& op) +{ + return cast(op.layerParent()); +} + +static ShadowLayerParent* +AsShadowLayer(const OpSetRoot& op) +{ + return cast(op.rootParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpInsertAfter& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpInsertAfter& op) +{ + return cast(op.childLayerParent()); +} +static ShadowLayerParent* +ShadowAfter(const OpInsertAfter& op) +{ + return cast(op.afterParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpAppendChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpAppendChild& op) +{ + return cast(op.childLayerParent()); +} + +static ShadowLayerParent* +ShadowContainer(const OpRemoveChild& op) +{ + return cast(op.containerParent()); +} +static ShadowLayerParent* +ShadowChild(const OpRemoveChild& op) +{ + return cast(op.childLayerParent()); +} + +//-------------------------------------------------- +// ShadowLayersParent +ShadowLayersParent::ShadowLayersParent(ShadowLayerManager* aManager) +{ + MOZ_COUNT_CTOR(ShadowLayersParent); + mLayerManager = aManager; +} + +ShadowLayersParent::~ShadowLayersParent() +{ + MOZ_COUNT_DTOR(ShadowLayersParent); +} + +bool +ShadowLayersParent::RecvUpdate(const nsTArray& cset, + nsTArray* reply) +{ + MOZ_LAYERS_LOG(("[ParentSide] recieved txn with %d edits", cset.Length())); + + EditReplyVector replyv; + + layer_manager()->BeginTransactionWithTarget(NULL); + + for (EditArray::index_type i = 0; i < cset.Length(); ++i) { + const Edit& edit = cset[i]; + + switch (edit.type()) { + // Create* ops + case Edit::TOpCreateThebesLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateThebesLayer")); + + nsRefPtr layer = layer_manager()->CreateShadowThebesLayer(); + AsShadowLayer(edit.get_OpCreateThebesLayer())->Bind(layer); + break; + } + case Edit::TOpCreateContainerLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateContainerLayer")); + + nsRefPtr layer = layer_manager()->CreateContainerLayer(); + AsShadowLayer(edit.get_OpCreateContainerLayer())->Bind(layer); + break; + } + case Edit::TOpCreateImageLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageLayer")); + + AsShadowLayer(edit.get_OpCreateImageLayer())->Bind( + layer_manager()->CreateShadowImageLayer().get()); + break; + } + case Edit::TOpCreateColorLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateColorLayer")); + + nsRefPtr layer = layer_manager()->CreateColorLayer(); + AsShadowLayer(edit.get_OpCreateColorLayer())->Bind(layer); + break; + } + case Edit::TOpCreateCanvasLayer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasLayer")); + + nsRefPtr layer = layer_manager()->CreateShadowCanvasLayer(); + AsShadowLayer(edit.get_OpCreateCanvasLayer())->Bind(layer); + break; + } + case Edit::TOpCreateThebesBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateThebesBuffer")); + + const OpCreateThebesBuffer& otb = edit.get_OpCreateThebesBuffer(); + ShadowThebesLayer* thebes = static_cast( + AsShadowLayer(otb)->AsLayer()); + + unused << thebes->Swap(new gfxSharedImageSurface(otb.initialFront()), + otb.bufferRect(), + nsIntPoint(0, 0)); + + break; + } + case Edit::TOpCreateCanvasBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateCanvasBuffer")); + + const OpCreateCanvasBuffer& ocb = edit.get_OpCreateCanvasBuffer(); + ShadowCanvasLayer* canvas = static_cast( + AsShadowLayer(ocb)->AsLayer()); + nsRefPtr front = + new gfxSharedImageSurface(ocb.initialFront()); + CanvasLayer::Data data; + data.mSurface = front; + data.mSize = ocb.size(); + + canvas->Initialize(data); + + break; + } + case Edit::TOpCreateImageBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] CreateImageBuffer")); + + const OpCreateImageBuffer ocb = edit.get_OpCreateImageBuffer(); + ShadowImageLayer* image = static_cast( + AsShadowLayer(ocb)->AsLayer()); + + image->Init(new gfxSharedImageSurface(ocb.initialFront()), ocb.size()); + + break; + } + + // Attributes + case Edit::TOpSetLayerAttributes: { + MOZ_LAYERS_LOG(("[ParentSide] SetLayerAttributes")); + + const OpSetLayerAttributes& osla = edit.get_OpSetLayerAttributes(); + Layer* layer = AsShadowLayer(osla)->AsLayer(); + const LayerAttributes& attrs = osla.attrs(); + + const CommonLayerAttributes& common = attrs.common(); + layer->SetVisibleRegion(common.visibleRegion()); + layer->SetIsOpaqueContent(common.isOpaqueContent()); + layer->SetOpacity(common.opacity()); + layer->SetClipRect(common.useClipRect() ? &common.clipRect() : NULL); + layer->SetTransform(common.transform()); + + typedef SpecificLayerAttributes Specific; + const SpecificLayerAttributes& specific = attrs.specific(); + switch (specific.type()) { + case Specific::Tnull_t: + break; + + case Specific::TThebesLayerAttributes: + MOZ_LAYERS_LOG(("[ParentSide] thebes layer")); + + static_cast(layer)->SetValidRegion( + specific.get_ThebesLayerAttributes().validRegion()); + break; + + case Specific::TColorLayerAttributes: + MOZ_LAYERS_LOG(("[ParentSide] color layer")); + + static_cast(layer)->SetColor( + specific.get_ColorLayerAttributes().color()); + break; + + case Specific::TCanvasLayerAttributes: + MOZ_LAYERS_LOG(("[ParentSide] canvas layer")); + + static_cast(layer)->SetFilter( + specific.get_CanvasLayerAttributes().filter()); + break; + + case Specific::TImageLayerAttributes: + MOZ_LAYERS_LOG(("[ParentSide] image layer")); + + static_cast(layer)->SetFilter( + specific.get_ImageLayerAttributes().filter()); + break; + + default: + NS_RUNTIMEABORT("not reached"); + } + break; + } + + // Tree ops + case Edit::TOpSetRoot: { + MOZ_LAYERS_LOG(("[ParentSide] SetRoot")); + + layer_manager()->SetRoot(AsShadowLayer(edit.get_OpSetRoot())->AsLayer()); + break; + } + case Edit::TOpInsertAfter: { + MOZ_LAYERS_LOG(("[ParentSide] InsertAfter")); + + const OpInsertAfter& oia = edit.get_OpInsertAfter(); + ShadowContainer(oia)->AsContainer()->InsertAfter( + ShadowChild(oia)->AsLayer(), ShadowAfter(oia)->AsLayer()); + break; + } + case Edit::TOpAppendChild: { + MOZ_LAYERS_LOG(("[ParentSide] AppendChild")); + + const OpAppendChild& oac = edit.get_OpAppendChild(); + ShadowContainer(oac)->AsContainer()->InsertAfter( + ShadowChild(oac)->AsLayer(), NULL); + break; + } + case Edit::TOpRemoveChild: { + MOZ_LAYERS_LOG(("[ParentSide] RemoveChild")); + + const OpRemoveChild& orc = edit.get_OpRemoveChild(); + Layer* childLayer = ShadowChild(orc)->AsLayer(); + ShadowContainer(orc)->AsContainer()->RemoveChild(childLayer); + break; + } + + case Edit::TOpPaintThebesBuffer: { + MOZ_LAYERS_LOG(("[ParentSide] Paint ThebesLayer")); + + const OpPaintThebesBuffer& op = edit.get_OpPaintThebesBuffer(); + ShadowLayerParent* shadow = AsShadowLayer(op); + ShadowThebesLayer* thebes = + static_cast(shadow->AsLayer()); + const ThebesBuffer& newFront = op.newFrontBuffer(); + + nsRefPtr newBack = + thebes->Swap(new gfxSharedImageSurface(newFront.buffer()), + newFront.rect(), + newFront.rotation()); + + // XXX figure me out + replyv.push_back(OpBufferSwap(shadow, NULL, + newBack->GetShmem())); + break; + } + case Edit::TOpPaintCanvas: { + MOZ_LAYERS_LOG(("[ParentSide] Paint CanvasLayer")); + + const OpPaintCanvas& op = edit.get_OpPaintCanvas(); + ShadowLayerParent* shadow = AsShadowLayer(op); + ShadowCanvasLayer* canvas = + static_cast(shadow->AsLayer()); + + nsRefPtr newBack = + canvas->Swap(new gfxSharedImageSurface(op.newFrontBuffer())); + canvas->Updated(op.updated()); + + replyv.push_back(OpBufferSwap(shadow, NULL, + newBack->GetShmem())); + + break; + } + case Edit::TOpPaintImage: { + MOZ_LAYERS_LOG(("[ParentSide] Paint ImageLayer")); + + const OpPaintImage& op = edit.get_OpPaintImage(); + ShadowLayerParent* shadow = AsShadowLayer(op); + ShadowImageLayer* image = + static_cast(shadow->AsLayer()); + + nsRefPtr newBack = + image->Swap(new gfxSharedImageSurface(op.newFrontBuffer())); + + replyv.push_back(OpBufferSwap(shadow, NULL, + newBack->GetShmem())); + + break; + } + + default: + NS_RUNTIMEABORT("not reached"); + } + } + + layer_manager()->EndTransaction(NULL, NULL); + + reply->SetCapacity(replyv.size()); + reply->AppendElements(replyv.data(), replyv.size()); + + return true; +} + +} // namespace layers +} // namespace mozilla diff --git a/gfx/layers/ipc/ShadowLayersParent.h b/gfx/layers/ipc/ShadowLayersParent.h new file mode 100644 index 000000000000..57676697a61d --- /dev/null +++ b/gfx/layers/ipc/ShadowLayersParent.h @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** 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 Code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * 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 ***** */ + +#ifndef mozilla_layers_ShadowLayersParent_h +#define mozilla_layers_ShadowLayersParent_h + +#include "mozilla/layers/PLayersParent.h" +#include "ShadowLayerParent.h" + +namespace mozilla { +namespace layers { + +class ShadowLayerManager; + +class ShadowLayersParent : public PLayersParent +{ + typedef nsTArray EditArray; + typedef nsTArray EditReplyArray; + +public: + ShadowLayersParent(ShadowLayerManager* aManager); + ~ShadowLayersParent(); + + ShadowLayerManager* layer_manager() const { return mLayerManager; } + +protected: + NS_OVERRIDE virtual bool RecvUpdate(const EditArray& cset, + EditReplyArray* reply); + + NS_OVERRIDE virtual PLayerParent* AllocPLayer() { + return new ShadowLayerParent(); + } + + NS_OVERRIDE virtual bool DeallocPLayer(PLayerParent* actor) { + delete actor; + return true; + } + +private: + nsRefPtr mLayerManager; +}; + +} // namespace layers +} // namespace mozilla + +#endif // ifndef mozilla_layers_ShadowLayersParent_h From 9b6c4949d1a737a7441d4deccfa8297b3be1dbd7 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 162/369] Bug 570294, part g: Implement basic ShadowLayers and share basic layers with remote processes. r=Bas sr=vlad --- gfx/layers/Makefile.in | 5 + gfx/layers/basic/BasicLayers.cpp | 469 ++++++++++++++++++++++++++++++- gfx/layers/basic/BasicLayers.h | 78 ++++- 3 files changed, 539 insertions(+), 13 deletions(-) diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 5a9eed6f11a5..2f397ca30235 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -98,6 +98,11 @@ endif endif ifdef MOZ_IPC #{ +EXPORTS_NAMESPACES = mozilla/layers +EXPORTS_mozilla/layers =\ + ShadowLayers.h \ + $(NULL) + CPPSRCS += \ ShadowLayers.cpp \ ShadowLayerParent.cpp \ diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 05aa6eb4453d..d1a9cd194ab1 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -20,6 +20,7 @@ * * Contributor(s): * Robert O'Callahan + * Chris Jones * * 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 @@ -35,6 +36,11 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef MOZ_IPC +# include "mozilla/layers/PLayerChild.h" +# include "mozilla/layers/PLayersChild.h" +#endif + #include "BasicLayers.h" #include "ImageLayers.h" @@ -55,6 +61,7 @@ namespace mozilla { namespace layers { class BasicContainerLayer; +class ShadowableLayer; /** * This is the ImplData for all Basic layers. It also exposes methods @@ -84,7 +91,7 @@ public: { MOZ_COUNT_CTOR(BasicImplData); } - ~BasicImplData() + virtual ~BasicImplData() { MOZ_COUNT_DTOR(BasicImplData); } @@ -99,6 +106,8 @@ public: LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, float aOpacity) {} + + virtual ShadowableLayer* AsShadowableLayer() { return NULL; } }; static BasicImplData* @@ -657,6 +666,9 @@ BasicCanvasLayer::Paint(gfxContext* aContext, void* aCallbackData, float aOpacity) { + NS_ASSERTION(BasicManager()->InDrawing(), + "Can only draw in drawing phase"); + nsRefPtr pat = new gfxPattern(mSurface); pat->SetFilter(mFilter); @@ -755,10 +767,10 @@ MayHaveOverlappingOrTransparentLayers(Layer* aLayer, } BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) : - mWidget(aWidget) #ifdef DEBUG - , mPhase(PHASE_NONE) + mPhase(PHASE_NONE), #endif + mWidget(aWidget) , mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(PR_FALSE) { MOZ_COUNT_CTOR(BasicLayerManager); @@ -766,10 +778,10 @@ BasicLayerManager::BasicLayerManager(nsIWidget* aWidget) : } BasicLayerManager::BasicLayerManager() : - mWidget(nsnull) #ifdef DEBUG - , mPhase(PHASE_NONE) + mPhase(PHASE_NONE), #endif + mWidget(nsnull) , mDoubleBuffering(BUFFER_NONE), mUsingDefaultTarget(PR_FALSE) { MOZ_COUNT_CTOR(BasicLayerManager); @@ -855,8 +867,8 @@ BasicLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, void* aCallbackData) { #ifdef MOZ_LAYERS_HAVE_LOG + MOZ_LAYERS_LOG((" ----- (beginning paint)")); Log(); - MOZ_LAYERS_LOG(("]----- EndTransaction")); #endif NS_ASSERTION(InConstruction(), "Should be in construction phase"); @@ -892,6 +904,11 @@ BasicLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, mTarget = nsnull; } +#ifdef MOZ_LAYERS_HAVE_LOG + Log(); + MOZ_LAYERS_LOG(("]----- EndTransaction")); +#endif + #ifdef DEBUG mPhase = PHASE_NONE; #endif @@ -1032,5 +1049,445 @@ BasicLayerManager::CreateCanvasLayer() return layer.forget(); } + +#ifdef MOZ_IPC + +class BasicShadowableLayer : public ShadowableLayer +{ +public: + BasicShadowableLayer() + { + MOZ_COUNT_CTOR(BasicShadowableLayer); + } + + ~BasicShadowableLayer() + { + if (HasShadow()) { + PLayerChild::Send__delete__(GetShadow()); + } + MOZ_COUNT_DTOR(BasicShadowableLayer); + } + + void SetShadow(PLayerChild* aShadow) + { + NS_ABORT_IF_FALSE(!mShadow, "can't have two shadows (yet)"); + mShadow = aShadow; + } +}; + +static ShadowableLayer* +ToShadowable(Layer* aLayer) +{ + return ToData(aLayer)->AsShadowableLayer(); +} + +class BasicShadowableContainerLayer : public BasicContainerLayer, + public BasicShadowableLayer { +public: + BasicShadowableContainerLayer(BasicShadowLayerManager* aManager) : + BasicContainerLayer(aManager) + { + MOZ_COUNT_CTOR(BasicShadowableContainerLayer); + } + virtual ~BasicShadowableContainerLayer() + { + MOZ_COUNT_DTOR(BasicShadowableContainerLayer); + } + + virtual void InsertAfter(Layer* aChild, Layer* aAfter); + virtual void RemoveChild(Layer* aChild); + + virtual Layer* AsLayer() { return this; } + virtual ShadowableLayer* AsShadowableLayer() { return this; } + +private: + BasicShadowLayerManager* ShadowManager() + { + return static_cast(mManager); + } +}; + +void +BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) +{ + if (HasShadow()) { + ShadowManager()->InsertAfter(ShadowManager()->Hold(this), + ShadowManager()->Hold(aChild), + aAfter ? ShadowManager()->Hold(aAfter) : NULL); + } + BasicContainerLayer::InsertAfter(aChild, aAfter); +} + +void +BasicShadowableContainerLayer::RemoveChild(Layer* aChild) +{ + if (HasShadow()) { + ShadowManager()->RemoveChild(ShadowManager()->Hold(this), + ShadowManager()->Hold(aChild)); + } + BasicContainerLayer::RemoveChild(aChild); +} + +class BasicShadowableThebesLayer : public BasicThebesLayer, + public BasicShadowableLayer +{ +public: + BasicShadowableThebesLayer(BasicShadowLayerManager* aManager) : + BasicThebesLayer(aManager) + { + MOZ_COUNT_CTOR(BasicShadowableThebesLayer); + } + virtual ~BasicShadowableThebesLayer() + { + MOZ_COUNT_DTOR(BasicShadowableThebesLayer); + } + + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) + { + aAttrs = ThebesLayerAttributes(GetValidRegion()); + } + + virtual Layer* AsLayer() { return this; } + virtual ShadowableLayer* AsShadowableLayer() { return this; } +}; + +class BasicShadowableImageLayer : public BasicImageLayer, + public BasicShadowableLayer +{ +public: + BasicShadowableImageLayer(BasicShadowLayerManager* aManager) : + BasicImageLayer(aManager) + { + MOZ_COUNT_CTOR(BasicShadowableImageLayer); + } + virtual ~BasicShadowableImageLayer() + { + MOZ_COUNT_DTOR(BasicShadowableImageLayer); + } + + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) + { + aAttrs = ImageLayerAttributes(mFilter); + } + + virtual Layer* AsLayer() { return this; } + virtual ShadowableLayer* AsShadowableLayer() { return this; } +}; + +class BasicShadowableColorLayer : public BasicColorLayer, + public BasicShadowableLayer +{ +public: + BasicShadowableColorLayer(BasicShadowLayerManager* aManager) : + BasicColorLayer(aManager) + { + MOZ_COUNT_CTOR(BasicShadowableColorLayer); + } + virtual ~BasicShadowableColorLayer() + { + MOZ_COUNT_DTOR(BasicShadowableColorLayer); + } + + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) + { + aAttrs = ColorLayerAttributes(GetColor()); + } + + virtual Layer* AsLayer() { return this; } + virtual ShadowableLayer* AsShadowableLayer() { return this; } +}; + +class BasicShadowableCanvasLayer : public BasicCanvasLayer, + public BasicShadowableLayer +{ +public: + BasicShadowableCanvasLayer(BasicShadowLayerManager* aManager) : + BasicCanvasLayer(aManager) + { + MOZ_COUNT_CTOR(BasicShadowableCanvasLayer); + } + virtual ~BasicShadowableCanvasLayer() + { + MOZ_COUNT_DTOR(BasicShadowableCanvasLayer); + } + + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) + { + aAttrs = CanvasLayerAttributes(mFilter); + } + + virtual Layer* AsLayer() { return this; } + virtual ShadowableLayer* AsShadowableLayer() { return this; } +}; + +class BasicShadowThebesLayer : public ShadowThebesLayer, BasicImplData { +public: + BasicShadowThebesLayer(BasicShadowLayerManager* aLayerManager) : + ShadowThebesLayer(aLayerManager, static_cast(this)) + { + MOZ_COUNT_CTOR(BasicShadowThebesLayer); + } + virtual ~BasicShadowThebesLayer() + { + MOZ_COUNT_DTOR(BasicShadowThebesLayer); + } + + virtual already_AddRefed + Swap(gfxSharedImageSurface* aNewFront, + const nsIntRect& aBufferRect, + const nsIntPoint& aRotation) + { return NULL; } + + virtual void Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) + {} + + MOZ_LAYER_DECL_NAME("BasicShadowThebesLayer", TYPE_SHADOW) +}; + +class BasicShadowImageLayer : public ShadowImageLayer, BasicImplData { +public: + BasicShadowImageLayer(BasicShadowLayerManager* aLayerManager) : + ShadowImageLayer(aLayerManager, static_cast(this)) + { + MOZ_COUNT_CTOR(BasicShadowImageLayer); + } + virtual ~BasicShadowImageLayer() + { + MOZ_COUNT_DTOR(BasicShadowImageLayer); + } + + virtual PRBool Init(gfxSharedImageSurface* front, const nsIntSize& size) + { return PR_TRUE; } + + virtual already_AddRefed + Swap(gfxSharedImageSurface* newFront) + { return NULL; } + + virtual void Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) + {} + + MOZ_LAYER_DECL_NAME("BasicShadowImageLayer", TYPE_SHADOW) +}; + +class BasicShadowCanvasLayer : public ShadowCanvasLayer, + BasicImplData +{ +public: + BasicShadowCanvasLayer(BasicShadowLayerManager* aLayerManager) : + ShadowCanvasLayer(aLayerManager, static_cast(this)) + { + MOZ_COUNT_CTOR(BasicShadowCanvasLayer); + } + virtual ~BasicShadowCanvasLayer() + { + MOZ_COUNT_DTOR(BasicShadowCanvasLayer); + } + + virtual void Initialize(const Data& aData) + {} + + virtual void Updated(const nsIntRect& aRect) + {} + + virtual already_AddRefed + Swap(gfxSharedImageSurface* newFront) + { return NULL; } + + virtual void Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) + {} + + MOZ_LAYER_DECL_NAME("BasicShadowCanvasLayer", TYPE_SHADOW) +}; + +// Create a shadow layer (PLayerChild) for aLayer, if we're forwarding +// our layer tree to a parent process. Record the new layer creation +// in the current open transaction as a side effect. +template +static void +MaybeCreateShadowFor(BasicShadowableLayer* aLayer, + BasicShadowLayerManager* aMgr, + CreatedMethod aMethod) +{ + if (!aMgr->HasShadowManager()) { + return; + } + + PLayerChild* shadow = aMgr->ConstructShadowFor(aLayer); + // XXX error handling + NS_ABORT_IF_FALSE(shadow, "failed to create shadow"); + + aLayer->SetShadow(shadow); + (aMgr->*aMethod)(aLayer); +} +#define MAYBE_CREATE_SHADOW(_type) \ + MaybeCreateShadowFor(layer, this, \ + &ShadowLayerForwarder::Created ## _type ## Layer) + +already_AddRefed +BasicShadowLayerManager::CreateThebesLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = + new BasicShadowableThebesLayer(this); + MAYBE_CREATE_SHADOW(Thebes); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateContainerLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = + new BasicShadowableContainerLayer(this); + MAYBE_CREATE_SHADOW(Container); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateImageLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = + new BasicShadowableImageLayer(this); + MAYBE_CREATE_SHADOW(Image); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateColorLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = + new BasicShadowableColorLayer(this); + MAYBE_CREATE_SHADOW(Color); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateCanvasLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = + new BasicShadowableCanvasLayer(this); + MAYBE_CREATE_SHADOW(Canvas); + return layer.forget(); +} +already_AddRefed +BasicShadowLayerManager::CreateShadowThebesLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = new BasicShadowThebesLayer(this); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateShadowImageLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = new BasicShadowImageLayer(this); + return layer.forget(); +} + +already_AddRefed +BasicShadowLayerManager::CreateShadowCanvasLayer() +{ + NS_ASSERTION(InConstruction(), "Only allowed in construction phase"); + nsRefPtr layer = new BasicShadowCanvasLayer(this); + return layer.forget(); +} + +BasicShadowLayerManager::BasicShadowLayerManager(nsIWidget* aWidget) : + BasicLayerManager(aWidget) +{ + MOZ_COUNT_CTOR(BasicShadowLayerManager); +} + +BasicShadowLayerManager::~BasicShadowLayerManager() +{ + // FIXME/bug 570294: shadow forwarders don't have __delete__ until + // they have manager protocols + // + //if (HasShadowManager()) + // PLayersChild::Send__delete__(mShadow); + MOZ_COUNT_DTOR(BasicShadowLayerManager); +} + +void +BasicShadowLayerManager::SetRoot(Layer* aLayer) +{ + if (mRoot != aLayer) { + if (HasShadowManager()) { + ShadowLayerForwarder::SetRoot(Hold(aLayer)); + } + BasicLayerManager::SetRoot(aLayer); + } +} + +void +BasicShadowLayerManager::Mutated(Layer* aLayer) +{ + NS_ASSERTION(InConstruction() || InDrawing(), "wrong phase"); + if (HasShadowManager()) { + ShadowLayerForwarder::Mutated(Hold(aLayer)); + } +} + +void +BasicShadowLayerManager::BeginTransactionWithTarget(gfxContext* aTarget) +{ + NS_ABORT_IF_FALSE(mKeepAlive.IsEmpty(), "uncommitted txn?"); + if (HasShadowManager()) { + ShadowLayerForwarder::BeginTransaction(); + } + BasicLayerManager::BeginTransactionWithTarget(aTarget); +} + +void +BasicShadowLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, + void* aCallbackData) +{ + BasicLayerManager::EndTransaction(aCallback, aCallbackData); +#ifdef DEBUG + mPhase = PHASE_FORWARD; +#endif + + // forward this transaction's changeset to our ShadowLayerManager + nsAutoTArray replies; + if (HasShadowManager() && !ShadowLayerForwarder::EndTransaction(&replies)) { + NS_WARNING("failed to forward Layers transaction"); + } + + // this may result in Layers being deleted, which results in + // PLayer::Send__delete__() + mKeepAlive.Clear(); + +#ifdef DEBUG + mPhase = PHASE_NONE; +#endif +} + +ShadowableLayer* +BasicShadowLayerManager::Hold(Layer* aLayer) +{ + NS_ABORT_IF_FALSE(HasShadowManager(), + "top-level tree, no shadow tree to remote to"); + + ShadowableLayer* shadowable = ToShadowable(aLayer); + NS_ABORT_IF_FALSE(shadowable, "trying to remote an unshadowable layer"); + + mKeepAlive.AppendElement(aLayer); + return shadowable; +} +#endif // MOZ_IPC + } } diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index f354e6f73f0a..460dde8215d7 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -1,4 +1,4 @@ -/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -45,11 +45,20 @@ #include "nsAutoRef.h" #include "nsThreadUtils.h" +#ifdef MOZ_IPC +#include "mozilla/layers/ShadowLayers.h" +#endif + class nsIWidget; namespace mozilla { namespace layers { +class BasicShadowableLayer; +class ShadowThebesLayer; +class ShadowImageLayer; +class ShadowCanvasLayer; + /** * This is a cairo/Thebes-only, main-thread-only implementation of layers. * @@ -58,7 +67,13 @@ namespace layers { * context (with appropriate clipping and Push/PopGroups performed * between layers). */ -class THEBES_API BasicLayerManager : public LayerManager { +class THEBES_API BasicLayerManager : +#ifdef MOZ_IPC + public ShadowLayerManager +#else + public LayerManager +#endif +{ public: /** * Construct a BasicLayerManager which will have no default @@ -119,11 +134,19 @@ public: virtual already_AddRefed CreateCanvasLayer(); virtual already_AddRefed CreateImageContainer(); virtual already_AddRefed CreateColorLayer(); + virtual already_AddRefed CreateShadowThebesLayer() + { return NULL; } + virtual already_AddRefed CreateShadowImageLayer() + { return NULL; } + virtual already_AddRefed CreateShadowCanvasLayer() + { return NULL; } + virtual LayersBackend GetBackendType() { return LAYERS_BASIC; } #ifdef DEBUG PRBool InConstruction() { return mPhase == PHASE_CONSTRUCTION; } PRBool InDrawing() { return mPhase == PHASE_DRAWING; } + PRBool InForward() { return mPhase == PHASE_FORWARD; } PRBool InTransaction() { return mPhase != PHASE_NONE; } #endif gfxContext* GetTarget() { return mTarget; } @@ -132,6 +155,15 @@ public: #ifdef MOZ_LAYERS_HAVE_LOG virtual const char* Name() const { return "Basic"; } #endif // MOZ_LAYERS_HAVE_LOG + +protected: +#ifdef DEBUG + enum TransactionPhase { + PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING, PHASE_FORWARD + }; + TransactionPhase mPhase; +#endif + private: // Paints aLayer to mTarget. void PaintLayer(Layer* aLayer, @@ -156,14 +188,46 @@ private: // Cached surface for double buffering gfxCachedTempSurface mCachedSurface; -#ifdef DEBUG - enum TransactionPhase { PHASE_NONE, PHASE_CONSTRUCTION, PHASE_DRAWING }; - TransactionPhase mPhase; -#endif - BufferMode mDoubleBuffering; PRPackedBool mUsingDefaultTarget; }; + + +#ifdef MOZ_IPC +class BasicShadowLayerManager : public BasicLayerManager, + public ShadowLayerForwarder +{ + typedef nsTArray > LayerRefArray; + +public: + BasicShadowLayerManager(nsIWidget* aWidget); + virtual ~BasicShadowLayerManager(); + + virtual void BeginTransactionWithTarget(gfxContext* aTarget); + virtual void EndTransaction(DrawThebesLayerCallback aCallback, + void* aCallbackData); + + virtual void SetRoot(Layer* aLayer); + + virtual void Mutated(Layer* aLayer); + + virtual already_AddRefed CreateThebesLayer(); + virtual already_AddRefed CreateContainerLayer(); + virtual already_AddRefed CreateImageLayer(); + virtual already_AddRefed CreateCanvasLayer(); + virtual already_AddRefed CreateColorLayer(); + virtual already_AddRefed CreateShadowThebesLayer(); + virtual already_AddRefed CreateShadowImageLayer(); + virtual already_AddRefed CreateShadowCanvasLayer(); + + virtual const char* Name() const { return "BasicShadowLayerManager"; } + + ShadowableLayer* Hold(Layer* aLayer); + +private: + LayerRefArray mKeepAlive; +}; +#endif // MOZ_IPC } } From f652f038cf9260518c2b809a550bf47308f42d36 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 163/369] Bug 570294, part h: Publish BasicCanvasLayer pixels to remote processes (slowly). sr=vlad --- gfx/layers/basic/BasicLayers.cpp | 164 +++++++++++++++++++++++++++++-- gfx/layers/ipc/ShadowLayers.cpp | 33 +++++++ 2 files changed, 189 insertions(+), 8 deletions(-) diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index d1a9cd194ab1..1ceaddc7cdf9 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -37,8 +37,12 @@ * ***** END LICENSE BLOCK ***** */ #ifdef MOZ_IPC +# include "gfxSharedImageSurface.h" + # include "mozilla/layers/PLayerChild.h" # include "mozilla/layers/PLayersChild.h" +# include "mozilla/layers/PLayersParent.h" +# include "ipc/ShadowLayerChild.h" #endif #include "BasicLayers.h" @@ -790,6 +794,9 @@ BasicLayerManager::BasicLayerManager() : BasicLayerManager::~BasicLayerManager() { NS_ASSERTION(!InTransaction(), "Died during transaction?"); + + mRoot = nsnull; + MOZ_COUNT_DTOR(BasicLayerManager); } @@ -1073,6 +1080,11 @@ public: NS_ABORT_IF_FALSE(!mShadow, "can't have two shadows (yet)"); mShadow = aShadow; } + + virtual void SetBackBuffer(gfxSharedImageSurface* aBuffer) + { + NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks"); + } }; static ShadowableLayer* @@ -1081,6 +1093,14 @@ ToShadowable(Layer* aLayer) return ToData(aLayer)->AsShadowableLayer(); } +template +static BasicShadowableLayer* +GetBasicShadowable(const OpT& op) +{ + return static_cast( + static_cast(op.layerChild())->layer()); +} + class BasicShadowableContainerLayer : public BasicContainerLayer, public BasicShadowableLayer { public: @@ -1208,9 +1228,17 @@ public: } virtual ~BasicShadowableCanvasLayer() { + if (mBackBuffer) + BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer); MOZ_COUNT_DTOR(BasicShadowableCanvasLayer); } + virtual void Initialize(const Data& aData); + virtual void Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity); + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = CanvasLayerAttributes(mFilter); @@ -1218,8 +1246,61 @@ public: virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; } + + virtual void SetBackBuffer(gfxSharedImageSurface* aBuffer) + { + mBackBuffer = aBuffer; + } + +private: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + nsRefPtr mBackBuffer; }; +void +BasicShadowableCanvasLayer::Initialize(const Data& aData) +{ + BasicCanvasLayer::Initialize(aData); + if (!HasShadow()) + return; + + nsRefPtr tmpFrontBuffer; + // XXX error handling? + if (!BasicManager()->AllocDoubleBuffer( + gfxIntSize(aData.mSize.width, aData.mSize.height), + gfxASurface::ImageFormatARGB32, + getter_AddRefs(tmpFrontBuffer), getter_AddRefs(mBackBuffer))) + NS_RUNTIMEABORT("creating CanvasLayer back buffer failed!"); + + BasicManager()->CreatedCanvasBuffer(BasicManager()->Hold(this), + aData.mSize, + tmpFrontBuffer); +} + +void +BasicShadowableCanvasLayer::Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) +{ + BasicCanvasLayer::Paint(aContext, aCallback, aCallbackData, aOpacity); + if (!HasShadow()) + return; + + // XXX this is yucky and slow. It'd be nice to draw directly into + // the shmem back buffer + nsRefPtr tmpCtx = new gfxContext(mBackBuffer); + tmpCtx->DrawSurface(mSurface, gfxSize(mBounds.width, mBounds.height)); + + BasicManager()->PaintedCanvas(BasicManager()->Hold(this), + mBackBuffer); +} + + class BasicShadowThebesLayer : public ShadowThebesLayer, BasicImplData { public: BasicShadowThebesLayer(BasicShadowLayerManager* aLayerManager) : @@ -1286,28 +1367,77 @@ public: } virtual ~BasicShadowCanvasLayer() { + if (mFrontSurface) + BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface); MOZ_COUNT_DTOR(BasicShadowCanvasLayer); } - virtual void Initialize(const Data& aData) - {} + virtual void Initialize(const Data& aData); virtual void Updated(const nsIntRect& aRect) {} virtual already_AddRefed - Swap(gfxSharedImageSurface* newFront) - { return NULL; } + Swap(gfxSharedImageSurface* newFront); virtual void Paint(gfxContext* aContext, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, - float aOpacity) - {} + float aOpacity); MOZ_LAYER_DECL_NAME("BasicShadowCanvasLayer", TYPE_SHADOW) + +private: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + nsRefPtr mFrontSurface; + nsIntRect mBounds; }; +void +BasicShadowCanvasLayer::Initialize(const Data& aData) +{ + NS_ASSERTION(mFrontSurface == nsnull, + "BasicCanvasLayer::Initialize called twice!"); + NS_ASSERTION(aData.mSurface && !aData.mGLContext, "no comprende OpenGL!"); + + mFrontSurface = static_cast(aData.mSurface); + mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); +} + +already_AddRefed +BasicShadowCanvasLayer::Swap(gfxSharedImageSurface* newFront) +{ + already_AddRefed tmp = mFrontSurface.forget(); + mFrontSurface = newFront; + return tmp; +} + +void +BasicShadowCanvasLayer::Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) +{ + MOZ_LAYERS_LOG(("[ShadowLayersChild] %s()", __FUNCTION__)); + + NS_ASSERTION(BasicManager()->InDrawing(), + "Can only draw in drawing phase"); + + nsRefPtr pat = new gfxPattern(mFrontSurface); + + pat->SetFilter(mFilter); + pat->SetExtend(gfxPattern::EXTEND_PAD); + + gfxRect r(0, 0, mBounds.width, mBounds.height); + aContext->NewPath(); + aContext->PixelSnappedRectangleAndSetPattern(r, pat); + aContext->Fill(); +} + // Create a shadow layer (PLayerChild) for aLayer, if we're forwarding // our layer tree to a parent process. Record the new layer creation // in the current open transaction as a side effect. @@ -1462,12 +1592,30 @@ BasicShadowLayerManager::EndTransaction(DrawThebesLayerCallback aCallback, // forward this transaction's changeset to our ShadowLayerManager nsAutoTArray replies; - if (HasShadowManager() && !ShadowLayerForwarder::EndTransaction(&replies)) { + if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) { + for (nsTArray::size_type i = 0; i < replies.Length(); ++i) { + const EditReply& reply = replies[i]; + + switch (reply.type()) { + case EditReply::TOpBufferSwap: { + MOZ_LAYERS_LOG(("[LayersForwarder] BufferSwap")); + + const OpBufferSwap& obs = reply.get_OpBufferSwap(); + GetBasicShadowable(obs)->SetBackBuffer( + new gfxSharedImageSurface(obs.newBackBuffer())); + break; + } + + default: + NS_RUNTIMEABORT("not reached"); + } + } + } else if (HasShadowManager()) { NS_WARNING("failed to forward Layers transaction"); } // this may result in Layers being deleted, which results in - // PLayer::Send__delete__() + // PLayer::Send__delete__() and DeallocShmem() mKeepAlive.Clear(); #ifdef DEBUG diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 4d43c514cd89..3994bf1b54fe 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -45,6 +45,7 @@ #include "mozilla/layers/PLayerChild.h" #include "mozilla/layers/PLayersChild.h" +#include "mozilla/layers/PLayersParent.h" #include "ShadowLayers.h" #include "ShadowLayerChild.h" @@ -294,6 +295,31 @@ ShadowLayerForwarder::EndTransaction(nsTArray* aReplies) return PR_TRUE; } +PRBool +ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize, + gfxASurface::gfxImageFormat aFormat, + gfxSharedImageSurface** aFrontBuffer, + gfxSharedImageSurface** aBackBuffer) +{ + NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to"); + + nsRefPtr front = new gfxSharedImageSurface(); + nsRefPtr back = new gfxSharedImageSurface(); + if (!front->Init(mShadowManager, aSize, aFormat) || + !back->Init(mShadowManager, aSize, aFormat)) + return PR_FALSE; + + *aFrontBuffer = NULL; *aBackBuffer = NULL; + front.swap(*aFrontBuffer); back.swap(*aBackBuffer); + return PR_TRUE; +} + +void +ShadowLayerForwarder::DestroySharedSurface(gfxSharedImageSurface* aSurface) +{ + mShadowManager->DeallocShmem(aSurface->GetShmem()); +} + PLayerChild* ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) { @@ -301,5 +327,12 @@ ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer) return mShadowManager->SendPLayerConstructor(new ShadowLayerChild(aLayer)); } + +void +ShadowLayerManager::DestroySharedSurface(gfxSharedImageSurface* aSurface) +{ + mForwarder->DeallocShmem(aSurface->GetShmem()); +} + } // namespace layers } // namespace mozilla From 6b214535a0fba5055414ca800d0460d8511a3174 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 164/369] Bug 570294, part i: Publish BasicImageLayer pixels to remote processes (slowly). r=Bas --- gfx/layers/basic/BasicLayers.cpp | 160 +++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 19 deletions(-) diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 1ceaddc7cdf9..3acf7a69fbf2 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -111,7 +111,7 @@ public: void* aCallbackData, float aOpacity) {} - virtual ShadowableLayer* AsShadowableLayer() { return NULL; } + virtual ShadowableLayer* AsShadowableLayer() { return nsnull; } }; static BasicImplData* @@ -425,11 +425,22 @@ public: void* aCallbackData, float aOpacity); + static void PaintContext(gfxPattern* aPattern, + const gfxIntSize& aSize, + float aOpacity, + gfxContext* aContext); + protected: BasicLayerManager* BasicManager() { return static_cast(mManager); } + + already_AddRefed + GetAndPaintCurrentImage(gfxContext* aContext, + float aOpacity); + + gfxIntSize mSize; }; void @@ -438,22 +449,38 @@ BasicImageLayer::Paint(gfxContext* aContext, void* aCallbackData, float aOpacity) { - if (!mContainer) - return; + nsRefPtr dontcare = GetAndPaintCurrentImage(aContext, aOpacity); +} - gfxIntSize size; - nsRefPtr surface = mContainer->GetCurrentAsSurface(&size); +already_AddRefed +BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext, + float aOpacity) +{ + if (!mContainer) + return nsnull; + + nsRefPtr surface = mContainer->GetCurrentAsSurface(&mSize); if (!surface) { - return; + return nsnull; } nsRefPtr pat = new gfxPattern(surface); if (!pat) { - return; + return nsnull; } pat->SetFilter(mFilter); + PaintContext(pat, mSize, aOpacity, aContext); + return pat.forget(); +} + +/*static*/ void +BasicImageLayer::PaintContext(gfxPattern* aPattern, + const gfxIntSize& aSize, + float aOpacity, + gfxContext* aContext) +{ // Set PAD mode so that when the video is being scaled, we do not sample // outside the bounds of the video image. gfxPattern::GraphicsExtend extend = gfxPattern::EXTEND_PAD; @@ -468,12 +495,12 @@ BasicImageLayer::Paint(gfxContext* aContext, extend = gfxPattern::EXTEND_NONE; } - pat->SetExtend(extend); + aPattern->SetExtend(extend); /* Draw RGB surface onto frame */ aContext->NewPath(); aContext->PixelSnappedRectangleAndSetPattern( - gfxRect(0, 0, size.width, size.height), pat); + gfxRect(0, 0, aSize.width, aSize.height), aPattern); if (aOpacity != 1.0) { aContext->Save(); aContext->Clip(); @@ -1133,7 +1160,7 @@ BasicShadowableContainerLayer::InsertAfter(Layer* aChild, Layer* aAfter) if (HasShadow()) { ShadowManager()->InsertAfter(ShadowManager()->Hold(this), ShadowManager()->Hold(aChild), - aAfter ? ShadowManager()->Hold(aAfter) : NULL); + aAfter ? ShadowManager()->Hold(aAfter) : nsnull); } BasicContainerLayer::InsertAfter(aChild, aAfter); } @@ -1182,9 +1209,17 @@ public: } virtual ~BasicShadowableImageLayer() { + if (mBackSurface) { + BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackSurface); + } MOZ_COUNT_DTOR(BasicShadowableImageLayer); } + virtual void Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity); + virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs) { aAttrs = ImageLayerAttributes(mFilter); @@ -1192,7 +1227,54 @@ public: virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; } + + virtual void SetBackBuffer(gfxSharedImageSurface* aBuffer) + { + mBackSurface = aBuffer; + } + +private: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + nsRefPtr mBackSurface; }; + +void +BasicShadowableImageLayer::Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) +{ + gfxIntSize oldSize = mSize; + nsRefPtr pat = GetAndPaintCurrentImage(aContext, aOpacity); + if (!pat || !HasShadow()) + return; + + if (oldSize != mSize) { + NS_ASSERTION(oldSize == gfxIntSize(0, 0), "video changed size?"); + + nsRefPtr tmpFrontSurface; + // XXX error handling? + if (!BasicManager()->AllocDoubleBuffer( + mSize, gfxASurface::ImageFormatARGB32, + getter_AddRefs(tmpFrontSurface), getter_AddRefs(mBackSurface))) + NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!"); + + BasicManager()->CreatedImageBuffer(BasicManager()->Hold(this), + nsIntSize(mSize.width, mSize.height), + tmpFrontSurface); + } + + nsRefPtr tmpCtx = new gfxContext(mBackSurface); + PaintContext(pat, mSize, 1.0, tmpCtx); + + BasicManager()->PaintedImage(BasicManager()->Hold(this), + mBackSurface); +} + class BasicShadowableColorLayer : public BasicColorLayer, public BasicShadowableLayer @@ -1228,8 +1310,9 @@ public: } virtual ~BasicShadowableCanvasLayer() { - if (mBackBuffer) + if (mBackBuffer) { BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer); + } MOZ_COUNT_DTOR(BasicShadowableCanvasLayer); } @@ -1317,7 +1400,7 @@ public: Swap(gfxSharedImageSurface* aNewFront, const nsIntRect& aBufferRect, const nsIntPoint& aRotation) - { return NULL; } + { return nsnull; } virtual void Paint(gfxContext* aContext, LayerManager::DrawThebesLayerCallback aCallback, @@ -1337,25 +1420,63 @@ public: } virtual ~BasicShadowImageLayer() { + if (mFrontSurface) { + BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface); + } MOZ_COUNT_DTOR(BasicShadowImageLayer); } - virtual PRBool Init(gfxSharedImageSurface* front, const nsIntSize& size) - { return PR_TRUE; } + virtual PRBool Init(gfxSharedImageSurface* front, const nsIntSize& size); virtual already_AddRefed - Swap(gfxSharedImageSurface* newFront) - { return NULL; } + Swap(gfxSharedImageSurface* newFront); virtual void Paint(gfxContext* aContext, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, - float aOpacity) - {} + float aOpacity); MOZ_LAYER_DECL_NAME("BasicShadowImageLayer", TYPE_SHADOW) + +protected: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + // XXX ShmemImage? + nsRefPtr mFrontSurface; + gfxIntSize mSize; }; +PRBool +BasicShadowImageLayer::Init(gfxSharedImageSurface* front, + const nsIntSize& size) +{ + mFrontSurface = front; + mSize = gfxIntSize(size.width, size.height); + return PR_TRUE; +} + +already_AddRefed +BasicShadowImageLayer::Swap(gfxSharedImageSurface* newFront) +{ + already_AddRefed tmp = mFrontSurface.forget(); + mFrontSurface = newFront; + return tmp; +} + +void +BasicShadowImageLayer::Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) +{ + nsRefPtr pat = new gfxPattern(mFrontSurface); + pat->SetFilter(mFilter); + BasicImageLayer::PaintContext(pat, mSize, aOpacity, aContext); +} + class BasicShadowCanvasLayer : public ShadowCanvasLayer, BasicImplData { @@ -1367,8 +1488,9 @@ public: } virtual ~BasicShadowCanvasLayer() { - if (mFrontSurface) + if (mFrontSurface) { BasicManager()->ShadowLayerManager::DestroySharedSurface(mFrontSurface); + } MOZ_COUNT_DTOR(BasicShadowCanvasLayer); } From 92180cc2160ebe3885a331e3cfa349780f9b25f0 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 16:17:33 -0500 Subject: [PATCH 165/369] Bug 570294, part j: Publish BasicThebesLayer pixels to remote processes (slowly). r=roc --- gfx/layers/ThebesLayerBuffer.h | 11 ++ gfx/layers/basic/BasicLayers.cpp | 268 +++++++++++++++++++++++++------ 2 files changed, 234 insertions(+), 45 deletions(-) diff --git a/gfx/layers/ThebesLayerBuffer.h b/gfx/layers/ThebesLayerBuffer.h index 0e65892e8f6a..936e1c3c2067 100644 --- a/gfx/layers/ThebesLayerBuffer.h +++ b/gfx/layers/ThebesLayerBuffer.h @@ -156,6 +156,17 @@ protected: const nsIntRect& BufferRect() const { return mBufferRect; } const nsIntPoint& BufferRotation() const { return mBufferRotation; } + already_AddRefed + SetBuffer(gfxASurface* aBuffer, + const nsIntRect& aBufferRect, const nsIntPoint& aBufferRotation) + { + gfxASurface* tmp = mBuffer; + mBuffer = aBuffer; + mBufferRect = aBufferRect; + mBufferRotation = aBufferRotation; + return tmp; + } + private: PRBool BufferSizeOkFor(const nsIntSize& aSize) { diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index 3acf7a69fbf2..eb9e519f2df8 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -227,8 +227,41 @@ BasicContainerLayer::RemoveChildInternal(Layer* aChild) NS_RELEASE(aChild); } +class BasicThebesLayer; +class BasicThebesLayerBuffer : public ThebesLayerBuffer { + typedef ThebesLayerBuffer Base; + +public: + BasicThebesLayerBuffer(BasicThebesLayer* aLayer) + : Base(ContainsVisibleBounds) + , mLayer(aLayer) + {} + + virtual ~BasicThebesLayerBuffer() + {} + + using Base::BufferRect; + using Base::BufferRotation; + + /** + * Complete the drawing operation. The region to draw must have been + * drawn before this is called. The contents of the buffer are drawn + * to aTarget. + */ + void DrawTo(ThebesLayer* aLayer, PRBool aIsOpaqueContent, + gfxContext* aTarget, float aOpacity); + + virtual already_AddRefed + CreateBuffer(ContentType aType, const nsIntSize& aSize); + +private: + BasicThebesLayer* mLayer; +}; + class BasicThebesLayer : public ThebesLayer, BasicImplData { public: + typedef BasicThebesLayerBuffer Buffer; + BasicThebesLayer(BasicLayerManager* aLayerManager) : ThebesLayer(aLayerManager, static_cast(this)), mBuffer(this) @@ -257,36 +290,6 @@ public: LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, float aOpacity); - -protected: - BasicLayerManager* BasicManager() - { - return static_cast(mManager); - } - - class Buffer : public ThebesLayerBuffer { - public: - Buffer(BasicThebesLayer* aLayer) - : ThebesLayerBuffer(ContainsVisibleBounds) - , mLayer(aLayer) - {} - - /** - * Complete the drawing operation. The region to draw must have been - * drawn before this is called. The contents of the buffer are drawn - * to aTarget. - */ - void DrawTo(PRBool aIsOpaqueContent, gfxContext* aTarget, float aOpacity); - - virtual already_AddRefed - CreateBuffer(ContentType aType, const nsIntSize& aSize) - { - return mLayer->CreateBuffer(aType, aSize); - } - - private: - BasicThebesLayer* mLayer; - }; virtual already_AddRefed CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize) @@ -309,6 +312,24 @@ protected: aType, gfxIntSize(aSize.width, aSize.height)); } +protected: + BasicLayerManager* BasicManager() + { + return static_cast(mManager); + } + + virtual void + PaintBuffer(gfxContext* aContext, + const nsIntRegion& aRegionToDraw, + const nsIntRegion& aRegionToInvalidate, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData) + { + aCallback(this, aContext, aRegionToDraw, aRegionToInvalidate, + aCallbackData); + mValidRegion.Or(mValidRegion, aRegionToDraw); + } + Buffer mBuffer; }; @@ -373,9 +394,9 @@ BasicThebesLayer::Paint(gfxContext* aContext, // from RGB to RGBA, because we might need to repaint with // subpixel AA) state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion); - aCallback(this, state.mContext, state.mRegionToDraw, - state.mRegionToInvalidate, aCallbackData); - mValidRegion.Or(mValidRegion, state.mRegionToDraw); + PaintBuffer(state.mContext, + state.mRegionToDraw, state.mRegionToInvalidate, + aCallback, aCallbackData); } else { // It's possible that state.mRegionToInvalidate is nonempty here, // if we are shrinking the valid region to nothing. @@ -384,16 +405,17 @@ BasicThebesLayer::Paint(gfxContext* aContext, } } - mBuffer.DrawTo(isOpaqueContent, target, aOpacity); + mBuffer.DrawTo(this, isOpaqueContent, target, aOpacity); } void -BasicThebesLayer::Buffer::DrawTo(PRBool aIsOpaqueContent, - gfxContext* aTarget, - float aOpacity) +BasicThebesLayerBuffer::DrawTo(ThebesLayer* aLayer, + PRBool aIsOpaqueContent, + gfxContext* aTarget, + float aOpacity) { aTarget->Save(); - ClipToRegion(aTarget, mLayer->GetVisibleRegion()); + ClipToRegion(aTarget, aLayer->GetVisibleRegion()); if (aIsOpaqueContent) { aTarget->SetOperator(gfxContext::OPERATOR_SOURCE); } @@ -401,6 +423,13 @@ BasicThebesLayer::Buffer::DrawTo(PRBool aIsOpaqueContent, aTarget->Restore(); } +already_AddRefed +BasicThebesLayerBuffer::CreateBuffer(ContentType aType, + const nsIntSize& aSize) +{ + return mLayer->CreateBuffer(aType, aSize); +} + class BasicImageLayer : public ImageLayer, BasicImplData { public: BasicImageLayer(BasicLayerManager* aLayerManager) : @@ -1178,6 +1207,8 @@ BasicShadowableContainerLayer::RemoveChild(Layer* aChild) class BasicShadowableThebesLayer : public BasicThebesLayer, public BasicShadowableLayer { + typedef BasicThebesLayer Base; + public: BasicShadowableThebesLayer(BasicShadowLayerManager* aManager) : BasicThebesLayer(aManager) @@ -1186,6 +1217,8 @@ public: } virtual ~BasicShadowableThebesLayer() { + if (mBackBuffer) + BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBuffer); MOZ_COUNT_DTOR(BasicShadowableThebesLayer); } @@ -1196,7 +1229,77 @@ public: virtual Layer* AsLayer() { return this; } virtual ShadowableLayer* AsShadowableLayer() { return this; } + + virtual void SetBackBuffer(gfxSharedImageSurface* aBuffer) + { + mBackBuffer = aBuffer; + } + +private: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + NS_OVERRIDE virtual void + PaintBuffer(gfxContext* aContext, + const nsIntRegion& aRegionToDraw, + const nsIntRegion& aRegionToInvalidate, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData); + + NS_OVERRIDE virtual already_AddRefed + CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize); + + nsRefPtr mBackBuffer; + nsIntSize mBufferSize; }; + +void +BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext, + const nsIntRegion& aRegionToDraw, + const nsIntRegion& aRegionToInvalidate, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData) +{ + NS_ABORT_IF_FALSE(!!mBackBuffer, "should have a back buffer by now"); + + Base::PaintBuffer(aContext, aRegionToDraw, aRegionToInvalidate, + aCallback, aCallbackData); + + nsRefPtr tmpCtx = new gfxContext(mBackBuffer); + tmpCtx->DrawSurface(aContext->OriginalSurface(), + gfxIntSize(mBufferSize.width, mBufferSize.height)); + + BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this), + mBuffer.BufferRect(), + mBuffer.BufferRotation(), + mBackBuffer); +} + +already_AddRefed +BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType, + const nsIntSize& aSize) +{ + nsRefPtr tmpFront; + // XXX error handling + if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height), + gfxASurface::ImageFormatARGB32, + getter_AddRefs(tmpFront), + getter_AddRefs(mBackBuffer))) + NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!"); + mBufferSize = aSize; + + BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this), + // only |aSize| really matters + // here, since Painted() soon + // follows + nsIntRect(nsIntPoint(0, 0), aSize), + tmpFront); + + return Base::CreateBuffer(aType, aSize); +} + class BasicShadowableImageLayer : public BasicImageLayer, public BasicShadowableLayer @@ -1383,6 +1486,40 @@ BasicShadowableCanvasLayer::Paint(gfxContext* aContext, mBackBuffer); } +class ShadowThebesLayerBuffer : public BasicThebesLayerBuffer +{ + typedef BasicThebesLayerBuffer Base; + +public: + ShadowThebesLayerBuffer() + : Base(NULL) + { + MOZ_COUNT_CTOR(ShadowThebesLayerBuffer); + } + + ~ShadowThebesLayerBuffer() + { + MOZ_COUNT_DTOR(ShadowThebesLayerBuffer); + } + + already_AddRefed + Swap(gfxSharedImageSurface* aNewFrontBuffer, + const nsIntRect& aBufferRect, + const nsIntPoint& aRotation=nsIntPoint(0, 0)) + { + nsRefPtr newBackBuffer = SetBuffer(aNewFrontBuffer, + aBufferRect, aRotation); + return static_cast(newBackBuffer.forget().get()); + } + +protected: + virtual already_AddRefed + CreateBuffer(ContentType aType, const nsIntSize& aSize) + { + NS_RUNTIMEABORT("ShadowThebesLayer can't paint content"); + return nsnull; + } +}; class BasicShadowThebesLayer : public ShadowThebesLayer, BasicImplData { public: @@ -1391,26 +1528,67 @@ public: { MOZ_COUNT_CTOR(BasicShadowThebesLayer); } - virtual ~BasicShadowThebesLayer() - { - MOZ_COUNT_DTOR(BasicShadowThebesLayer); - } + virtual ~BasicShadowThebesLayer(); virtual already_AddRefed Swap(gfxSharedImageSurface* aNewFront, const nsIntRect& aBufferRect, const nsIntPoint& aRotation) - { return nsnull; } + { + return mFrontBuffer.Swap(aNewFront, aBufferRect, aRotation); + } virtual void Paint(gfxContext* aContext, LayerManager::DrawThebesLayerCallback aCallback, void* aCallbackData, - float aOpacity) - {} + float aOpacity); MOZ_LAYER_DECL_NAME("BasicShadowThebesLayer", TYPE_SHADOW) + +private: + BasicShadowLayerManager* BasicManager() + { + return static_cast(mManager); + } + + ShadowThebesLayerBuffer mFrontBuffer; }; +BasicShadowThebesLayer::~BasicShadowThebesLayer() +{ + nsRefPtr frontBuffer = + mFrontBuffer.Swap(0, nsIntRect()); + if (frontBuffer) { + BasicManager()->ShadowLayerManager::DestroySharedSurface(frontBuffer); + } + + MOZ_COUNT_DTOR(BasicShadowThebesLayer); +} + +void +BasicShadowThebesLayer::Paint(gfxContext* aContext, + LayerManager::DrawThebesLayerCallback aCallback, + void* aCallbackData, + float aOpacity) +{ + NS_ASSERTION(BasicManager()->InDrawing(), + "Can only draw in drawing phase"); + NS_ASSERTION(BasicManager()->IsRetained(), + "ShadowThebesLayer makes no sense without retained mode"); + + gfxContext* target = BasicManager()->GetTarget(); + NS_ASSERTION(target, "We shouldn't be called if there's no target"); + + nsRefPtr targetSurface = aContext->CurrentSurface(); + PRBool isOpaqueContent = + (targetSurface->AreSimilarSurfacesSensitiveToContentType() && + aOpacity == 1.0 && + CanUseOpaqueSurface()); + + mFrontBuffer.DrawTo(this, isOpaqueContent, target, aOpacity); +} + + class BasicShadowImageLayer : public ShadowImageLayer, BasicImplData { public: BasicShadowImageLayer(BasicShadowLayerManager* aLayerManager) : From 23560b18413f34f8130bbd327e964930cad8262f Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 21 Jul 2010 18:13:24 -0500 Subject: [PATCH 166/369] Followup to bug 570294: Avoid vector::data(), it's not C++98. --- gfx/layers/ipc/ShadowLayers.cpp | 3 ++- gfx/layers/ipc/ShadowLayersParent.cpp | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gfx/layers/ipc/ShadowLayers.cpp b/gfx/layers/ipc/ShadowLayers.cpp index 3994bf1b54fe..f0222eb63c08 100644 --- a/gfx/layers/ipc/ShadowLayers.cpp +++ b/gfx/layers/ipc/ShadowLayers.cpp @@ -283,8 +283,9 @@ ShadowLayerForwarder::EndTransaction(nsTArray* aReplies) } nsAutoTArray cset; + NS_ABORT_IF_FALSE(mTxn->mCset.size() > 0, "should have bailed by now"); cset.SetCapacity(mTxn->mCset.size()); - cset.AppendElements(mTxn->mCset.data(), mTxn->mCset.size()); + cset.AppendElements(&mTxn->mCset.front(), mTxn->mCset.size()); if (!mShadowManager->SendUpdate(cset, aReplies)) { MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!")); diff --git a/gfx/layers/ipc/ShadowLayersParent.cpp b/gfx/layers/ipc/ShadowLayersParent.cpp index 7af5a7b2e1c9..9b4b71428132 100644 --- a/gfx/layers/ipc/ShadowLayersParent.cpp +++ b/gfx/layers/ipc/ShadowLayersParent.cpp @@ -367,7 +367,9 @@ ShadowLayersParent::RecvUpdate(const nsTArray& cset, layer_manager()->EndTransaction(NULL, NULL); reply->SetCapacity(replyv.size()); - reply->AppendElements(replyv.data(), replyv.size()); + if (replyv.size() > 0) { + reply->AppendElements(&replyv.front(), replyv.size()); + } return true; } From 6a46fe1b33caac95b283ccbd7b643a09fc8420ae Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Thu, 22 Jul 2010 12:34:13 -0700 Subject: [PATCH 167/369] Bug 580870 - Alias gBrowser to the tabcandy context to remove getCurrentWindow Add aliases for gWindow, gBrowser, gTabDeck, gTabFrame for the tabcandy chrome context. Update uses and simplify some code like removing Navbar. --- .../base/content/tabview/modules/utils.jsm | 48 ++++++++++--------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 373555825dcf..bdeef67039ba 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -46,6 +46,29 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gWindow", function() { + let windows = Services.wm.getEnumerator("navigator:browser"); + while (windows.hasMoreElements()) { + let browser = windows.getNext(); + let tabCandyFrame = browser.document.getElementById("tab-candy"); + if (tabCandyFrame.contentWindow == window) + return browser; + } +}); + +XPCOMUtils.defineLazyGetter(this, "gBrowser", function() gWindow.gBrowser); + +XPCOMUtils.defineLazyGetter(this, "gTabDeck", function() { + return gWindow.document.getElementById("tab-candy-deck"); +}); + +XPCOMUtils.defineLazyGetter(this, "gTabFrame", function() { + return gWindow.document.getElementById("tab-candy"); +}); + var consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); @@ -507,10 +530,9 @@ var Utils = { // The tab that represents the active tab in the active window. get activeTab(){ try { - var tabBrowser = this.getCurrentWindow().gBrowser; - Utils.assert('tabBrowser', tabBrowser); + Utils.assert('tabBrowser', gBrowser); - var rawTab = tabBrowser.selectedTab; + var rawTab = gBrowser.selectedTab; for ( var i=0; i Date: Thu, 22 Jul 2010 12:36:17 -0700 Subject: [PATCH 168/369] Bug 581078 - Get rid of Utils.activeTab to avoid iterating over all Tabs Directly access gBrowser.selectedTab and compare other raw tabs against it until BrowserTab goes away. Get the reference to the BrowserTab instead of iterating over Tabs through .tabcandyBrowserTab. --- .../base/content/tabview/modules/utils.jsm | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index bdeef67039ba..b49e6f937267 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -523,27 +523,6 @@ window.Subscribable.prototype = { // Class: Utils // Singelton with common utility functions. var Utils = { - // ___ Windows and Tabs - - // ---------- - // Variable: activeTab - // The tab that represents the active tab in the active window. - get activeTab(){ - try { - Utils.assert('tabBrowser', gBrowser); - - var rawTab = gBrowser.selectedTab; - for ( var i=0; i Date: Thu, 22 Jul 2010 15:42:29 -0400 Subject: [PATCH 169/369] Bug 580878: removing some underutilized Utils functions: getMilliseconds, and testLogging --HG-- extra : rebase_source : 28ff49903f395240a3aca3b3761c7638ee6d4b7d --- .../base/content/tabview/modules/utils.jsm | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index b49e6f937267..8b8405ff86b4 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -646,17 +646,6 @@ var Utils = { return s; }, - // ---------- - // Funtion: testLogging - // Prints some test messages with the various logging methods. - testLogging: function() { - this.log('beginning logging test'); - this.error('this is an error'); - this.trace('this is a trace'); - this.log(1, null, {'foo': 'hello', 'bar': 2}, 'whatever'); - this.log('ending logging test'); - }, - // ___ Misc // ---------- @@ -667,18 +656,9 @@ var Utils = { return (event.which == 3); if (event.button) return (event.button == 2); - return false; }, - // ---------- - // Function: getMilliseconds - // Returns the total milliseconds on the system clock right now. - getMilliseconds: function() { - var date = new Date(); - return date.getTime(); - }, - // ---------- // Function: isDOMElement // Returns true if the given object is a DOM element. From 318e64a28f992be81fa3b268ba7c88e1fd8f0722 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Thu, 22 Jul 2010 15:46:51 -0400 Subject: [PATCH 170/369] Bug 581143: Cleanup geometry utilities in utils.js 1. Move isRect, isPoint, isRange to Utils, to facilitate JSMing and to clean up namespace 2. Remove unused/underutilized methods of Rect/Point/Range 3. Use QuickDraw-style comparison of pixel overlap (using < instead of <=) throughout. 4. Range.overlaps is now Range.contains, to be more consistent with Rect. --HG-- extra : rebase_source : cc3507bb3bbe92f922d3147cc9130c1f713391b2 --- .../base/content/tabview/modules/utils.jsm | 137 ++++++------------ 1 file changed, 46 insertions(+), 91 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 8b8405ff86b4..d7a211cbff45 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -81,10 +81,9 @@ var consoleService = Cc["@mozilla.org/consoleservice;1"] // and creates a Point with it along with y. If either a or y are omitted, // 0 is used in their place. window.Point = function(a, y) { - if (isPoint(a)) { + if (Utils.isPoint(a)) { // Variable: x this.x = a.x; - // Variable: y this.y = a.y; } else { @@ -92,30 +91,14 @@ window.Point = function(a, y) { this.y = (Utils.isNumber(y) ? y : 0); } }; - -// ---------- -// Function: isPoint -// Returns true if the given object (p) looks like a . -// Note that this is not an actual method of , but a global routine. -window.isPoint = function(p) { - return (p && Utils.isNumber(p.x) && Utils.isNumber(p.y)); -}; - window.Point.prototype = { // ---------- // Function: distance // Returns the distance from this point to the given . distance: function(point) { - var ax = Math.abs(this.x - point.x); - var ay = Math.abs(this.y - point.y); + var ax = this.x - point.x; + var ay = this.y - point.y; return Math.sqrt((ax * ax) + (ay * ay)); - }, - - // ---------- - // Function: plus - // Returns a new point with the result of adding this point to the given . - plus: function(point) { - return new Point(this.x + point.x, this.y + point.y); } }; @@ -130,7 +113,7 @@ window.Point.prototype = { // and creates a Rect with it along with top, width, and height. window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' - if (isRect(a)) { + if (Utils.isRect(a)) { // Variable: left this.left = a.left; @@ -150,18 +133,6 @@ window.Rect = function(a, top, width, height) { } }; -// ---------- -// Function: isRect -// Returns true if the given object (r) looks like a . -// Note that this is not an actual method of , but a global routine. -window.isRect = function(r) { - return (r - && Utils.isNumber(r.left) - && Utils.isNumber(r.top) - && Utils.isNumber(r.width) - && Utils.isNumber(r.height)); -}; - window.Rect.prototype = { // ---------- // Variable: right @@ -223,20 +194,6 @@ window.Rect.prototype = { return null; }, - // ---------- - // Function: containsPoint - // Returns a boolean denoting if the is inside of - // the bounding rect. - // - // Paramaters - // - A - containsPoint: function(point){ - return( point.x > this.left - && point.x < this.right - && point.y > this.top - && point.y < this.bottom ) - }, - // ---------- // Function: contains // Returns a boolean denoting if the is contained inside @@ -287,7 +244,7 @@ window.Rect.prototype = { // Paramaters // - A or two arguments: x and y inset: function(a, b) { - if (typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + if (Utils.isPoint(a)) { b = a.y; a = a.x; } @@ -305,7 +262,7 @@ window.Rect.prototype = { // Paramaters // - A or two arguments: x and y offset: function(a, b) { - if (typeof(a.x) != 'undefined' && typeof(a.y) != 'undefined') { + if (Utils.isPoint(a)) { this.left += a.x; this.top += a.y; } else { @@ -317,11 +274,11 @@ window.Rect.prototype = { // ---------- // Function: equals // Returns true if this rectangle is identical to the given . - equals: function(a) { - return (a.left == this.left - && a.top == this.top - && a.width == this.width - && a.height == this.height); + equals: function(rect) { + return (rect.left == this.left + && rect.top == this.top + && rect.width == this.width + && rect.height == this.height); }, // ---------- @@ -349,7 +306,8 @@ window.Rect.prototype = { // ---------- // Function: css - // Returns an object with the dimensions of this rectangle, suitable for passing into iQ.fn.css. + // Returns an object with the dimensions of this rectangle, suitable for passing + // into iQ.fn.css. // You could of course just pass the rectangle straight in, but this is cleaner. css: function() { return { @@ -368,7 +326,7 @@ window.Rect.prototype = { // Constructor: Range // Creates a Range with the given min and max window.Range = function(min, max) { - if (isRange(min) && !max) { // if the one variable given is a range, copy it. + if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it. this.min = min.min; this.max = min.max; } else { @@ -377,16 +335,6 @@ window.Range = function(min, max) { } }; -// ---------- -// Function: isRange -// Returns true if the given object (r) looks like a . -// Note that this is not an actual method of , but a global routine. -window.isRange = function(r) { - return (r - && Utils.isNumber(r.min) - && Utils.isNumber(r.max)); -}; - window.Range.prototype = { // Variable: extent // Equivalent to max-min @@ -400,36 +348,16 @@ window.Range.prototype = { // ---------- // Function: contains - // Whether the contains the given value or not + // Whether the contains the given or value or not. // // Paramaters // - a number or contains: function(value) { return Utils.isNumber(value) ? - ( value >= this.min && value <= this.max ) : - ( value.min >= this.min && value.max <= this.max ); - }, - // ---------- - // Function: containsWithin - // Whether the 's interior contains the given value or not - // - // Paramaters - // - a number or - containsWithin: function(value) { - return Utils.isNumber(value) ? - ( value > this.min && value < this.max ) : - ( value.min > this.min && value.max < this.max ); - }, - // ---------- - // Function: overlaps - // Whether the overlaps with the given or not. - // - // Paramaters - // - a number or - overlaps: function(value) { - return Utils.isNumber(value) ? - this.contains(value) : - ( value.min <= this.max && this.min <= value.max ); + value >= this.min && value <= this.max : + Utils.isRange(value) ? + ( value.min <= this.max && this.min <= value.max ) : + false; }, }; @@ -672,6 +600,33 @@ var Utils = { isNumber: function(n) { return (typeof(n) == 'number' && !isNaN(n)); }, + + // ---------- + // Function: isRect + // Returns true if the given object (r) looks like a . + isRect: function(r) { + return (r + && this.isNumber(r.left) + && this.isNumber(r.top) + && this.isNumber(r.width) + && this.isNumber(r.height)); + }, + + // ---------- + // Function: isRange + // Returns true if the given object (r) looks like a . + isRange: function(r) { + return (r + && this.isNumber(r.min) + && this.isNumber(r.max)); + }, + + // ---------- + // Function: isPoint + // Returns true if the given object (p) looks like a . + isPoint: function(p) { + return (p && this.isNumber(p.x) && this.isNumber(p.y)); + }, // ---------- // Function: copy From 613b2fbeb5530f34c6918c0b654e40bc3d401ff4 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Fri, 23 Jul 2010 00:35:07 -0400 Subject: [PATCH 171/369] Rewrote various scaling/easing functions in terms of the Range's, with the new proportion and scale methods. - removed Math.tanh, instead moving it into the Range proportion method for its "smooth" option + rewrote TabItem close button opacity and title font size using the new Range utilities. Should make code a tad easier to follow. --- .../base/content/tabview/modules/utils.jsm | 56 ++++++++++++++++--- 1 file changed, 48 insertions(+), 8 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index d7a211cbff45..6150c293996b 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -355,10 +355,55 @@ window.Range.prototype = { contains: function(value) { return Utils.isNumber(value) ? value >= this.min && value <= this.max : - Utils.isRange(value) ? + Utils.isRange(value) ? ( value.min <= this.max && this.min <= value.max ) : false; }, + + // ---------- + // Function: proportion + // Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min, + // returns 1 if the value >= the max, and returns an interpolated "proportion" in (min, max). + // + // Paramaters + // - a number + // - (bool) smooth? If true, a smooth tanh-based function will be used instead of the linear. + proportion: function(value, smooth) { + if (value <= this.min) + return 0; + if (this.max <= value) + return 1; + + var proportion = (value - this.min) / this.extent; + + if (smooth) { + // The ease function ".5+.5*Math.tanh(4*x-2)" is a pretty + // little graph. It goes from near 0 at x=0 to near 1 at x=1 + // smoothly and beautifully. + // http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29 + function tanh(x){ + var e = Math.exp(x); + return (e - 1/e) / (e + 1/e); + } + return .5 - .5 * tanh(2 - 4 * proportion); + } + + return proportion; + }, + + // ---------- + // Function: scale + // Takes the given value in [0,1] and maps it to the associated value on the Range. + // + // Paramaters + // - a number in [0,1] + scale: function(value) { + if (value > 1) + value = 1; + if (value < 0) + value = 0; + return this.min + this.extent * value; + } }; // ########## @@ -600,7 +645,7 @@ var Utils = { isNumber: function(n) { return (typeof(n) == 'number' && !isNaN(n)); }, - + // ---------- // Function: isRect // Returns true if the given object (r) looks like a . @@ -611,7 +656,7 @@ var Utils = { && this.isNumber(r.width) && this.isNumber(r.height)); }, - + // ---------- // Function: isRange // Returns true if the given object (r) looks like a . @@ -646,9 +691,4 @@ var Utils = { window.Utils = Utils; -window.Math.tanh = function tanh(x){ - var e = Math.exp(x); - return (e - 1/e) / (e + 1/e); -} - })(); From 7a146e662da860a4659453b69beaf274dc3540a5 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Thu, 22 Jul 2010 22:41:58 -0700 Subject: [PATCH 172/369] Followup bug 580870 to rename gTabDeck/gTabFrame to gTabView*. --- browser/base/content/tabview/modules/utils.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 6150c293996b..0d060530f97e 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -61,11 +61,11 @@ XPCOMUtils.defineLazyGetter(this, "gWindow", function() { XPCOMUtils.defineLazyGetter(this, "gBrowser", function() gWindow.gBrowser); -XPCOMUtils.defineLazyGetter(this, "gTabDeck", function() { +XPCOMUtils.defineLazyGetter(this, "gTabViewDeck", function() { return gWindow.document.getElementById("tab-candy-deck"); }); -XPCOMUtils.defineLazyGetter(this, "gTabFrame", function() { +XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() { return gWindow.document.getElementById("tab-candy"); }); From 0de35204dc5d6ebcd76c4213a952f8bbe46f9519 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Fri, 23 Jul 2010 17:33:02 -0400 Subject: [PATCH 173/369] Utils + iQ cleanup! - utility functions in iQ have been moved to Utils: isFunction, isArray, isPlainObject, isEmptyObject, merge, extend, timeout - iQ depends on Utils. Utils no longer depends on iQ. - simplified Utils.extend. Deep copy is no longer supported, nor is extending iQ and iQ.fn using extend. They were not being used. --HG-- extra : rebase_source : ab8f86e4e6a65cb747bc79ae0aa36cb39819421a --- browser/base/content/tabview/iq.js | 189 ++---------------- .../base/content/tabview/modules/utils.jsm | 181 +++++++++++++++-- 2 files changed, 176 insertions(+), 194 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9e644777d5ff..915f62760955 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -70,10 +70,6 @@ var iQ = function(selector, context) { // Match a standalone tag rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - // Save a reference to some core methods - toString = Object.prototype.toString, - hasOwnProperty = Object.prototype.hasOwnProperty, - rclass = /[\n\t]/g, rspace = /\s+/; @@ -128,7 +124,7 @@ iQ.fn = iQ.prototype = { ret = rsingleTag.exec( selector ); if ( ret ) { - if ( iQ.isPlainObject( context ) ) { + if ( Utils.isPlainObject( context ) ) { Utils.assert('does not support HTML creation with context', false); } else { selector = [ doc.createElement( ret[1] ) ]; @@ -138,7 +134,7 @@ iQ.fn = iQ.prototype = { Utils.assert('does not support complex HTML creation', false); } - return iQ.merge( this, selector ); + return Utils.merge( this, selector ); // HANDLE $("#id") } else { @@ -159,7 +155,7 @@ iQ.fn = iQ.prototype = { this.selector = selector; this.context = document; selector = document.getElementsByTagName( selector ); - return iQ.merge( this, selector ); + return Utils.merge( this, selector ); // HANDLE $(expr, $(...)) } else if ( !context || context.iq ) { @@ -173,7 +169,7 @@ iQ.fn = iQ.prototype = { // HANDLE $(function) // Shortcut for document ready - } else if ( iQ.isFunction( selector ) ) { + } else if ( Utils.isFunction( selector ) ) { Utils.log('iQ does not support ready functions'); return null; } @@ -189,10 +185,10 @@ iQ.fn = iQ.prototype = { // The window, strings (and functions) also have 'length' // The extra typeof function check is to prevent crashes // in Safari 2 (See: #3039) - if ( selector.length == null || typeof selector === "string" || iQ.isFunction(selector) || (typeof selector !== "function" && selector.setInterval) ) { + if ( selector.length == null || typeof selector === "string" || Utils.isFunction(selector) || (typeof selector !== "function" && selector.setInterval) ) { Array.prototype.push.call( ret, selector ); } else { - iQ.merge( ret, selector ); + Utils.merge( ret, selector ); } } return ret; @@ -227,7 +223,7 @@ iQ.fn = iQ.prototype = { // Function: each // Execute a callback for every element in the matched set. each: function( callback ) { - if ( !iQ.isFunction(callback) ) { + if ( !Utils.isFunction(callback) ) { Utils.assert("each's argument must be a function", false); return null; } @@ -241,7 +237,7 @@ iQ.fn = iQ.prototype = { // Function: addClass // Adds the given class(es) to the receiver. addClass: function( value ) { - if ( iQ.isFunction(value) ) { + if ( Utils.isFunction(value) ) { Utils.assert('does not support function argument', false); return null; } @@ -264,7 +260,7 @@ iQ.fn = iQ.prototype = { // Function: removeClass // Removes the given class(es) from the receiver. removeClass: function( value ) { - if ( iQ.isFunction(value) ) { + if ( Utils.isFunction(value) ) { Utils.assert('does not support function argument', false); return null; } @@ -309,7 +305,7 @@ iQ.fn = iQ.prototype = { for ( var i = 0, l = this.length; i < l; i++ ) { length = ret.length; try { - iQ.merge(ret, this[i].querySelectorAll( selector ) ); + Utils.merge(ret, this[i].querySelectorAll( selector ) ); } catch(e) { Utils.log('iQ.find error (bad selector)', e); } @@ -602,14 +598,14 @@ iQ.fn = iQ.prototype = { this.css(css); var self = this; - iQ.timeout(function() { + Utils.timeout(function() { self.css({ '-moz-transition-property': 'none', '-moz-transition-duration': '', '-moz-transition-timing-function': '' }); - if (iQ.isFunction(options.complete)) + if (Utils.isFunction(options.complete)) options.complete.apply(self); }, duration); } catch(e) { @@ -624,14 +620,14 @@ iQ.fn = iQ.prototype = { // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { try { - Utils.assert('does not yet support duration', iQ.isFunction(callback) || callback === undefined); + Utils.assert('does not yet support duration', Utils.isFunction(callback) || callback === undefined); this.animate({ opacity: 0 }, { duration: 400, complete: function() { iQ(this).css({display: 'none'}); - if (iQ.isFunction(callback)) + if (Utils.isFunction(callback)) callback.apply(this); } }); @@ -691,7 +687,7 @@ iQ.fn = iQ.prototype = { // Binds the given function to the given event type. Also wraps the function // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { - Utils.assert('does not support eventData argument', iQ.isFunction(func)); + Utils.assert('does not support eventData argument', Utils.isFunction(func)); var handler = function(event) { try { @@ -724,7 +720,7 @@ iQ.fn = iQ.prototype = { // Binds the given function to the given event type, but only for one call; // automatically unbinds after the event fires once. one: function(type, func) { - Utils.assert('does not support eventData argument', iQ.isFunction(func)); + Utils.assert('does not support eventData argument', Utils.isFunction(func)); var handler = function(e) { iQ(this).unbind(type, handler); @@ -738,7 +734,7 @@ iQ.fn = iQ.prototype = { // Function: unbind // Unbinds the given function from the given event type. unbind: function(type, func) { - Utils.assert('Must provide a function', iQ.isFunction(func)); + Utils.assert('Must provide a function', Utils.isFunction(func)); for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { var handler = func; @@ -764,157 +760,6 @@ iQ.fn = iQ.prototype = { // Give the init function the iQ prototype for later instantiation iQ.fn.init.prototype = iQ.fn; -// ########## -// Class: iQ -// Additional utility functions. - -// ---------- -// Function: extend -// Pass several objects in and it will combine them all into the first object and return it. -iQ.extend = iQ.fn.extend = function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy; - - // Handle a deep copy situation - if ( typeof target === "boolean" ) { - deep = target; - target = arguments[1] || {}; - // skip the boolean and the target - i = 2; - } - - // Handle case when target is a string or something (possible in deep copy) - if ( typeof target !== "object" && !iQ.isFunction(target) ) { - target = {}; - } - - // extend iQ itself if only one argument is passed - if ( length === i ) { - target = this; - --i; - } - - for ( ; i < length; i++ ) { - // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { - // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; - - // Prevent never-ending loop - if ( target === copy ) { - continue; - } - - // Recurse if we're merging object literal values or arrays - if ( deep && copy && ( iQ.isPlainObject(copy) || iQ.isArray(copy) ) ) { - var clone = src && ( iQ.isPlainObject(src) || iQ.isArray(src) ) ? src - : iQ.isArray(copy) ? [] : {}; - - // Never move original objects, clone them - target[ name ] = iQ.extend( deep, clone, copy ); - - // Don't bring in undefined values - } else if ( copy !== undefined ) { - target[ name ] = copy; - } - } - } - } - - // Return the modified object - return target; -}; - -iQ.extend({ - // ----------- - // Function: isFunction - // Returns true if the given object is a function. - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - - // ---------- - // Function: isArray - // Returns true if the given object is an array. - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - - // ---------- - // Function: isPlainObject - // Check to see if an object is a plain object (created using "{}" or "new Object"). - isPlainObject: function( obj ) { - // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. - // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { - return false; - } - - // Not own constructor property must be Object - if ( obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { - return false; - } - - // Own properties are enumerated firstly, so to speed up, - // if last one is own, then all properties are own. - - var key; - for ( key in obj ) {} - - return key === undefined || hasOwnProperty.call( obj, key ); - }, - - // ---------- - // Function: isEmptyObject - // Returns true if the given object has no members. - isEmptyObject: function( obj ) { - for ( var name in obj ) { - return false; - } - return true; - }, - - // ---------- - // Function: merge - // Merge two arrays and return the result. - merge: function( first, second ) { - var i = first.length, j = 0; - - if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } - } - - first.length = i; - - return first; - }, - - // ---------- - // Function: timeout - // wraps setTimeout with try/catch - timeout: function(func, delay) { - setTimeout(function() { - try { - func(); - } catch(e) { - Utils.log(e); - } - }, delay); - } -}); - // ---------- // Create various event aliases (function() { diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 0d060530f97e..95cd8ff6fe67 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -22,6 +22,13 @@ * Ian Gilman * Michael Yoshitaka Erlewine * + * Some portions copied from: + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * * 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"), @@ -46,6 +53,10 @@ const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; +// Save a reference to some core methods +const toString = Object.prototype.toString; +const hasOwnProperty = Object.prototype.hasOwnProperty; + Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -421,7 +432,7 @@ window.Subscribable.prototype = { addSubscriber: function(refObject, eventName, callback) { try { Utils.assertThrow("refObject", refObject); - Utils.assertThrow("callback must be a function", iQ.isFunction(callback)); + Utils.assertThrow("callback must be a function", Utils.isFunction(callback)); Utils.assertThrow("eventName must be a non-empty string", eventName && typeof(eventName) == "string"); @@ -482,7 +493,7 @@ window.Subscribable.prototype = { return; var self = this; - var subsCopy = iQ.merge([], this.subscribers[eventName]); + var subsCopy = Utils.merge([], this.subscribers[eventName]); subsCopy.forEach(function(object) { object.callback(self, eventInfo); }); @@ -524,16 +535,12 @@ var Utils = { // Pass as many arguments as you want, it'll print them all. trace: function() { var text = this.expandArgumentsForLog(arguments); - try { // coerce an error - throw new Error("error"); - } catch (e) { - // cut off the first two lines of the stack trace, because they're just this function. - var stack = e.stack.replace(/^.*?\n.*?\n/,''); - // if the caller was assert, cut out the line for the assert function as well. - if (this.trace.caller.name == 'Utils_assert') - stack = stack.replace(/^.*?\n/,''); - this.log('trace: ' + text + '\n' + stack); - } + // cut off the first two lines of the stack trace, because they're just this function. + var stack = Error().stack.replace(/^.*?\n.*?\n/,''); + // if the caller was assert, cut out the line for the assert function as well. + if (this.trace.caller.name == 'Utils_assert') + stack = stack.replace(/^.*?\n/,''); + this.log('trace: ' + text + '\n' + stack); }, // ---------- @@ -562,12 +569,8 @@ var Utils = { else text = 'tabcandy assert: ' + label; - try { // coerce an error - throw new Error("error"); - } catch (e) { - // cut off the first two lines of the stack trace, because they're just this function. - text += e.stack.replace(/^.*?\n.*?\n/,''); - } + // cut off the first two lines of the stack trace, because they're just this function. + text += Error().stack.replace(/^.*?\n.*?\n/,''); throw text; } @@ -646,6 +649,20 @@ var Utils = { return (typeof(n) == 'number' && !isNaN(n)); }, + // ----------- + // Function: isFunction + // Returns true if the given object is a function. + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + // ---------- + // Function: isArray + // Returns true if the given object is an array. + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + // ---------- // Function: isRect // Returns true if the given object (r) looks like a . @@ -673,20 +690,140 @@ var Utils = { return (p && this.isNumber(p.x) && this.isNumber(p.y)); }, + // ---------- + // Function: isPlainObject + // Check to see if an object is a plain object (created using "{}" or "new Object"). + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + return false; + } + + // Not own constructor property must be Object + if ( obj.constructor + && !hasOwnProperty.call(obj, "constructor") + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwnProperty.call( obj, key ); + }, + + // ---------- + // Function: isEmptyObject + // Returns true if the given object has no members. + isEmptyObject: function( obj ) { + for ( var name in obj ) + return false; + return true; + }, + // ---------- // Function: copy // Returns a copy of the argument. Note that this is a shallow copy; if the argument // has properties that are themselves objects, those properties will be copied by reference. copy: function(value) { if (value && typeof(value) == 'object') { - if (iQ.isArray(value)) - return iQ.extend([], value); + if (this.isArray(value)) + return this.extend([], value); + return this.extend({}, value); + } + return value; + }, + + // ---------- + // Function: merge + // Merge two arrays and return the result. + merge: function( first, second ) { + var i = first.length, j = 0; - return iQ.extend({}, value); + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } } - return value; + first.length = i; + + return first; + }, + + // ---------- + // Function: extend + // Pass several objects in and it will combine them all into the first object and return it. + extend: function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, options, name, src, copy; + + // Deep copy is not supported + if ( typeof target === "boolean" ) { + this.assert("The first argument of extend cannot be a boolean." + +"Deep copy is not supported.",false); + return target; + } + + // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it. + // This is no longer supported. + if ( length === 1 ) { + this.assert("Extending the iQ prototype using extend is not supported.",false); + return target; + } + + // Handle case when target is a string or something + if ( typeof target !== "object" && !this.isFunction(target) ) { + target = {}; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + if ( copy !== undefined ) + target[ name ] = copy; + } + } + } + + // Return the modified object + return target; + }, + + // ---------- + // Function: timeout + // wraps setTimeout with try/catch + // + // Note to our beloved reviewers: we are keeping this trivial wrapper in case we + // make Utils a JSM. + timeout: function(func, delay) { + setTimeout(function() { + try { + func(); + } catch(e) { + Utils.log(e); + } + }, delay); } + }; window.Utils = Utils; From dc99f6001012e8c00bc0a7e754896924c5095324 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Fri, 23 Jul 2010 19:29:32 -0700 Subject: [PATCH 174/369] Inline isFunction as typeof == function checks. --- browser/base/content/tabview/iq.js | 22 +++++++++---------- .../base/content/tabview/modules/utils.jsm | 11 ++-------- 2 files changed, 13 insertions(+), 20 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 915f62760955..9dd7d0a9065b 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -169,7 +169,7 @@ iQ.fn = iQ.prototype = { // HANDLE $(function) // Shortcut for document ready - } else if ( Utils.isFunction( selector ) ) { + } else if (typeof selector == "function") { Utils.log('iQ does not support ready functions'); return null; } @@ -185,7 +185,7 @@ iQ.fn = iQ.prototype = { // The window, strings (and functions) also have 'length' // The extra typeof function check is to prevent crashes // in Safari 2 (See: #3039) - if ( selector.length == null || typeof selector === "string" || Utils.isFunction(selector) || (typeof selector !== "function" && selector.setInterval) ) { + if (selector.length == null || typeof selector == "string" || typeof selector == "function" || (typeof selector != "function" && selector.setInterval)) { Array.prototype.push.call( ret, selector ); } else { Utils.merge( ret, selector ); @@ -223,7 +223,7 @@ iQ.fn = iQ.prototype = { // Function: each // Execute a callback for every element in the matched set. each: function( callback ) { - if ( !Utils.isFunction(callback) ) { + if (typeof callback != "function") { Utils.assert("each's argument must be a function", false); return null; } @@ -237,7 +237,7 @@ iQ.fn = iQ.prototype = { // Function: addClass // Adds the given class(es) to the receiver. addClass: function( value ) { - if ( Utils.isFunction(value) ) { + if (typeof value == "function") { Utils.assert('does not support function argument', false); return null; } @@ -260,7 +260,7 @@ iQ.fn = iQ.prototype = { // Function: removeClass // Removes the given class(es) from the receiver. removeClass: function( value ) { - if ( Utils.isFunction(value) ) { + if (typeof value == "function") { Utils.assert('does not support function argument', false); return null; } @@ -605,7 +605,7 @@ iQ.fn = iQ.prototype = { '-moz-transition-timing-function': '' }); - if (Utils.isFunction(options.complete)) + if (typeof options.complete == "function") options.complete.apply(self); }, duration); } catch(e) { @@ -620,14 +620,14 @@ iQ.fn = iQ.prototype = { // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { try { - Utils.assert('does not yet support duration', Utils.isFunction(callback) || callback === undefined); + Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined); this.animate({ opacity: 0 }, { duration: 400, complete: function() { iQ(this).css({display: 'none'}); - if (Utils.isFunction(callback)) + if (typeof callback == "function") callback.apply(this); } }); @@ -687,7 +687,7 @@ iQ.fn = iQ.prototype = { // Binds the given function to the given event type. Also wraps the function // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { - Utils.assert('does not support eventData argument', Utils.isFunction(func)); + Utils.assert('does not support eventData argument', typeof func == "function"); var handler = function(event) { try { @@ -720,7 +720,7 @@ iQ.fn = iQ.prototype = { // Binds the given function to the given event type, but only for one call; // automatically unbinds after the event fires once. one: function(type, func) { - Utils.assert('does not support eventData argument', Utils.isFunction(func)); + Utils.assert('does not support eventData argument', typeof func == "function"); var handler = function(e) { iQ(this).unbind(type, handler); @@ -734,7 +734,7 @@ iQ.fn = iQ.prototype = { // Function: unbind // Unbinds the given function from the given event type. unbind: function(type, func) { - Utils.assert('Must provide a function', Utils.isFunction(func)); + Utils.assert('Must provide a function', typeof func == "function"); for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { var handler = func; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 95cd8ff6fe67..8779072471e1 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -432,7 +432,7 @@ window.Subscribable.prototype = { addSubscriber: function(refObject, eventName, callback) { try { Utils.assertThrow("refObject", refObject); - Utils.assertThrow("callback must be a function", Utils.isFunction(callback)); + Utils.assertThrow("callback must be a function", typeof callback == "function"); Utils.assertThrow("eventName must be a non-empty string", eventName && typeof(eventName) == "string"); @@ -649,13 +649,6 @@ var Utils = { return (typeof(n) == 'number' && !isNaN(n)); }, - // ----------- - // Function: isFunction - // Returns true if the given object is a function. - isFunction: function( obj ) { - return toString.call(obj) === "[object Function]"; - }, - // ---------- // Function: isArray // Returns true if the given object is an array. @@ -782,7 +775,7 @@ var Utils = { } // Handle case when target is a string or something - if ( typeof target !== "object" && !this.isFunction(target) ) { + if (typeof target != "object" && typeof target != "function") { target = {}; } From b6e2f7c7f79a988647f6d3464bac6a43e9f684c1 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Mon, 26 Jul 2010 11:02:52 -0700 Subject: [PATCH 175/369] Switch to Array.isArray from Utils.isArray. --- browser/base/content/tabview/modules/utils.jsm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 8779072471e1..0fdbca892118 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -649,13 +649,6 @@ var Utils = { return (typeof(n) == 'number' && !isNaN(n)); }, - // ---------- - // Function: isArray - // Returns true if the given object is an array. - isArray: function( obj ) { - return toString.call(obj) === "[object Array]"; - }, - // ---------- // Function: isRect // Returns true if the given object (r) looks like a . @@ -725,7 +718,7 @@ var Utils = { // has properties that are themselves objects, those properties will be copied by reference. copy: function(value) { if (value && typeof(value) == 'object') { - if (this.isArray(value)) + if (Array.isArray(value)) return this.extend([], value); return this.extend({}, value); } From 0825b66deefe8aee8a08a112125521c738d00f51 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 26 Jul 2010 17:15:19 -0700 Subject: [PATCH 176/369] + We now do no work while the script is loading; everything waits until the UI.init() call at the very bottom --HG-- extra : rebase_source : 301788f78500bfe57512b0df396cb6be2ca8a02f --- .../base/content/tabview/modules/utils.jsm | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 0fdbca892118..0b96d49b355f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -48,15 +48,12 @@ (function(){ +// ######### const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cr = Components.results; -// Save a reference to some core methods -const toString = Object.prototype.toString; -const hasOwnProperty = Object.prototype.hasOwnProperty; - Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); @@ -80,9 +77,6 @@ XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() { return gWindow.document.getElementById("tab-candy"); }); -var consoleService = Cc["@mozilla.org/consoleservice;1"] - .getService(Components.interfaces.nsIConsoleService); - // ########## // Class: Point // A simple point. @@ -102,6 +96,7 @@ window.Point = function(a, y) { this.y = (Utils.isNumber(y) ? y : 0); } }; + window.Point.prototype = { // ---------- // Function: distance @@ -506,7 +501,9 @@ window.Subscribable.prototype = { // ########## // Class: Utils // Singelton with common utility functions. -var Utils = { +window.Utils = { + consoleService: null, + // ___ Logging // ---------- @@ -514,8 +511,13 @@ var Utils = { // Prints the given arguments to the JavaScript error console as a message. // Pass as many arguments as you want, it'll print them all. log: function() { + if (!this.consoleService) { + this.consoleService = Cc["@mozilla.org/consoleservice;1"] + .getService(Components.interfaces.nsIConsoleService); + } + var text = this.expandArgumentsForLog(arguments); - consoleService.logStringMessage(text); + this.consoleService.logStringMessage(text); }, // ---------- @@ -683,11 +685,14 @@ var Utils = { // Must be an Object. // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { + if ( !obj || Object.prototype.toString.call(obj) !== "[object Object]" + || obj.nodeType || obj.setInterval ) { return false; } // Not own constructor property must be Object + const hasOwnProperty = Object.prototype.hasOwnProperty; + if ( obj.constructor && !hasOwnProperty.call(obj, "constructor") && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { @@ -809,9 +814,6 @@ var Utils = { } }, delay); } - }; -window.Utils = Utils; - })(); From 50ce1d5cfbe78cf992855731cbc2667beec13628 Mon Sep 17 00:00:00 2001 From: Frank Yan Date: Tue, 27 Jul 2010 10:07:17 -0600 Subject: [PATCH 177/369] Bug 582200: rm cross-browser code from iQ, follow Mozilla style better, r=mitcho --- browser/base/content/tabview/iq.js | 4 +- .../base/content/tabview/modules/utils.jsm | 94 ++++++++----------- 2 files changed, 40 insertions(+), 58 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9dd7d0a9065b..ac9c207d8564 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -183,9 +183,7 @@ iQ.fn = iQ.prototype = { var ret = this || []; if ( selector != null ) { // The window, strings (and functions) also have 'length' - // The extra typeof function check is to prevent crashes - // in Safari 2 (See: #3039) - if (selector.length == null || typeof selector == "string" || typeof selector == "function" || (typeof selector != "function" && selector.setInterval)) { + if (selector.length == null || typeof selector == "string" || typeof selector == "function" || selector.setInterval) { Array.prototype.push.call( ret, selector ); } else { Utils.merge( ret, selector ); diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 0b96d49b355f..447ed44b8a9f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -515,7 +515,7 @@ window.Utils = { this.consoleService = Cc["@mozilla.org/consoleservice;1"] .getService(Components.interfaces.nsIConsoleService); } - + var text = this.expandArgumentsForLog(arguments); this.consoleService.logStringMessage(text); }, @@ -524,7 +524,6 @@ window.Utils = { // Function: error // Prints the given arguments to the JavaScript error console as an error. // Pass as many arguments as you want, it'll print them all. - // TODO: Does this still work? error: function() { var text = this.expandArgumentsForLog(arguments); Cu.reportError('tabcandy error: ' + text); @@ -550,7 +549,7 @@ window.Utils = { // Prints a stack trace along with label (as a console message) if condition is false. assert: function Utils_assert(label, condition) { if (!condition) { - var text; + let text; if (typeof(label) == 'undefined') text = 'badly formed assert'; else @@ -565,7 +564,7 @@ window.Utils = { // Throws label as an exception if condition is false. assertThrow: function(label, condition) { if (!condition) { - var text; + let text; if (typeof(label) == 'undefined') text = 'badly formed assert'; else @@ -582,46 +581,36 @@ window.Utils = { // Function: expandObject // Prints the given object to a string, including all of its properties. expandObject: function(obj) { - var s = obj + ' = {'; - for (prop in obj) { - var value; - try { - value = obj[prop]; - } catch(e) { - value = '[!!error retrieving property]'; - } - - s += prop + ': '; - if (typeof(value) == 'string') - s += '\'' + value + '\''; - else if (typeof(value) == 'function') - s += 'function'; - else - s += value; - - s += ", "; + var s = obj + ' = {'; + for (let prop in obj) { + let value; + try { + value = obj[prop]; + } catch(e) { + value = '[!!error retrieving property]'; } - return s + '}'; - }, + + s += prop + ': '; + if (typeof(value) == 'string') + s += '\'' + value + '\''; + else if (typeof(value) == 'function') + s += 'function'; + else + s += value; + + s += ', '; + } + return s + '}'; + }, // ---------- // Function: expandArgumentsForLog // Expands all of the given args (an array) into a single string. expandArgumentsForLog: function(args) { - var s = ''; - var count = args.length; - var a; - for (a = 0; a < count; a++) { - var arg = args[a]; - if (typeof(arg) == 'object') - arg = this.expandObject(arg); - - s += arg; - if (a < count - 1) - s += '; '; - } - - return s; + var that = this; + return Array.map(args, function(arg) { + return typeof(arg) == 'object' ? that.expandObject(arg) : arg; + }).join('; '); }, // ___ Misc @@ -630,11 +619,7 @@ window.Utils = { // Function: isRightClick // Given a DOM mouse event, returns true if it was for the right mouse button. isRightClick: function(event) { - if (event.which) - return (event.which == 3); - if (event.button) - return (event.button == 2); - return false; + return event.button == 2; }, // ---------- @@ -683,7 +668,6 @@ window.Utils = { // Check to see if an object is a plain object (created using "{}" or "new Object"). isPlainObject: function( obj ) { // Must be an Object. - // Because of IE, we also have to check the presence of the constructor property. // Make sure that DOM nodes and window objects don't pass through, as well if ( !obj || Object.prototype.toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) { @@ -692,7 +676,7 @@ window.Utils = { // Not own constructor property must be Object const hasOwnProperty = Object.prototype.hasOwnProperty; - + if ( obj.constructor && !hasOwnProperty.call(obj, "constructor") && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { @@ -707,7 +691,7 @@ window.Utils = { return key === undefined || hasOwnProperty.call( obj, key ); }, - + // ---------- // Function: isEmptyObject // Returns true if the given object has no members. @@ -729,7 +713,7 @@ window.Utils = { } return value; }, - + // ---------- // Function: merge // Merge two arrays and return the result. @@ -737,7 +721,7 @@ window.Utils = { var i = first.length, j = 0; if ( typeof second.length === "number" ) { - for ( var l = second.length; j < l; j++ ) { + for ( let l = second.length; j < l; j++ ) { first[ i++ ] = second[ j ]; } } else { @@ -750,14 +734,14 @@ window.Utils = { return first; }, - + // ---------- // Function: extend // Pass several objects in and it will combine them all into the first object and return it. extend: function() { // copy reference to target object var target = arguments[0] || {}, i = 1, length = arguments.length, options, name, src, copy; - + // Deep copy is not supported if ( typeof target === "boolean" ) { this.assert("The first argument of extend cannot be a boolean." @@ -776,7 +760,7 @@ window.Utils = { if (typeof target != "object" && typeof target != "function") { target = {}; } - + for ( ; i < length; i++ ) { // Only deal with non-null/undefined values if ( (options = arguments[ i ]) != null ) { @@ -784,21 +768,21 @@ window.Utils = { for ( name in options ) { src = target[ name ]; copy = options[ name ]; - + // Prevent never-ending loop if ( target === copy ) continue; - + if ( copy !== undefined ) target[ name ] = copy; } } } - + // Return the modified object return target; }, - + // ---------- // Function: timeout // wraps setTimeout with try/catch From 63327c73adec52cc9a14ffb419cf0a5a4496cbaf Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Wed, 28 Jul 2010 21:33:43 -0600 Subject: [PATCH 178/369] Bug 582023: iQ changes based on comments from gavin - let is the new var, no multiple declarations on the same line. - renamed iQ.fn.init to be iQClass, iQ.fn to be iQClass.prototype, to look much more normal. iQ now just creates a new iQClass. No more brain hurt. - no more (function(){...}) closure - Array.prototype.xxx.call replaced by Array.xxx - a couple other changes recommended by gavin. - Utils: rm some comments, but haven't touched JSM, isDOMElement, or merge. --- browser/base/content/tabview/iq.js | 482 +++++++++--------- .../base/content/tabview/modules/utils.jsm | 42 +- 2 files changed, 237 insertions(+), 287 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index ac9c207d8564..0ba9ea4bca8d 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -47,151 +47,141 @@ // Title: iq.js // Various helper functions, in the vein of jQuery. -(function( window, undefined ) { +// ---------- +// Function: iQ +// Returns an iQClass object which represents an individual element or a group +// of elements. It works pretty much like jQuery(), with a few exceptions, +// most notably that you can't use strings with complex html, +// just simple tags like '
'. +function iQ(selector, context) { + // The iQ object is actually just the init constructor 'enhanced' + return new iQClass( selector, context ); +}; -var iQ = function(selector, context) { - // The iQ object is actually just the init constructor 'enhanced' - return new iQ.fn.init( selector, context ); - }, +// A simple way to check for HTML strings or ID strings +// (both of which we optimize for) +let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/; - // Map over iQ in case of overwrite - _iQ = window.iQ, - - // Use the correct document accordingly with window argument (sandbox) - document = window.document, - - // A central reference to the root iQ(document) - rootiQ, - - // A simple way to check for HTML strings or ID strings - // (both of which we optimize for) - quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, - - rclass = /[\n\t]/g, - rspace = /\s+/; +// Match a standalone tag +let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/; // ########## -// Class: iQ.fn -// An individual element or group of elements. -iQ.fn = iQ.prototype = { - // ---------- - // Function: init - // You don't call this directly; this is what's called by iQ(). - // It works pretty much like jQuery(), with a few exceptions, - // most notably that you can't use strings with complex html, - // just simple tags like '
'. - init: function( selector, context ) { - var match, elem, ret, doc; +// Class: iQClass +// The actual class of iQ result objects, representing an individual element +// or a group of elements. +// +// ---------- +// Function: iQClass +// You don't call this directly; this is what's called by iQ(). +let iQClass = function(selector, context) { - // Handle $(""), $(null), or $(undefined) - if ( !selector ) { - return this; - } + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } - // Handle $(DOMElement) - if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - } + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = selector; + this[0] = selector; + this.length = 1; + return this; + } - // The body element only exists once, optimize finding it - if ( selector === "body" && !context ) { - this.context = document; - this[0] = document.body; - this.selector = "body"; - this.length = 1; - return this; - } + // The body element only exists once, optimize finding it + if ( selector === "body" && !context ) { + this.context = document; + this[0] = document.body; + this.selector = "body"; + this.length = 1; + return this; + } - // Handle HTML strings - if ( typeof selector === "string" ) { - // Are we dealing with HTML string or an ID? - match = quickExpr.exec( selector ); + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? - // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { + let match = quickExpr.exec( selector ); - // HANDLE $(html) -> $(array) - if ( match[1] ) { - doc = (context ? context.ownerDocument || context : document); + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { - // If a single string is passed in and it's a single tag - // just do a createElement and skip the rest - ret = rsingleTag.exec( selector ); + // HANDLE $(html) -> $(array) + if ( match[1] ) { + let doc = (context ? context.ownerDocument || context : document); - if ( ret ) { - if ( Utils.isPlainObject( context ) ) { - Utils.assert('does not support HTML creation with context', false); - } else { - selector = [ doc.createElement( ret[1] ) ]; - } + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + let ret = rsingleTag.exec( selector ); + if ( ret ) { + if ( Utils.isPlainObject( context ) ) { + Utils.assert('does not support HTML creation with context', false); } else { - Utils.assert('does not support complex HTML creation', false); + selector = [ doc.createElement( ret[1] ) ]; } - return Utils.merge( this, selector ); - - // HANDLE $("#id") } else { - elem = document.getElementById( match[2] ); - - if ( elem ) { - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; + Utils.assert('does not support complex HTML creation', false); } - // HANDLE $("TAG") - } else if ( !context && /^\w+$/.test( selector ) ) { - this.selector = selector; - this.context = document; - selector = document.getElementsByTagName( selector ); return Utils.merge( this, selector ); - // HANDLE $(expr, $(...)) - } else if ( !context || context.iq ) { - return (context || rootiQ).find( selector ); - - // HANDLE $(expr, context) - // (which is just equivalent to: $(context).find(expr) + // HANDLE $("#id") } else { - return iQ( context ).find( selector ); + let elem = document.getElementById( match[2] ); + + if ( elem ) { + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; } - // HANDLE $(function) - // Shortcut for document ready - } else if (typeof selector == "function") { - Utils.log('iQ does not support ready functions'); - return null; + // HANDLE $("TAG") + } else if ( !context && /^\w+$/.test( selector ) ) { + this.selector = selector; + this.context = document; + selector = document.getElementsByTagName( selector ); + return Utils.merge( this, selector ); + + // HANDLE $(expr, $(...)) + } else if ( !context || context.iq ) { + return (context || iQ(document)).find( selector ); + + // HANDLE $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return iQ( context ).find( selector ); } - if (selector.selector !== undefined) { - this.selector = selector.selector; - this.context = selector.context; - } + // HANDLE $(function) + // Shortcut for document ready + } else if (typeof selector == "function") { + Utils.log('iQ does not support ready functions'); + return null; + } - // this used to be makeArray: - var ret = this || []; - if ( selector != null ) { - // The window, strings (and functions) also have 'length' - if (selector.length == null || typeof selector == "string" || typeof selector == "function" || selector.setInterval) { - Array.prototype.push.call( ret, selector ); - } else { - Utils.merge( ret, selector ); - } - } - return ret; + if (selector.selector !== undefined) { + this.selector = selector.selector; + this.context = selector.context; + } - }, + let ret = this || []; + if ( selector != null ) { + // The window, strings (and functions) also have 'length' + if (selector.length == null || typeof selector == "string" || selector.setInterval) { + Array.push( ret, selector ); + } else { + Utils.merge( ret, selector ); + } + } + return ret; +} +iQClass.prototype = { // Start with an empty selector selector: "", @@ -207,14 +197,12 @@ iQ.fn = iQ.prototype = { // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num == null ? + if (num == null) // Return a 'clean' array + return Array.slice( this, 0 ); - // Return a 'clean' array - // was toArray - Array.prototype.slice.call( this, 0 ) : - - // Return just the object - ( num < 0 ? this[ num + this.length ] : this[ num ] ); + // Return just the Nth object + let index = num < 0 ? num + this.length : num; + return this[index]; }, // ---------- @@ -225,8 +213,8 @@ iQ.fn = iQ.prototype = { Utils.assert("each's argument must be a function", false); return null; } - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - callback(elem); + for ( let i = 0; this[i] != null; i++ ) { + callback(this[i]); } return this; }, @@ -235,19 +223,18 @@ iQ.fn = iQ.prototype = { // Function: addClass // Adds the given class(es) to the receiver. addClass: function( value ) { - if (typeof value == "function") { - Utils.assert('does not support function argument', false); + if ( typeof value != "string" || !value ) { + Utils.assert('requires a valid string argument', false); return null; } - if ( value && typeof value === "string" ) { - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - if ( elem.nodeType === 1 ) { - (value || "").split( rspace ).forEach(function(className) { - elem.classList.add(className); - }); - } + let length = this.length; + for ( let i = 0; i < length; i++ ) { + let elem = this[i]; + if ( elem.nodeType === 1 ) { + value.split( /\s+/ ).forEach(function(className) { + elem.classList.add(className); + }); } } @@ -258,23 +245,18 @@ iQ.fn = iQ.prototype = { // Function: removeClass // Removes the given class(es) from the receiver. removeClass: function( value ) { - if (typeof value == "function") { + if ( typeof value != "string" || !value ) { Utils.assert('does not support function argument', false); return null; } - if ( (value && typeof value === "string") || value === undefined ) { - for ( var i = 0, l = this.length; i < l; i++ ) { - var elem = this[i]; - if ( elem.nodeType === 1 && elem.className ) { - if ( value ) { - (value || "").split(rspace).forEach(function(className) { - elem.classList.remove(className); - }); - } else { - elem.className = ""; - } - } + let length = this.length; + for ( let i = 0; i < length; i++ ) { + let elem = this[i]; + if ( elem.nodeType === 1 && elem.className ) { + value.split( /\s+/ ).forEach(function(className) { + elem.classList.remove(className); + }); } } @@ -284,9 +266,10 @@ iQ.fn = iQ.prototype = { // ---------- // Function: hasClass // Returns true is the receiver has the given css class. - hasClass: function( selector ) { - for ( var i = 0, l = this.length; i < l; i++ ) { - if ( this[i].classList.contains( selector ) ) { + hasClass: function( singleClassName ) { + let length = this.length; + for ( let i = 0; i < length; i++ ) { + if ( this[i].classList.contains( singleClassName ) ) { return true; } } @@ -298,9 +281,11 @@ iQ.fn = iQ.prototype = { // Searches the receiver and its children, returning a new iQ object with // elements that match the given selector. find: function( selector ) { - var ret = [], length = 0; + let ret = []; + let length = 0; - for ( var i = 0, l = this.length; i < l; i++ ) { + let l = this.length; + for ( let i = 0; i < l; i++ ) { length = ret.length; try { Utils.merge(ret, this[i].querySelectorAll( selector ) ); @@ -310,8 +295,8 @@ iQ.fn = iQ.prototype = { if ( i > 0 ) { // Make sure that the results are unique - for ( var n = length; n < ret.length; n++ ) { - for ( var r = 0; r < length; r++ ) { + for ( let n = length; n < ret.length; n++ ) { + for ( let r = 0; r < length; r++ ) { if ( ret[r] === ret[n] ) { ret.splice(n--, 1); break; @@ -329,12 +314,12 @@ iQ.fn = iQ.prototype = { // Removes the receiver from the DOM. remove: function(unused) { Utils.assert('does not accept a selector', unused === undefined); - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); + elem.parentNode.removeChild( elem ); } } - return this; }, @@ -342,12 +327,12 @@ iQ.fn = iQ.prototype = { // Function: empty // Removes all of the reciever's children and HTML content from the DOM. empty: function() { - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; while ( elem.firstChild ) { elem.removeChild( elem.firstChild ); } } - return this; }, @@ -369,7 +354,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: position - // Returns an object with the receiver's position in left and top properties. + // Returns an object with the receiver's position in left and top + // properties. position: function(unused) { Utils.assert('does not yet support setting', unused === undefined); return { @@ -381,9 +367,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: bounds // Returns a with the receiver's bounds. - bounds: function(unused) { - Utils.assert('does not yet support setting', unused === undefined); - var p = this.position(); + bounds: function() { + let p = this.position(); return new Rect(p.left, p.top, this.width(), this.height()); }, @@ -392,14 +377,18 @@ iQ.fn = iQ.prototype = { // Pass in both key and value to attach some data to the receiver; // pass in just key to retrieve it. data: function(key, value) { - var data = null; + let data = null; if (value === undefined) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); data = this[0].iQData; - return (data ? data[key] : null); + if (data) + return data[key]; + else + return null; } - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; data = elem.iQData; if (!data) @@ -413,8 +402,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: html - // Given a value, sets the receiver's innerHTML to it; otherwise returns what's already there. - // TODO: security + // Given a value, sets the receiver's innerHTML to it; otherwise returns + // what's already there. html: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) @@ -426,7 +415,8 @@ iQ.fn = iQ.prototype = { // ---------- // Function: text - // Given a value, sets the receiver's textContent to it; otherwise returns what's already there. + // Given a value, sets the receiver's textContent to it; otherwise returns + // what's already there. text: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); if (value === undefined) { @@ -462,9 +452,8 @@ iQ.fn = iQ.prototype = { // Function: append // Appends the result of iQ(selector) to the receiver. append: function(selector) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - var object = iQ(selector); - Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1); + let object = iQ(selector); + Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1 && this.length == 1); this[0].appendChild(object[0]); return this; }, @@ -479,8 +468,8 @@ iQ.fn = iQ.prototype = { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); } - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - elem.setAttribute(key, value); + for ( let i = 0; this[i] != null; i++ ) { + this[i].setAttribute(key, value); } } catch(e) { Utils.log(e); @@ -499,14 +488,14 @@ iQ.fn = iQ.prototype = { // a: string, b: undefined - gets property specified by a // a: string, b: string/number - sets property specified by a to b css: function(a, b) { - var properties = null; + let properties = null; if (typeof a === 'string') { - var key = a; + let key = a; if (b === undefined) { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); - var substitutions = { + let substitutions = { 'MozTransform': '-moz-transform', 'zIndex': 'z-index' }; @@ -519,7 +508,7 @@ iQ.fn = iQ.prototype = { properties = a; } - var pixels = { + let pixels = { 'left': true, 'top': true, 'right': true, @@ -528,9 +517,10 @@ iQ.fn = iQ.prototype = { 'height': true }; - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - for (var key in properties) { - var value = properties[key]; + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; + for (let key in properties) { + let value = properties[key]; if (pixels[key] && typeof(value) != 'string') value += 'px'; @@ -554,10 +544,10 @@ iQ.fn = iQ.prototype = { // // Possible "options" properties: // duration - how long to animate, in milliseconds - // easing - easing function to use. Possibilities include 'tabcandyBounce', 'easeInQuad'. - // Default is 'ease'. - // complete - function to call once the animation is done, takes nothing in, but "this" - // is set to the element that was animated. + // easing - easing function to use. Possibilities include + // 'tabcandyBounce', 'easeInQuad'. Default is 'ease'. + // complete - function to call once the animation is done, takes nothing + // in, but "this" is set to the element that was animated. animate: function(css, options) { try { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); @@ -565,28 +555,27 @@ iQ.fn = iQ.prototype = { if (!options) options = {}; - var easings = { + let easings = { tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care fast: 'cubic-bezier(0.7,0,1,1)' }; - var duration = (options.duration || 400); - var easing = (easings[options.easing] || 'ease'); + let duration = (options.duration || 400); + let easing = (easings[options.easing] || 'ease'); - // The latest versions of Firefox do not animate from a non-explicitly set - // css properties. So for each element to be animated, go through and - // explicitly define 'em. - var rupper = /([A-Z])/g; + // The latest versions of Firefox do not animate from a non-explicitly + // set css properties. So for each element to be animated, go through + // and explicitly define 'em. + let rupper = /([A-Z])/g; this.each(function(elem){ - var cStyle = window.getComputedStyle(elem, null); - for (var prop in css){ + let cStyle = window.getComputedStyle(elem, null); + for (let prop in css){ prop = prop.replace( rupper, "-$1" ).toLowerCase(); iQ(elem).css(prop, cStyle.getPropertyValue(prop)); } }); - this.css({ '-moz-transition-property': 'all', // TODO: just animate the properties we're changing '-moz-transition-duration': (duration / 1000) + 's', @@ -595,7 +584,7 @@ iQ.fn = iQ.prototype = { this.css(css); - var self = this; + let self = this; Utils.timeout(function() { self.css({ '-moz-transition-property': 'none', @@ -617,21 +606,17 @@ iQ.fn = iQ.prototype = { // Function: fadeOut // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { - try { - Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined); - this.animate({ - opacity: 0 - }, { - duration: 400, - complete: function() { - iQ(this).css({display: 'none'}); - if (typeof callback == "function") - callback.apply(this); - } - }); - } catch(e) { - Utils.log(e); - } + Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined); + this.animate({ + opacity: 0 + }, { + duration: 400, + complete: function() { + iQ(this).css({display: 'none'}); + if (typeof callback == "function") + callback.apply(this); + } + }); return this; }, @@ -687,7 +672,7 @@ iQ.fn = iQ.prototype = { bind: function(type, func) { Utils.assert('does not support eventData argument', typeof func == "function"); - var handler = function(event) { + let handler = function(event) { try { return func.apply(this, [event]); } catch(e) { @@ -695,7 +680,8 @@ iQ.fn = iQ.prototype = { } }; - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; if (!elem.iQEventData) elem.iQEventData = {}; @@ -720,7 +706,7 @@ iQ.fn = iQ.prototype = { one: function(type, func) { Utils.assert('does not support eventData argument', typeof func == "function"); - var handler = function(e) { + let handler = function(e) { iQ(this).unbind(type, handler); return func.apply(this, [e]); }; @@ -734,11 +720,13 @@ iQ.fn = iQ.prototype = { unbind: function(type, func) { Utils.assert('Must provide a function', typeof func == "function"); - for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { - var handler = func; + for ( let i = 0; this[i] != null; i++ ) { + let elem = this[i]; + let handler = func; if (elem.iQEventData && elem.iQEventData[type]) { - for (var a = 0, count = elem.iQEventData[type].length; a < count; a++) { - var pair = elem.iQEventData[type][a]; + let count = elem.iQEventData[type].length; + for (let a = 0; a < count; a++) { + let pair = elem.iQEventData[type][a]; if (pair.original == func) { handler = pair.modified; elem.iQEventData[type].splice(a, 1); @@ -754,41 +742,25 @@ iQ.fn = iQ.prototype = { } }; -// ---------- -// Give the init function the iQ prototype for later instantiation -iQ.fn.init.prototype = iQ.fn; - // ---------- // Create various event aliases -(function() { - var events = [ - 'keyup', - 'keydown', - 'mouseup', - 'mousedown', - 'mouseover', - 'mouseout', - 'mousemove', - 'click', - 'resize', - 'change', - 'blur', - 'focus' - ]; +let events = [ + 'keyup', + 'keydown', + 'mouseup', + 'mousedown', + 'mouseover', + 'mouseout', + 'mousemove', + 'click', + 'resize', + 'change', + 'blur', + 'focus' +]; - events.forEach(function(event) { - iQ.fn[event] = function(func) { - return this.bind(event, func); - }; - }); -})(); - -// ---------- -// All iQ objects should point back to these -rootiQ = iQ(document); - -// ---------- -// Expose iQ to the global object -window.iQ = iQ; - -})(window); +events.forEach(function(event) { + iQClass.prototype[event] = function(func) { + return this.bind(event, func); + }; +}); diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 447ed44b8a9f..deff97c6638c 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -87,9 +87,7 @@ XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() { // 0 is used in their place. window.Point = function(a, y) { if (Utils.isPoint(a)) { - // Variable: x this.x = a.x; - // Variable: y this.y = a.y; } else { this.x = (Utils.isNumber(a) ? a : 0); @@ -120,16 +118,9 @@ window.Point.prototype = { window.Rect = function(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' if (Utils.isRect(a)) { - // Variable: left this.left = a.left; - - // Variable: top this.top = a.top; - - // Variable: width this.width = a.width; - - // Variable: height this.height = a.height; } else { this.left = a; @@ -140,24 +131,13 @@ window.Rect = function(a, top, width, height) { }; window.Rect.prototype = { - // ---------- - // Variable: right - get right() { - return this.left + this.width; - }, - // ---------- + get right() this.left + this.width, set right(value) { this.width = value - this.left; }, - // ---------- - // Variable: bottom - get bottom() { - return this.top + this.height; - }, - - // ---------- + get bottom() this.top + this.height, set bottom(value) { this.height = value - this.top; }, @@ -165,16 +145,12 @@ window.Rect.prototype = { // ---------- // Variable: xRange // Gives you a new for the horizontal dimension. - get xRange() { - return new Range(this.left,this.right); - }, + get xRange() new Range(this.left,this.right), // ---------- // Variable: yRange // Gives you a new for the vertical dimension. - get yRange() { - return new Range(this.top,this.bottom); - }, + get yRange() new Range(this.top,this.bottom), // ---------- // Function: intersects @@ -312,9 +288,11 @@ window.Rect.prototype = { // ---------- // Function: css - // Returns an object with the dimensions of this rectangle, suitable for passing - // into iQ.fn.css. - // You could of course just pass the rectangle straight in, but this is cleaner. + // Returns an object with the dimensions of this rectangle, suitable for + // passing into iQ's css method. You could of course just pass the rectangle + // straight in, but this is cleaner, as it removes all the extraneous + // properties. If you give a to without this, it will + // ignore the extraneous properties, but result in CSS warnings. css: function() { return { left: this.left, @@ -488,7 +466,7 @@ window.Subscribable.prototype = { return; var self = this; - var subsCopy = Utils.merge([], this.subscribers[eventName]); + var subsCopy = this.subscribers[eventName].concat(); subsCopy.forEach(function(object) { object.callback(self, eventInfo); }); From ee7d279fa21ffc7d555639e3e4715a527a1b8463 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Wed, 28 Jul 2010 21:40:55 -0700 Subject: [PATCH 179/369] Clean up spaces around (), [], , in iq and utils for bug 577968. --- browser/base/content/tabview/iq.js | 122 +++++++++--------- .../base/content/tabview/modules/utils.jsm | 70 +++++----- 2 files changed, 96 insertions(+), 96 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 0ba9ea4bca8d..a0e26f1cc402 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -55,7 +55,7 @@ // just simple tags like '
'. function iQ(selector, context) { // The iQ object is actually just the init constructor 'enhanced' - return new iQClass( selector, context ); + return new iQClass(selector, context); }; // A simple way to check for HTML strings or ID strings @@ -76,12 +76,12 @@ let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/; let iQClass = function(selector, context) { // Handle $(""), $(null), or $(undefined) - if ( !selector ) { + if (!selector) { return this; } // Handle $(DOMElement) - if ( selector.nodeType ) { + if (selector.nodeType) { this.context = selector; this[0] = selector; this.length = 1; @@ -89,7 +89,7 @@ let iQClass = function(selector, context) { } // The body element only exists once, optimize finding it - if ( selector === "body" && !context ) { + if (selector === "body" && !context) { this.context = document; this[0] = document.body; this.selector = "body"; @@ -98,40 +98,40 @@ let iQClass = function(selector, context) { } // Handle HTML strings - if ( typeof selector === "string" ) { + if (typeof selector === "string") { // Are we dealing with HTML string or an ID? - let match = quickExpr.exec( selector ); + let match = quickExpr.exec(selector); // Verify a match, and that no context was specified for #id - if ( match && (match[1] || !context) ) { + if (match && (match[1] || !context)) { // HANDLE $(html) -> $(array) - if ( match[1] ) { + if (match[1]) { let doc = (context ? context.ownerDocument || context : document); // If a single string is passed in and it's a single tag // just do a createElement and skip the rest - let ret = rsingleTag.exec( selector ); + let ret = rsingleTag.exec(selector); - if ( ret ) { - if ( Utils.isPlainObject( context ) ) { + if (ret) { + if (Utils.isPlainObject(context)) { Utils.assert('does not support HTML creation with context', false); } else { - selector = [ doc.createElement( ret[1] ) ]; + selector = [doc.createElement(ret[1])]; } } else { Utils.assert('does not support complex HTML creation', false); } - return Utils.merge( this, selector ); + return Utils.merge(this, selector); // HANDLE $("#id") } else { - let elem = document.getElementById( match[2] ); + let elem = document.getElementById(match[2]); - if ( elem ) { + if (elem) { this.length = 1; this[0] = elem; } @@ -142,20 +142,20 @@ let iQClass = function(selector, context) { } // HANDLE $("TAG") - } else if ( !context && /^\w+$/.test( selector ) ) { + } else if (!context && /^\w+$/.test(selector)) { this.selector = selector; this.context = document; - selector = document.getElementsByTagName( selector ); - return Utils.merge( this, selector ); + selector = document.getElementsByTagName(selector); + return Utils.merge(this, selector); // HANDLE $(expr, $(...)) - } else if ( !context || context.iq ) { - return (context || iQ(document)).find( selector ); + } else if (!context || context.iq) { + return (context || iQ(document)).find(selector); // HANDLE $(expr, context) // (which is just equivalent to: $(context).find(expr) } else { - return iQ( context ).find( selector ); + return iQ(context).find(selector); } // HANDLE $(function) @@ -171,12 +171,12 @@ let iQClass = function(selector, context) { } let ret = this || []; - if ( selector != null ) { + if (selector != null) { // The window, strings (and functions) also have 'length' if (selector.length == null || typeof selector == "string" || selector.setInterval) { - Array.push( ret, selector ); + Array.push(ret, selector); } else { - Utils.merge( ret, selector ); + Utils.merge(ret, selector); } } return ret; @@ -196,9 +196,9 @@ iQClass.prototype = { // Function: get // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array - get: function( num ) { + get: function(num) { if (num == null) // Return a 'clean' array - return Array.slice( this, 0 ); + return Array.slice(this, 0); // Return just the Nth object let index = num < 0 ? num + this.length : num; @@ -208,12 +208,12 @@ iQClass.prototype = { // ---------- // Function: each // Execute a callback for every element in the matched set. - each: function( callback ) { + each: function(callback) { if (typeof callback != "function") { Utils.assert("each's argument must be a function", false); return null; } - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { callback(this[i]); } return this; @@ -222,17 +222,17 @@ iQClass.prototype = { // ---------- // Function: addClass // Adds the given class(es) to the receiver. - addClass: function( value ) { - if ( typeof value != "string" || !value ) { + addClass: function(value) { + if (typeof value != "string" || !value) { Utils.assert('requires a valid string argument', false); return null; } let length = this.length; - for ( let i = 0; i < length; i++ ) { + for (let i = 0; i < length; i++) { let elem = this[i]; - if ( elem.nodeType === 1 ) { - value.split( /\s+/ ).forEach(function(className) { + if (elem.nodeType === 1) { + value.split(/\s+/).forEach(function(className) { elem.classList.add(className); }); } @@ -244,17 +244,17 @@ iQClass.prototype = { // ---------- // Function: removeClass // Removes the given class(es) from the receiver. - removeClass: function( value ) { - if ( typeof value != "string" || !value ) { + removeClass: function(value) { + if (typeof value != "string" || !value) { Utils.assert('does not support function argument', false); return null; } let length = this.length; - for ( let i = 0; i < length; i++ ) { + for (let i = 0; i < length; i++) { let elem = this[i]; - if ( elem.nodeType === 1 && elem.className ) { - value.split( /\s+/ ).forEach(function(className) { + if (elem.nodeType === 1 && elem.className) { + value.split(/\s+/).forEach(function(className) { elem.classList.remove(className); }); } @@ -266,10 +266,10 @@ iQClass.prototype = { // ---------- // Function: hasClass // Returns true is the receiver has the given css class. - hasClass: function( singleClassName ) { + hasClass: function(singleClassName) { let length = this.length; - for ( let i = 0; i < length; i++ ) { - if ( this[i].classList.contains( singleClassName ) ) { + for (let i = 0; i < length; i++) { + if (this[i].classList.contains(singleClassName)) { return true; } } @@ -280,24 +280,24 @@ iQClass.prototype = { // Function: find // Searches the receiver and its children, returning a new iQ object with // elements that match the given selector. - find: function( selector ) { + find: function(selector) { let ret = []; let length = 0; let l = this.length; - for ( let i = 0; i < l; i++ ) { + for (let i = 0; i < l; i++) { length = ret.length; try { - Utils.merge(ret, this[i].querySelectorAll( selector ) ); + Utils.merge(ret, this[i].querySelectorAll(selector)); } catch(e) { Utils.log('iQ.find error (bad selector)', e); } - if ( i > 0 ) { + if (i > 0) { // Make sure that the results are unique - for ( let n = length; n < ret.length; n++ ) { - for ( let r = 0; r < length; r++ ) { - if ( ret[r] === ret[n] ) { + for (let n = length; n < ret.length; n++) { + for (let r = 0; r < length; r++) { + if (ret[r] === ret[n]) { ret.splice(n--, 1); break; } @@ -314,10 +314,10 @@ iQClass.prototype = { // Removes the receiver from the DOM. remove: function(unused) { Utils.assert('does not accept a selector', unused === undefined); - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; - if ( elem.parentNode ) { - elem.parentNode.removeChild( elem ); + if (elem.parentNode) { + elem.parentNode.removeChild(elem); } } return this; @@ -327,10 +327,10 @@ iQClass.prototype = { // Function: empty // Removes all of the reciever's children and HTML content from the DOM. empty: function() { - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; - while ( elem.firstChild ) { - elem.removeChild( elem.firstChild ); + while (elem.firstChild) { + elem.removeChild(elem.firstChild); } } return this; @@ -387,7 +387,7 @@ iQClass.prototype = { return null; } - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; data = elem.iQData; @@ -423,7 +423,7 @@ iQClass.prototype = { return this[0].textContent; } - return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value)); + return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value)); }, // ---------- @@ -468,7 +468,7 @@ iQClass.prototype = { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); } - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { this[i].setAttribute(key, value); } } catch(e) { @@ -517,7 +517,7 @@ iQClass.prototype = { 'height': true }; - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; for (let key in properties) { let value = properties[key]; @@ -571,7 +571,7 @@ iQClass.prototype = { this.each(function(elem){ let cStyle = window.getComputedStyle(elem, null); for (let prop in css){ - prop = prop.replace( rupper, "-$1" ).toLowerCase(); + prop = prop.replace(rupper, "-$1").toLowerCase(); iQ(elem).css(prop, cStyle.getPropertyValue(prop)); } }); @@ -680,7 +680,7 @@ iQClass.prototype = { } }; - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; if (!elem.iQEventData) elem.iQEventData = {}; @@ -720,7 +720,7 @@ iQClass.prototype = { unbind: function(type, func) { Utils.assert('Must provide a function', typeof func == "function"); - for ( let i = 0; this[i] != null; i++ ) { + for (let i = 0; this[i] != null; i++) { let elem = this[i]; let handler = func; if (elem.iQEventData && elem.iQEventData[type]) { diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index deff97c6638c..3fd88b375701 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -145,12 +145,12 @@ window.Rect.prototype = { // ---------- // Variable: xRange // Gives you a new for the horizontal dimension. - get xRange() new Range(this.left,this.right), + get xRange() new Range(this.left, this.right), // ---------- // Variable: yRange // Gives you a new for the vertical dimension. - get yRange() new Range(this.top,this.bottom), + get yRange() new Range(this.top, this.bottom), // ---------- // Function: intersects @@ -184,10 +184,10 @@ window.Rect.prototype = { // Paramaters // - A contains: function(rect){ - return( rect.left > this.left + return(rect.left > this.left && rect.right < this.right && rect.top > this.top - && rect.bottom < this.bottom ) + && rect.bottom < this.bottom) }, // ---------- @@ -340,7 +340,7 @@ window.Range.prototype = { return Utils.isNumber(value) ? value >= this.min && value <= this.max : Utils.isRange(value) ? - ( value.min <= this.max && this.min <= value.max ) : + (value.min <= this.max && this.min <= value.max) : false; }, @@ -515,10 +515,10 @@ window.Utils = { trace: function() { var text = this.expandArgumentsForLog(arguments); // cut off the first two lines of the stack trace, because they're just this function. - var stack = Error().stack.replace(/^.*?\n.*?\n/,''); + let stack = Error().stack.replace(/^.*?\n.*?\n/, ""); // if the caller was assert, cut out the line for the assert function as well. if (this.trace.caller.name == 'Utils_assert') - stack = stack.replace(/^.*?\n/,''); + stack = stack.replace(/^.*?\n/, ""); this.log('trace: ' + text + '\n' + stack); }, @@ -549,7 +549,7 @@ window.Utils = { text = 'tabcandy assert: ' + label; // cut off the first two lines of the stack trace, because they're just this function. - text += Error().stack.replace(/^.*?\n.*?\n/,''); + text += Error().stack.replace(/^.*?\n.*?\n/, ""); throw text; } @@ -644,20 +644,20 @@ window.Utils = { // ---------- // Function: isPlainObject // Check to see if an object is a plain object (created using "{}" or "new Object"). - isPlainObject: function( obj ) { + isPlainObject: function(obj) { // Must be an Object. // Make sure that DOM nodes and window objects don't pass through, as well - if ( !obj || Object.prototype.toString.call(obj) !== "[object Object]" - || obj.nodeType || obj.setInterval ) { + if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" + || obj.nodeType || obj.setInterval) { return false; } // Not own constructor property must be Object const hasOwnProperty = Object.prototype.hasOwnProperty; - if ( obj.constructor + if (obj.constructor && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) { + && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) { return false; } @@ -665,16 +665,16 @@ window.Utils = { // if last one is own, then all properties are own. var key; - for ( key in obj ) {} + for (key in obj) {} - return key === undefined || hasOwnProperty.call( obj, key ); + return key === undefined || hasOwnProperty.call(obj, key); }, // ---------- // Function: isEmptyObject // Returns true if the given object has no members. - isEmptyObject: function( obj ) { - for ( var name in obj ) + isEmptyObject: function(obj) { + for (let name in obj) return false; return true; }, @@ -695,16 +695,16 @@ window.Utils = { // ---------- // Function: merge // Merge two arrays and return the result. - merge: function( first, second ) { + merge: function(first, second) { var i = first.length, j = 0; - if ( typeof second.length === "number" ) { - for ( let l = second.length; j < l; j++ ) { - first[ i++ ] = second[ j ]; + if (typeof second.length === "number") { + for (let l = second.length; j < l; j++) { + first[i++] = second[j]; } } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; + while (second[j] !== undefined) { + first[i++] = second[j++]; } } @@ -721,16 +721,16 @@ window.Utils = { var target = arguments[0] || {}, i = 1, length = arguments.length, options, name, src, copy; // Deep copy is not supported - if ( typeof target === "boolean" ) { + if (typeof target === "boolean") { this.assert("The first argument of extend cannot be a boolean." - +"Deep copy is not supported.",false); + +"Deep copy is not supported.", false); return target; } // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it. // This is no longer supported. - if ( length === 1 ) { - this.assert("Extending the iQ prototype using extend is not supported.",false); + if (length === 1) { + this.assert("Extending the iQ prototype using extend is not supported.", false); return target; } @@ -739,20 +739,20 @@ window.Utils = { target = {}; } - for ( ; i < length; i++ ) { + for (; i < length; i++) { // Only deal with non-null/undefined values - if ( (options = arguments[ i ]) != null ) { + if ((options = arguments[i]) != null) { // Extend the base object - for ( name in options ) { - src = target[ name ]; - copy = options[ name ]; + for (name in options) { + src = target[name]; + copy = options[name]; // Prevent never-ending loop - if ( target === copy ) + if (target === copy) continue; - if ( copy !== undefined ) - target[ name ] = copy; + if (copy !== undefined) + target[name] = copy; } } } From 36a978e234bf750c44cde4d680ff11695dc47a90 Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Wed, 28 Jul 2010 21:56:31 -0700 Subject: [PATCH 180/369] Move lazy gWindow, gBrowser, etc into tabcandy.js out of utils.js for bug 582023. --- .../base/content/tabview/modules/utils.jsm | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 3fd88b375701..b2a0d9651635 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -55,27 +55,6 @@ const Cu = Components.utils; const Cr = Components.results; Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyGetter(this, "gWindow", function() { - let windows = Services.wm.getEnumerator("navigator:browser"); - while (windows.hasMoreElements()) { - let browser = windows.getNext(); - let tabCandyFrame = browser.document.getElementById("tab-candy"); - if (tabCandyFrame.contentWindow == window) - return browser; - } -}); - -XPCOMUtils.defineLazyGetter(this, "gBrowser", function() gWindow.gBrowser); - -XPCOMUtils.defineLazyGetter(this, "gTabViewDeck", function() { - return gWindow.document.getElementById("tab-candy-deck"); -}); - -XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() { - return gWindow.document.getElementById("tab-candy"); -}); // ########## // Class: Point From d876852546e9520f69554471230935d813185b1d Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Wed, 28 Jul 2010 23:02:26 -0600 Subject: [PATCH 181/369] Bug 582023: rm iQ.get, as we were only using it in one way. --- browser/base/content/tabview/iq.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index a0e26f1cc402..0473c87d1ea5 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -192,19 +192,6 @@ iQClass.prototype = { // The default length of a iQ object is 0 length: 0, - // ---------- - // Function: get - // Get the Nth element in the matched element set OR - // Get the whole matched element set as a clean array - get: function(num) { - if (num == null) // Return a 'clean' array - return Array.slice(this, 0); - - // Return just the Nth object - let index = num < 0 ? num + this.length : num; - return this[index]; - }, - // ---------- // Function: each // Execute a callback for every element in the matched set. From 411897722b0dab1d238606f412486e753736612e Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Wed, 28 Jul 2010 22:21:34 -0700 Subject: [PATCH 182/369] Move utils.js into a javascript module that exports Point, Rect, Range, Subscribable, and Utils for bug 582023. --HG-- extra : rebase_source : 8fea0b5439bcc45464f029609ff125e284b74731 --- .../base/content/tabview/modules/utils.jsm | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index b2a0d9651635..f74560e9a328 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -28,7 +28,7 @@ * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license - * + * * 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"), @@ -46,7 +46,7 @@ // ********** // Title: utils.js -(function(){ +let EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils"]; // ######### const Cc = Components.classes; @@ -64,7 +64,7 @@ Cu.import("resource://gre/modules/Services.jsm"); // If a is a Point, creates a copy of it. Otherwise, expects a to be x, // and creates a Point with it along with y. If either a or y are omitted, // 0 is used in their place. -window.Point = function(a, y) { +function Point(a, y) { if (Utils.isPoint(a)) { this.x = a.x; this.y = a.y; @@ -74,7 +74,7 @@ window.Point = function(a, y) { } }; -window.Point.prototype = { +Point.prototype = { // ---------- // Function: distance // Returns the distance from this point to the given . @@ -94,7 +94,7 @@ window.Point.prototype = { // Constructor: Rect // If a is a Rect, creates a copy of it. Otherwise, expects a to be left, // and creates a Rect with it along with top, width, and height. -window.Rect = function(a, top, width, height) { +function Rect(a, top, width, height) { // Note: perhaps 'a' should really be called 'rectOrLeft' if (Utils.isRect(a)) { this.left = a.left; @@ -109,7 +109,7 @@ window.Rect = function(a, top, width, height) { } }; -window.Rect.prototype = { +Rect.prototype = { get right() this.left + this.width, set right(value) { @@ -268,7 +268,7 @@ window.Rect.prototype = { // ---------- // Function: css // Returns an object with the dimensions of this rectangle, suitable for - // passing into iQ's css method. You could of course just pass the rectangle + // passing into iQ's css method. You could of course just pass the rectangle // straight in, but this is cleaner, as it removes all the extraneous // properties. If you give a to without this, it will // ignore the extraneous properties, but result in CSS warnings. @@ -288,7 +288,7 @@ window.Rect.prototype = { // // Constructor: Range // Creates a Range with the given min and max -window.Range = function(min, max) { +function Range(min, max) { if (Utils.isRange(min) && !max) { // if the one variable given is a range, copy it. this.min = min.min; this.max = min.max; @@ -298,7 +298,7 @@ window.Range = function(min, max) { } }; -window.Range.prototype = { +Range.prototype = { // Variable: extent // Equivalent to max-min get extent() { @@ -372,11 +372,11 @@ window.Range.prototype = { // ########## // Class: Subscribable // A mix-in for allowing objects to collect subscribers for custom events. -window.Subscribable = function() { +function Subscribable() { this.subscribers = null; }; -window.Subscribable.prototype = { +Subscribable.prototype = { // ---------- // Function: addSubscriber // The given callback will be called when the Subscribable fires the given event. @@ -458,9 +458,7 @@ window.Subscribable.prototype = { // ########## // Class: Utils // Singelton with common utility functions. -window.Utils = { - consoleService: null, - +let Utils = { // ___ Logging // ---------- @@ -468,13 +466,8 @@ window.Utils = { // Prints the given arguments to the JavaScript error console as a message. // Pass as many arguments as you want, it'll print them all. log: function() { - if (!this.consoleService) { - this.consoleService = Cc["@mozilla.org/consoleservice;1"] - .getService(Components.interfaces.nsIConsoleService); - } - var text = this.expandArgumentsForLog(arguments); - this.consoleService.logStringMessage(text); + Services.console.logStringMessage(text); }, // ---------- @@ -626,7 +619,7 @@ window.Utils = { isPlainObject: function(obj) { // Must be an Object. // Make sure that DOM nodes and window objects don't pass through, as well - if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" + if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval) { return false; } @@ -742,19 +735,18 @@ window.Utils = { // ---------- // Function: timeout - // wraps setTimeout with try/catch - // - // Note to our beloved reviewers: we are keeping this trivial wrapper in case we - // make Utils a JSM. + // wraps a delayed function call with try/catch timeout: function(func, delay) { - setTimeout(function() { - try { - func(); - } catch(e) { - Utils.log(e); + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ + notify: function notify() { + try { + func(); + } + catch(ex) { + Utils.log(timer, ex); + } } - }, delay); + }, delay, timer.TYPE_ONE_SHOT); } }; - -})(); From 88ef92422fd6f731b4779a865781433f19933ffe Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Thu, 29 Jul 2010 12:37:25 -0700 Subject: [PATCH 183/369] Bug 583044 - Rename code references of TabCandy to TabView Move files and update references to tabview from tabcandy. Only remaining candy reference is the link to aza's webm video. --- browser/base/content/tabview/iq.js | 4 ++-- browser/base/content/tabview/modules/utils.jsm | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 0473c87d1ea5..991f0f17d784 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -532,7 +532,7 @@ iQClass.prototype = { // Possible "options" properties: // duration - how long to animate, in milliseconds // easing - easing function to use. Possibilities include - // 'tabcandyBounce', 'easeInQuad'. Default is 'ease'. + // "tabviewBounce", "easeInQuad". Default is "ease". // complete - function to call once the animation is done, takes nothing // in, but "this" is set to the element that was animated. animate: function(css, options) { @@ -543,7 +543,7 @@ iQClass.prototype = { options = {}; let easings = { - tabcandyBounce: 'cubic-bezier(0.0, 0.63, .6, 1.29)', + tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)", easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care fast: 'cubic-bezier(0.7,0,1,1)' }; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index f74560e9a328..96a0e4877828 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -476,7 +476,7 @@ let Utils = { // Pass as many arguments as you want, it'll print them all. error: function() { var text = this.expandArgumentsForLog(arguments); - Cu.reportError('tabcandy error: ' + text); + Cu.reportError("tabview error: " + text); }, // ---------- @@ -503,7 +503,7 @@ let Utils = { if (typeof(label) == 'undefined') text = 'badly formed assert'; else - text = 'tabcandy assert: ' + label; + text = "tabview assert: " + label; this.trace(text); } @@ -518,7 +518,7 @@ let Utils = { if (typeof(label) == 'undefined') text = 'badly formed assert'; else - text = 'tabcandy assert: ' + label; + text = "tabview assert: " + label; // cut off the first two lines of the stack trace, because they're just this function. text += Error().stack.replace(/^.*?\n.*?\n/, ""); From 183c85bc14b89a0b587befdcf335e72fb8a8033a Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Thu, 29 Jul 2010 13:02:51 -0700 Subject: [PATCH 184/369] Simplify Utils.merge to use Array.forEach and Array.push for bug 582023. --- browser/base/content/tabview/modules/utils.jsm | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 96a0e4877828..ab915f12cf76 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -666,22 +666,9 @@ let Utils = { // ---------- // Function: merge - // Merge two arrays and return the result. + // Merge two array-like objects into the first and return it. merge: function(first, second) { - var i = first.length, j = 0; - - if (typeof second.length === "number") { - for (let l = second.length; j < l; j++) { - first[i++] = second[j]; - } - } else { - while (second[j] !== undefined) { - first[i++] = second[j++]; - } - } - - first.length = i; - + Array.forEach(second, function(el) Array.push(first, el)); return first; }, From 2e67d4d8fc2cd7f79cecf9f4514d43166570690c Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Thu, 29 Jul 2010 13:15:21 -0700 Subject: [PATCH 185/369] Just use instanceof Ci.nsIDOMElement for Utils.isDOMElement for bug 582023. --- browser/base/content/tabview/modules/utils.jsm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index ab915f12cf76..40896b6c6e0b 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -576,7 +576,7 @@ let Utils = { // Function: isDOMElement // Returns true if the given object is a DOM element. isDOMElement: function(object) { - return (object && typeof(object.nodeType) != 'undefined' ? true : false); + return object instanceof Ci.nsIDOMElement; }, // ---------- From ac81876e0cb58a043e2f3c7592599abdcc58dc2d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Thu, 29 Jul 2010 17:23:49 -0700 Subject: [PATCH 186/369] + Improved startup sequence: don't do anything until sessionstore is available, and then do only minimal work if we're not loading directly into the UI; most setup now happens when we first show the UI. Known issue: tabs aren't set up properly in the tab bar until after you go to the UI once. + fixed our use of undefined in iq.js + more tabItems cleanup, including removing a stray call to the now non-existent TabCanvas.detach() + "Group sites" no longer makes a "mixed" group if it would be empty --- browser/base/content/tabview/iq.js | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 991f0f17d784..90a5da80d770 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -165,7 +165,7 @@ let iQClass = function(selector, context) { return null; } - if (selector.selector !== undefined) { + if (typeof selector.selector !== "undefined") { this.selector = selector.selector; this.context = selector.context; } @@ -299,8 +299,7 @@ iQClass.prototype = { // ---------- // Function: remove // Removes the receiver from the DOM. - remove: function(unused) { - Utils.assert('does not accept a selector', unused === undefined); + remove: function() { for (let i = 0; this[i] != null; i++) { let elem = this[i]; if (elem.parentNode) { @@ -326,16 +325,14 @@ iQClass.prototype = { // ---------- // Function: width // Returns the width of the receiver. - width: function(unused) { - Utils.assert('does not yet support setting', unused === undefined); + width: function() { return parseInt(this.css('width')); }, // ---------- // Function: height // Returns the height of the receiver. - height: function(unused) { - Utils.assert('does not yet support setting', unused === undefined); + height: function() { return parseInt(this.css('height')); }, @@ -343,8 +340,7 @@ iQClass.prototype = { // Function: position // Returns an object with the receiver's position in left and top // properties. - position: function(unused) { - Utils.assert('does not yet support setting', unused === undefined); + position: function() { return { left: parseInt(this.css('left')), top: parseInt(this.css('top')) @@ -365,7 +361,7 @@ iQClass.prototype = { // pass in just key to retrieve it. data: function(key, value) { let data = null; - if (value === undefined) { + if (typeof value === "undefined") { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); data = this[0].iQData; if (data) @@ -393,7 +389,7 @@ iQClass.prototype = { // what's already there. html: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if (value === undefined) + if (typeof value === "undefined") return this[0].innerHTML; this[0].innerHTML = value; @@ -406,7 +402,7 @@ iQClass.prototype = { // what's already there. text: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if (value === undefined) { + if (typeof value === "undefined") { return this[0].textContent; } @@ -418,7 +414,7 @@ iQClass.prototype = { // Given a value, sets the receiver's value to it; otherwise returns what's already there. val: function(value) { Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); - if (value === undefined) { + if (typeof value === "undefined") { return this[0].value; } @@ -451,7 +447,7 @@ iQClass.prototype = { attr: function(key, value) { try { Utils.assert('string key', typeof key === 'string'); - if (value === undefined) { + if (typeof value === "undefined") { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); return this[0].getAttribute(key); } @@ -479,7 +475,7 @@ iQClass.prototype = { if (typeof a === 'string') { let key = a; - if (b === undefined) { + if (typeof b === "undefined") { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); let substitutions = { @@ -593,7 +589,9 @@ iQClass.prototype = { // Function: fadeOut // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { - Utils.assert('does not yet support duration', typeof callback == "function" || callback === undefined); + Utils.assert('does not yet support duration', typeof callback == "function" + || typeof callback === "undefined"); + this.animate({ opacity: 0 }, { From 8d11361d0af427b16e1853dab064d98d2273cc52 Mon Sep 17 00:00:00 2001 From: Raymond Lee Date: Fri, 30 Jul 2010 17:54:30 +0800 Subject: [PATCH 187/369] Remove extra spaces inside () and add spacing for before { bracket --- browser/base/content/tabview/iq.js | 18 +++++++++--------- browser/base/content/tabview/modules/utils.jsm | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 90a5da80d770..77c323265d8b 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -338,7 +338,7 @@ iQClass.prototype = { // ---------- // Function: position - // Returns an object with the receiver's position in left and top + // Returns an object with the receiver's position in left and top // properties. position: function() { return { @@ -527,9 +527,9 @@ iQClass.prototype = { // // Possible "options" properties: // duration - how long to animate, in milliseconds - // easing - easing function to use. Possibilities include + // easing - easing function to use. Possibilities include // "tabviewBounce", "easeInQuad". Default is "ease". - // complete - function to call once the animation is done, takes nothing + // complete - function to call once the animation is done, takes nothing // in, but "this" is set to the element that was animated. animate: function(css, options) { try { @@ -547,13 +547,13 @@ iQClass.prototype = { let duration = (options.duration || 400); let easing = (easings[options.easing] || 'ease'); - // The latest versions of Firefox do not animate from a non-explicitly - // set css properties. So for each element to be animated, go through + // The latest versions of Firefox do not animate from a non-explicitly + // set css properties. So for each element to be animated, go through // and explicitly define 'em. let rupper = /([A-Z])/g; - this.each(function(elem){ + this.each(function(elem) { let cStyle = window.getComputedStyle(elem, null); - for (let prop in css){ + for (let prop in css) { prop = prop.replace(rupper, "-$1").toLowerCase(); iQ(elem).css(prop, cStyle.getPropertyValue(prop)); } @@ -589,9 +589,9 @@ iQClass.prototype = { // Function: fadeOut // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { - Utils.assert('does not yet support duration', typeof callback == "function" + Utils.assert('does not yet support duration', typeof callback == "function" || typeof callback === "undefined"); - + this.animate({ opacity: 0 }, { diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 40896b6c6e0b..4445eaa2fda2 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -162,7 +162,7 @@ Rect.prototype = { // // Paramaters // - A - contains: function(rect){ + contains: function(rect) { return(rect.left > this.left && rect.right < this.right && rect.top > this.top @@ -245,7 +245,7 @@ Rect.prototype = { // ---------- // Function: union // Returns a new with the union of this rectangle and the given . - union: function(a){ + union: function(a) { var newLeft = Math.min(a.left, this.left); var newTop = Math.min(a.top, this.top); var newWidth = Math.max(a.right, this.right) - newLeft; @@ -344,7 +344,7 @@ Range.prototype = { // little graph. It goes from near 0 at x=0 to near 1 at x=1 // smoothly and beautifully. // http://www.wolframalpha.com/input/?i=.5+%2B+.5+*+tanh%28%284+*+x%29+-+2%29 - function tanh(x){ + function tanh(x) { var e = Math.exp(x); return (e - 1/e) / (e + 1/e); } From 807345ad128cfd02267065fb1977035030721860 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Wed, 4 Aug 2010 12:26:29 -0500 Subject: [PATCH 188/369] Bug 582477: e10s: HttpChannelChild triggers abort on bing.com r=jduell,jdm --- netwerk/protocol/http/HttpChannelChild.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index b241d8b7d35e..1d82e119e71d 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -146,8 +146,7 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, // - Send Cancel msg to parent // - drop any in flight OnDataAvail msgs we receive // - make sure we do call OnStopRequest eventually - // - return true here, not false - return false; + return true; } if (mResponseHead) @@ -226,7 +225,6 @@ HttpChannelChild::OnDataAvailable(const nsCString& data, stringStream->Close(); if (NS_FAILED(rv)) { // TODO: Cancel request: see OnStartRequest. Bug 536317 - return false; } return true; } From 919046442e850b204c4f4e33f3973ff4e5ad20c4 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Thu, 5 Aug 2010 00:20:14 -0700 Subject: [PATCH 189/369] Backed out bing fix, because entire e10s branch is in some messed up hg state. --- netwerk/protocol/http/HttpChannelChild.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 1d82e119e71d..b241d8b7d35e 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -146,7 +146,8 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, // - Send Cancel msg to parent // - drop any in flight OnDataAvail msgs we receive // - make sure we do call OnStopRequest eventually - return true; + // - return true here, not false + return false; } if (mResponseHead) @@ -225,6 +226,7 @@ HttpChannelChild::OnDataAvailable(const nsCString& data, stringStream->Close(); if (NS_FAILED(rv)) { // TODO: Cancel request: see OnStartRequest. Bug 536317 + return false; } return true; } From 82afeb308dbaea235b099f0fe7b179552343c172 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Tue, 10 Aug 2010 13:14:42 -0400 Subject: [PATCH 190/369] Bug 585083 - Android crash after showing external URI app chooser dialog r=mwu blocking-fennec=2.0a1+ --- uriloader/exthandler/android/nsMIMEInfoAndroid.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp index 27874bd8535f..ce2a9749867a 100644 --- a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp +++ b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp @@ -42,7 +42,7 @@ #include "nsStringEnumerator.h" #include "nsNetUtil.h" -NS_IMPL_ISUPPORTS1(nsMIMEInfoAndroid, nsIMIMEInfo) +NS_IMPL_ISUPPORTS2(nsMIMEInfoAndroid, nsIMIMEInfo, nsIHandlerInfo) nsMIMEInfoAndroid::~nsMIMEInfoAndroid() { From 3ca04d07ac15eb9f2e30732d171a3f1c316908d0 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Tue, 10 Aug 2010 13:14:45 -0400 Subject: [PATCH 191/369] bug 583542 - [e10s] nsExternalHelperAppService fails when called from content process r=dougt blocking-fennec=2.0a1+ --- dom/ipc/ContentParent.cpp | 13 +++++++++++++ dom/ipc/ContentParent.h | 2 ++ dom/ipc/Makefile.in | 1 + dom/ipc/PContent.ipdl | 2 ++ uriloader/exthandler/Makefile.in | 2 ++ .../exthandler/nsExternalHelperAppService.cpp | 16 ++++++++++++++++ 6 files changed, 36 insertions(+) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 0bdd603408b4..0d898f88bc83 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -53,6 +53,8 @@ #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "nsChromeRegistryChrome.h" +#include "nsExternalHelperAppService.h" +#include "nsCExternalHandlerService.h" #ifdef ANDROID #include "AndroidBridge.h" @@ -470,6 +472,17 @@ ContentParent::RecvSetURITitle(const IPC::URI& uri, return true; } +bool +ContentParent::RecvLoadURIExternal(const IPC::URI& uri) +{ + nsCOMPtr extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID)); + if (!extProtService) + return true; + nsCOMPtr ourURI(uri); + extProtService->LoadURI(ourURI, nsnull); + return true; +} + /* void onDispatchedEvent (in nsIThreadInternal thread); */ NS_IMETHODIMP ContentParent::OnDispatchedEvent(nsIThreadInternal *thread) diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index a183d87e40e7..d6daab20264a 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -162,6 +162,8 @@ private: ; + virtual bool RecvLoadURIExternal(const IPC::URI& uri); + mozilla::Monitor mMonitor; GeckoChildProcessHost* mSubprocess; diff --git a/dom/ipc/Makefile.in b/dom/ipc/Makefile.in index 3dc4680c4f0f..38d62de76484 100644 --- a/dom/ipc/Makefile.in +++ b/dom/ipc/Makefile.in @@ -82,6 +82,7 @@ LOCAL_INCLUDES += \ -I$(srcdir)/../../toolkit/components/places/src \ -I$(srcdir)/../src/geolocation \ -I$(topsrcdir)/chrome/src \ + -I$(topsrcdir)/uriloader/exthandler \ $(NULL) CXXFLAGS += $(TK_CFLAGS) diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e38ff064878e..8e878aad3914 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -81,6 +81,8 @@ parent: async VisitURI(URI uri, URI referrer, PRUint32 flags); async SetURITitle(URI uri, nsString title); + async LoadURIExternal(URI uri); + // PrefService messages sync GetPrefType(nsCString prefName) returns (PRInt32 retValue, nsresult rv); sync GetBoolPref(nsCString prefName) returns (PRBool retValue, nsresult rv); diff --git a/uriloader/exthandler/Makefile.in b/uriloader/exthandler/Makefile.in index 5d68475e79bf..0cc3fa57e524 100644 --- a/uriloader/exthandler/Makefile.in +++ b/uriloader/exthandler/Makefile.in @@ -175,6 +175,8 @@ EXTRA_COMPONENTS = \ # we don't want the shared lib, but we want to force the creation of a static lib. FORCE_STATIC_LIB = 1 SRCS_IN_OBJDIR = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk ifneq (,$(filter qt gtk2, $(MOZ_WIDGET_TOOLKIT))) diff --git a/uriloader/exthandler/nsExternalHelperAppService.cpp b/uriloader/exthandler/nsExternalHelperAppService.cpp index c87af9b58743..11b4ec7b475c 100644 --- a/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -42,6 +42,15 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG +#endif + +#ifdef MOZ_IPC +#include "mozilla/dom/ContentChild.h" +#include "nsXULAppAPI.h" +#endif + #include "nsExternalHelperAppService.h" #include "nsCExternalHandlerService.h" #include "nsIURI.h" @@ -861,6 +870,13 @@ nsExternalHelperAppService::LoadURI(nsIURI *aURI, { NS_ENSURE_ARG_POINTER(aURI); +#ifdef MOZ_IPC + if (XRE_GetProcessType() == GeckoProcessType_Content) { + mozilla::dom::ContentChild::GetSingleton()->SendLoadURIExternal(aURI); + return NS_OK; + } +#endif + nsCAutoString spec; aURI->GetSpec(spec); From 9614214ce5b652e4256e2d2d673c4f8195948e9b Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Tue, 10 Aug 2010 13:18:03 -0400 Subject: [PATCH 192/369] bug 585871 - Add android:debuggable="true" to AndroidManifest.xml.in r=mwu a2.0=stuart --- embedding/android/AndroidManifest.xml.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 976a922280c8..b6b13161de18 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -14,7 +14,8 @@ + android:icon="@drawable/icon" + android:debuggable="true"> Date: Tue, 10 Aug 2010 14:02:27 -0400 Subject: [PATCH 193/369] bug 584896 - MIME Chooser dialog causes crash on Android r=mwu blocking-fennec=2.0a1+ --- uriloader/exthandler/android/nsMIMEInfoAndroid.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp index ce2a9749867a..33fbbeb499ef 100644 --- a/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp +++ b/uriloader/exthandler/android/nsMIMEInfoAndroid.cpp @@ -230,9 +230,7 @@ nsMIMEInfoAndroid::SetPreferredAction(nsHandlerInfoAction aPrefAction) NS_IMETHODIMP nsMIMEInfoAndroid::GetAlwaysAskBeforeHandling(PRBool* aAlwaysAsk) { - // the chooser dialog currently causes a crash on Android, avoid this by returning false here - // but be sure to return mAlwaysAsk when that gets fixed (bug 584896) - *aAlwaysAsk = PR_FALSE; + *aAlwaysAsk = mAlwaysAsk; return NS_OK; } From 129ef245f0f5de460e81797934afcf0147edfbfd Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Tue, 10 Aug 2010 14:02:27 -0400 Subject: [PATCH 194/369] bug 585873 implement RealBreak for arm r=doug a2.0=bsmedberg --- xpcom/base/nsDebugImpl.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xpcom/base/nsDebugImpl.cpp b/xpcom/base/nsDebugImpl.cpp index 410d495034ee..82ffce7e6da1 100644 --- a/xpcom/base/nsDebugImpl.cpp +++ b/xpcom/base/nsDebugImpl.cpp @@ -393,8 +393,10 @@ RealBreak() raise(SIGTRAP); #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__)) asm("int $3"); +#elif defined(__arm__) + asm("BKPT #0"); #else - // don't know how to break on this platform +#warning don't know how to break on this platform #endif } @@ -493,8 +495,10 @@ Break(const char *aMsg) RealBreak(); #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__)) RealBreak(); +#elif defined(__arm__) + RealBreak(); #else - // don't know how to break on this platform +#warning don't know how to break on this platform #endif } From ab7c7bb63ff54881d0d567190f06b2b2a6dded04 Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 10 Aug 2010 12:53:17 -0700 Subject: [PATCH 195/369] Bug 584727 - contentaction.h system-header is missed. r=doug.turner --- config/system-headers | 3 +++ js/src/config/system-headers | 3 +++ 2 files changed, 6 insertions(+) diff --git a/config/system-headers b/config/system-headers index b9b29b8811ac..de25c54cfe80 100644 --- a/config/system-headers +++ b/config/system-headers @@ -1036,4 +1036,7 @@ event.h #ifdef MOZ_ENABLE_LIBPROXY proxy.h #endif +#if MOZ_PLATFORM_MAEMO==6 +contentaction/contentaction.h +#endif diff --git a/js/src/config/system-headers b/js/src/config/system-headers index b9b29b8811ac..de25c54cfe80 100644 --- a/js/src/config/system-headers +++ b/js/src/config/system-headers @@ -1036,4 +1036,7 @@ event.h #ifdef MOZ_ENABLE_LIBPROXY proxy.h #endif +#if MOZ_PLATFORM_MAEMO==6 +contentaction/contentaction.h +#endif From 6c91320d0935559cb3119e46a3625a960007ee17 Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 10 Aug 2010 13:01:34 -0700 Subject: [PATCH 196/369] Bug 584730 - change the licenses to be tri-licenses for MozMeegoAppService.h. r=doug.turner --- toolkit/xre/MozMeegoAppService.h | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/toolkit/xre/MozMeegoAppService.h b/toolkit/xre/MozMeegoAppService.h index 464da2adbd41..c5efc88952cd 100644 --- a/toolkit/xre/MozMeegoAppService.h +++ b/toolkit/xre/MozMeegoAppService.h @@ -1,6 +1,7 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1 + * 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 @@ -12,14 +13,25 @@ * for the specific language governing rights and limitations under the * License. * - * The Original Code is mozilla.org code. + * The Original Code is Nokia. * - * The Initial Developer of the Original Code is - * Nokia Corporation. + * The Initial Developer of the Original Code is Nokia Corporation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * - * Contributor(s): + * Contributor(s): + * + * 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 ***** */ From 2eeea4735dea8323a92d7ce60692112e3f36e74b Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 10 Aug 2010 13:06:38 -0700 Subject: [PATCH 197/369] Bug 576518 - e10s: InitDBFile and NS_GetSpecialDirectory fail in nsNavHistory. r=bzbarsky. a+ --- embedding/browser/webBrowser/nsWebBrowser.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/embedding/browser/webBrowser/nsWebBrowser.cpp b/embedding/browser/webBrowser/nsWebBrowser.cpp index 5626b04e750d..365eb1338479 100644 --- a/embedding/browser/webBrowser/nsWebBrowser.cpp +++ b/embedding/browser/webBrowser/nsWebBrowser.cpp @@ -91,6 +91,7 @@ // PSM2 includes #include "nsISecureBrowserUI.h" +#include "nsXULAppAPI.h" using namespace mozilla::layers; @@ -790,6 +791,7 @@ NS_IMETHODIMP nsWebBrowser::SetProperty(PRUint32 aId, PRUint32 aValue) NS_ENSURE_TRUE((aValue == PR_TRUE || aValue == PR_FALSE), NS_ERROR_INVALID_ARG); mDocShell->SetAllowDNSPrefetch(!!aValue); } + break; case nsIWebBrowserSetup::SETUP_USE_GLOBAL_HISTORY: { NS_ENSURE_STATE(mDocShell); @@ -1230,10 +1232,12 @@ NS_IMETHODIMP nsWebBrowser::Create() NS_ENSURE_SUCCESS(rv, rv); } mDocShellAsNav->SetSessionHistory(mInitInfo->sessionHistory); - - // Hook up global history. Do not fail if we can't - just warn. - rv = EnableGlobalHistory(mShouldEnableHistory); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); + + if (XRE_GetProcessType() == GeckoProcessType_Default) { + // Hook up global history. Do not fail if we can't - just warn. + rv = EnableGlobalHistory(mShouldEnableHistory); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); + } NS_ENSURE_SUCCESS(mDocShellAsWin->Create(), NS_ERROR_FAILURE); From afe1f4eb6f9bc403ddcf5e1c5bd6fd6ca2f11726 Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 10 Aug 2010 13:12:21 -0700 Subject: [PATCH 198/369] Bug 560630 - NotificationService for e10s fennec initialized and used in different threads. r=bent.mozilla. a+ --- ipc/chromium/src/chrome/common/child_process_host.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipc/chromium/src/chrome/common/child_process_host.cc b/ipc/chromium/src/chrome/common/child_process_host.cc index 6c3b2eb9cf0b..d52074bdfc7d 100644 --- a/ipc/chromium/src/chrome/common/child_process_host.cc +++ b/ipc/chromium/src/chrome/common/child_process_host.cc @@ -11,6 +11,7 @@ #include "base/singleton.h" #include "base/waitable_event.h" #ifdef CHROMIUM_MOZILLA_BUILD +#include "mozilla/ipc/ProcessChild.h" #include "mozilla/ipc/BrowserProcessSubThread.h" typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; #else @@ -121,6 +122,8 @@ bool ChildProcessHost::Send(IPC::Message* msg) { void ChildProcessHost::Notify(NotificationType type) { #ifdef CHROMIUM_MOZILLA_BUILD MessageLoop* loop = ChromeThread::GetMessageLoop(ChromeThread::IO); + if (!loop) + loop = mozilla::ipc::ProcessChild::message_loop(); if (!loop) loop = MessageLoop::current(); loop->PostTask( From 06928c3e51830aaad1182f099589a12a7b242a93 Mon Sep 17 00:00:00 2001 From: Wolfgang Rosenauer Date: Tue, 10 Aug 2010 12:58:00 -0700 Subject: [PATCH 199/369] Bug 568459 - read system locale from gconf, r=benjamin a+ --- intl/locale/src/nsLocaleService.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/intl/locale/src/nsLocaleService.cpp b/intl/locale/src/nsLocaleService.cpp index a2a10acd88a1..f59c2f23073c 100644 --- a/intl/locale/src/nsLocaleService.cpp +++ b/intl/locale/src/nsLocaleService.cpp @@ -60,6 +60,9 @@ # include # include # include "nsIPosixLocale.h" +#if (MOZ_PLATFORM_MAEMO >= 6) +# include "nsIGConfService.h" +#endif #endif // @@ -174,24 +177,43 @@ nsLocaleService::nsLocaleService(void) if ( resultLocale == NULL ) { return; } + + // Get system configuration + char* lang = getenv("LANG"); +#if (MOZ_PLATFORM_MAEMO >= 6) + nsCAutoString gconfLocaleString; + nsresult rv; + nsCOMPtr gconf = + do_GetService(NS_GCONFSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = gconf->GetString(NS_LITERAL_CSTRING("/meegotouch/i18n/language"), + gconfLocaleString); + if (NS_SUCCEEDED(rv) && !gconfLocaleString.IsEmpty()) { + lang = static_cast(gconfLocaleString.get()); + // For setlocale() doing the right thing we need to export + // this as LANG to the environment + setenv("LANG", lang, 1); + } + } +#endif for( i = 0; i < LocaleListLength; i++ ) { nsresult result; + // setlocale( , "") evaluates LC_* and LANG char* lc_temp = setlocale(posix_locale_category[i], ""); CopyASCIItoUTF16(LocaleList[i], category); - category_platform = category; + category_platform = category; category_platform.AppendLiteral("##PLATFORM"); if (lc_temp != nsnull) { result = posixConverter->GetXPLocale(lc_temp, xpLocale); CopyASCIItoUTF16(lc_temp, platformLocale); } else { - char* lang = getenv("LANG"); if ( lang == nsnull ) { platformLocale.AssignLiteral("en_US"); result = posixConverter->GetXPLocale("en-US", xpLocale); } else { CopyASCIItoUTF16(lang, platformLocale); - result = posixConverter->GetXPLocale(lang, xpLocale); + result = posixConverter->GetXPLocale(lang, xpLocale); } } if (NS_FAILED(result)) { From 82c4fde3eb129f7b08d8499c0bc142beb2601d64 Mon Sep 17 00:00:00 2001 From: Steffen Imhof Date: Tue, 10 Aug 2010 14:24:13 -0700 Subject: [PATCH 200/369] Bug 584783 - Re-insert runtime switch between MWindow and QGraphicsView. r=doug.turner. qt-only --- widget/src/qt/nsWindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/widget/src/qt/nsWindow.cpp b/widget/src/qt/nsWindow.cpp index e9970943541e..5548d9e4984f 100644 --- a/widget/src/qt/nsWindow.cpp +++ b/widget/src/qt/nsWindow.cpp @@ -2082,7 +2082,9 @@ nsWindow::createQWidget(MozQWidget *parent, nsWidgetInitData *aInitData) if (mIsTopLevel) { QGraphicsView* newView = nsnull; #ifdef MOZ_ENABLE_MEEGOTOUCH - newView = new MozMGraphicsView(widget); + if (XRE_GetProcessType() == GeckoProcessType_Default) { + newView = new MozMGraphicsView(widget); + } else #else newView = new MozQGraphicsView(widget); #endif From 699c96697a6d3e3a05757167242cec2fd15fb67c Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 10 Aug 2010 15:03:26 -0700 Subject: [PATCH 201/369] Bug 585869 - Check for file scheme in nsResURL::EnsureFile, r=bsmedberg a=blocking2.0 --- netwerk/protocol/res/nsResProtocolHandler.cpp | 27 ++++++++----------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/netwerk/protocol/res/nsResProtocolHandler.cpp b/netwerk/protocol/res/nsResProtocolHandler.cpp index 8122c0d6405d..12f74e1ca84c 100644 --- a/netwerk/protocol/res/nsResProtocolHandler.cpp +++ b/netwerk/protocol/res/nsResProtocolHandler.cpp @@ -95,25 +95,20 @@ nsResURL::EnsureFile() nsCAutoString spec; rv = gResHandler->ResolveURI(this, spec); - if (NS_FAILED(rv)) return rv; - -#if defined(MOZ_CHROME_FILE_FORMAT_JAR) || defined(MOZ_OMNIJAR) - nsCAutoString host; - rv = GetHost(host); if (NS_FAILED(rv)) return rv; - // Deal with the fact resource://gre-resouces/ urls do not resolve to files - if (host.Equals(kGRE_RESOURCES)) + + nsCAutoString scheme; + rv = net_ExtractURLScheme(spec, nsnull, nsnull, &scheme); + if (NS_FAILED(rv)) + return rv; + + // Bug 585869: + // In most cases, the scheme is jar if it's not file. + // Regardless, net_GetFileFromURLSpec should be avoided + // when the scheme isn't file. + if (!scheme.Equals(NS_LITERAL_CSTRING("file"))) return NS_ERROR_NO_INTERFACE; -#endif -#ifdef MOZ_OMNIJAR - if (mozilla::OmnijarPath()) { - if (host.Equals(kGRE)) - return NS_ERROR_NO_INTERFACE; - if (host.IsEmpty()) - return NS_ERROR_NO_INTERFACE; - } -#endif rv = net_GetFileFromURLSpec(spec, getter_AddRefs(mFile)); #ifdef DEBUG_bsmedberg From dc746e4977c30dd56f55fcda0fc783b175d1c0c4 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 10 Aug 2010 15:08:06 -0700 Subject: [PATCH 202/369] Bug 556644 - 1. Move omnijar setup to NS_InitXPCOM and use omni.jar by default, r=bsmedberg a=blocking2.0 --- ipc/glue/GeckoChildProcessHost.cpp | 31 ++++++++++++++------ toolkit/xre/nsAppRunner.cpp | 43 +++++++++++++--------------- xpcom/build/Omnijar.cpp | 45 +++++++++++++++++++----------- xpcom/build/nsXPComInit.cpp | 26 +++++++++++++++++ 4 files changed, 97 insertions(+), 48 deletions(-) diff --git a/ipc/glue/GeckoChildProcessHost.cpp b/ipc/glue/GeckoChildProcessHost.cpp index 73cea3e8a240..e2c747c078c3 100644 --- a/ipc/glue/GeckoChildProcessHost.cpp +++ b/ipc/glue/GeckoChildProcessHost.cpp @@ -237,14 +237,6 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) newEnvVars["LD_LIBRARY_PATH"] = path.get(); #elif OS_MACOSX newEnvVars["DYLD_LIBRARY_PATH"] = path.get(); -#endif -#ifdef MOZ_OMNIJAR - // Make sure the child process can find the omnijar - // See ScopedXPCOMStartup::Initialize in nsAppRunner.cpp - nsCAutoString omnijarPath; - if (mozilla::OmnijarPath()) - mozilla::OmnijarPath()->GetNativePath(omnijarPath); - newEnvVars["OMNIJAR_PATH"] = omnijarPath.get(); #endif } else { @@ -280,6 +272,17 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) childArgv.insert(childArgv.end(), aExtraOpts.begin(), aExtraOpts.end()); +#ifdef MOZ_OMNIJAR + // Make sure the child process can find the omnijar + // See XRE_InitCommandLine in nsAppRunner.cpp + nsCAutoString omnijarPath; + if (mozilla::OmnijarPath()) { + mozilla::OmnijarPath()->GetNativePath(omnijarPath); + childArgv.push_back("-omnijar"); + childArgv.push_back(omnijarPath.get()); + } +#endif + childArgv.push_back(pidstring); childArgv.push_back(childProcessType); @@ -330,6 +333,18 @@ GeckoChildProcessHost::PerformAsyncLaunch(std::vector aExtraOpts) } cmdLine.AppendLooseValue(std::wstring(mGroupId.get())); + +#ifdef MOZ_OMNIJAR + // Make sure the child process can find the omnijar + // See XRE_InitCommandLine in nsAppRunner.cpp + nsAutoString omnijarPath; + if (mozilla::OmnijarPath()) { + mozilla::OmnijarPath()->GetPath(omnijarPath); + cmdLine.AppendLooseValue(UTF8ToWide("-omnijar")); + cmdLine.AppendLooseValue(omnijarPath.get()); + } +#endif + cmdLine.AppendLooseValue(UTF8ToWide(pidstring)); cmdLine.AppendLooseValue(UTF8ToWide(childProcessType)); #if defined(MOZ_CRASHREPORTER) diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index 40b58e4fe9b2..7c6c64e0c5b5 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -1114,10 +1114,6 @@ ScopedXPCOMStartup::~ScopedXPCOMStartup() NS_ShutdownXPCOM(mServiceManager); mServiceManager = nsnull; - -#ifdef MOZ_OMNIJAR - mozilla::SetOmnijar(nsnull); -#endif } } @@ -1175,16 +1171,6 @@ ScopedXPCOMStartup::Initialize() NS_ASSERTION(gDirServiceProvider, "Should not get here!"); nsresult rv; -#ifdef MOZ_OMNIJAR - nsCOMPtr lf; - char *omnijarPath = getenv("OMNIJAR_PATH"); - if (omnijarPath) - rv = NS_NewNativeLocalFile(nsDependentCString(omnijarPath), PR_TRUE, getter_AddRefs(lf)); - else - rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); - if (NS_SUCCEEDED(rv)) - mozilla::SetOmnijar(lf); -#endif #ifndef MOZ_ENABLE_LIBXUL #ifndef _BUILD_STATIC_BIN @@ -3777,16 +3763,6 @@ XRE_InitCommandLine(int aArgc, char* aArgv[]) #if defined(OS_WIN) CommandLine::Init(aArgc, aArgv); #else -#ifdef MOZ_OMNIJAR - nsCOMPtr lf; - char *omnijarPath = getenv("OMNIJAR_PATH"); - if (omnijarPath) - rv = NS_NewNativeLocalFile(nsDependentCString(omnijarPath), PR_TRUE, getter_AddRefs(lf)); - else - rv = XRE_GetBinaryPath(gArgv[0], getter_AddRefs(lf)); - if (NS_SUCCEEDED(rv)) - mozilla::SetOmnijar(lf); -#endif // these leak on error, but that's OK: we'll just exit() char** canonArgs = new char*[aArgc]; @@ -3818,6 +3794,25 @@ XRE_InitCommandLine(int aArgc, char* aArgv[]) delete[] canonArgs; #endif #endif + +#ifdef MOZ_OMNIJAR + const char *omnijarPath = nsnull; + ArgResult ar = CheckArg("omnijar", PR_FALSE, &omnijarPath); + if (ar == ARG_BAD) { + PR_fprintf(PR_STDERR, "Error: argument -omnijar requires an omnijar path\n"); + return NS_ERROR_FAILURE; + } + + if (!omnijarPath) + return rv; + + nsCOMPtr omnijar; + rv = NS_NewNativeLocalFile(nsDependentCString(omnijarPath), PR_TRUE, + getter_AddRefs(omnijar)); + if (NS_SUCCEEDED(rv)) + mozilla::SetOmnijar(omnijar); +#endif + return rv; } diff --git a/xpcom/build/Omnijar.cpp b/xpcom/build/Omnijar.cpp index af8923eda349..14a0dcf8d332 100644 --- a/xpcom/build/Omnijar.cpp +++ b/xpcom/build/Omnijar.cpp @@ -45,15 +45,43 @@ static nsILocalFile* sOmnijarPath = nsnull; static nsZipArchive* sOmnijarReader = nsnull; +static void +SetupReader() +{ + if (!sOmnijarPath) { + return; + } + + nsZipArchive* zipReader = new nsZipArchive(); + if (!zipReader) { + NS_IF_RELEASE(sOmnijarPath); + return; + } + + if (NS_FAILED(zipReader->OpenArchive(sOmnijarPath))) { + delete zipReader; + NS_IF_RELEASE(sOmnijarPath); + return; + } + + sOmnijarReader = zipReader; +} + nsILocalFile* mozilla::OmnijarPath() { + if (!sOmnijarReader) + SetupReader(); + return sOmnijarPath; } nsZipArchive* mozilla::OmnijarReader() { + if (!sOmnijarReader) + SetupReader(); + return sOmnijarReader; } @@ -67,22 +95,7 @@ mozilla::SetOmnijar(nsILocalFile* aPath) sOmnijarReader = nsnull; } - if (!aPath) { - return; - } - - nsZipArchive* zipReader = new nsZipArchive(); - if (!zipReader) { - return; - } - - if (NS_FAILED(zipReader->OpenArchive(aPath))) { - delete zipReader; - return; - } - - sOmnijarReader = zipReader; sOmnijarPath = aPath; - NS_ADDREF(sOmnijarPath); + NS_IF_ADDREF(sOmnijarPath); } diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp index e151228fa485..5c0677004e6d 100644 --- a/xpcom/build/nsXPComInit.cpp +++ b/xpcom/build/nsXPComInit.cpp @@ -142,6 +142,7 @@ extern nsresult nsStringInputStreamConstructor(nsISupports *, REFNSIID, void **) #include #include "mozilla/Services.h" #include "mozilla/FunctionTimer.h" +#include "mozilla/Omnijar.h" #include "nsChromeRegistry.h" #include "nsChromeProtocolHandler.h" @@ -419,6 +420,7 @@ NS_InitXPCOM2(nsIServiceManager* *result, NS_StartupNativeCharsetUtils(); #endif + NS_TIME_FUNCTION_MARK("Next: startup local file"); NS_StartupLocalFile(); @@ -457,6 +459,26 @@ NS_InitXPCOM2(nsIServiceManager* *result, if (NS_FAILED(rv)) return rv; } +#ifdef MOZ_OMNIJAR + NS_TIME_FUNCTION_MARK("Next: Omnijar init"); + + if (!mozilla::OmnijarPath()) { + nsCOMPtr omnijar; + nsCOMPtr file; + + rv = NS_ERROR_FAILURE; + nsDirectoryService::gService->Get(NS_GRE_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(file)); + if (file) + rv = file->Append(NS_LITERAL_STRING("omni.jar")); + if (NS_SUCCEEDED(rv)) + omnijar = do_QueryInterface(file); + if (NS_SUCCEEDED(rv)) + mozilla::SetOmnijar(omnijar); + } +#endif + #ifdef MOZ_IPC if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) { NS_TIME_FUNCTION_MARK("Next: IPC command line init"); @@ -734,6 +756,10 @@ ShutdownXPCOM(nsIServiceManager* servMgr) } #endif +#ifdef MOZ_OMNIJAR + mozilla::SetOmnijar(nsnull); +#endif + NS_LogTerm(); return NS_OK; From 1b419040e0c98b7daddd4f267d4f1a29b4cf341e Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 10 Aug 2010 15:09:18 -0700 Subject: [PATCH 203/369] Bug 556644 - 2. Make android work with new omnijar setup, r=dougt a=blocking2.0 --- embedding/android/GeckoAppShell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index be4a16625a6c..b84322401b87 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -157,7 +157,7 @@ class GeckoAppShell sGeckoRunning = true; // First argument is the .apk path - String combinedArgs = apkPath; + String combinedArgs = apkPath + " -omnijar " + apkPath; if (args != null) combinedArgs += " " + args; if (url != null) From ab347cf3c62d7da0a7eb0aab55c666876a439399 Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 10 Aug 2010 15:15:26 -0700 Subject: [PATCH 204/369] Bug 556644 - 3. Let the browser reset bookmarks from the omnijar, r=sdwilsh sr=vlad a=blocking2.0 --- browser/components/nsBrowserGlue.js | 21 ++++--- .../public/nsIPlacesImportExportService.idl | 8 ++- .../src/nsPlacesImportExportService.cpp | 55 ++++++++++++++++--- .../places/src/nsPlacesImportExportService.h | 2 + 4 files changed, 71 insertions(+), 15 deletions(-) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 0ca39bf8437d..dd0b172e7670 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -52,6 +52,11 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { + Cu.import("resource://gre/modules/NetUtil.jsm"); + return NetUtil; +}); + const PREF_EM_NEW_ADDONS_LIST = "extensions.newAddons"; const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; @@ -840,16 +845,18 @@ BrowserGlue.prototype = { var dirService = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); - var bookmarksFile = null; + var bookmarksURI = null; if (restoreDefaultBookmarks) { // User wants to restore bookmarks.html file from default profile folder - bookmarksFile = dirService.get("profDef", Ci.nsILocalFile); - bookmarksFile.append("bookmarks.html"); + bookmarksURI = NetUtil.newURI("resource:///defaults/profile/bookmarks.html"); + } + else { + var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile); + if (bookmarksFile.exists()) + bookmarksURI = NetUtil.newURI(bookmarksFile); } - else - bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile); - if (bookmarksFile.exists()) { + if (bookmarksURI) { // Add an import observer. It will ensure that smart bookmarks are // created once the operation is complete. Services.obs.addObserver(this, "bookmarks-restore-success", false); @@ -859,7 +866,7 @@ BrowserGlue.prototype = { try { var importer = Cc["@mozilla.org/browser/places/import-export-service;1"]. getService(Ci.nsIPlacesImportExportService); - importer.importHTMLFromFile(bookmarksFile, true /* overwrite existing */); + importer.importHTMLFromURI(bookmarksURI, true /* overwrite existing */); } catch (err) { // Report the error, but ignore it. Cu.reportError("Bookmarks.html file could be corrupt. " + err); diff --git a/toolkit/components/places/public/nsIPlacesImportExportService.idl b/toolkit/components/places/public/nsIPlacesImportExportService.idl index 12f8a81970e4..18050f253cec 100644 --- a/toolkit/components/places/public/nsIPlacesImportExportService.idl +++ b/toolkit/components/places/public/nsIPlacesImportExportService.idl @@ -39,13 +39,14 @@ #include "nsISupports.idl" interface nsILocalFile; +interface nsIURI; /** * The PlacesImportExport interface provides methods for importing * and exporting Places data. */ -[scriptable, uuid(21c00314-fa63-11db-8314-0800200c9a66)] +[scriptable, uuid(47a4a09e-c708-4e68-b2f2-664d982ce026)] interface nsIPlacesImportExportService: nsISupports { /** @@ -64,6 +65,11 @@ interface nsIPlacesImportExportService: nsISupports */ void importHTMLFromFile(in nsILocalFile aFile, in boolean aIsInitialImport); + /** + * Same thing as importHTMLFromFile, but takes a URI instead + */ + void importHTMLFromURI(in nsIURI aURI, in boolean aIsInitialImport); + /** * Loads the given bookmarks.html file and puts it in the given folder * diff --git a/toolkit/components/places/src/nsPlacesImportExportService.cpp b/toolkit/components/places/src/nsPlacesImportExportService.cpp index 192d389ec5f4..0a0b393685c5 100644 --- a/toolkit/components/places/src/nsPlacesImportExportService.cpp +++ b/toolkit/components/places/src/nsPlacesImportExportService.cpp @@ -2181,6 +2181,33 @@ nsPlacesImportExportService::ImportHTMLFromFile(nsILocalFile* aFile, } +NS_IMETHODIMP +nsPlacesImportExportService::ImportHTMLFromURI(nsIURI* aURI, + PRBool aIsInitialImport) +{ + NotifyImportObservers(RESTORE_BEGIN_NSIOBSERVER_TOPIC, -1, aIsInitialImport); + + // this version is exposed on the interface and disallows changing of roots + nsresult rv = ImportHTMLFromURIInternal(aURI, + PR_FALSE, + 0, + aIsInitialImport); + + if (NS_FAILED(rv)) { + NotifyImportObservers(RESTORE_FAILED_NSIOBSERVER_TOPIC, + -1, + aIsInitialImport); + } + else { + NotifyImportObservers(RESTORE_SUCCESS_NSIOBSERVER_TOPIC, + -1, + aIsInitialImport); + } + + return rv; +} + + NS_IMETHODIMP nsPlacesImportExportService::ImportHTMLFromFileToFolder(nsILocalFile* aFile, PRInt64 aFolderId, @@ -2217,8 +2244,7 @@ nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile, PRInt64 aFolder, PRBool aIsImportDefaults) { - nsresult rv = EnsureServiceState(); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv; nsCOMPtr file = do_QueryInterface(aFile); NS_ENSURE_STATE(file); @@ -2237,6 +2263,24 @@ nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile, return NS_ERROR_INVALID_ARG; } + nsCOMPtr ioservice = do_GetIOService(&rv); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr fileURI; + rv = ioservice->NewFileURI(file, getter_AddRefs(fileURI)); + NS_ENSURE_SUCCESS(rv, rv); + + return ImportHTMLFromURIInternal(fileURI, aAllowRootChanges, aFolder, aIsImportDefaults); +} + +nsresult +nsPlacesImportExportService::ImportHTMLFromURIInternal(nsIURI* aURI, + PRBool aAllowRootChanges, + PRInt64 aFolder, + PRBool aIsImportDefaults) +{ + nsresult rv = EnsureServiceState(); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr parser = do_CreateInstance(kParserCID); NS_ENSURE_TRUE(parser, NS_ERROR_OUT_OF_MEMORY); @@ -2250,16 +2294,13 @@ nsPlacesImportExportService::ImportHTMLFromFileInternal(nsILocalFile* aFile, // will confuse the parser. nsCOMPtr ioservice = do_GetIOService(&rv); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr fileURI; - rv = ioservice->NewFileURI(file, getter_AddRefs(fileURI)); - NS_ENSURE_SUCCESS(rv, rv); - rv = ioservice->NewChannelFromURI(fileURI, getter_AddRefs(mImportChannel)); + rv = ioservice->NewChannelFromURI(aURI, getter_AddRefs(mImportChannel)); NS_ENSURE_SUCCESS(rv, rv); rv = mImportChannel->SetContentType(NS_LITERAL_CSTRING("text/html")); NS_ENSURE_SUCCESS(rv, rv); // Init parser. - rv = parser->Parse(fileURI, nsnull); + rv = parser->Parse(aURI, nsnull); NS_ENSURE_SUCCESS(rv, rv); // Run the import in batch mode, so it will be executed in a transaction diff --git a/toolkit/components/places/src/nsPlacesImportExportService.h b/toolkit/components/places/src/nsPlacesImportExportService.h index 78a8dfc8f3de..c02159727683 100644 --- a/toolkit/components/places/src/nsPlacesImportExportService.h +++ b/toolkit/components/places/src/nsPlacesImportExportService.h @@ -50,6 +50,8 @@ class nsPlacesImportExportService : public nsIPlacesImportExportService, nsresult ImportHTMLFromFileInternal(nsILocalFile* aFile, PRBool aAllowRootChanges, PRInt64 aFolder, PRBool aIsImportDefaults); + nsresult ImportHTMLFromURIInternal(nsIURI* aURI, PRBool aAllowRootChanges, + PRInt64 aFolder, PRBool aIsImportDefaults); nsresult WriteContainer(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput); nsresult WriteContainerHeader(nsINavHistoryResultNode* aFolder, const nsACString& aIndent, nsIOutputStream* aOutput); nsresult WriteTitle(nsINavHistoryResultNode* aItem, nsIOutputStream* aOutput); From 6937d281d770f1139d2b1f0d5bff31864f5eba6d Mon Sep 17 00:00:00 2001 From: Michael Wu Date: Tue, 10 Aug 2010 15:18:40 -0700 Subject: [PATCH 205/369] Bug 556644 - 4. Fix tests, r=bsmedberg a=blocking2.0 --HG-- rename : caps/tests/mochitest/test_bug292789.html => caps/tests/mochitest/test_bug292789.html.in --- caps/tests/mochitest/Makefile.in | 4 ++++ .../{test_bug292789.html => test_bug292789.html.in} | 8 ++++++++ js/src/xpconnect/tests/unit/test_import.js | 3 +-- xpcom/components/nsComponentManager.cpp | 3 +++ 4 files changed, 16 insertions(+), 2 deletions(-) rename caps/tests/mochitest/{test_bug292789.html => test_bug292789.html.in} (91%) diff --git a/caps/tests/mochitest/Makefile.in b/caps/tests/mochitest/Makefile.in index 15c38f825410..4be86f8b1328 100644 --- a/caps/tests/mochitest/Makefile.in +++ b/caps/tests/mochitest/Makefile.in @@ -51,5 +51,9 @@ _TEST_FILES = test_bug423375.html \ test_bug470804.html \ $(NULL) +test_bug292789.html : % : %.in + $(PYTHON) $(topsrcdir)/config/Preprocessor.py \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ + libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) diff --git a/caps/tests/mochitest/test_bug292789.html b/caps/tests/mochitest/test_bug292789.html.in similarity index 91% rename from caps/tests/mochitest/test_bug292789.html rename to caps/tests/mochitest/test_bug292789.html.in index 2cb1619a6b3a..a80f630f2251 100644 --- a/caps/tests/mochitest/test_bug292789.html +++ b/caps/tests/mochitest/test_bug292789.html.in @@ -38,7 +38,11 @@ function testScriptSrc(aCallback) { ** moved the resource **/ var resjs = document.createElement("script"); +#ifdef MOZ_CHROME_FILE_FORMAT_JAR resjs.src = "jar:resource://gre/chrome/toolkit.jar!/content/mozapps/xpinstall/xpinstallConfirm.js"; +#else + resjs.src = "resource://gre/chrome/toolkit/content/mozapps/xpinstall/xpinstallConfirm.js"; +#endif resjs.onload = scriptOnload; document.getElementById("content").appendChild(resjs); @@ -55,7 +59,11 @@ function testScriptSrc(aCallback) { /** tests **/ var img_global = "chrome://global/skin/icons/Error.png"; var img_mozapps = "chrome://mozapps/skin/passwordmgr/key.png"; +#ifdef MOZ_CHROME_FILE_FORMAT_JAR var res_mozapps = "jar:resource://gre/chrome/toolkit.jar!/skin/classic/mozapps/passwordmgr/key.png"; +#else +var res_mozapps = "resource://gre/chrome/toolkit/skin/classic/mozapps/passwordmgr/key.png"; +#endif var imgTests = [[img_global, "success"], [img_mozapps, "fail"], diff --git a/js/src/xpconnect/tests/unit/test_import.js b/js/src/xpconnect/tests/unit/test_import.js index 242fe646ad24..18d30e8bd149 100644 --- a/js/src/xpconnect/tests/unit/test_import.js +++ b/js/src/xpconnect/tests/unit/test_import.js @@ -67,13 +67,12 @@ function run_test() { do_check_true(scope2.XPCOMUtils == scope.XPCOMUtils); - // try on a new object using a file URL + // try on a new object using the resolved URL var res = Components.classes["@mozilla.org/network/protocol;1?name=resource"] .getService(Components.interfaces.nsIResProtocolHandler); var resURI = res.newURI("resource://gre/modules/XPCOMUtils.jsm", null, null); dump("resURI: " + resURI + "\n"); var filePath = res.resolveURI(resURI); - do_check_eq(filePath.indexOf("file://"), 0); var scope3 = {}; Components.utils.import(filePath, scope3); do_check_eq(typeof(scope3.XPCOMUtils), "object"); diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp index 627dbdb6d55a..c463f8445c31 100644 --- a/xpcom/components/nsComponentManager.cpp +++ b/xpcom/components/nsComponentManager.cpp @@ -526,6 +526,9 @@ GetExtension(nsILocalFile* file) void nsComponentManagerImpl::RegisterOmnijar(const char* aPath, bool aChromeOnly) { + if (!mozilla::OmnijarPath()) + return; + nsCOMPtr is = mManifestLoader->LoadEntry(aPath); PRUint32 flen; From 241e54ce10990292d74194c6054840e2261fd70b Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 10 Aug 2010 17:20:17 -0700 Subject: [PATCH 206/369] Bug 586116 - Crash in nsHTMLDNSPrefetch::Prefetch gNeckoChild == 0x0. r=jduell.mcbugs a=blocking-fennec2.0a1+ --- content/html/content/src/nsHTMLDNSPrefetch.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index dc7d98eb7c2c..914efe3b6090 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -106,6 +106,11 @@ nsHTMLDNSPrefetch::Initialize() rv = CallGetService(kDNSServiceCID, &sDNSService); if (NS_FAILED(rv)) return rv; +#ifdef MOZ_IPC + if (mozilla::net::IsNeckoChild()) + mozilla::net::NeckoChild::InitNeckoChild(); +#endif + sInitialized = PR_TRUE; return NS_OK; } From 7ec8ac2aba1b414d4b07cd5564c5c0808c8d1d39 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Fri, 6 Aug 2010 00:59:37 -0400 Subject: [PATCH 207/369] Bug 584604 - e10s HTTP: Clean up IPDL buffering. r=jduell,cjones jduell: Made a bunch of structural/naming changes. Merged in test patches. --- netwerk/protocol/http/HttpChannelChild.cpp | 251 ++++++++++++------ netwerk/protocol/http/HttpChannelChild.h | 71 +++-- netwerk/test/unit/test_progress.js | 61 ++++- netwerk/test/unit/test_xmlhttprequest.js | 45 +++- .../test/unit_ipc/test_xmlhttprequest_wrap.js | 3 +- 5 files changed, 311 insertions(+), 120 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 1d82e119e71d..edf67be49a4d 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -49,15 +49,32 @@ #include "nsMimeTypes.h" #include "nsNetUtil.h" -class Callback -{ - public: - virtual bool Run() = 0; -}; - namespace mozilla { namespace net { +class ChildChannelEvent +{ + public: + virtual void Run() = 0; +}; + +// Ensures any incoming IPDL msgs are queued during its lifetime, and flushes +// the queue when it goes out of scope. +class AutoEventEnqueuer +{ +public: + AutoEventEnqueuer(HttpChannelChild* channel) : mChannel(channel) + { + mChannel->BeginEventQueueing(); + } + ~AutoEventEnqueuer() + { + mChannel->FlushEventQueue(); + } +private: + HttpChannelChild *mChannel; +}; + // C++ file contents HttpChannelChild::HttpChannelChild() : mIsFromCache(PR_FALSE) @@ -65,7 +82,7 @@ HttpChannelChild::HttpChannelChild() , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) , mState(HCC_NEW) , mIPCOpen(false) - , mShouldBuffer(true) + , mQueuePhase(PHASE_UNQUEUED) { LOG(("Creating HttpChannelChild @%x\n", this)); } @@ -118,6 +135,67 @@ HttpChannelChild::ReleaseIPDLReference() Release(); } +void +HttpChannelChild::FlushEventQueue() +{ + NS_ABORT_IF_FALSE(mQueuePhase != PHASE_UNQUEUED, + "Queue flushing should not occur if PHASE_UNQUEUED"); + + // Queue already being flushed. + if (mQueuePhase != PHASE_QUEUEING) + return; + + if (mEventQueue.Length() > 0) { + // It is possible for new callbacks to be enqueued as we are + // flushing the queue, so the queue must not be cleared until + // all callbacks have run. + mQueuePhase = PHASE_FLUSHING; + + nsCOMPtr kungFuDeathGrip(this); + for (PRUint32 i = 0; i < mEventQueue.Length(); i++) { + mEventQueue[i]->Run(); + } + mEventQueue.Clear(); + } + + mQueuePhase = PHASE_UNQUEUED; +} + +class StartRequestEvent : public ChildChannelEvent +{ + public: + StartRequestEvent(HttpChannelChild* child, + const nsHttpResponseHead& responseHead, + const PRBool& useResponseHead, + const PRBool& isFromCache, + const PRBool& cacheEntryAvailable, + const PRUint32& cacheExpirationTime, + const nsCString& cachedCharset) + : mChild(child) + , mResponseHead(responseHead) + , mUseResponseHead(useResponseHead) + , mIsFromCache(isFromCache) + , mCacheEntryAvailable(cacheEntryAvailable) + , mCacheExpirationTime(cacheExpirationTime) + , mCachedCharset(cachedCharset) + {} + + void Run() + { + mChild->OnStartRequest(mResponseHead, mUseResponseHead, mIsFromCache, + mCacheEntryAvailable, mCacheExpirationTime, + mCachedCharset); + } + private: + HttpChannelChild* mChild; + nsHttpResponseHead mResponseHead; + PRBool mUseResponseHead; + PRBool mIsFromCache; + PRBool mCacheEntryAvailable; + PRUint32 mCacheExpirationTime; + nsCString mCachedCharset; +}; + bool HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& useResponseHead, @@ -125,6 +203,25 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, const PRBool& cacheEntryAvailable, const PRUint32& cacheExpirationTime, const nsCString& cachedCharset) +{ + if (ShouldEnqueue()) { + EnqueueEvent(new StartRequestEvent(this, responseHead, useResponseHead, + isFromCache, cacheEntryAvailable, + cacheExpirationTime, cachedCharset)); + } else { + OnStartRequest(responseHead, useResponseHead, isFromCache, + cacheEntryAvailable, cacheExpirationTime, cachedCharset); + } + return true; +} + +void +HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, + const PRBool& useResponseHead, + const PRBool& isFromCache, + const PRBool& cacheEntryAvailable, + const PRUint32& cacheExpirationTime, + const nsCString& cachedCharset) { LOG(("HttpChannelChild::RecvOnStartRequest [this=%x]\n", this)); @@ -140,31 +237,21 @@ HttpChannelChild::RecvOnStartRequest(const nsHttpResponseHead& responseHead, mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; + AutoEventEnqueuer ensureSerialDispatch(this); + nsresult rv = mListener->OnStartRequest(this, mListenerContext); - if (NS_FAILED(rv)) { + if (NS_SUCCEEDED(rv)) { + if (mResponseHead) + SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); + } else { // TODO: Cancel request: (bug 536317) // - Send Cancel msg to parent // - drop any in flight OnDataAvail msgs we receive // - make sure we do call OnStopRequest eventually - return true; } - - if (mResponseHead) - SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); - - bool ret = true; - nsCOMPtr kungFuDeathGrip(this); - for (PRUint32 i = 0; i < mBufferedCallbacks.Length(); i++) { - ret = mBufferedCallbacks[i]->Run(); - if (!ret) - break; - } - mBufferedCallbacks.Clear(); - mShouldBuffer = false; - return ret; } -class DataAvailableEvent : public Callback +class DataAvailableEvent : public ChildChannelEvent { public: DataAvailableEvent(HttpChannelChild* child, @@ -176,11 +263,7 @@ class DataAvailableEvent : public Callback , mOffset(offset) , mCount(count) {} - bool Run() - { - return mChild->OnDataAvailable(mData, mOffset, mCount); - } - + void Run() { mChild->OnDataAvailable(mData, mOffset, mCount); } private: HttpChannelChild* mChild; nsCString mData; @@ -193,11 +276,15 @@ HttpChannelChild::RecvOnDataAvailable(const nsCString& data, const PRUint32& offset, const PRUint32& count) { - DataAvailableEvent* event = new DataAvailableEvent(this, data, offset, count); - return BufferOrDispatch(event); + if (ShouldEnqueue()) { + EnqueueEvent(new DataAvailableEvent(this, data, offset, count)); + } else { + OnDataAvailable(data, offset, count); + } + return true; } -bool +void HttpChannelChild::OnDataAvailable(const nsCString& data, const PRUint32& offset, const PRUint32& count) @@ -218,18 +305,20 @@ HttpChannelChild::OnDataAvailable(const nsCString& data, NS_ASSIGNMENT_DEPEND); if (NS_FAILED(rv)) { // TODO: what to do here? Cancel request? Very unlikely to fail. - return false; + return; } + + AutoEventEnqueuer ensureSerialDispatch(this); + rv = mListener->OnDataAvailable(this, mListenerContext, stringStream, offset, count); stringStream->Close(); if (NS_FAILED(rv)) { // TODO: Cancel request: see OnStartRequest. Bug 536317 } - return true; } -class StopRequestEvent : public Callback +class StopRequestEvent : public ChildChannelEvent { public: StopRequestEvent(HttpChannelChild* child, @@ -237,11 +326,7 @@ class StopRequestEvent : public Callback : mChild(child) , mStatusCode(statusCode) {} - bool Run() - { - return mChild->OnStopRequest(mStatusCode); - } - + void Run() { mChild->OnStopRequest(mStatusCode); } private: HttpChannelChild* mChild; nsresult mStatusCode; @@ -250,11 +335,15 @@ class StopRequestEvent : public Callback bool HttpChannelChild::RecvOnStopRequest(const nsresult& statusCode) { - StopRequestEvent* event = new StopRequestEvent(this, statusCode); - return BufferOrDispatch(event); + if (ShouldEnqueue()) { + EnqueueEvent(new StopRequestEvent(this, statusCode)); + } else { + OnStopRequest(statusCode); + } + return true; } -bool +void HttpChannelChild::OnStopRequest(const nsresult& statusCode) { LOG(("HttpChannelChild::RecvOnStopRequest [this=%x status=%u]\n", @@ -264,21 +353,26 @@ HttpChannelChild::OnStopRequest(const nsresult& statusCode) mIsPending = PR_FALSE; mStatus = statusCode; - mListener->OnStopRequest(this, mListenerContext, statusCode); - mListener = 0; - mListenerContext = 0; - mCacheEntryAvailable = PR_FALSE; - if (mLoadGroup) - mLoadGroup->RemoveRequest(this, nsnull, statusCode); + { // We must flush the queue before we Send__delete__, + // so make sure this goes out of scope before then. + AutoEventEnqueuer ensureSerialDispatch(this); + + mListener->OnStopRequest(this, mListenerContext, statusCode); + mListener = 0; + mListenerContext = 0; + mCacheEntryAvailable = PR_FALSE; + + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nsnull, statusCode); + } // This calls NeckoChild::DeallocPHttpChannel(), which deletes |this| if IPDL // holds the last reference. Don't rely on |this| existing after here. PHttpChannelChild::Send__delete__(this); - return true; } -class ProgressEvent : public Callback +class ProgressEvent : public ChildChannelEvent { public: ProgressEvent(HttpChannelChild* child, @@ -288,11 +382,7 @@ class ProgressEvent : public Callback , mProgress(progress) , mProgressMax(progressMax) {} - bool Run() - { - return mChild->OnProgress(mProgress, mProgressMax); - } - + void Run() { mChild->OnProgress(mProgress, mProgressMax); } private: HttpChannelChild* mChild; PRUint64 mProgress, mProgressMax; @@ -302,11 +392,15 @@ bool HttpChannelChild::RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax) { - ProgressEvent* event = new ProgressEvent(this, progress, progressMax); - return BufferOrDispatch(event); + if (ShouldEnqueue()) { + EnqueueEvent(new ProgressEvent(this, progress, progressMax)); + } else { + OnProgress(progress, progressMax); + } + return true; } -bool +void HttpChannelChild::OnProgress(const PRUint64& progress, const PRUint64& progressMax) { @@ -317,6 +411,8 @@ HttpChannelChild::OnProgress(const PRUint64& progress, if (!mProgressSink) GetCallback(mProgressSink); + AutoEventEnqueuer ensureSerialDispatch(this); + // block socket status event after Cancel or OnStopRequest has been called. if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) @@ -326,11 +422,9 @@ HttpChannelChild::OnProgress(const PRUint64& progress, mProgressSink->OnProgress(this, nsnull, progress, progressMax); } } - - return true; } -class StatusEvent : public Callback +class StatusEvent : public ChildChannelEvent { public: StatusEvent(HttpChannelChild* child, @@ -340,11 +434,7 @@ class StatusEvent : public Callback , mStatus(status) , mStatusArg(statusArg) {} - bool Run() - { - return mChild->OnStatus(mStatus, mStatusArg); - } - + void Run() { mChild->OnStatus(mStatus, mStatusArg); } private: HttpChannelChild* mChild; nsresult mStatus; @@ -355,11 +445,15 @@ bool HttpChannelChild::RecvOnStatus(const nsresult& status, const nsString& statusArg) { - StatusEvent* event = new StatusEvent(this, status, statusArg); - return BufferOrDispatch(event); + if (ShouldEnqueue()) { + EnqueueEvent(new StatusEvent(this, status, statusArg)); + } else { + OnStatus(status, statusArg); + } + return true; } -bool +void HttpChannelChild::OnStatus(const nsresult& status, const nsString& statusArg) { @@ -369,27 +463,14 @@ HttpChannelChild::OnStatus(const nsresult& status, if (!mProgressSink) GetCallback(mProgressSink); + AutoEventEnqueuer ensureSerialDispatch(this); + // block socket status event after Cancel or OnStopRequest has been called. if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending && !(mLoadFlags & LOAD_BACKGROUND)) { mProgressSink->OnStatus(this, nsnull, status, statusArg.get()); } - - return true; -} - -bool -HttpChannelChild::BufferOrDispatch(Callback* callback) -{ - if (mShouldBuffer) { - mBufferedCallbacks.AppendElement(callback); - return true; - } - - bool result = callback->Run(); - delete callback; - return result; } //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 7e9e093413ae..faf30eb0466f 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -58,14 +58,12 @@ #include "nsIResumableChannel.h" #include "nsIProxiedChannel.h" #include "nsITraceableChannel.h" -#include "mozilla/Mutex.h" - -class nsIRunnable; -class Callback; namespace mozilla { namespace net { +class ChildChannelEvent; + // TODO: replace with IPDL states: bug 536319 enum HttpChannelChildState { HCC_NEW, @@ -148,29 +146,70 @@ private: enum HttpChannelChildState mState; bool mIPCOpen; - // Workaround for Necko re-entrancy dangers. We buffer all messages - // received until OnStartRequest completes. - nsTArray > mBufferedCallbacks; - bool mShouldBuffer; + // Workaround for Necko re-entrancy dangers. We buffer IPDL messages in a + // queue if still dispatching previous one(s) to listeners/observers. + // Otherwise synchronous XMLHttpRequests and/or other code that spins the + // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for + // instance) to be called before mListener->OnStartRequest has completed. + void BeginEventQueueing(); + void FlushEventQueue(); + void EnqueueEvent(ChildChannelEvent* callback); + bool ShouldEnqueue(); - bool BufferOrDispatch(Callback* callback); + nsTArray > mEventQueue; + enum { + PHASE_UNQUEUED, + PHASE_QUEUEING, + PHASE_FLUSHING + } mQueuePhase; - // This class does not actually implement the stream listener interface. - // These functions actually perform the actions associated with the - // corresponding IPDL receivers above. - bool OnDataAvailable(const nsCString& data, + void OnStartRequest(const nsHttpResponseHead& responseHead, + const PRBool& useResponseHead, + const PRBool& isFromCache, + const PRBool& cacheEntryAvailable, + const PRUint32& cacheExpirationTime, + const nsCString& cachedCharset); + void OnDataAvailable(const nsCString& data, const PRUint32& offset, const PRUint32& count); - bool OnStopRequest(const nsresult& statusCode); - bool OnProgress(const PRUint64& progress, const PRUint64& progressMax); - bool OnStatus(const nsresult& status, const nsString& statusArg); + void OnStopRequest(const nsresult& statusCode); + void OnProgress(const PRUint64& progress, const PRUint64& progressMax); + void OnStatus(const nsresult& status, const nsString& statusArg); + friend class AutoEventEnqueuer; + friend class StartRequestEvent; friend class StopRequestEvent; friend class DataAvailableEvent; friend class ProgressEvent; friend class StatusEvent; }; +//----------------------------------------------------------------------------- +// inline functions +//----------------------------------------------------------------------------- + +inline void +HttpChannelChild::BeginEventQueueing() +{ + if (mQueuePhase == PHASE_FLUSHING) + return; + // Store incoming IPDL messages for later. + mQueuePhase = PHASE_QUEUEING; +} + +inline bool +HttpChannelChild::ShouldEnqueue() +{ + return mQueuePhase != PHASE_UNQUEUED; +} + +inline void +HttpChannelChild::EnqueueEvent(ChildChannelEvent* callback) +{ + mEventQueue.AppendElement(callback); +} + + } // namespace net } // namespace mozilla diff --git a/netwerk/test/unit/test_progress.js b/netwerk/test/unit/test_progress.js index d432c2bc6b45..607213653c36 100644 --- a/netwerk/test/unit/test_progress.js +++ b/netwerk/test/unit/test_progress.js @@ -10,26 +10,81 @@ var last = 0, max = 0; const STATUS_RECEIVING_FROM = 0x804b0006; const LOOPS = 50000; +const TYPE_ONSTATUS = 1; +const TYPE_ONPROGRESS = 2; +const TYPE_ONSTARTREQUEST = 3; +const TYPE_ONDATAAVAILABLE = 4; +const TYPE_ONSTOPREQUEST = 5; + var progressCallback = { + _listener: null, + _got_onstartrequest: false, + _got_onstatus_after_onstartrequest: false, + _last_callback_handled: null, + QueryInterface: function (iid) { - if (iid.equals(Ci.nsISupports) || iid.equals(Ci.nsIProgressEventSink)) + if (iid.equals(Ci.nsISupports) || + iid.equals(Ci.nsIProgressEventSink) || + iid.equals(Ci.nsIStreamListener) || + iid.equals(Ci.nsIRequestObserver)) return this; throw Cr.NS_ERROR_NO_INTERFACE; }, getInterface: function (iid) { - if (iid.equals(Ci.nsIProgressEventSink)) + if (iid.equals(Ci.nsIProgressEventSink) || + iid.equals(Ci.nsIStreamListener) || + iid.equals(Ci.nsIRequestObserver)) return this; throw Cr.NS_ERROR_NO_INTERFACE; }, + onStartRequest: function(request, context) { + do_check_eq(this._last_callback_handled, TYPE_ONSTATUS); + this._got_onstartrequest = true; + this._last_callback_handled = TYPE_ONSTARTREQUEST; + + this._listener = new ChannelListener(checkRequest, request); + this._listener.onStartRequest(request, context); + }, + + onDataAvailable: function(request, context, data, offset, count) { + do_check_eq(this._last_callback_handled, TYPE_ONPROGRESS); + this._last_callback_handled = TYPE_ONDATAAVAILABLE; + + this._listener.onDataAvailable(request, context, data, offset, count); + }, + + onStopRequest: function(request, context, status) { + do_check_eq(this._last_callback_handled, TYPE_ONDATAAVAILABLE); + do_check_true(this._got_onstatus_after_onstartrequest); + this._last_callback_handled = TYPE_ONSTOPREQUEST; + + this._listener.onStopRequest(request, context, status); + delete this._listener; + }, + onProgress: function (request, context, progress, progressMax) { + do_check_eq(this._last_callback_handled, TYPE_ONSTATUS); + this._last_callback_handled = TYPE_ONPROGRESS; + do_check_eq(mStatus, STATUS_RECEIVING_FROM); last = progress; max = progressMax; }, onStatus: function (request, context, status, statusArg) { + if (!this._got_onstartrequest) { + // Ensure that all messages before onStartRequest are onStatus + if (this._last_callback_handled) + do_check_eq(this._last_callback_handled, TYPE_ONSTATUS); + } else if (this._last_callback_handled == TYPE_ONSTARTREQUEST) { + this._got_onstatus_after_onstartrequest = true; + } else { + do_check_eq(this._last_callback_handled, TYPE_ONDATAAVAILABLE); + } + this._last_callback_handled = TYPE_ONSTATUS; + do_check_eq(statusArg, "localhost"); mStatus = status; }, @@ -41,7 +96,7 @@ function run_test() { httpserver.registerPathHandler(testpath, serverHandler); httpserver.start(4444); var channel = setupChannel(testpath); - channel.asyncOpen(new ChannelListener(checkRequest, channel), null); + channel.asyncOpen(progressCallback, null); do_test_pending(); } diff --git a/netwerk/test/unit/test_xmlhttprequest.js b/netwerk/test/unit/test_xmlhttprequest.js index 91a5e625a071..273316ad590c 100644 --- a/netwerk/test/unit/test_xmlhttprequest.js +++ b/netwerk/test/unit/test_xmlhttprequest.js @@ -5,27 +5,44 @@ var httpserver = new nsHttpServer(); var testpath = "/simple"; var httpbody = "0123456789"; +function createXHR(async) +{ + var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Ci.nsIXMLHttpRequest); + xhr.open("GET", "http://localhost:4444" + testpath, async); + return xhr; +} + +function checkResults(xhr) +{ + if (xhr.readyState != 4) + return false; + + do_check_eq(xhr.status, 200); + do_check_eq(xhr.responseText, httpbody); + + var root_node = xhr.responseXML.getElementsByTagName('root').item(0); + do_check_eq(root_node.firstChild.data, "0123456789"); + return true; +} + function run_test() { httpserver.registerPathHandler(testpath, serverHandler); httpserver.start(4444); - var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - xhr.open("GET", "http://localhost:4444" + testpath, true); - xhr.onreadystatechange = function(event) { - if (xhr.readyState == 4) { - do_check_eq(xhr.status, 200); - do_check_eq(xhr.responseText, httpbody); - - var root_node = xhr.responseXML.getElementsByTagName('root').item(0); - do_check_eq(root_node.firstChild.data, "0123456789"); + // Test sync XHR sending + var sync = createXHR(false); + sync.send(null); + checkResults(sync); + // Test async XHR sending + let async = createXHR(true); + async.onreadystatechange = function(event) { + if (checkResults(async)) httpserver.stop(do_test_finished); - } - } - xhr.send(null); - + }; + async.send(null); do_test_pending(); } diff --git a/netwerk/test/unit_ipc/test_xmlhttprequest_wrap.js b/netwerk/test/unit_ipc/test_xmlhttprequest_wrap.js index d9a5df4f0748..00b4a0b85f8d 100644 --- a/netwerk/test/unit_ipc/test_xmlhttprequest_wrap.js +++ b/netwerk/test/unit_ipc/test_xmlhttprequest_wrap.js @@ -1,4 +1,3 @@ function run_test() { - _dump('FIXME/bug 564351: temporarily disabled for perma-crash on e10s tinderbox'); - //run_test_in_child("../unit/test_xmlhttprequest.js"); + run_test_in_child("../unit/test_xmlhttprequest.js"); } From 28fceb25684636d35abf981bbbc2bbef90e5e893 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Tue, 10 Aug 2010 17:50:30 -0700 Subject: [PATCH 208/369] Bug 584863: make nsHttpChannel::AsyncOpen fail immediately if channel already cancelled. r=biesi --- netwerk/protocol/http/nsHttpChannel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 078e25fb7119..a5d7ac77ae68 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -3249,6 +3249,9 @@ nsHttpChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *context) nsresult rv; + if (mCanceled) + return mStatus; + rv = NS_CheckPortSafety(mURI); if (NS_FAILED(rv)) return rv; From 0b88c1dad41109139cb2eb7ac29fafc1d4055299 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Sat, 31 Jul 2010 12:07:13 -0400 Subject: [PATCH 209/369] Bug 564351 - e10s: fix XMLHttpRequests in xpcshell. r=bent --- ipc/testshell/XPCShellEnvironment.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index 0d4ccab5fd69..c5f139631d80 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -1215,6 +1215,9 @@ bool XPCShellEnvironment::EvaluateString(const nsString& aString, nsString* aResult) { + XPCShellEnvironment* env = Environment(mCx); + XPCShellEnvironment::AutoContextPusher pusher(env); + JSAutoRequest ar(mCx); JS_ClearPendingException(mCx); From e8ac5034262549b4b2907263a876c0bc73686351 Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Mon, 2 Aug 2010 13:29:46 -0700 Subject: [PATCH 210/369] + Updated the iQ license block as specified in https://bugzilla.mozilla.org/show_bug.cgi?id=582025 --- browser/base/content/tabview/iq.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 77c323265d8b..23a981066d21 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -22,9 +22,10 @@ * Aza Raskin * Michael Yoshitaka Erlewine * - * Some portions copied from: - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ + * This file incorporates work from: + * jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js + * This incorporated work is covered by the following copyright and + * permission notice: * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license From dbcf0436f653ac93efc1e95a7294886ca3e78f9b Mon Sep 17 00:00:00 2001 From: Aza Raskin Date: Mon, 2 Aug 2010 13:38:29 -0700 Subject: [PATCH 211/369] + Also includes the jQuery inclusion text with utils.js --- browser/base/content/tabview/modules/utils.jsm | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 4445eaa2fda2..9e0aca0371fa 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -22,9 +22,10 @@ * Ian Gilman * Michael Yoshitaka Erlewine * - * Some portions copied from: - * jQuery JavaScript Library v1.4.2 - * http://jquery.com/ + * This file incorporates work from: + * jQuery JavaScript Library v1.4.2: http://code.jquery.com/jquery-1.4.2.js + * This incorporated work is covered by the following copyright and + * permission notice: * Copyright 2010, John Resig * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license From d3c2e2f9f54f07844240f5eb65d9674b83b740cc Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 3 Aug 2010 22:20:40 +0200 Subject: [PATCH 212/369] Bug 584170 - Make test_service_cluster.js less prone to timing problems [r=mconnor] --- services/sync/tests/unit/test_service_cluster.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/services/sync/tests/unit/test_service_cluster.js b/services/sync/tests/unit/test_service_cluster.js index 710a98b3b57e..56d200a4c66c 100644 --- a/services/sync/tests/unit/test_service_cluster.js +++ b/services/sync/tests/unit/test_service_cluster.js @@ -120,24 +120,26 @@ function test_updateCluster() { do_check_eq(Svc.Prefs.get("lastClusterUpdate"), null); _("Set the cluster URL."); + let before = Date.now(); do_check_true(Weave.Service._updateCluster()); do_check_eq(Weave.Service.clusterURL, "http://weave.user.node/"); - let lastUpdate = parseInt(Svc.Prefs.get("lastClusterUpdate"), 10); - do_check_true(lastUpdate > Date.now() - 1000); + let lastUpdate = parseFloat(Svc.Prefs.get("lastClusterUpdate")); + do_check_true(lastUpdate >= before); _("Trying to update the cluster URL within the backoff timeout won't do anything."); do_check_false(Weave.Service._updateCluster()); do_check_eq(Weave.Service.clusterURL, "http://weave.user.node/"); - do_check_eq(parseInt(Svc.Prefs.get("lastClusterUpdate"), 10), lastUpdate); + do_check_eq(parseFloat(Svc.Prefs.get("lastClusterUpdate")), lastUpdate); _("Time travel 30 mins into the past and the update will work."); Weave.Service.username = "janedoe"; Svc.Prefs.set("lastClusterUpdate", (lastUpdate - 30*60*1000).toString()); + before = Date.now(); do_check_true(Weave.Service._updateCluster()); do_check_eq(Weave.Service.clusterURL, "http://weave.cluster.url/"); - lastUpdate = parseInt(Svc.Prefs.get("lastClusterUpdate"), 10); - do_check_true(lastUpdate > Date.now() - 1000); + lastUpdate = parseFloat(Svc.Prefs.get("lastClusterUpdate")); + do_check_true(lastUpdate >= before); } finally { Svc.Prefs.resetBranch(""); From 9c826435e5df7bbd0bbce1d6c144d21bb3dfd71d Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 3 Aug 2010 19:57:53 +0200 Subject: [PATCH 213/369] Bug 584040 - Fix leaky tests [r=mconnor] Avoid holding on to the original nsIFormHistory2 service object in FormNotifier.js and explicitly null the reference on shutdown. --- services/sync/FormNotifier.js | 46 ++++++++++--------- .../tests/unit/test_engines_forms_store.js | 3 -- .../sync/tests/unit/test_utils_queryAsync.js | 3 -- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/services/sync/FormNotifier.js b/services/sync/FormNotifier.js index 3f5546ea615c..82e23746eccc 100644 --- a/services/sync/FormNotifier.js +++ b/services/sync/FormNotifier.js @@ -10,43 +10,47 @@ function FormNotifier() { let obs = Cc["@mozilla.org/observer-service;1"]. getService(Ci.nsIObserverService); - for (let keyval in Iterator(baseForm)) { - // Make a local copy of these values - let [key, val] = keyval; - - // Don't overwrite something we already have - if (key in this) - continue; - - // Make a getter to grab non-functions - if (typeof val != "function") { - this.__defineGetter__(key, function() baseForm[key]); - continue; - } - - // XXX Bug 568707 Make use of "key" to prevent it from disappearing - (function(){})(key); - - // Wrap the function with notifications - this[key] = function() { + function wrap(method) { + return function() { let args = Array.slice(arguments); let notify = function(type) { obs.notifyObservers(null, "form-notifier", JSON.stringify({ args: args, - func: key, + func: method, type: type })); }; notify("before"); try { - return val.apply(this, arguments); + return baseForm[method].apply(this, arguments); } finally { notify("after"); } }; } + + this.__defineGetter__("DBConnection", function() baseForm.DBConnection); + this.__defineGetter__("hasEntries", function() baseForm.hasEntries); + + this.addEntry = wrap("addEntry"); + this.entryExists = wrap("entryExists"); + this.nameExists = wrap("nameExists"); + this.removeAllEntries = wrap("removeAllEntries"); + this.removeEntriesByTimeframe = wrap("removeEntriesByTimeframe"); + this.removeEntriesForName = wrap("removeEntriesForName"); + this.removeEntry = wrap("removeEntry"); + + // Avoid leaking the base form service. + obs.addObserver({ + observe: function() { + obs.removeObserver(this, "profile-before-change"); + baseForm = null; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, + Ci.nsIObserver]) + }, "profile-before-change", true); } FormNotifier.prototype = { classDescription: "Form Notifier Wrapper", diff --git a/services/sync/tests/unit/test_engines_forms_store.js b/services/sync/tests/unit/test_engines_forms_store.js index 59f794c771ba..9caa4c68c2b6 100644 --- a/services/sync/tests/unit/test_engines_forms_store.js +++ b/services/sync/tests/unit/test_engines_forms_store.js @@ -3,9 +3,6 @@ Cu.import("resource://services-sync/engines/forms.js"); Cu.import("resource://services-sync/type_records/forms.js"); function run_test() { - //XXXzpao test is disabled (bug 583985) - return; - let store = new FormEngine()._store; _("Remove any existing entries"); diff --git a/services/sync/tests/unit/test_utils_queryAsync.js b/services/sync/tests/unit/test_utils_queryAsync.js index 766807f92e59..52d0c8597522 100644 --- a/services/sync/tests/unit/test_utils_queryAsync.js +++ b/services/sync/tests/unit/test_utils_queryAsync.js @@ -2,9 +2,6 @@ _("Make sure queryAsync will synchronously fetch rows for a query asyncly"); Cu.import("resource://services-sync/util.js"); function run_test() { - //XXXzpao test is disabled (bug 583985) - return; - _("Using the form service to test queries"); function c(query) Svc.Form.DBConnection.createStatement(query); From c719dfb93c552eee6d783f91525d7911d37ea8c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=E2=80=99Shannessy?= Date: Wed, 4 Aug 2010 14:50:44 +0200 Subject: [PATCH 214/369] Bug 584040 - Fix leaky tests [r=mconnor] Make sure we explicitly nullify the cached services in the BookmarksStore & BookmarksTracker. Also, explicitly finalize the statement if it was created. --- services/sync/modules/engines/bookmarks.js | 75 ++++++++++++------- .../sync/tests/unit/test_bookmark_order.js | 3 - .../tests/unit/test_bookmark_predecessor.js | 3 - 3 files changed, 49 insertions(+), 32 deletions(-) diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 886aa36eda60..b50ed0c09011 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -249,6 +249,17 @@ BookmarksEngine.prototype = { function BookmarksStore(name) { Store.call(this, name); + + // Explicitly nullify our references to our cached services so we don't leak + Observers.add("places-shutdown", function() { + this.__bms = null; + this.__hsvc = null; + this.__ls = null; + this.__ms = null; + this.__ts = null; + if (this.__frecencyStm) + this.__frecencyStm.finalize(); + }, this); } BookmarksStore.prototype = { __proto__: Store.prototype, @@ -273,22 +284,24 @@ BookmarksStore.prototype = { get _ls() { if (!this.__ls) this.__ls = Cc["@mozilla.org/browser/livemark-service;2"]. - getService(Ci.nsILivemarkService); + getService(Ci.nsILivemarkService); return this.__ls; }, + __ms: null, get _ms() { - let ms; - try { - ms = Cc["@mozilla.org/microsummary/service;1"]. - getService(Ci.nsIMicrosummaryService); - } catch (e) { - ms = null; - this._log.warn("Could not load microsummary service"); - this._log.debug(e); + if (!this.__ms) { + try { + this.__ms = Cc["@mozilla.org/microsummary/service;1"]. + getService(Ci.nsIMicrosummaryService); + } catch (e) { + this._log.warn("Could not load microsummary service"); + this._log.debug(e); + // Redefine our getter so we won't keep trying to get the service + this.__defineGetter__("_ms", function() null); + } } - this.__defineGetter__("_ms", function() ms); - return ms; + return this.__ms; }, __ts: null, @@ -835,14 +848,16 @@ BookmarksStore.prototype = { return record; }, + __frecencyStm: null, get _frecencyStm() { - this._log.trace("Creating SQL statement: _frecencyStm"); - let stm = Svc.History.DBConnection.createStatement( - "SELECT frecency " + - "FROM moz_places " + - "WHERE url = :url"); - this.__defineGetter__("_frecencyStm", function() stm); - return stm; + if (!this.__frecencyStm) { + this._log.trace("Creating SQL statement: _frecencyStm"); + this.__frecencyStm = Svc.History.DBConnection.createStatement( + "SELECT frecency " + + "FROM moz_places " + + "WHERE url = :url"); + } + return this.__frecencyStm; }, _calculateIndex: function _calculateIndex(record) { @@ -987,22 +1002,30 @@ function BookmarksTracker(name) { this.ignoreID(guid); Svc.Bookmark.addObserver(this, false); + + // Explicitly nullify our references to our cached services so we don't leak + Observers.add("places-shutdown", function() { + this.__ls = null; + this.__bms = null; + }, this); } BookmarksTracker.prototype = { __proto__: Tracker.prototype, + __bms: null, get _bms() { - let bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. - getService(Ci.nsINavBookmarksService); - this.__defineGetter__("_bms", function() bms); - return bms; + if (!this.__bms) + this.__bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + return this.__bms; }, + __ls: null, get _ls() { - let ls = Cc["@mozilla.org/browser/livemark-service;2"]. - getService(Ci.nsILivemarkService); - this.__defineGetter__("_ls", function() ls); - return ls; + if (!this.__ls) + this.__ls = Cc["@mozilla.org/browser/livemark-service;2"]. + getService(Ci.nsILivemarkService); + return this.__ls; }, QueryInterface: XPCOMUtils.generateQI([ diff --git a/services/sync/tests/unit/test_bookmark_order.js b/services/sync/tests/unit/test_bookmark_order.js index ecb2f44a753f..dba583eea0cd 100644 --- a/services/sync/tests/unit/test_bookmark_order.js +++ b/services/sync/tests/unit/test_bookmark_order.js @@ -39,9 +39,6 @@ function check(expected) { } function run_test() { - //XXXzpao test is disabled (bug 583985) - return; - _("Starting with a clean slate of no bookmarks"); let store = new (new BookmarksEngine())._storeObj(); store.wipe(); diff --git a/services/sync/tests/unit/test_bookmark_predecessor.js b/services/sync/tests/unit/test_bookmark_predecessor.js index a54ac15792f2..6007d53b76a6 100644 --- a/services/sync/tests/unit/test_bookmark_predecessor.js +++ b/services/sync/tests/unit/test_bookmark_predecessor.js @@ -3,9 +3,6 @@ Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/util.js"); function run_test() { - //XXXzpao test is disabled (bug 583985) - return; - _("Starting with a clean slate of no bookmarks"); let store = new (new BookmarksEngine())._storeObj(); store.wipe(); From e414ab20ea1fe62e124545d4eae8ebb68606158a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=E2=80=99Shannessy?= Date: Wed, 4 Aug 2010 14:59:34 +0200 Subject: [PATCH 215/369] Bug 545752 - DB Statements aren't finalized in HistoryStore [r=mconnor] Also nullifies the cached service so we don't leak. --- services/sync/modules/engines/history.js | 47 ++++++++++++++---------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index b367dc3706e9..526fa3122fb2 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -86,27 +86,44 @@ HistoryEngine.prototype = { function HistoryStore(name) { Store.call(this, name); + + // Explicitly nullify our references to our cached services so we don't leak + Observers.add("places-shutdown", function() { + for each([query, stmt] in Iterator(this._stmts)) + stmt.finalize(); + this.__hsvc = null; + this._stmts = []; + }, this); } HistoryStore.prototype = { __proto__: Store.prototype, + __hsvc: null, get _hsvc() { - let hsvc = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsINavHistoryService); - hsvc.QueryInterface(Ci.nsIGlobalHistory2); - hsvc.QueryInterface(Ci.nsIBrowserHistory); - hsvc.QueryInterface(Ci.nsPIPlacesDatabase); - this.__defineGetter__("_hsvc", function() hsvc); - return hsvc; + if (!this.__hsvc) + this.__hsvc = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService). + QueryInterface(Ci.nsIGlobalHistory2). + QueryInterface(Ci.nsIBrowserHistory). + QueryInterface(Ci.nsPIPlacesDatabase); + return this.__hsvc; }, get _db() { return this._hsvc.DBConnection; }, + _stmts: [], + _getStmt: function(query) { + if (query in this._stmts) + return this._stmts[query]; + + this._log.trace("Creating SQL statement: " + query); + return this._stmts[query] = this._db.createStatement(query); + }, + get _visitStm() { - this._log.trace("Creating SQL statement: _visitStm"); - let stm = this._db.createStatement( + return this._getStmt( "SELECT visit_type type, visit_date date " + "FROM moz_historyvisits_view " + "WHERE place_id = (" + @@ -114,13 +131,10 @@ HistoryStore.prototype = { "FROM moz_places_view " + "WHERE url = :url) " + "ORDER BY date DESC LIMIT 10"); - this.__defineGetter__("_visitStm", function() stm); - return stm; }, get _urlStm() { - this._log.trace("Creating SQL statement: _urlStm"); - let stm = this._db.createStatement( + return this._getStmt( "SELECT url, title, frecency " + "FROM moz_places_view " + "WHERE id = (" + @@ -130,20 +144,15 @@ HistoryStore.prototype = { "SELECT id " + "FROM moz_anno_attributes " + "WHERE name = '" + GUID_ANNO + "'))"); - this.__defineGetter__("_urlStm", function() stm); - return stm; }, get _allUrlStm() { - this._log.trace("Creating SQL statement: _allUrlStm"); - let stm = this._db.createStatement( + return this._getStmt( "SELECT url " + "FROM moz_places_view " + "WHERE last_visit_date > :cutoff_date " + "ORDER BY frecency DESC " + "LIMIT :max_results"); - this.__defineGetter__("_allUrlStm", function() stm); - return stm; }, // See bug 320831 for why we use SQL here From 967ecb4058010a04b99b94494e95ffd4adc7eee2 Mon Sep 17 00:00:00 2001 From: Mike Connor Date: Wed, 4 Aug 2010 10:42:09 -0400 Subject: [PATCH 216/369] [mq]: weakRefForms --- services/sync/modules/engines/forms.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index 69bec968a95c..07ece07dd932 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -207,14 +207,15 @@ function FormTracker(name) { // nsHTMLFormElement doesn't use the normal observer/observe pattern and looks // up nsIFormSubmitObservers to .notify() them so add manually to observers Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService). - addObserver(this, "earlyformsubmit", false); + addObserver(this, "earlyformsubmit", true); } FormTracker.prototype = { __proto__: Tracker.prototype, QueryInterface: XPCOMUtils.generateQI([ Ci.nsIFormSubmitObserver, - Ci.nsIObserver]), + Ci.nsIObserver, + Ci.nsISupportsWeakReference]), trackEntry: function trackEntry(name, value) { this.addChangedID(FormWrapper.getGUID(name, value)); From 213217a82e20c0e7ac41c70ce25dcef63d0adcf8 Mon Sep 17 00:00:00 2001 From: Mike Connor Date: Wed, 4 Aug 2010 11:07:50 -0400 Subject: [PATCH 217/369] Bug 584369 - history sync is busted on tip, r=philiKON --- services/sync/modules/engines/history.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index 526fa3122fb2..9f8a7d0d6f83 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -88,7 +88,7 @@ function HistoryStore(name) { Store.call(this, name); // Explicitly nullify our references to our cached services so we don't leak - Observers.add("places-shutdown", function() { + Svc.Obs.add("places-shutdown", function() { for each([query, stmt] in Iterator(this._stmts)) stmt.finalize(); this.__hsvc = null; From bf06e6e72e9e9f6c261600895a67f95c88e3ff56 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 4 Aug 2010 21:07:27 +0200 Subject: [PATCH 218/369] Bug 584402 - Audit references across XPCOM borders [r=mconnor] Nuke refs to XPCOM services on shutdown. --- services/sync/modules/engines/prefs.js | 75 +++++++++++++++----------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/services/sync/modules/engines/prefs.js b/services/sync/modules/engines/prefs.js index 757954b63fdf..dc75956cdc46 100644 --- a/services/sync/modules/engines/prefs.js +++ b/services/sync/modules/engines/prefs.js @@ -76,25 +76,30 @@ PrefsEngine.prototype = { function PrefStore(name) { Store.call(this, name); + Svc.Obs.add("profile-before-change", function() { + this.__prefs = null; + this.__syncPrefs = null; + }, this); } PrefStore.prototype = { __proto__: Store.prototype, + __prefs: null, get _prefs() { - let prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - this.__defineGetter__("_prefs", function() prefs); - return prefs; + if (!this.__prefs) + this.__prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch2); + return this.__prefs; }, + __syncPrefs: null, get _syncPrefs() { - let service = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - let syncPrefs = service.getBranch(WEAVE_SYNC_PREFS).getChildList("", {}); - - this.__defineGetter__("_syncPrefs", function() syncPrefs); - return syncPrefs; + if (!this.__syncPrefs) + this.__syncPrefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(WEAVE_SYNC_PREFS). + getChildList("", {}); + return this.__syncPrefs; }, _getAllPrefs: function PrefStore__getAllPrefs() { @@ -211,7 +216,7 @@ PrefStore.prototype = { }, remove: function PrefStore_remove(record) { - this._log.trace("Ignoring remove request") + this._log.trace("Ignoring remove request"); }, update: function PrefStore_update(record) { @@ -227,36 +232,44 @@ PrefStore.prototype = { function PrefTracker(name) { Tracker.call(this, name); this._prefs.addObserver("", this, false); + Svc.Obs.add("profile-before-change", this); } PrefTracker.prototype = { __proto__: Tracker.prototype, + __prefs: null, get _prefs() { - let prefs = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch2); - - this.__defineGetter__("_prefs", function() prefs); - return prefs; + if (!this.__prefs) + this.__prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch2); + return this.__prefs; }, + __syncPrefs: null, get _syncPrefs() { - let service = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefService); - let syncPrefs = service.getBranch(WEAVE_SYNC_PREFS).getChildList("", {}); - - this.__defineGetter__("_syncPrefs", function() syncPrefs); - return syncPrefs; + if (!this.__syncPrefs) + this.__syncPrefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(WEAVE_SYNC_PREFS). + getChildList("", {}); + return this.__syncPrefs; }, - /* 25 points per pref change */ observe: function(aSubject, aTopic, aData) { - if (aTopic != "nsPref:changed") - return; - - if (this._syncPrefs.indexOf(aData) != -1) { - this.score += 1; - this.addChangedID(WEAVE_PREFS_GUID); - this._log.trace("Preference " + aData + " changed"); + switch (aTopic) { + case "profile-before-change": + this.__prefs = null; + this.__syncPrefs = null; + this._prefs.removeObserver("", this); + break; + case "nsPref:changed": + // 25 points per pref change + if (this._syncPrefs.indexOf(aData) != -1) { + this.score += 1; + this.addChangedID(WEAVE_PREFS_GUID); + this._log.trace("Preference " + aData + " changed"); + } + break; } } }; From e97c6b419fbd3d50c6bfc30382e11d6e4f3eba4e Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 4 Aug 2010 21:07:53 +0200 Subject: [PATCH 219/369] Bug 584402 - Audit references across XPCOM borders [r=mconnor] Use weak references for observers. --- services/sync/modules/engines/bookmarks.js | 5 +++-- services/sync/modules/engines/history.js | 5 +++-- services/sync/modules/engines/tabs.js | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index b50ed0c09011..0e8c564dad82 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -1001,7 +1001,7 @@ function BookmarksTracker(name) { for (let guid in kSpecialIds) this.ignoreID(guid); - Svc.Bookmark.addObserver(this, false); + Svc.Bookmark.addObserver(this, true); // Explicitly nullify our references to our cached services so we don't leak Observers.add("places-shutdown", function() { @@ -1030,7 +1030,8 @@ BookmarksTracker.prototype = { QueryInterface: XPCOMUtils.generateQI([ Ci.nsINavBookmarkObserver, - Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS + Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS, + Ci.nsISupportsWeakReference ]), /** diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index 9f8a7d0d6f83..98d61b6457f8 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -257,14 +257,15 @@ HistoryStore.prototype = { function HistoryTracker(name) { Tracker.call(this, name); - Svc.History.addObserver(this, false); + Svc.History.addObserver(this, true); } HistoryTracker.prototype = { __proto__: Tracker.prototype, QueryInterface: XPCOMUtils.generateQI([ Ci.nsINavHistoryObserver, - Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS + Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS, + Ci.nsISupportsWeakReference ]), onBeginUpdateBatch: function HT_onBeginUpdateBatch() {}, diff --git a/services/sync/modules/engines/tabs.js b/services/sync/modules/engines/tabs.js index e8eab8e2f85c..5cac4cca788b 100644 --- a/services/sync/modules/engines/tabs.js +++ b/services/sync/modules/engines/tabs.js @@ -232,7 +232,7 @@ function TabTracker(name) { this.onTab = Utils.bind2(this, this.onTab); // Register as an observer so we can catch windows opening and closing: - Svc.WinWatcher.registerNotification(this); + Svc.Obs.add("domwindowopened", this); // Also register listeners on already open windows let wins = Svc.WinMediator.getEnumerator("navigator:browser"); From 3960a6887160cf1adf506bca27da60c7efc445bd Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 5 Aug 2010 16:52:17 +0200 Subject: [PATCH 220/369] Bug 584486 - Changing password via web leads to unknown error [r=mconnor] --- services/sync/modules/service.js | 11 ++- .../sync/tests/unit/test_service_sync_401.js | 71 +++++++++++++++++++ 2 files changed, 79 insertions(+), 3 deletions(-) create mode 100644 services/sync/tests/unit/test_service_sync_401.js diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index e01f332f8df8..3eb0bf5e1c5e 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -1317,8 +1317,6 @@ WeaveSvc.prototype = { // we'll handle that later Status.resetBackoff(); - this.globalScore = 0; - // Ping the server with a special info request once a day let infoURL = this.infoURL; let now = Math.floor(Date.now() / 1000); @@ -1330,8 +1328,15 @@ WeaveSvc.prototype = { // Figure out what the last modified time is for each collection let info = new Resource(infoURL).get(); - if (!info.success) + if (!info.success) { + if (info.status == 401) { + this.logout(); + Status.login = LOGIN_FAILED_LOGIN_REJECTED; + } throw "aborting sync, failed to get collections"; + } + + this.globalScore = 0; // Convert the response to an object and read out the modified times for each (let engine in [Clients].concat(Engines.getAll())) diff --git a/services/sync/tests/unit/test_service_sync_401.js b/services/sync/tests/unit/test_service_sync_401.js new file mode 100644 index 000000000000..ab348dd0a321 --- /dev/null +++ b/services/sync/tests/unit/test_service_sync_401.js @@ -0,0 +1,71 @@ +Cu.import("resource://services-sync/service.js"); + +function login_handler(request, response) { + // btoa('johndoe:ilovejane') == am9obmRvZTppbG92ZWphbmU= + let body; + if (request.hasHeader("Authorization") && + request.getHeader("Authorization") == "Basic am9obmRvZTppbG92ZWphbmU=") { + body = "{}"; + response.setStatusLine(request.httpVersion, 200, "OK"); + } else { + body = "Unauthorized"; + response.setStatusLine(request.httpVersion, 401, "Unauthorized"); + } + response.bodyOutputStream.write(body, body.length); +} + +function run_test() { + let logger = Log4Moz.repository.rootLogger; + Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender()); + + do_test_pending(); + let server = httpd_setup({ + "/1.0/johndoe/info/collections": login_handler + }); + + const GLOBAL_SCORE = 42; + + try { + _("Set up test fixtures."); + Weave.Service.serverURL = "http://localhost:8080/"; + Weave.Service.clusterURL = "http://localhost:8080/"; + Weave.Service.username = "johndoe"; + Weave.Service.password = "ilovejane"; + Weave.Service.passphrase = "foo"; + Weave.Service.globalScore = GLOBAL_SCORE; + // Avoid daily ping + Weave.Svc.Prefs.set("lastPing", Math.floor(Date.now() / 1000)); + + let threw = false; + Weave.Svc.Obs.add("weave:service:sync:error", function (subject, data) { + threw = true; + }); + + _("Initial state: We're successfully logged in."); + Weave.Service.login(); + do_check_true(Weave.Service.isLoggedIn); + do_check_eq(Weave.Status.login, Weave.LOGIN_SUCCEEDED); + + _("Simulate having changed the password somehwere else."); + Weave.Service.password = "ilovejosephine"; + + _("Let's try to sync."); + Weave.Service.sync(); + + _("Verify that sync() threw an exception."); + do_check_true(threw); + + _("We're no longer logged in."); + do_check_false(Weave.Service.isLoggedIn); + + _("Sync status."); + do_check_eq(Weave.Status.login, Weave.LOGIN_FAILED_LOGIN_REJECTED); + + _("globalScore is unchanged."); + do_check_eq(Weave.Service.globalScore, GLOBAL_SCORE); + + } finally { + Weave.Svc.Prefs.resetBranch(""); + server.stop(do_test_finished); + } +} From c163a5ae51a3aabb57ae15f55da32e0f4b343acb Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 5 Aug 2010 18:24:45 +0200 Subject: [PATCH 221/369] Bug 578835 - wipeServer doesn't deal well with failure [r=mconnor] Have wipeServer() abort if it encounters a server error (anything other than 200 or 404) and throw an exception. Don't catch any exceptions wipeServer() throws, let them bubble up to the callers. --- services/sync/modules/service.js | 26 ++- .../tests/unit/test_service_wipeServer.js | 194 ++++++++++++++++++ 2 files changed, 209 insertions(+), 11 deletions(-) create mode 100644 services/sync/tests/unit/test_service_wipeServer.js diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 3eb0bf5e1c5e..0201b2fb23a2 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -1526,7 +1526,7 @@ WeaveSvc.prototype = { * Array of collections to wipe. If not given, all collections are wiped. */ wipeServer: function WeaveSvc_wipeServer(collections) - this._catch(this._notify("wipe-server", "", function() { + this._notify("wipe-server", "", function() { if (!collections) { collections = []; let info = new Resource(this.infoURL).get(); @@ -1534,19 +1534,23 @@ WeaveSvc.prototype = { collections.push(name); } for each (let name in collections) { - try { - new Resource(this.storageURL + name).delete(); - - // Remove the crypto record from the server and local cache - let crypto = this.storageURL + "crypto/" + name; - new Resource(crypto).delete(); - CryptoMetas.del(crypto); + let url = this.storageURL + name; + let response = new Resource(url).delete(); + if (response.status != 200 && response.status != 404) { + throw "Aborting wipeServer. Server responded with " + + response.status + " response for " + url; } - catch(ex) { - this._log.debug("Exception on wipe of '" + name + "': " + Utils.exceptionStr(ex)); + + // Remove the crypto record from the server and local cache + let crypto = this.storageURL + "crypto/" + name; + response = new Resource(crypto).delete(); + CryptoMetas.del(crypto); + if (response.status != 200 && response.status != 404) { + throw "Aborting wipeServer. Server responded with " + + response.status + " response for " + crypto; } } - }))(), + })(), /** * Wipe all local user data. diff --git a/services/sync/tests/unit/test_service_wipeServer.js b/services/sync/tests/unit/test_service_wipeServer.js new file mode 100644 index 000000000000..07d17b3de43c --- /dev/null +++ b/services/sync/tests/unit/test_service_wipeServer.js @@ -0,0 +1,194 @@ +Cu.import("resource://services-sync/util.js"); +Cu.import("resource://services-sync/service.js"); +Cu.import("resource://services-sync/base_records/crypto.js"); +Cu.import("resource://services-sync/base_records/keys.js"); +Cu.import("resource://services-sync/resource.js"); + +function FakeCollection() { + this.deleted = false; +} +FakeCollection.prototype = { + handler: function() { + let self = this; + return function(request, response) { + let body = ""; + if (request.method == "DELETE") { + body = JSON.stringify(Date.now() / 1000); + self.deleted = true; + } + response.setStatusLine(request.httpVersion, 200, "OK"); + response.bodyOutputStream.write(body, body.length); + }; + } +}; + +function serviceUnavailable(request, response) { + let body = "Service Unavailable"; + response.setStatusLine(request.httpVersion, 503, "Service Unavailable"); + response.bodyOutputStream.write(body, body.length); +} + +function createAndUploadKeypair() { + let keys = PubKeys.createKeypair(ID.get("WeaveCryptoID"), + PubKeys.defaultKeyUri, + PrivKeys.defaultKeyUri); + PubKeys.uploadKeypair(keys); +} + +function createAndUploadSymKey(url) { + let symkey = Svc.Crypto.generateRandomKey(); + let pubkey = PubKeys.getDefaultKey(); + let meta = new CryptoMeta(url); + meta.addUnwrappedKey(pubkey, symkey); + let res = new Resource(meta.uri); + res.put(meta); + CryptoMetas.set(url, meta); +} + +function setUpTestFixtures() { + let cryptoService = new FakeCryptoService(); + + Weave.Service.clusterURL = "http://localhost:8080/"; + Weave.Service.username = "johndoe"; + Weave.Service.passphrase = "secret"; + + createAndUploadKeypair(); + createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/steam"); + createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/petrol"); + createAndUploadSymKey("http://localhost:8080/1.0/johndoe/storage/crypto/diesel"); +} + +function test_withCollectionList_failOnCrypto() { + _("Weave.Service.wipeServer() deletes collections given as argument and aborts if a collection delete fails."); + + let steam_coll = new FakeCollection(); + let petrol_coll = new FakeCollection(); + let diesel_coll = new FakeCollection(); + let crypto_steam = new ServerWBO('steam'); + let crypto_diesel = new ServerWBO('diesel'); + + let server = httpd_setup({ + "/1.0/johndoe/storage/keys/pubkey": (new ServerWBO('pubkey')).handler(), + "/1.0/johndoe/storage/keys/privkey": (new ServerWBO('privkey')).handler(), + "/1.0/johndoe/storage/steam": steam_coll.handler(), + "/1.0/johndoe/storage/petrol": petrol_coll.handler(), + "/1.0/johndoe/storage/diesel": diesel_coll.handler(), + "/1.0/johndoe/storage/crypto/steam": crypto_steam.handler(), + "/1.0/johndoe/storage/crypto/petrol": serviceUnavailable, + "/1.0/johndoe/storage/crypto/diesel": crypto_diesel.handler() + }); + do_test_pending(); + + try { + setUpTestFixtures(); + + _("Confirm initial environment."); + do_check_false(steam_coll.deleted); + do_check_false(petrol_coll.deleted); + do_check_false(diesel_coll.deleted); + + do_check_true(crypto_steam.payload != undefined); + do_check_true(crypto_diesel.payload != undefined); + + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel")); + + _("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection's symkey."); + let error; + try { + Weave.Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]); + } catch(ex) { + error = ex; + } + _("wipeServer() threw this exception: " + error); + do_check_true(error != undefined); + + _("wipeServer stopped deleting after encountering an error with the 'petrol' collection's symkey, thus only 'steam' and 'petrol' have been deleted."); + do_check_true(steam_coll.deleted); + do_check_true(petrol_coll.deleted); + do_check_false(diesel_coll.deleted); + + do_check_true(crypto_steam.payload == undefined); + do_check_true(crypto_diesel.payload != undefined); + + do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam")); + do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel")); + + } finally { + server.stop(do_test_finished); + Svc.Prefs.resetBranch(""); + CryptoMetas.clearCache(); + } +} + +function test_withCollectionList_failOnCollection() { + _("Weave.Service.wipeServer() deletes collections given as argument."); + + let steam_coll = new FakeCollection(); + let diesel_coll = new FakeCollection(); + let crypto_steam = new ServerWBO('steam'); + let crypto_petrol = new ServerWBO('petrol'); + let crypto_diesel = new ServerWBO('diesel'); + + let server = httpd_setup({ + "/1.0/johndoe/storage/keys/pubkey": (new ServerWBO('pubkey')).handler(), + "/1.0/johndoe/storage/keys/privkey": (new ServerWBO('privkey')).handler(), + "/1.0/johndoe/storage/steam": steam_coll.handler(), + "/1.0/johndoe/storage/petrol": serviceUnavailable, + "/1.0/johndoe/storage/diesel": diesel_coll.handler(), + "/1.0/johndoe/storage/crypto/steam": crypto_steam.handler(), + "/1.0/johndoe/storage/crypto/petrol": crypto_petrol.handler(), + "/1.0/johndoe/storage/crypto/diesel": crypto_diesel.handler() + }); + do_test_pending(); + + try { + setUpTestFixtures(); + + _("Confirm initial environment."); + do_check_false(steam_coll.deleted); + do_check_false(diesel_coll.deleted); + + do_check_true(crypto_steam.payload != undefined); + do_check_true(crypto_petrol.payload != undefined); + do_check_true(crypto_diesel.payload != undefined); + + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel")); + + _("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection."); + let error; + try { + Weave.Service.wipeServer(["non-existent", "steam", "petrol", "diesel"]); + } catch(ex) { + error = ex; + } + _("wipeServer() threw this exception: " + error); + do_check_true(error != undefined); + + _("wipeServer stopped deleting after encountering an error with the 'petrol' collection, thus only 'steam' has been deleted."); + do_check_true(steam_coll.deleted); + do_check_false(diesel_coll.deleted); + + do_check_true(crypto_steam.payload == undefined); + do_check_true(crypto_petrol.payload != undefined); + do_check_true(crypto_diesel.payload != undefined); + + do_check_false(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/steam")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/petrol")); + do_check_true(CryptoMetas.contains("http://localhost:8080/1.0/johndoe/storage/crypto/diesel")); + + } finally { + server.stop(do_test_finished); + Svc.Prefs.resetBranch(""); + CryptoMetas.clearCache(); + } +} + +function run_test() { + test_withCollectionList_failOnCollection(); + test_withCollectionList_failOnCrypto(); +} From 55360ab17414e58a70ff19b84eadf10e2fdcaa54 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 5 Aug 2010 18:31:58 +0200 Subject: [PATCH 222/369] Bug 584478 - Resource alias registered too early [r=mconnor] --- services/sync/Weave.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/sync/Weave.js b/services/sync/Weave.js index a113fe5803ae..bfa28a26e868 100644 --- a/services/sync/Weave.js +++ b/services/sync/Weave.js @@ -56,12 +56,13 @@ WeaveService.prototype = { switch (topic) { case "app-startup": let os = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); + getService(Ci.nsIObserverService); os.addObserver(this, "final-ui-startup", true); - this.addResourceAlias(); break; case "final-ui-startup": + this.addResourceAlias(); + // Force Weave service to load if it hasn't triggered from overlays this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this.timer.initWithCallback({ From 905ac1573e7f34b3a9d2049f5e1f542e4f04ca7b Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 6 Aug 2010 16:55:06 +0200 Subject: [PATCH 223/369] Bug 584722 - Make test_uploadOutgoing_failed (test_syncengine_sync.js) async [r=mconnor] --- services/sync/tests/unit/test_syncengine_sync.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/sync/tests/unit/test_syncengine_sync.js b/services/sync/tests/unit/test_syncengine_sync.js index c6582df865c8..b70236b85cc1 100644 --- a/services/sync/tests/unit/test_syncengine_sync.js +++ b/services/sync/tests/unit/test_syncengine_sync.js @@ -863,6 +863,7 @@ function test_uploadOutgoing_failed() { "/1.0/foo/storage/crypto/steam": crypto_steam.handler(), "/1.0/foo/storage/steam": collection.handler() }); + do_test_pending(); createAndUploadKeypair(); createAndUploadSymKey("http://localhost:8080/1.0/foo/storage/crypto/steam"); @@ -898,7 +899,7 @@ function test_uploadOutgoing_failed() { do_check_eq(engine._tracker.changedIDs['peppercorn'], PEPPERCORN_CHANGED); } finally { - server.stop(function() {}); + server.stop(do_test_finished); Svc.Prefs.resetBranch(""); Records.clearCache(); CryptoMetas.clearCache(); From 57c4f23de7ee652ed1325053edca6b949a744ddf Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 6 Aug 2010 17:30:58 +0200 Subject: [PATCH 224/369] Bug 584241 - Disable trackers when client isn't configured [r=mconnor] --- services/sync/modules/engines/bookmarks.js | 43 +++++++--- services/sync/modules/engines/forms.js | 38 +++++++-- services/sync/modules/engines/history.js | 21 ++++- services/sync/modules/engines/passwords.js | 22 +++++- services/sync/modules/engines/prefs.js | 16 +++- services/sync/modules/engines/tabs.js | 92 ++++++++++++++-------- services/sync/modules/service.js | 16 +++- 7 files changed, 189 insertions(+), 59 deletions(-) diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 0e8c564dad82..7ace9da63873 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -54,7 +54,6 @@ catch(ex) { } Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines.js"); -Cu.import("resource://services-sync/ext/Observers.js"); Cu.import("resource://services-sync/stores.js"); Cu.import("resource://services-sync/trackers.js"); Cu.import("resource://services-sync/type_records/bookmark.js"); @@ -115,12 +114,12 @@ BookmarksEngine.prototype = { _trackerObj: BookmarksTracker, _handleImport: function _handleImport() { - Observers.add("bookmarks-restore-begin", function() { + Svc.Obs.add("bookmarks-restore-begin", function() { this._log.debug("Ignoring changes from importing bookmarks"); this._tracker.ignoreAll = true; }, this); - Observers.add("bookmarks-restore-success", function() { + Svc.Obs.add("bookmarks-restore-success", function() { this._log.debug("Tracking all items on successful import"); this._tracker.ignoreAll = false; @@ -129,7 +128,7 @@ BookmarksEngine.prototype = { this._tracker.addChangedID(id); }, this); - Observers.add("bookmarks-restore-failed", function() { + Svc.Obs.add("bookmarks-restore-failed", function() { this._tracker.ignoreAll = false; }, this); }, @@ -251,7 +250,7 @@ function BookmarksStore(name) { Store.call(this, name); // Explicitly nullify our references to our cached services so we don't leak - Observers.add("places-shutdown", function() { + Svc.Obs.add("places-shutdown", function() { this.__bms = null; this.__hsvc = null; this.__ls = null; @@ -1001,17 +1000,37 @@ function BookmarksTracker(name) { for (let guid in kSpecialIds) this.ignoreID(guid); - Svc.Bookmark.addObserver(this, true); - - // Explicitly nullify our references to our cached services so we don't leak - Observers.add("places-shutdown", function() { - this.__ls = null; - this.__bms = null; - }, this); + Svc.Obs.add("places-shutdown", this); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); } BookmarksTracker.prototype = { __proto__: Tracker.prototype, + _enabled: false, + observe: function observe(subject, topic, data) { + switch (topic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + Svc.Bookmark.addObserver(this, true); + this._enabled = true; + } + break; + case "weave:engine:stop-tracking": + if (this._enabled) { + Svc.Bookmark.removeObserver(this); + this._enabled = false; + } + // Fall through to clean up. + case "places-shutdown": + // Explicitly nullify our references to our cached services so + // we don't leak + this.__ls = null; + this.__bms = null; + break; + } + }, + __bms: null, get _bms() { if (!this.__bms) diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index 07ece07dd932..a51cfe72b674 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -202,12 +202,8 @@ FormStore.prototype = { function FormTracker(name) { Tracker.call(this, name); - Svc.Obs.add("form-notifier", this); - - // nsHTMLFormElement doesn't use the normal observer/observe pattern and looks - // up nsIFormSubmitObservers to .notify() them so add manually to observers - Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService). - addObserver(this, "earlyformsubmit", true); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); } FormTracker.prototype = { __proto__: Tracker.prototype, @@ -222,7 +218,37 @@ FormTracker.prototype = { this.score += 10; }, + _enabled: false, observe: function observe(subject, topic, data) { + switch (topic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + Svc.Obs.add("form-notifier", this); + // nsHTMLFormElement doesn't use the normal observer/observe + // pattern and looks up nsIFormSubmitObservers to .notify() + // them so add manually to observers + Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .addObserver(this, "earlyformsubmit", true); + this._enabled = true; + } + break; + case "weave:engine:stop-tracking": + if (this._enabled) { + Svc.Obs.remove("form-notifier", this); + Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .removeObserver(this, "earlyformsubmit"); + this._enabled = false; + } + break; + case "form-notifier": + this.onFormNotifier(data); + break; + } + }, + + onFormNotifier: function onFormNotifier(data) { let name, value; // Figure out if it's a function that we care about tracking diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index 98d61b6457f8..f237f258e64c 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -257,11 +257,30 @@ HistoryStore.prototype = { function HistoryTracker(name) { Tracker.call(this, name); - Svc.History.addObserver(this, true); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); } HistoryTracker.prototype = { __proto__: Tracker.prototype, + _enabled: false, + observe: function observe(subject, topic, data) { + switch (topic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + Svc.History.addObserver(this, true); + this._enabled = true; + } + break; + case "weave:engine:stop-tracking": + if (this._enabled) { + Svc.History.removeObserver(this); + this._enabled = false; + } + break; + } + }, + QueryInterface: XPCOMUtils.generateQI([ Ci.nsINavHistoryObserver, Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS, diff --git a/services/sync/modules/engines/passwords.js b/services/sync/modules/engines/passwords.js index 8721436d2ff4..f2650be9505a 100644 --- a/services/sync/modules/engines/passwords.js +++ b/services/sync/modules/engines/passwords.js @@ -44,7 +44,6 @@ const Ci = Components.interfaces; Cu.import("resource://services-sync/base_records/collection.js"); Cu.import("resource://services-sync/constants.js"); Cu.import("resource://services-sync/engines.js"); -Cu.import("resource://services-sync/ext/Observers.js"); Cu.import("resource://services-sync/stores.js"); Cu.import("resource://services-sync/trackers.js"); Cu.import("resource://services-sync/type_records/passwords.js"); @@ -224,16 +223,33 @@ PasswordStore.prototype = { function PasswordTracker(name) { Tracker.call(this, name); - Observers.add("passwordmgr-storage-changed", this); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); } PasswordTracker.prototype = { __proto__: Tracker.prototype, - /* A single add, remove or change is 15 points, all items removed is 50 */ + _enabled: false, observe: function PasswordTracker_observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + Svc.Obs.add("passwordmgr-storage-changed", this); + this._enabled = true; + } + return; + case "weave:engine:stop-tracking": + if (this._enabled) { + Svc.Obs.remove("passwordmgr-storage-changed", this); + this._enabled = false; + } + return; + } + if (this.ignoreAll) return; + // A single add, remove or change is 15 points, all items removed is 50 switch (aData) { case 'modifyLogin': aSubject = aSubject.QueryInterface(Ci.nsIArray). diff --git a/services/sync/modules/engines/prefs.js b/services/sync/modules/engines/prefs.js index dc75956cdc46..7eb863314fb2 100644 --- a/services/sync/modules/engines/prefs.js +++ b/services/sync/modules/engines/prefs.js @@ -231,8 +231,9 @@ PrefStore.prototype = { function PrefTracker(name) { Tracker.call(this, name); - this._prefs.addObserver("", this, false); Svc.Obs.add("profile-before-change", this); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); } PrefTracker.prototype = { __proto__: Tracker.prototype, @@ -255,8 +256,21 @@ PrefTracker.prototype = { return this.__syncPrefs; }, + _enabled: false, observe: function(aSubject, aTopic, aData) { switch (aTopic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + this._prefs.addObserver("", this, false); + this._enabled = true; + } + break; + case "weave:engine:stop-tracking": + if (this._enabled) { + this._prefs.removeObserver("", this); + this._enabled = false; + } + // Fall through to clean up. case "profile-before-change": this.__prefs = null; this.__syncPrefs = null; diff --git a/services/sync/modules/engines/tabs.js b/services/sync/modules/engines/tabs.js index 5cac4cca788b..4c1e0f5a9eea 100644 --- a/services/sync/modules/engines/tabs.js +++ b/services/sync/modules/engines/tabs.js @@ -225,53 +225,75 @@ TabStore.prototype = { function TabTracker(name) { Tracker.call(this, name); - - Svc.Obs.add("private-browsing", this); + Svc.Obs.add("weave:engine:start-tracking", this); + Svc.Obs.add("weave:engine:stop-tracking", this); // Make sure "this" pointer is always set correctly for event listeners this.onTab = Utils.bind2(this, this.onTab); - - // Register as an observer so we can catch windows opening and closing: - Svc.Obs.add("domwindowopened", this); - - // Also register listeners on already open windows - let wins = Svc.WinMediator.getEnumerator("navigator:browser"); - while (wins.hasMoreElements()) - this._registerListenersForWindow(wins.getNext()); + this._unregisterListeners = Utils.bind2(this, this._unregisterListeners); } TabTracker.prototype = { __proto__: Tracker.prototype, QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), - _registerListenersForWindow: function TabTracker__registerListen(window) { - this._log.trace("Registering tab listeners in new window"); - - // For each topic, add or remove onTab as the listener - let topics = ["pageshow", "TabOpen", "TabClose", "TabSelect"]; - let onTab = this.onTab; - let addRem = function(add) topics.forEach(function(topic) { - window[(add ? "add" : "remove") + "EventListener"](topic, onTab, false); - }); - - // Add the listeners now and remove them on unload - addRem(true); - window.addEventListener("unload", function() addRem(false), false); + _topics: ["pageshow", "TabOpen", "TabClose", "TabSelect"], + _registerListenersForWindow: function registerListenersFW(window) { + this._log.trace("Registering tab listeners in window"); + for each (let topic in this._topics) { + window.addEventListener(topic, this.onTab, false); + } + window.addEventListener("unload", this._unregisterListeners, false); }, - observe: function TabTracker_observe(aSubject, aTopic, aData) { - // Add tab listeners now that a window has opened - if (aTopic == "domwindowopened") { - let self = this; - aSubject.addEventListener("load", function onLoad(event) { - aSubject.removeEventListener("load", onLoad, false); - // Only register after the window is done loading to avoid unloads - self._registerListenersForWindow(aSubject); - }, false); + _unregisterListeners: function unregisterListeners(event) { + this._unregisterListenersForWindow(event.target); + }, + + _unregisterListenersForWindow: function unregisterListenersFW(window) { + this._log.trace("Removing tab listeners in window"); + window.removeEventListener("unload", this._unregisterListeners, false); + for each (let topic in this._topics) { + window.removeEventListener(topic, this.onTab, false); + } + }, + + _enabled: false, + observe: function TabTracker_observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "weave:engine:start-tracking": + if (!this._enabled) { + Svc.Obs.add("private-browsing", this); + Svc.Obs.add("domwindowopened", this); + let wins = Svc.WinMediator.getEnumerator("navigator:browser"); + while (wins.hasMoreElements()) + this._registerListenersForWindow(wins.getNext()); + this._enabled = true; + } + break; + case "weave:engine:stop-tracking": + if (this._enabled) { + Svc.Obs.remove("private-browsing", this); + Svc.Obs.remove("domwindowopened", this); + let wins = Svc.WinMediator.getEnumerator("navigator:browser"); + while (wins.hasMoreElements()) + this._unregisterListenersForWindow(wins.getNext()); + this._enabled = false; + } + return; + case "domwindowopened": + // Add tab listeners now that a window has opened + let self = this; + aSubject.addEventListener("load", function onLoad(event) { + aSubject.removeEventListener("load", onLoad, false); + // Only register after the window is done loading to avoid unloads + self._registerListenersForWindow(aSubject); + }, false); + break; + case "private-browsing": + if (aData == "enter" && !PBPrefs.get("autostart")) + this.clearChangedIDs(); } - else if (aTopic == "private-browsing" && aData == "enter" - && !PBPrefs.get("autostart")) - this.clearChangedIDs(); }, onTab: function onTab(event) { diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 0201b2fb23a2..ba4db30c312d 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -273,6 +273,7 @@ WeaveSvc.prototype = { "Weave, since it will not work correctly."); } + Svc.Obs.add("weave:service:setup-complete", this); Svc.Obs.add("network:offline-status-changed", this); Svc.Obs.add("weave:service:sync:finish", this); Svc.Obs.add("weave:service:sync:error", this); @@ -292,6 +293,10 @@ WeaveSvc.prototype = { this._updateCachedURLs(); + let status = this._checkSetup(); + if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED) + Svc.Obs.notify("weave:engine:start-tracking"); + // Applications can specify this preference if they want autoconnect // to happen after a fixed delay. let delay = Svc.Prefs.get("autoconnectDelay"); @@ -306,7 +311,10 @@ WeaveSvc.prototype = { }, _checkSetup: function WeaveSvc__checkSetup() { - if (!this.username) { + if (!this.enabled) { + Status.service = STATUS_DISABLED; + } + else if (!this.username) { this._log.debug("checkSetup: no username set"); Status.login = LOGIN_FAILED_NO_USERNAME; } @@ -404,6 +412,11 @@ WeaveSvc.prototype = { observe: function WeaveSvc__observe(subject, topic, data) { switch (topic) { + case "weave:service:setup-complete": + let status = this._checkSetup(); + if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED) + Svc.Obs.notify("weave:engine:start-tracking"); + break; case "network:offline-status-changed": // Whether online or offline, we'll reschedule syncs this._log.trace("Network offline status change: " + data); @@ -704,6 +717,7 @@ WeaveSvc.prototype = { Svc.Login.removeLogin(login); }); Svc.Obs.notify("weave:service:start-over"); + Svc.Obs.notify("weave:engine:stop-tracking"); }, delayedAutoConnect: function delayedAutoConnect(delay) { From 9f3ccbe46f13405acef810ba24c137c441964ff7 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 6 Aug 2010 17:31:21 +0200 Subject: [PATCH 225/369] Bug 584241 - Disable trackers when client isn't configured [r=mconnor] Tests and test fixes. --HG-- rename : services/sync/tests/unit/test_engines_forms_store.js => services/sync/tests/unit/test_forms_store.js --- .../sync/tests/unit/test_bookmark_tracker.js | 48 +++++++++++++++++++ ...nes_forms_store.js => test_forms_store.js} | 0 .../sync/tests/unit/test_forms_tracker.js | 39 +++++++++++++++ .../sync/tests/unit/test_history_tracker.js | 45 +++++++++++++++++ .../sync/tests/unit/test_service_login.js | 3 +- .../tests/unit/test_service_verifyLogin.js | 3 +- 6 files changed, 136 insertions(+), 2 deletions(-) create mode 100644 services/sync/tests/unit/test_bookmark_tracker.js rename services/sync/tests/unit/{test_engines_forms_store.js => test_forms_store.js} (100%) create mode 100644 services/sync/tests/unit/test_forms_tracker.js create mode 100644 services/sync/tests/unit/test_history_tracker.js diff --git a/services/sync/tests/unit/test_bookmark_tracker.js b/services/sync/tests/unit/test_bookmark_tracker.js new file mode 100644 index 000000000000..baa46195ec04 --- /dev/null +++ b/services/sync/tests/unit/test_bookmark_tracker.js @@ -0,0 +1,48 @@ +Cu.import("resource://services-sync/engines/bookmarks.js"); +Cu.import("resource://services-sync/util.js"); + +function run_test() { + _("Verify we've got an empty tracker to work with."); + let tracker = new BookmarksEngine()._tracker; + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + let folder = Svc.Bookmark.createFolder(Svc.Bookmark.bookmarksMenuFolder, + "Test Folder", + Svc.Bookmark.DEFAULT_INDEX); + function createBmk() { + Svc.Bookmark.insertBookmark(folder, + Utils.makeURI("http://getfirefox.com"), + Svc.Bookmark.DEFAULT_INDEX, + "Get Firefox!"); + } + + try { + _("Create bookmark. Won't show because we haven't started tracking yet"); + createBmk(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Tell the tracker to start tracking changes."); + Svc.Obs.notify("weave:engine:start-tracking"); + createBmk(); + do_check_eq([id for (id in tracker.changedIDs)].length, 1); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:start-tracking"); + createBmk(); + do_check_eq([id for (id in tracker.changedIDs)].length, 2); + + _("Let's stop tracking again."); + tracker.clearChangedIDs(); + Svc.Obs.notify("weave:engine:stop-tracking"); + createBmk(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:stop-tracking"); + createBmk(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + } finally { + _("Clean up."); + Svc.Bookmark.removeItem(folder); + } +} diff --git a/services/sync/tests/unit/test_engines_forms_store.js b/services/sync/tests/unit/test_forms_store.js similarity index 100% rename from services/sync/tests/unit/test_engines_forms_store.js rename to services/sync/tests/unit/test_forms_store.js diff --git a/services/sync/tests/unit/test_forms_tracker.js b/services/sync/tests/unit/test_forms_tracker.js new file mode 100644 index 000000000000..46ff13802be6 --- /dev/null +++ b/services/sync/tests/unit/test_forms_tracker.js @@ -0,0 +1,39 @@ +Cu.import("resource://services-sync/engines/forms.js"); +Cu.import("resource://services-sync/util.js"); + +function run_test() { + _("Verify we've got an empty tracker to work with."); + let tracker = new FormEngine()._tracker; + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + try { + _("Create an entry. Won't show because we haven't started tracking yet"); + Svc.Form.addEntry("name", "John Doe"); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Tell the tracker to start tracking changes."); + Svc.Obs.notify("weave:engine:start-tracking"); + Svc.Form.removeEntry("name", "John Doe"); + Svc.Form.addEntry("email", "john@doe.com"); + do_check_eq([id for (id in tracker.changedIDs)].length, 2); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:start-tracking"); + Svc.Form.addEntry("address", "Memory Lane"); + do_check_eq([id for (id in tracker.changedIDs)].length, 3); + + _("Let's stop tracking again."); + tracker.clearChangedIDs(); + Svc.Obs.notify("weave:engine:stop-tracking"); + Svc.Form.removeEntry("address", "Memory Lane"); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:stop-tracking"); + Svc.Form.removeEntry("email", "john@doe.com"); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + } finally { + _("Clean up."); + Svc.Form.removeAllEntries(); + } +} diff --git a/services/sync/tests/unit/test_history_tracker.js b/services/sync/tests/unit/test_history_tracker.js new file mode 100644 index 000000000000..fad247239807 --- /dev/null +++ b/services/sync/tests/unit/test_history_tracker.js @@ -0,0 +1,45 @@ +Cu.import("resource://services-sync/engines/history.js"); +Cu.import("resource://services-sync/util.js"); + +function run_test() { + _("Verify we've got an empty tracker to work with."); + let tracker = new HistoryEngine()._tracker; + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + let _counter = 0; + function addVisit() { + Svc.History.addVisit(Utils.makeURI("http://getfirefox.com/" + _counter), + Date.now() * 1000, null, 1, false, 0); + _counter += 1; + } + + try { + _("Create bookmark. Won't show because we haven't started tracking yet"); + addVisit(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Tell the tracker to start tracking changes."); + Svc.Obs.notify("weave:engine:start-tracking"); + addVisit(); + do_check_eq([id for (id in tracker.changedIDs)].length, 1); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:start-tracking"); + addVisit(); + do_check_eq([id for (id in tracker.changedIDs)].length, 2); + + _("Let's stop tracking again."); + tracker.clearChangedIDs(); + Svc.Obs.notify("weave:engine:stop-tracking"); + addVisit(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + + _("Notifying twice won't do any harm."); + Svc.Obs.notify("weave:engine:stop-tracking"); + addVisit(); + do_check_eq([id for (id in tracker.changedIDs)].length, 0); + } finally { + _("Clean up."); + Svc.History.removeAllPages(); + } +} diff --git a/services/sync/tests/unit/test_service_login.js b/services/sync/tests/unit/test_service_login.js index 73d14a0238d7..af41863ebccb 100644 --- a/services/sync/tests/unit/test_service_login.js +++ b/services/sync/tests/unit/test_service_login.js @@ -35,7 +35,8 @@ function run_test() { Weave.Service.clusterURL = "http://localhost:8080/"; Svc.Prefs.set("autoconnect", false); - _("Initial state is ok."); + _("Force the initial state."); + Status.service = STATUS_OK; do_check_eq(Status.service, STATUS_OK); _("Try logging in. It wont' work because we're not configured yet."); diff --git a/services/sync/tests/unit/test_service_verifyLogin.js b/services/sync/tests/unit/test_service_verifyLogin.js index ef0e369f2f47..aa311ea48902 100644 --- a/services/sync/tests/unit/test_service_verifyLogin.js +++ b/services/sync/tests/unit/test_service_verifyLogin.js @@ -31,7 +31,8 @@ function run_test() { Weave.Service.serverURL = "http://localhost:8080/"; Weave.Service.clusterURL = "http://localhost:8080/"; - _("Initial state is ok."); + _("Force the initial state."); + Status.service = STATUS_OK; do_check_eq(Status.service, STATUS_OK); _("Credentials won't check out because we're not configured yet."); From 6cefdc1625a7b61c0f00e4fb1b01190be68310f1 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 6 Aug 2010 17:31:36 +0200 Subject: [PATCH 226/369] Bug 487558 - Use satchel notifications to track form data [r=mconnor] --- services/sync/FormNotifier.js | 67 -------------------------- services/sync/modules/engines/forms.js | 15 ++++++ 2 files changed, 15 insertions(+), 67 deletions(-) delete mode 100644 services/sync/FormNotifier.js diff --git a/services/sync/FormNotifier.js b/services/sync/FormNotifier.js deleted file mode 100644 index 82e23746eccc..000000000000 --- a/services/sync/FormNotifier.js +++ /dev/null @@ -1,67 +0,0 @@ -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -function FormNotifier() { - let formClass = Components.classesByID["{a2059c0e-5a58-4c55-ab7c-26f0557546ef}"] || - Components.classesByID["{0c1bb408-71a2-403f-854a-3a0659829ded}"]; - let baseForm = formClass.getService(Ci.nsIFormHistory2); - let obs = Cc["@mozilla.org/observer-service;1"]. - getService(Ci.nsIObserverService); - - function wrap(method) { - return function() { - let args = Array.slice(arguments); - let notify = function(type) { - obs.notifyObservers(null, "form-notifier", JSON.stringify({ - args: args, - func: method, - type: type - })); - }; - - notify("before"); - try { - return baseForm[method].apply(this, arguments); - } - finally { - notify("after"); - } - }; - } - - this.__defineGetter__("DBConnection", function() baseForm.DBConnection); - this.__defineGetter__("hasEntries", function() baseForm.hasEntries); - - this.addEntry = wrap("addEntry"); - this.entryExists = wrap("entryExists"); - this.nameExists = wrap("nameExists"); - this.removeAllEntries = wrap("removeAllEntries"); - this.removeEntriesByTimeframe = wrap("removeEntriesByTimeframe"); - this.removeEntriesForName = wrap("removeEntriesForName"); - this.removeEntry = wrap("removeEntry"); - - // Avoid leaking the base form service. - obs.addObserver({ - observe: function() { - obs.removeObserver(this, "profile-before-change"); - baseForm = null; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, - Ci.nsIObserver]) - }, "profile-before-change", true); -} -FormNotifier.prototype = { - classDescription: "Form Notifier Wrapper", - contractID: "@mozilla.org/satchel/form-history;1", - classID: Components.ID("{be5a097b-6ee6-4c6a-8eca-6bce87d570e9}"), - QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormHistory2]), -}; - -// Gecko <2.0 -function NSGetModule(compMgr, fileSpec) XPCOMUtils.generateModule([FormNotifier]); - -// Gecko >=2.0 -if (typeof XPCOMUtils.generateNSGetFactory == "function") - const NSGetFactory = XPCOMUtils.generateNSGetFactory([FormNotifier]); diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index a51cfe72b674..3c5dc10a9598 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -224,6 +224,7 @@ FormTracker.prototype = { case "weave:engine:start-tracking": if (!this._enabled) { Svc.Obs.add("form-notifier", this); + Svc.Obs.add("satchel-storage-changed", this); // nsHTMLFormElement doesn't use the normal observer/observe // pattern and looks up nsIFormSubmitObservers to .notify() // them so add manually to observers @@ -236,18 +237,32 @@ FormTracker.prototype = { case "weave:engine:stop-tracking": if (this._enabled) { Svc.Obs.remove("form-notifier", this); + Svc.Obs.remove("satchel-storage-changed", this); Cc["@mozilla.org/observer-service;1"] .getService(Ci.nsIObserverService) .removeObserver(this, "earlyformsubmit"); this._enabled = false; } break; + // Firefox 4.0 + case "satchel-storage-changed": + if (data == "addEntry" || data == "before-removeEntry") { + subject = subject.QueryInterface(Ci.nsIArray); + let name = subject.queryElementAt(0, Ci.nsISupportsString) + .toString(); + let value = subject.queryElementAt(1, Ci.nsISupportsString) + .toString(); + this.trackEntry(name, value); + } + break; + // Firefox 3.5/3.6 case "form-notifier": this.onFormNotifier(data); break; } }, + // Firefox 3.5/3.6 onFormNotifier: function onFormNotifier(data) { let name, value; From ac08f9116fc38d9bc5dab0782c5173bf4c86f546 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 6 Aug 2010 23:25:59 +0200 Subject: [PATCH 227/369] Bug 585190 - Fix tracker tests on m-c [r=mconnor] Ensure that the bookmark tracker test starts out with a clean slate. Svc.History.removeAllPages(), as called by the history tracker test during clean up, needs the "UHist" alias registered, which isn't available by default in xpcshell. --- .../sync/tests/unit/test_bookmark_tracker.js | 7 ++++-- .../sync/tests/unit/test_history_tracker.js | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/services/sync/tests/unit/test_bookmark_tracker.js b/services/sync/tests/unit/test_bookmark_tracker.js index baa46195ec04..b837afb78f3b 100644 --- a/services/sync/tests/unit/test_bookmark_tracker.js +++ b/services/sync/tests/unit/test_bookmark_tracker.js @@ -2,8 +2,11 @@ Cu.import("resource://services-sync/engines/bookmarks.js"); Cu.import("resource://services-sync/util.js"); function run_test() { + let engine = new BookmarksEngine(); + engine._store.wipe(); + _("Verify we've got an empty tracker to work with."); - let tracker = new BookmarksEngine()._tracker; + let tracker = engine._tracker; do_check_eq([id for (id in tracker.changedIDs)].length, 0); let folder = Svc.Bookmark.createFolder(Svc.Bookmark.bookmarksMenuFolder, @@ -43,6 +46,6 @@ function run_test() { do_check_eq([id for (id in tracker.changedIDs)].length, 0); } finally { _("Clean up."); - Svc.Bookmark.removeItem(folder); + engine._store.wipe(); } } diff --git a/services/sync/tests/unit/test_history_tracker.js b/services/sync/tests/unit/test_history_tracker.js index fad247239807..5bf97ac18946 100644 --- a/services/sync/tests/unit/test_history_tracker.js +++ b/services/sync/tests/unit/test_history_tracker.js @@ -1,6 +1,29 @@ +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines/history.js"); Cu.import("resource://services-sync/util.js"); + +// See toolkit/components/places/tests/head_common.js +const NS_APP_HISTORY_50_FILE = "UHist"; +const dirsvc = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIDirectoryService); + +// Add our own dirprovider for old history.dat. +let provider = { + getFile: function(prop, persistent) { + persistent.value = true; + if (prop == NS_APP_HISTORY_50_FILE) { + let histFile = dirsvc.get("ProfD", Ci.nsIFile); + histFile.append("history.dat"); + return histFile; + } + throw Cr.NS_ERROR_FAILURE; + }, + QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]) +}; +dirsvc.registerProvider(provider); + + function run_test() { _("Verify we've got an empty tracker to work with."); let tracker = new HistoryEngine()._tracker; From 1564fa3521e93eb0693be920287516ac159be1da Mon Sep 17 00:00:00 2001 From: Edward Lee Date: Fri, 6 Aug 2010 17:16:28 -0700 Subject: [PATCH 228/369] Remove unnecessary try/catch wrapping around a timeout-ed call. --HG-- extra : rebase_source : 3b608473ff899668acea4e0e6964734c9db4d01d --- browser/base/content/tabview/modules/utils.jsm | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index 9e0aca0371fa..e208114df857 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -723,17 +723,12 @@ let Utils = { // ---------- // Function: timeout - // wraps a delayed function call with try/catch + // delay a function call giving the timer as this timeout: function(func, delay) { let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); timer.initWithCallback({ notify: function notify() { - try { - func(); - } - catch(ex) { - Utils.log(timer, ex); - } + func.call(timer); } }, delay, timer.TYPE_ONE_SHOT); } From 2c057d47879df533d4bae5141a3ef0169b5622c3 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Mon, 9 Aug 2010 00:06:54 -0400 Subject: [PATCH 229/369] Bug 584699: quick fix to -moz-transform rendering bug --HG-- extra : rebase_source : 89019c5a0acff884ab47f18b96503435c6a56e7e --- browser/base/content/tabview/iq.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 23a981066d21..7cd8aca8210e 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -465,7 +465,7 @@ iQClass.prototype = { // ---------- // Function: css // Sets or gets CSS properties on the receiver. When setting certain numerical properties, - // will automatically add "px". + // will automatically add "px". A property can be removed by setting it to null. // // Possible call patterns: // a: object, b: undefined - sets with properties from a @@ -508,7 +508,9 @@ iQClass.prototype = { if (pixels[key] && typeof(value) != 'string') value += 'px'; - if (key.indexOf('-') != -1) + if (value == null) { + elem.style.removeProperty(key); + } else if (key.indexOf('-') != -1) elem.style.setProperty(key, value, ''); else elem.style[key] = value; From 96c792a8d43bfcdc0f9daa7c42f2a4503c639d18 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Mon, 9 Aug 2010 18:38:18 +0200 Subject: [PATCH 230/369] Bug 583852 - Weave should not be querying on places views [r=mconnor] Tests for the history store. --- services/sync/tests/unit/head_helpers.js | 17 ++- .../sync/tests/unit/test_history_store.js | 135 ++++++++++++++++++ .../sync/tests/unit/test_history_tracker.js | 22 --- 3 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 services/sync/tests/unit/test_history_store.js diff --git a/services/sync/tests/unit/head_helpers.js b/services/sync/tests/unit/head_helpers.js index 596498f5408e..83a056e5d00c 100644 --- a/services/sync/tests/unit/head_helpers.js +++ b/services/sync/tests/unit/head_helpers.js @@ -8,11 +8,18 @@ let ds = Cc["@mozilla.org/file/directory_service;1"] let provider = { getFile: function(prop, persistent) { persistent.value = true; - if (prop == "ExtPrefDL") - return [ds.get("CurProcD", Ci.nsIFile)]; - else if (prop == "ProfD") - return ds.get("CurProcD", Ci.nsIFile); - throw Cr.NS_ERROR_FAILURE; + switch (prop) { + case "ExtPrefDL": + return [ds.get("CurProcD", Ci.nsIFile)]; + case "ProfD": + return ds.get("CurProcD", Ci.nsIFile); + case "UHist": + let histFile = ds.get("CurProcD", Ci.nsIFile); + histFile.append("history.dat"); + return histFile; + default: + throw Cr.NS_ERROR_FAILURE; + } }, QueryInterface: function(iid) { if (iid.equals(Ci.nsIDirectoryServiceProvider) || diff --git a/services/sync/tests/unit/test_history_store.js b/services/sync/tests/unit/test_history_store.js new file mode 100644 index 000000000000..c239136c9dc3 --- /dev/null +++ b/services/sync/tests/unit/test_history_store.js @@ -0,0 +1,135 @@ +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://services-sync/engines/history.js"); +Cu.import("resource://services-sync/type_records/history.js"); +Cu.import("resource://services-sync/ext/Sync.js"); +Cu.import("resource://services-sync/util.js"); + +const TIMESTAMP1 = 1281077113313976; +const TIMESTAMP2 = 1281088209595212; +const TIMESTAMP3 = 1281199249129950; + +function queryPlaces(uri, options) { + let query = Svc.History.getNewQuery(); + query.uri = uri; + let res = Svc.History.executeQuery(query, options); + res.root.containerOpen = true; + + let results = []; + for (let i = 0; i < res.root.childCount; i++) + results.push(res.root.getChild(i)); + return results; +} + +function queryHistoryVisits(uri) { + let options = Svc.History.getNewQueryOptions(); + options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY; + options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT; + options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING; + return queryPlaces(uri, options); +} + +function waitForTitleChanged(test) { + let [exec, cb] = Sync.withCb(function (callback) { + Svc.History.addObserver({ + onBeginUpdateBatch: function onBeginUpdateBatch() {}, + onEndUpdateBatch: function onEndUpdateBatch() {}, + onPageChanged: function onPageChanged() {}, + onTitleChanged: function onTitleChanged() { + Svc.History.removeObserver(this); + callback(); + }, + onVisit: function onVisit() {}, + onDeleteVisits: function onDeleteVisits() {}, + onPageExpired: function onPageExpired() {}, + onBeforeDeleteURI: function onBeforeDeleteURI() {}, + onDeleteURI: function onDeleteURI() {}, + onClearHistory: function onClearHistory() {}, + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsINavHistoryObserver, + Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS, + Ci.nsISupportsWeakReference + ]) + }, true); + test(); + }); + exec(cb); +} + +function run_test() { + _("Verify that we've got an empty store to work with."); + let store = new HistoryEngine()._store; + do_check_eq([id for (id in store.getAllIDs())].length, 0); + + try { + _("Let's create an entry in the database."); + let fxuri = Utils.makeURI("http://getfirefox.com/"); + Svc.History.addPageWithDetails(fxuri, "Get Firefox!", TIMESTAMP1); + + _("Verify that the entry exists."); + let ids = [id for (id in store.getAllIDs())]; + do_check_eq(ids.length, 1); + let fxguid = ids[0]; + do_check_true(store.itemExists(fxguid)); + + _("If we query a non-existent record, it's marked as deleted."); + let record = store.createRecord("non-existent"); + do_check_true(record.deleted); + + _("Verify createRecord() returns a complete record."); + record = store.createRecord(fxguid); + do_check_eq(record.histUri, fxuri.spec); + do_check_eq(record.title, "Get Firefox!"); + do_check_eq(record.visits.length, 1); + do_check_eq(record.visits[0].date, TIMESTAMP1); + do_check_eq(record.visits[0].type, Ci.nsINavHistoryService.TRANSITION_LINK); + + _("Let's modify the record and have the store update the database."); + let secondvisit = {date: TIMESTAMP2, + type: Ci.nsINavHistoryService.TRANSITION_TYPED}; + waitForTitleChanged(function() { + store.update({histUri: record.histUri, + title: "Hol Dir Firefox!", + visits: [record.visits[0], secondvisit]}); + }); + let queryres = queryHistoryVisits(fxuri); + do_check_eq(queryres.length, 2); + do_check_eq(queryres[0].time, TIMESTAMP1); + do_check_eq(queryres[0].title, "Hol Dir Firefox!"); + do_check_eq(queryres[1].time, TIMESTAMP2); + do_check_eq(queryres[1].title, "Hol Dir Firefox!"); + + _("Create a brand new record through the store."); + let tbguid = Utils.makeGUID(); + let tburi = Utils.makeURI("http://getthunderbird.com"); + waitForTitleChanged(function() { + store.create({id: tbguid, + histUri: tburi.spec, + title: "The bird is the word!", + visits: [{date: TIMESTAMP3, + type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}); + }); + do_check_eq([id for (id in store.getAllIDs())].length, 2); + queryres = queryHistoryVisits(tburi); + do_check_eq(queryres.length, 1); + do_check_eq(queryres[0].time, TIMESTAMP3); + do_check_eq(queryres[0].title, "The bird is the word!"); + + _("Remove a record from the store."); + store.remove({id: fxguid}); + do_check_false(store.itemExists(fxguid)); + queryres = queryHistoryVisits(fxuri); + do_check_eq(queryres.length, 0); + + _("Make sure wipe works."); + store.wipe(); + do_check_eq([id for (id in store.getAllIDs())].length, 0); + queryres = queryHistoryVisits(fxuri); + do_check_eq(queryres.length, 0); + queryres = queryHistoryVisits(tburi); + do_check_eq(queryres.length, 0); + + } finally { + _("Clean up."); + Svc.History.removeAllPages(); + } +} diff --git a/services/sync/tests/unit/test_history_tracker.js b/services/sync/tests/unit/test_history_tracker.js index 5bf97ac18946..9bc512a0ca6c 100644 --- a/services/sync/tests/unit/test_history_tracker.js +++ b/services/sync/tests/unit/test_history_tracker.js @@ -2,28 +2,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://services-sync/engines/history.js"); Cu.import("resource://services-sync/util.js"); - -// See toolkit/components/places/tests/head_common.js -const NS_APP_HISTORY_50_FILE = "UHist"; -const dirsvc = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIDirectoryService); - -// Add our own dirprovider for old history.dat. -let provider = { - getFile: function(prop, persistent) { - persistent.value = true; - if (prop == NS_APP_HISTORY_50_FILE) { - let histFile = dirsvc.get("ProfD", Ci.nsIFile); - histFile.append("history.dat"); - return histFile; - } - throw Cr.NS_ERROR_FAILURE; - }, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider]) -}; -dirsvc.registerProvider(provider); - - function run_test() { _("Verify we've got an empty tracker to work with."); let tracker = new HistoryEngine()._tracker; From 5a745d9fdbd0cc88a483894a48bcb790927e2360 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Mon, 9 Aug 2010 18:38:18 +0200 Subject: [PATCH 231/369] Bug 583852 - Weave should not be querying on places views [r=mconnor] Query moz_places, moz_historyvisits *and* moz_places_temp, moz_historyvisits_temp instead of the slower views. If the temp tables aren't available (bug 552023), just query the regular tables. Fixed some style and syntax nits. --- services/sync/modules/engines/history.js | 70 +++++++++++++++++++----- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index f237f258e64c..e20accb13fd4 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -89,7 +89,7 @@ function HistoryStore(name) { // Explicitly nullify our references to our cached services so we don't leak Svc.Obs.add("places-shutdown", function() { - for each([query, stmt] in Iterator(this._stmts)) + for each ([query, stmt] in Iterator(this._stmts)) stmt.finalize(); this.__hsvc = null; this._stmts = []; @@ -113,7 +113,7 @@ HistoryStore.prototype = { return this._hsvc.DBConnection; }, - _stmts: [], + _stmts: {}, _getStmt: function(query) { if (query in this._stmts) return this._stmts[query]; @@ -122,34 +122,78 @@ HistoryStore.prototype = { return this._stmts[query] = this._db.createStatement(query); }, + get _haveTempTablesStm() { + return this._getStmt( + "SELECT name FROM sqlite_temp_master " + + "WHERE name IN ('moz_places_temp', 'moz_historyvisits_temp')"); + }, + + get _haveTempTables() { + if (this.__haveTempTables == null) + this.__haveTempTables = !!Utils.queryAsync(this._haveTempTablesStm, + ["name"]).length; + return this.__haveTempTables; + }, + get _visitStm() { + // Gecko <2.0 + if (this._haveTempTables) { + let where = + "WHERE place_id = IFNULL( " + + "(SELECT id FROM moz_places_temp WHERE url = :url), " + + "(SELECT id FROM moz_places WHERE url = :url) " + + ") "; + return this._getStmt( + "SELECT visit_type type, visit_date date " + + "FROM moz_historyvisits_temp " + where + "UNION " + + "SELECT visit_type type, visit_date date " + + "FROM moz_historyvisits " + where + + "ORDER BY date DESC LIMIT 10 "); + } + // Gecko 2.0 return this._getStmt( "SELECT visit_type type, visit_date date " + - "FROM moz_historyvisits_view " + - "WHERE place_id = (" + - "SELECT id " + - "FROM moz_places_view " + - "WHERE url = :url) " + + "FROM moz_historyvisits " + + "WHERE place_id = (SELECT id FROM moz_places WHERE url = :url) " + "ORDER BY date DESC LIMIT 10"); }, get _urlStm() { - return this._getStmt( - "SELECT url, title, frecency " + - "FROM moz_places_view " + + let where = "WHERE id = (" + "SELECT place_id " + "FROM moz_annos " + "WHERE content = :guid AND anno_attribute_id = (" + "SELECT id " + "FROM moz_anno_attributes " + - "WHERE name = '" + GUID_ANNO + "'))"); + "WHERE name = '" + GUID_ANNO + "')) "; + // Gecko <2.0 + if (this._haveTempTables) + return this._getStmt( + "SELECT url, title, frecency FROM moz_places_temp " + where + + "UNION ALL " + + "SELECT url, title, frecency FROM moz_places " + where + "LIMIT 1"); + // Gecko 2.0 + return this._getStmt( + "SELECT url, title, frecency FROM moz_places " + where + "LIMIT 1"); }, get _allUrlStm() { + // Gecko <2.0 + if (this._haveTempTables) + return this._getStmt( + "SELECT url, frecency FROM moz_places_temp " + + "WHERE last_visit_date > :cutoff_date " + + "UNION " + + "SELECT url, frecency FROM moz_places " + + "WHERE last_visit_date > :cutoff_date " + + "ORDER BY 2 DESC " + + "LIMIT :max_results"); + + // Gecko 2.0 return this._getStmt( "SELECT url " + - "FROM moz_places_view " + + "FROM moz_places " + "WHERE last_visit_date > :cutoff_date " + "ORDER BY frecency DESC " + "LIMIT :max_results"); @@ -191,7 +235,7 @@ HistoryStore.prototype = { }, remove: function HistStore_remove(record) { - let page = this._findURLByGUID(record.id) + let page = this._findURLByGUID(record.id); if (page == null) { this._log.debug("Page already removed: " + record.id); return; From 76e998252a3637fed7e477344c4295203b1aa495 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Mon, 9 Aug 2010 18:38:18 +0200 Subject: [PATCH 232/369] Bug 583847 - Weave should be using createAsyncStatement instead of createStatement [r=mconnor] --- services/sync/modules/engines/bookmarks.js | 18 ++++++++---------- services/sync/modules/engines/forms.js | 4 ++-- services/sync/modules/engines/history.js | 2 +- services/sync/modules/util.js | 9 +++++++++ 4 files changed, 20 insertions(+), 13 deletions(-) diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index 7ace9da63873..de31fa57e56a 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -851,10 +851,12 @@ BookmarksStore.prototype = { get _frecencyStm() { if (!this.__frecencyStm) { this._log.trace("Creating SQL statement: _frecencyStm"); - this.__frecencyStm = Svc.History.DBConnection.createStatement( + this.__frecencyStm = Utils.createStatement( + Svc.History.DBConnection, "SELECT frecency " + "FROM moz_places " + - "WHERE url = :url"); + "WHERE url = :url " + + "LIMIT 1"); } return this.__frecencyStm; }, @@ -868,14 +870,10 @@ BookmarksStore.prototype = { // Add in the bookmark's frecency if we have something if (record.bmkUri != null) { - try { - this._frecencyStm.params.url = record.bmkUri; - if (this._frecencyStm.step()) - index += this._frecencyStm.row.frecency; - } - finally { - this._frecencyStm.reset(); - } + this._frecencyStm.params.url = record.bmkUri; + let result = Utils.queryAsync(this._frecencyStm, ["frecency"]); + if (result.length) + index += result.frecency; } return index; diff --git a/services/sync/modules/engines/forms.js b/services/sync/modules/engines/forms.js index 3c5dc10a9598..2a46fc902629 100644 --- a/services/sync/modules/engines/forms.js +++ b/services/sync/modules/engines/forms.js @@ -110,7 +110,7 @@ let FormWrapper = { createStatement: function createStatement(query) { try { // Just return the statement right away if it's okay - return Svc.Form.DBConnection.createStatement(query); + return Utils.createStatement(Svc.Form.DBConnection, query); } catch(ex) { // Assume guid column must not exist yet, so add it with an index @@ -121,7 +121,7 @@ let FormWrapper = { "ON moz_formhistory (guid)"); // Try creating the query now that the column exists - return Svc.Form.DBConnection.createStatement(query); + return Utils.createStatement(Svc.Form.DBConnection, query); } } }; diff --git a/services/sync/modules/engines/history.js b/services/sync/modules/engines/history.js index e20accb13fd4..30e17a9e0384 100644 --- a/services/sync/modules/engines/history.js +++ b/services/sync/modules/engines/history.js @@ -119,7 +119,7 @@ HistoryStore.prototype = { return this._stmts[query]; this._log.trace("Creating SQL statement: " + query); - return this._stmts[query] = this._db.createStatement(query); + return this._stmts[query] = Utils.createStatement(this._db, query); }, get _haveTempTablesStm() { diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index 2db234d030a2..c1b1d24c3e51 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -146,6 +146,15 @@ let Utils = { throw batchEx; }; }, + + createStatement: function createStatement(db, query) { + // Gecko 2.0 + if (db.createAsyncStatement) + return db.createAsyncStatement(query); + + // Gecko <2.0 + return db.createStatement(query); + }, queryAsync: function(query, names) { // Allow array of names, single name, and no name From 2179aaadf50980ae23a55076bd02402fd9f818e1 Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Mon, 9 Aug 2010 14:57:34 -0700 Subject: [PATCH 233/369] + Made the Groups.killNewTabGroup test a little tighter (in case someone has made a group called "new tabs") + Worked around bug 575672 (CSS transition assert) --HG-- extra : rebase_source : 6f758e737c2092214ec3185be847f2c5da406be0 --- browser/base/content/tabview/iq.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 7cd8aca8210e..1626c2b60851 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -542,7 +542,9 @@ iQClass.prototype = { options = {}; let easings = { - tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)", + tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)", + // TODO: change 1.0 above to 1.29 after bug 575672 is fixed + easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care fast: 'cubic-bezier(0.7,0,1,1)' }; From 7904dca00a78844279110e8c11feebbc0e5e54c1 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 10 Aug 2010 01:46:54 +0200 Subject: [PATCH 234/369] Bug 585740 - Weave.Service.login() should start trackers if it's called in lieu of a setup process [r=mconnor] --- services/sync/modules/service.js | 7 +++++++ services/sync/tests/unit/test_service_login.js | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index ba4db30c312d..feeca3850923 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -766,6 +766,7 @@ WeaveSvc.prototype = { if (Svc.IO.offline) throw "Application is offline, login should not be called"; + let initialStatus = this._checkSetup(); if (username) this.username = username; if (password) @@ -776,6 +777,12 @@ WeaveSvc.prototype = { if (this._checkSetup() == CLIENT_NOT_CONFIGURED) throw "aborting login, client not configured"; + // Calling login() with parameters when the client was + // previously not configured means setup was completed. + if (initialStatus == CLIENT_NOT_CONFIGURED + && (username || password || passphrase)) + Svc.Obs.notify("weave:service:setup-complete"); + this._log.info("Logging in user " + this.username); if (!this.verifyLogin()) { diff --git a/services/sync/tests/unit/test_service_login.js b/services/sync/tests/unit/test_service_login.js index af41863ebccb..7148f2aa2647 100644 --- a/services/sync/tests/unit/test_service_login.js +++ b/services/sync/tests/unit/test_service_login.js @@ -78,6 +78,21 @@ function run_test() { do_check_eq(Status.login, LOGIN_SUCCEEDED); do_check_true(Weave.Service.isLoggedIn); do_check_true(Svc.Prefs.get("autoconnect")); + + _("Calling login() with parameters when the client is unconfigured sends notification."); + let notified = false; + Weave.Svc.Obs.add("weave:service:setup-complete", function() { + notified = true; + }); + Weave.Service.username = ""; + Weave.Service.password = ""; + Weave.Service.passphrase = ""; + Weave.Service.login("janedoe", "ilovejohn", "bar"); + do_check_true(notified); + do_check_eq(Status.service, STATUS_OK); + do_check_eq(Status.login, LOGIN_SUCCEEDED); + do_check_true(Weave.Service.isLoggedIn); + do_check_true(Svc.Prefs.get("autoconnect")); _("Logout."); Weave.Service.logout(); From a602452e16d6c97fe6ed6b949304d1ca68efc414 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 10 Aug 2010 01:59:26 +0200 Subject: [PATCH 235/369] Bug 585753 - Changeset 7e8b1f7852a9 breaks bookmark sync [r=mconnor] --- services/sync/modules/engines/bookmarks.js | 2 +- .../sync/tests/unit/test_bookmark_store.js | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 services/sync/tests/unit/test_bookmark_store.js diff --git a/services/sync/modules/engines/bookmarks.js b/services/sync/modules/engines/bookmarks.js index de31fa57e56a..af7983fbb705 100644 --- a/services/sync/modules/engines/bookmarks.js +++ b/services/sync/modules/engines/bookmarks.js @@ -873,7 +873,7 @@ BookmarksStore.prototype = { this._frecencyStm.params.url = record.bmkUri; let result = Utils.queryAsync(this._frecencyStm, ["frecency"]); if (result.length) - index += result.frecency; + index += result[0].frecency; } return index; diff --git a/services/sync/tests/unit/test_bookmark_store.js b/services/sync/tests/unit/test_bookmark_store.js new file mode 100644 index 000000000000..b337a2daf51d --- /dev/null +++ b/services/sync/tests/unit/test_bookmark_store.js @@ -0,0 +1,49 @@ +Cu.import("resource://services-sync/engines/bookmarks.js"); +Cu.import("resource://services-sync/util.js"); + +function run_test() { + let store = new BookmarksEngine()._store; + store.wipe(); + + try { + _("Ensure the record isn't present yet."); + let fxuri = Utils.makeURI("http://getfirefox.com/"); + let ids = Svc.Bookmark.getBookmarkIdsForURI(fxuri, {}); + do_check_eq(ids.length, 0); + + _("Let's create a new record."); + let fxrecord = {id: "{5d81b87c-d5fc-42d9-a114-d69b7342f10e}0", + type: "bookmark", + bmkUri: fxuri.spec, + title: "Get Firefox!", + tags: [], + keyword: "awesome", + loadInSidebar: false, + parentName: "Bookmarks Toolbar", + parentid: "toolbar"}; + store.applyIncoming(fxrecord); + + _("Verify it has been created correctly."); + ids = Svc.Bookmark.getBookmarkIdsForURI(fxuri, {}); + do_check_eq(ids.length, 1); + let id = ids[0]; + do_check_eq(Svc.Bookmark.getItemGUID(id), fxrecord.id); + do_check_eq(Svc.Bookmark.getItemType(id), Svc.Bookmark.TYPE_BOOKMARK); + do_check_eq(Svc.Bookmark.getItemTitle(id), fxrecord.title); + do_check_eq(Svc.Bookmark.getFolderIdForItem(id), + Svc.Bookmark.toolbarFolder); + do_check_eq(Svc.Bookmark.getKeywordForBookmark(id), fxrecord.keyword); + + _("Have the store create a new record object. Verify that it has the same data."); + let newrecord = store.createRecord(fxrecord.id); + for each (let property in ["type", "bmkUri", "title", "keyword", + "parentName", "parentid"]) + do_check_eq(newrecord[property], fxrecord[property]); + + _("The calculated sort index is based on frecency data."); + do_check_true(newrecord.sortindex >= 150); + } finally { + _("Clean up."); + store.wipe(); + } +} From 8f13a531dd5218a8bc2634c048667500d3488f00 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 10 Aug 2010 09:22:38 -0400 Subject: [PATCH 236/369] Bug 582023: move to use getBoundingClientRect instead of reading off the computed CSS for bounds; simplify getPropertyValue usage (based on comments from Dao) --- browser/base/content/tabview/iq.js | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 1626c2b60851..f883758f7aad 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -327,14 +327,16 @@ iQClass.prototype = { // Function: width // Returns the width of the receiver. width: function() { - return parseInt(this.css('width')); + let bounds = this.bounds(); + return bounds.width }, // ---------- // Function: height // Returns the height of the receiver. height: function() { - return parseInt(this.css('height')); + let bounds = this.bounds(); + return bounds.height; }, // ---------- @@ -342,18 +344,18 @@ iQClass.prototype = { // Returns an object with the receiver's position in left and top // properties. position: function() { - return { - left: parseInt(this.css('left')), - top: parseInt(this.css('top')) - }; + let bounds = this.bounds(); + return new Point(bounds.left, bounds.top); }, // ---------- // Function: bounds // Returns a with the receiver's bounds. bounds: function() { - let p = this.position(); - return new Rect(p.left, p.top, this.width(), this.height()); + Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + let rect = this[0].getBoundingClientRect(); + return new Rect(Math.floor(rect.left), Math.floor(rect.top), + Math.floor(rect.width), Math.floor(rect.height)); }, // ---------- @@ -479,12 +481,7 @@ iQClass.prototype = { if (typeof b === "undefined") { Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); - let substitutions = { - 'MozTransform': '-moz-transform', - 'zIndex': 'z-index' - }; - - return window.getComputedStyle(this[0], null).getPropertyValue(substitutions[key] || key); + return window.getComputedStyle(this[0], null).getPropertyValue(key); } properties = {}; properties[key] = b; From 28ede2d5b5ea498b1f12be494e0b52f379eb44d8 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 10 Aug 2010 09:30:23 -0400 Subject: [PATCH 237/369] Bug 582023: rm Utils.timeout --- browser/base/content/tabview/iq.js | 2 +- browser/base/content/tabview/modules/utils.jsm | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f883758f7aad..f361dcefe67c 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -570,7 +570,7 @@ iQClass.prototype = { this.css(css); let self = this; - Utils.timeout(function() { + setTimeout(function() { self.css({ '-moz-transition-property': 'none', '-moz-transition-duration': '', diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index e208114df857..f7691d3a8cce 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -719,17 +719,5 @@ let Utils = { // Return the modified object return target; - }, - - // ---------- - // Function: timeout - // delay a function call giving the timer as this - timeout: function(func, delay) { - let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - timer.initWithCallback({ - notify: function notify() { - func.call(timer); - } - }, delay, timer.TYPE_ONE_SHOT); } }; From 898652d52abedf165883b54373f78c6ba41346eb Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 10 Aug 2010 11:52:32 -0400 Subject: [PATCH 238/369] Bug 572290 - Fix mistakes in editor/libeditor/html/tests/test_bug520189.html; r=bzbarsky a=NPOTB --- .../libeditor/html/tests/test_bug520189.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index d62a9db220f1..3b244b5f45e1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -202,26 +202,26 @@ var tests = [ { id: "s", isIFrame: true, - payload: invalidStyle1Payload, + payload: invalidStyle3Payload, rootElement: function() document.getElementById("s").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "t", - payload: invalidStyle1Payload, + payload: invalidStyle3Payload, rootElement: function() document.getElementById("t"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "u", isIFrame: true, - payload: invalidStyle2Payload, + payload: invalidStyle4Payload, rootElement: function() document.getElementById("u").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "v", - payload: invalidStyle2Payload, + payload: invalidStyle4Payload, rootElement: function() document.getElementById("v"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, @@ -301,39 +301,39 @@ var tests = [ { id: "gg", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc1Payload, rootElement: function() document.getElementById("gg").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "hh", - payload: invalidStyle6Payload, + payload: validImgSrc1Payload, rootElement: function() document.getElementById("hh"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ii", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc2Payload, rootElement: function() document.getElementById("ii").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "jj", - payload: invalidStyle6Payload, + payload: validImgSrc2Payload, rootElement: function() document.getElementById("jj"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "kk", isIFrame: true, - payload: invalidStyle6Payload, + payload: validImgSrc3Payload, rootElement: function() document.getElementById("kk").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ll", - payload: invalidStyle6Payload, + payload: validImgSrc3Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, From 4138c802e86f1020c38eb23c792206392fe87ac2 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:32:43 +0200 Subject: [PATCH 239/369] Bug 578384: Clear to to transparent black instead of solid white. r=jrmuizel --- gfx/layers/d3d9/LayerManagerD3D9.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index b29278e6ce14..7ac7ecdc0dc4 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -327,7 +327,7 @@ LayerManagerD3D9::Render() nsIntRect rect; mWidget->GetClientBounds(rect); - mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0xffffffff, 0, 0); + mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0, 0); mDevice->BeginScene(); From 9ea6e6543e850537cce263d99982384e8e95a2e3 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:32:45 +0200 Subject: [PATCH 240/369] Bug 585248: Use a single device for all LayerManagerD3D9's. r=vlad --- gfx/layers/Makefile.in | 6 +- gfx/layers/d3d9/CanvasLayerD3D9.cpp | 2 +- gfx/layers/d3d9/ColorLayerD3D9.cpp | 2 +- gfx/layers/d3d9/ContainerLayerD3D9.cpp | 2 +- gfx/layers/d3d9/DeviceManagerD3D9.cpp | 480 +++++++++++++++++++++++++ gfx/layers/d3d9/DeviceManagerD3D9.h | 194 ++++++++++ gfx/layers/d3d9/ImageLayerD3D9.cpp | 4 +- gfx/layers/d3d9/LayerManagerD3D9.cpp | 338 ++--------------- gfx/layers/d3d9/LayerManagerD3D9.h | 61 +--- gfx/layers/d3d9/ThebesLayerD3D9.cpp | 6 +- 10 files changed, 733 insertions(+), 362 deletions(-) create mode 100644 gfx/layers/d3d9/DeviceManagerD3D9.cpp create mode 100644 gfx/layers/d3d9/DeviceManagerD3D9.h diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 2f397ca30235..9ea382af17eb 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -84,7 +84,10 @@ CPPSRCS = \ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) ifdef MOZ_ENABLE_D3D9_LAYER -EXPORTS += LayerManagerD3D9.h +EXPORTS += \ + LayerManagerD3D9.h \ + DeviceManagerD3D9.h \ + $(NULL) CPPSRCS += \ LayerManagerD3D9.cpp \ @@ -93,6 +96,7 @@ CPPSRCS += \ ImageLayerD3D9.cpp \ ColorLayerD3D9.cpp \ CanvasLayerD3D9.cpp \ + DeviceManagerD3D9.cpp \ $(NULL) endif endif diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index 976d3a82eb69..756f373e01ec 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -230,7 +230,7 @@ CanvasLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); if (!mGLBufferIsPremultiplied) { device()->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); diff --git a/gfx/layers/d3d9/ColorLayerD3D9.cpp b/gfx/layers/d3d9/ColorLayerD3D9.cpp index ad7fde80292c..a1f7d12db556 100644 --- a/gfx/layers/d3d9/ColorLayerD3D9.cpp +++ b/gfx/layers/d3d9/ColorLayerD3D9.cpp @@ -76,7 +76,7 @@ ColorLayerD3D9::RenderLayer() device()->SetPixelShaderConstantF(0, color, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::SOLIDCOLORLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::SOLIDCOLORLAYER); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } diff --git a/gfx/layers/d3d9/ContainerLayerD3D9.cpp b/gfx/layers/d3d9/ContainerLayerD3D9.cpp index f2f3f8fef409..8f1b2a708b45 100644 --- a/gfx/layers/d3d9/ContainerLayerD3D9.cpp +++ b/gfx/layers/d3d9/ContainerLayerD3D9.cpp @@ -254,7 +254,7 @@ ContainerLayerD3D9::RenderLayer() opacityVector[0] = opacity; device()->SetPixelShaderConstantF(0, opacityVector, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, renderTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp new file mode 100644 index 000000000000..90573dce4c09 --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 ***** */ + +#include "DeviceManagerD3D9.h" +#include "LayerManagerD3D9Shaders.h" +#include "ThebesLayerD3D9.h" +#include "nsIServiceManager.h" +#include "nsIConsoleService.h" +#include "nsPrintfCString.h" + +namespace mozilla { +namespace layers { + +const LPCWSTR kClassName = L"D3D9WindowClass"; + +typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( + UINT SDKVersion +); + +struct vertex { + float x, y; +}; + +SwapChainD3D9::SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager) + : mDeviceManager(aDeviceManager) + , mWnd(0) +{ + mDeviceManager->mSwapChains.AppendElement(this); +} + +SwapChainD3D9::~SwapChainD3D9() +{ + mDeviceManager->mSwapChains.RemoveElement(this); +} + +bool +SwapChainD3D9::Init(HWND hWnd) +{ + RECT r; + ::GetClientRect(hWnd, &r); + + mWnd = hWnd; + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferFormat = D3DFMT_UNKNOWN; + pp.SwapEffect = D3DSWAPEFFECT_COPY; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + pp.hDeviceWindow = mWnd; + if (r.left == r.right || r.top == r.bottom) { + pp.BackBufferHeight = 1; + pp.BackBufferWidth = 1; + } + + HRESULT hr = mDeviceManager->device()-> + CreateAdditionalSwapChain(&pp, + getter_AddRefs(mSwapChain)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create swap chain for window."); + return false; + } + + return true; +} + +bool +SwapChainD3D9::PrepareForRendering() +{ + RECT r; + if (!::GetClientRect(mWnd, &r)) { + return false; + } + + if (!mDeviceManager->VerifyReadyForRendering()) { + return false; + } + + if (!mSwapChain) { + Init(mWnd); + } + + if (mSwapChain) { + nsRefPtr backBuffer; + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + D3DSURFACE_DESC desc; + backBuffer->GetDesc(&desc); + + if (desc.Width == r.right - r.left && desc.Height == r.bottom - r.top) { + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + return true; + } + + mSwapChain = nsnull; + + Init(mWnd); + + if (!mSwapChain) { + return false; + } + + mSwapChain->GetBackBuffer(0, + D3DBACKBUFFER_TYPE_MONO, + getter_AddRefs(backBuffer)); + + mDeviceManager->device()->SetRenderTarget(0, backBuffer); + + return true; + } + return false; +} + +void +SwapChainD3D9::Present(const nsIntRect &aRect) +{ + RECT r; + r.left = aRect.x; + r.top = aRect.y; + r.right = aRect.XMost(); + r.bottom = aRect.YMost(); + + mSwapChain->Present(&r, &r, 0, 0, 0); +} + +void +SwapChainD3D9::Reset() +{ + mSwapChain = nsnull; +} + +#define HAS_CAP(a, b) (((a) & (b)) == (b)) +#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + +DeviceManagerD3D9::DeviceManagerD3D9() +{ +} + +bool +DeviceManagerD3D9::Init() +{ + WNDCLASSW wc; + if (!GetClassInfoW(GetModuleHandle(NULL), kClassName, &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.hInstance = GetModuleHandle(NULL); + wc.lpfnWndProc = ::DefWindowProc; + wc.lpszClassName = kClassName; + if (!RegisterClassW(&wc)) { + NS_WARNING("Failed to register window class for DeviceManager."); + return false; + } + } + + mFocusWnd = CreateWindow(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, + NULL, GetModuleHandle(NULL), NULL); + + if (!mFocusWnd) { + NS_WARNING("Failed to create DeviceManagerD3D9 Window."); + return false; + } + + Direct3DCreate9Func d3d9create = (Direct3DCreate9Func) + GetProcAddress(LoadLibraryW(L"d3d9.dll"), "Direct3DCreate9"); + + if (!d3d9create) { + return false; + } + + mD3D9 = dont_AddRef(d3d9create(D3D_SDK_VERSION)); + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + HRESULT hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + getter_AddRefs(mDevice)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create Device for DeviceManagerD3D9."); + return false; + } + + if (!VerifyCaps()) { + return false; + } + + hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, + getter_AddRefs(mLayerVS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, + getter_AddRefs(mRGBPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, + getter_AddRefs(mYCbCrPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, + getter_AddRefs(mSolidColorPS)); + + if (FAILED(hr)) { + return false; + } + + hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, + 0, + 0, + D3DPOOL_MANAGED, + getter_AddRefs(mVB), + NULL); + + if (FAILED(hr)) { + return false; + } + + vertex *vertices; + hr = mVB->Lock(0, 0, (void**)&vertices, 0); + if (FAILED(hr)) { + return false; + } + + vertices[0].x = vertices[0].y = 0; + vertices[1].x = 1; vertices[1].y = 0; + vertices[2].x = 0; vertices[2].y = 1; + vertices[3].x = 1; vertices[3].y = 1; + + mVB->Unlock(); + + hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + if (FAILED(hr)) { + return false; + } + + D3DVERTEXELEMENT9 elements[] = { + { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, + D3DDECLUSAGE_POSITION, 0 }, + D3DDECL_END() + }; + + mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); + + nsCOMPtr + console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); + + D3DADAPTER_IDENTIFIER9 identifier; + mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); + + if (console) { + nsString msg; + msg += + NS_LITERAL_STRING("Direct3D 9 DeviceManager Initialized Succesfully.\nDriver: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Driver)); + msg += NS_LITERAL_STRING("\nDescription: "); + msg += NS_ConvertUTF8toUTF16( + nsDependentCString((const char*)identifier.Description)); + msg += NS_LITERAL_STRING("\nVersion: "); + msg += NS_ConvertUTF8toUTF16( + nsPrintfCString("%d.%d.%d.%d", + HIWORD(identifier.DriverVersion.HighPart), + LOWORD(identifier.DriverVersion.HighPart), + HIWORD(identifier.DriverVersion.LowPart), + LOWORD(identifier.DriverVersion.LowPart))); + console->LogStringMessage(msg.get()); + } + + return true; +} + +void +DeviceManagerD3D9::SetupRenderState() +{ + mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); + mDevice->SetVertexDeclaration(mVD); + mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); + mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); + mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); + mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); + mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); +} + +already_AddRefed +DeviceManagerD3D9::CreateSwapChain(HWND hWnd) +{ + nsRefPtr swapChain = new SwapChainD3D9(this); + + if (!swapChain->Init(hWnd)) { + return nsnull; + } + + return swapChain.forget(); +} + +void +DeviceManagerD3D9::SetShaderMode(ShaderMode aMode) +{ + switch (aMode) { + case RGBLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mRGBPS); + break; + case YCBCRLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mYCbCrPS); + break; + case SOLIDCOLORLAYER: + mDevice->SetVertexShader(mLayerVS); + mDevice->SetPixelShader(mSolidColorPS); + break; + } +} + +bool +DeviceManagerD3D9::VerifyReadyForRendering() +{ + HRESULT hr = mDevice->TestCooperativeLevel(); + + if (SUCCEEDED(hr)) { + return true; + } + + if (hr != D3DERR_DEVICENOTRESET) { + return false; + } + + for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { + mThebesLayers[i]->CleanResources(); + } + for(unsigned int i = 0; i < mSwapChains.Length(); i++) { + mSwapChains[i]->Reset(); + } + + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDevice->Reset(&pp); + + if (FAILED(hr)) { + return false; + } + + return true; +} + +bool +DeviceManagerD3D9::VerifyCaps() +{ + D3DCAPS9 caps; + HRESULT hr = mDevice->GetDeviceCaps(&caps); + + if (FAILED(hr)) { + return false; + } + + if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { + return false; + } + + if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { + return false; + } + + if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || + LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || + LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { + return false; + } + + if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { + return false; + } + + if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || + HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || + (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && + LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { + return false; + } + + if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || + LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { + return false; + } + + if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { + return false; + } + + if (caps.MaxTextureHeight < 4096 || + caps.MaxTextureWidth < 4096) { + return false; + } + + if ((caps.PixelShaderVersion & 0xffff) < 0x200 || + (caps.VertexShaderVersion & 0xffff) < 0x200) { + return false; + } + + return true; +} + +} /* namespace layers */ +} /* namespace mozilla */ diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h new file mode 100644 index 000000000000..ae92fa651b27 --- /dev/null +++ b/gfx/layers/d3d9/DeviceManagerD3D9.h @@ -0,0 +1,194 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 ***** */ + +#ifndef GFX_DEVICEMANAGERD3D9_H +#define GFX_DEVICEMANAGERD3D9_H + +#include "gfxTypes.h" +#include "nsRect.h" +#include "nsAutoPtr.h" +#include "d3d9.h" +#include "nsTArray.h" + +namespace mozilla { +namespace layers { + +class DeviceManagerD3D9; +class ThebesLayerD3D9; + +/** + * SwapChain class, this class manages the swap chain belonging to a + * LayerManagerD3D9. + */ +class THEBES_API SwapChainD3D9 +{ + NS_INLINE_DECL_REFCOUNTING(SwapChainD3D9) +public: + ~SwapChainD3D9(); + + /** + * This function will prepare the device this swap chain belongs to for + * rendering to this swap chain. Only after calling this function can the + * swap chain be drawn to, and only until this function is called on another + * swap chain belonging to this device will the device draw to it. Passed in + * is the size of the swap chain. If the window size differs from the size + * during the last call to this function the swap chain will resize. Note that + * in no case does this function guarantee the backbuffer to still have its + * old content. + */ + bool PrepareForRendering(); + + /** + * This function will present the selected rectangle of the swap chain to + * its associated window. + */ + void Present(const nsIntRect &aRect); + +private: + friend class DeviceManagerD3D9; + + SwapChainD3D9(DeviceManagerD3D9 *aDeviceManager); + + bool Init(HWND hWnd); + + /** + * This causes us to release our swap chain, clearing out our resource usage + * so the master device may reset. + */ + void Reset(); + + nsRefPtr mSwapChain; + nsRefPtr mDeviceManager; + HWND mWnd; +}; + +/** + * Device manager, this class is used by the layer managers to share the D3D9 + * device and create swap chains for the individual windows the layer managers + * belong to. + */ +class THEBES_API DeviceManagerD3D9 +{ +public: + DeviceManagerD3D9(); + + // We want the nsrefcnt return value. So we cannot use the inline refcnt macro + NS_IMPL_ADDREF(DeviceManagerD3D9) + NS_IMPL_RELEASE(DeviceManagerD3D9) + + bool Init(); + + /** + * Sets up the render state for the device for layer rendering. + */ + void SetupRenderState(); + + /** + * Create a swap chain setup to work with the specified window. + */ + already_AddRefed CreateSwapChain(HWND hWnd); + + IDirect3DDevice9 *device() { return mDevice; } + + enum ShaderMode { + RGBLAYER, + YCBCRLAYER, + SOLIDCOLORLAYER + }; + + void SetShaderMode(ShaderMode aMode); + + /** + * We keep a list of all thebes layers since we need their D3DPOOL_DEFAULT + * surfaces to be released when we want to reset the device. + */ + nsTArray mThebesLayers; +private: + friend class SwapChainD3D9; + + /** + * This function verifies the device is ready for rendering, internally this + * will test the cooperative level of the device and reset the device if + * needed. If this returns false subsequent rendering calls may return errors. + */ + bool VerifyReadyForRendering(); + + /* Array used to store all swap chains for device resets */ + nsTArray mSwapChains; + + /* The D3D device we use */ + nsRefPtr mDevice; + + /* An instance of the D3D9 object */ + nsRefPtr mD3D9; + + /* Vertex shader used for layer quads */ + nsRefPtr mLayerVS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mRGBPS; + + /* Pixel shader used for RGB textures */ + nsRefPtr mYCbCrPS; + + /* Pixel shader used for solid colors */ + nsRefPtr mSolidColorPS; + + /* Vertex buffer containing our basic vertex structure */ + nsRefPtr mVB; + + /* Our vertex declaration */ + nsRefPtr mVD; + + /* Our focus window - this is really a dummy window we can associate our + * device with. + */ + HWND mFocusWnd; + + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD + + /** + * Verifies all required device capabilities are present. + */ + bool VerifyCaps(); +}; + +} /* namespace layers */ +} /* namespace mozilla */ + +#endif /* GFX_DEVICEMANAGERD3D9_H */ diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index d4fba66d40ed..1bb1de06e166 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -185,7 +185,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::YCBCRLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::YCBCRLAYER); device()->SetTexture(0, yuvImage->mYTexture); device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); @@ -227,7 +227,7 @@ ImageLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, cairoImage->mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index 7ac7ecdc0dc4..13a5d56bca63 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -43,25 +43,10 @@ #include "ColorLayerD3D9.h" #include "CanvasLayerD3D9.h" -#include "LayerManagerD3D9Shaders.h" - -#include "nsIServiceManager.h" -#include "nsIConsoleService.h" -#include "nsPrintfCString.h" - namespace mozilla { namespace layers { -struct vertex { - float x, y; -}; - -IDirect3D9 *LayerManagerD3D9::mD3D9 = NULL; - -typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( - UINT SDKVersion -); - +DeviceManagerD3D9 *LayerManagerD3D9::mDeviceManager = nsnull; LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) { @@ -72,145 +57,39 @@ LayerManagerD3D9::LayerManagerD3D9(nsIWidget *aWidget) LayerManagerD3D9::~LayerManagerD3D9() { -} + /* Important to release this first since it also holds a reference to the + * device manager + */ + mSwapChain = nsnull; -#define HAS_CAP(a, b) (((a) & (b)) == (b)) -#define LACKS_CAP(a, b) !(((a) & (b)) == (b)) + if (mDeviceManager) { + if (!mDeviceManager->Release()) { + mDeviceManager = nsnull; + } + } +} PRBool LayerManagerD3D9::Initialize() { - if (!mD3D9) { - Direct3DCreate9Func d3d9create = (Direct3DCreate9Func) - GetProcAddress(LoadLibraryW(L"d3d9.dll"), "Direct3DCreate9"); - if (!d3d9create) { - return PR_FALSE; - } + if (!mDeviceManager) { + mDeviceManager = new DeviceManagerD3D9; - mD3D9 = d3d9create(D3D_SDK_VERSION); - if (!mD3D9) { + if (!mDeviceManager->Init()) { + mDeviceManager = nsnull; return PR_FALSE; } } - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + mDeviceManager->AddRef(); - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); + mSwapChain = mDeviceManager-> + CreateSwapChain((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW)); - HRESULT hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - NULL, - D3DCREATE_FPU_PRESERVE | - D3DCREATE_MULTITHREADED | - D3DCREATE_MIXED_VERTEXPROCESSING, - &pp, - getter_AddRefs(mDevice)); - - if (FAILED(hr)) { + if (!mSwapChain) { return PR_FALSE; } - if (!VerifyCaps()) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexShader((DWORD*)LayerQuadVS, - getter_AddRefs(mLayerVS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)RGBShaderPS, - getter_AddRefs(mRGBPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)YCbCrShaderPS, - getter_AddRefs(mYCbCrPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreatePixelShader((DWORD*)SolidColorShaderPS, - getter_AddRefs(mSolidColorPS)); - - if (FAILED(hr)) { - return PR_FALSE; - } - - hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, - 0, - 0, - D3DPOOL_MANAGED, - getter_AddRefs(mVB), - NULL); - - if (FAILED(hr)) { - return PR_FALSE; - } - - vertex *vertices; - hr = mVB->Lock(0, 0, (void**)&vertices, 0); - if (FAILED(hr)) { - return PR_FALSE; - } - - vertices[0].x = vertices[0].y = 0; - vertices[1].x = 1; vertices[1].y = 0; - vertices[2].x = 0; vertices[2].y = 1; - vertices[3].x = 1; vertices[3].y = 1; - - mVB->Unlock(); - - hr = mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - if (FAILED(hr)) { - return PR_FALSE; - } - - D3DVERTEXELEMENT9 elements[] = { - { 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, - D3DDECLUSAGE_POSITION, 0 }, - D3DDECL_END() - }; - - mDevice->CreateVertexDeclaration(elements, getter_AddRefs(mVD)); - - SetupRenderState(); - - nsCOMPtr - console(do_GetService(NS_CONSOLESERVICE_CONTRACTID)); - - D3DADAPTER_IDENTIFIER9 identifier; - mD3D9->GetAdapterIdentifier(D3DADAPTER_DEFAULT, 0, &identifier); - - if (console) { - nsString msg; - msg += - NS_LITERAL_STRING("Direct3D 9 LayerManager Initialized Succesfully.\nDriver: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Driver)); - msg += NS_LITERAL_STRING("\nDescription: "); - msg += NS_ConvertUTF8toUTF16( - nsDependentCString((const char*)identifier.Description)); - msg += NS_LITERAL_STRING("\nVersion: "); - msg += NS_ConvertUTF8toUTF16( - nsPrintfCString("%d.%d.%d.%d", - HIWORD(identifier.DriverVersion.HighPart), - LOWORD(identifier.DriverVersion.HighPart), - HIWORD(identifier.DriverVersion.LowPart), - LOWORD(identifier.DriverVersion.LowPart))); - console->LogStringMessage(msg.get()); - } - return PR_TRUE; } @@ -298,38 +177,21 @@ LayerManagerD3D9::CreateImageContainer() return container.forget(); } -void -LayerManagerD3D9::SetShaderMode(ShaderMode aMode) -{ - switch (aMode) { - case RGBLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mRGBPS); - break; - case YCBCRLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mYCbCrPS); - break; - case SOLIDCOLORLAYER: - mDevice->SetVertexShader(mLayerVS); - mDevice->SetPixelShader(mSolidColorPS); - break; - } -} - void LayerManagerD3D9::Render() { - if (!SetupBackBuffer()) { + if (!mSwapChain->PrepareForRendering()) { return; } + deviceManager()->SetupRenderState(); + SetupPipeline(); nsIntRect rect; mWidget->GetClientBounds(rect); - mDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0, 0); + device()->Clear(0, NULL, D3DCLEAR_TARGET, 0x00000000, 0, 0); - mDevice->BeginScene(); + device()->BeginScene(); if (mRootLayer) { const nsIntRect *clipRect = mRootLayer->GetLayer()->GetClipRect(); @@ -344,24 +206,18 @@ LayerManagerD3D9::Render() r.right = rect.width; r.bottom = rect.height; } - mDevice->SetScissorRect(&r); + device()->SetScissorRect(&r); mRootLayer->RenderLayer(); } - mDevice->EndScene(); + device()->EndScene(); if (!mTarget) { const nsIntRect *r; for (nsIntRegionRectIterator iter(mClippingRegion); (r = iter.Next()) != nsnull;) { - RECT rect; - rect.left = r->x; - rect.top = r->y; - rect.right = r->XMost(); - rect.bottom = r->YMost(); - - mDevice->Present(&rect, &rect, NULL, NULL); + mSwapChain->Present(*r); } } else { PaintToTarget(); @@ -387,86 +243,29 @@ LayerManagerD3D9::SetupPipeline() viewMatrix[3][1] = 1.0f; viewMatrix[3][3] = 1.0f; - HRESULT hr = mDevice->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); + HRESULT hr = device()->SetVertexShaderConstantF(8, &viewMatrix[0][0], 4); if (FAILED(hr)) { NS_WARNING("Failed to set projection shader constant!"); } } -PRBool -LayerManagerD3D9::SetupBackBuffer() -{ - nsRefPtr backBuffer; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, - getter_AddRefs(backBuffer)); - - D3DSURFACE_DESC desc; - nsIntRect rect; - mWidget->GetClientBounds(rect); - backBuffer->GetDesc(&desc); - - HRESULT hr = mDevice->TestCooperativeLevel(); - - /* The device is lost or something else is wrong, failure */ - if (FAILED(hr) && hr != D3DERR_DEVICENOTRESET) { - return PR_FALSE; - } - - /* - * If the backbuffer is the right size, and the device is not lost, we can - * safely render without doing anything. - */ - if ((desc.Width == rect.width && desc.Height == rect.height) && - SUCCEEDED(hr)) { - return PR_TRUE; - } - - /* - * Our device is lost or our backbuffer needs resizing, start by clearing - * out all D3DPOOL_DEFAULT surfaces. - */ - for(unsigned int i = 0; i < mThebesLayers.Length(); i++) { - mThebesLayers[i]->CleanResources(); - } - - backBuffer = NULL; - - D3DPRESENT_PARAMETERS pp; - memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); - - pp.BackBufferFormat = D3DFMT_A8R8G8B8; - pp.SwapEffect = D3DSWAPEFFECT_COPY; - pp.Windowed = TRUE; - pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - pp.hDeviceWindow = (HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW); - - hr = mDevice->Reset(&pp); - if (FAILED(hr)) { - return PR_FALSE; - } - - SetupRenderState(); - - return PR_TRUE; -} - void LayerManagerD3D9::PaintToTarget() { nsRefPtr backBuff; nsRefPtr destSurf; - mDevice->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, + device()->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, getter_AddRefs(backBuff)); D3DSURFACE_DESC desc; backBuff->GetDesc(&desc); - mDevice->CreateOffscreenPlainSurface(desc.Width, desc.Height, + device()->CreateOffscreenPlainSurface(desc.Width, desc.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, getter_AddRefs(destSurf), NULL); - mDevice->GetRenderTargetData(backBuff, destSurf); + device()->GetRenderTargetData(backBuff, destSurf); D3DLOCKED_RECT rect; destSurf->LockRect(&rect, NULL, D3DLOCK_READONLY); @@ -483,81 +282,6 @@ LayerManagerD3D9::PaintToTarget() destSurf->UnlockRect(); } -void -LayerManagerD3D9::SetupRenderState() -{ - mDevice->SetStreamSource(0, mVB, 0, sizeof(vertex)); - mDevice->SetVertexDeclaration(mVD); - mDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - mDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - mDevice->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD); - mDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - mDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE); - mDevice->SetRenderState(D3DRS_SCISSORTESTENABLE, TRUE); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(1, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); - mDevice->SetSamplerState(2, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); -} - -PRBool -LayerManagerD3D9::VerifyCaps() -{ - D3DCAPS9 caps; - HRESULT hr = mDevice->GetDeviceCaps(&caps); - - if (FAILED(hr)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.DevCaps, D3DDEVCAPS_TEXTUREVIDEOMEMORY)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.PrimitiveMiscCaps, D3DPMISCCAPS_CULLNONE)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.SrcBlendCaps, D3DPBLENDCAPS_ONE) || - LACKS_CAP(caps.SrcBlendCaps, D3DBLEND_SRCALPHA) || - LACKS_CAP(caps.DestBlendCaps, D3DPBLENDCAPS_INVSRCALPHA)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.RasterCaps, D3DPRASTERCAPS_SCISSORTEST)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_ALPHA) || - HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_SQUAREONLY) || - (HAS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_POW2) && - LACKS_CAP(caps.TextureCaps, D3DPTEXTURECAPS_NONPOW2CONDITIONAL))) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MAGFLINEAR) || - LACKS_CAP(caps.TextureFilterCaps, D3DPTFILTERCAPS_MINFLINEAR)) { - return PR_FALSE; - } - - if (LACKS_CAP(caps.TextureAddressCaps, D3DPTADDRESSCAPS_CLAMP)) { - return PR_FALSE; - } - - if (caps.MaxTextureHeight < 4096 || - caps.MaxTextureWidth < 4096) { - return PR_FALSE; - } - - if ((caps.PixelShaderVersion & 0xffff) < 0x200 || - (caps.VertexShaderVersion & 0xffff) < 0x200) { - return PR_FALSE; - } - return PR_TRUE; -} - LayerD3D9::LayerD3D9(LayerManagerD3D9 *aManager) : mD3DManager(aManager) { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index b17c9a4a125b..56f7bcaad45c 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -46,6 +46,8 @@ #include "gfxContext.h" #include "nsIWidget.h" +#include "DeviceManagerD3D9.h" + namespace mozilla { namespace layers { @@ -122,49 +124,27 @@ public: */ void SetClippingEnabled(PRBool aEnabled); - IDirect3DDevice9 *device() const { return mDevice; } + void SetShaderMode(DeviceManagerD3D9::ShaderMode aMode) + { mDeviceManager->SetShaderMode(aMode); } - enum ShaderMode { - RGBLAYER, - YCBCRLAYER, - SOLIDCOLORLAYER - }; - - void SetShaderMode(ShaderMode aMode); - - nsTArray mThebesLayers; + IDirect3DDevice9 *device() const { return mDeviceManager->device(); } + DeviceManagerD3D9 *deviceManager() const { return mDeviceManager; } private: - /* Direct3D9 instance */ - static IDirect3D9 *mD3D9; + /* Device manager instance */ + static DeviceManagerD3D9 *mDeviceManager; + + /* Swap chain associated with this layer manager */ + nsRefPtr mSwapChain; /* Widget associated with this layer manager */ nsIWidget *mWidget; + /* * Context target, NULL when drawing directly to our swap chain. */ nsRefPtr mTarget; - nsRefPtr mDevice; - - /* Vertex shader used for layer quads */ - nsRefPtr mLayerVS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mRGBPS; - - /* Pixel shader used for RGB textures */ - nsRefPtr mYCbCrPS; - - /* Pixel shader used for solid colors */ - nsRefPtr mSolidColorPS; - - /* Vertex buffer containing our basic vertex structure */ - nsRefPtr mVB; - - /* Our vertex declaration */ - nsRefPtr mVD; - /* Current root layer. */ LayerD3D9 *mRootLayer; @@ -175,32 +155,21 @@ private: * Region we're clipping our current drawing to. */ nsIntRegion mClippingRegion; + /* * Render the current layer tree to the active target. */ void Render(); + /* * Setup the pipeline. */ void SetupPipeline(); - /* - * Setup the backbuffer. - * - * \return PR_TRUE if setup was succesful - */ - PRBool SetupBackBuffer(); - /* - * Setup the render state for the surface. - */ - void SetupRenderState(); + /* * Copies the content of our backbuffer to the set transaction target. */ void PaintToTarget(); - /* - * Verifies all required device capabilities are present. - */ - PRBool VerifyCaps(); }; diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index ac659e431284..b8c0c3567855 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -69,12 +69,12 @@ ThebesLayerD3D9::ThebesLayerD3D9(LayerManagerD3D9 *aManager) , LayerD3D9(aManager) { mImplData = static_cast(this); - aManager->mThebesLayers.AppendElement(this); + aManager->deviceManager()->mThebesLayers.AppendElement(this); } ThebesLayerD3D9::~ThebesLayerD3D9() { - mD3DManager->mThebesLayers.RemoveElement(this); + mD3DManager->deviceManager()->mThebesLayers.RemoveElement(this); } /** @@ -338,7 +338,7 @@ ThebesLayerD3D9::RenderLayer() opacity[0] = GetOpacity(); device()->SetPixelShaderConstantF(0, opacity, 1); - mD3DManager->SetShaderMode(LayerManagerD3D9::RGBLAYER); + mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); device()->SetTexture(0, mTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); From 93ae62051a6dac1322b6a1ad15af1081f1d61d6d Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:39:45 +0200 Subject: [PATCH 241/369] Bug 584754: Use the D3D9Ex device where available. r=vlad --- gfx/layers/d3d9/CanvasLayerD3D9.cpp | 14 +- gfx/layers/d3d9/DeviceManagerD3D9.cpp | 118 ++++++++++++++--- gfx/layers/d3d9/DeviceManagerD3D9.h | 13 ++ gfx/layers/d3d9/ImageLayerD3D9.cpp | 179 +++++++++++++++++--------- 4 files changed, 243 insertions(+), 81 deletions(-) diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index 756f373e01ec..a3ef316b0fd0 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -70,9 +70,17 @@ CanvasLayerD3D9::Initialize(const Data& aData) mBounds.SetRect(0, 0, aData.mSize.width, aData.mSize.height); - device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, - D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mD3DManager->deviceManager()->HasDynamicTextures()) { + device()->CreateTexture(mBounds.width, mBounds.height, 1, D3DUSAGE_DYNAMIC, + D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + } else { + // D3DPOOL_MANAGED is fine here since we require Dynamic Textures for D3D9Ex + // devices. + device()->CreateTexture(mBounds.width, mBounds.height, 1, 0, + D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + } } void diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp index 90573dce4c09..bf04df6ae4a1 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -47,10 +47,17 @@ namespace layers { const LPCWSTR kClassName = L"D3D9WindowClass"; +#define USE_D3D9EX + typedef IDirect3D9* (WINAPI*Direct3DCreate9Func)( UINT SDKVersion ); +typedef HRESULT (WINAPI*Direct3DCreate9ExFunc)( + UINT SDKVersion, + IDirect3D9Ex **ppD3D +); + struct vertex { float x, y; }; @@ -171,6 +178,7 @@ SwapChainD3D9::Reset() #define LACKS_CAP(a, b) !(((a) & (b)) == (b)) DeviceManagerD3D9::DeviceManagerD3D9() + : mHasDynamicTextures(false) { } @@ -178,6 +186,8 @@ bool DeviceManagerD3D9::Init() { WNDCLASSW wc; + HRESULT hr; + if (!GetClassInfoW(GetModuleHandle(NULL), kClassName, &wc)) { ZeroMemory(&wc, sizeof(WNDCLASSW)); wc.hInstance = GetModuleHandle(NULL); @@ -198,14 +208,32 @@ DeviceManagerD3D9::Init() return false; } - Direct3DCreate9Func d3d9create = (Direct3DCreate9Func) - GetProcAddress(LoadLibraryW(L"d3d9.dll"), "Direct3DCreate9"); + HMODULE d3d9 = LoadLibraryW(L"d3d9.dll"); + Direct3DCreate9Func d3d9Create = (Direct3DCreate9Func) + GetProcAddress(d3d9, "Direct3DCreate9"); + Direct3DCreate9ExFunc d3d9CreateEx = (Direct3DCreate9ExFunc) + GetProcAddress(d3d9, "Direct3DCreate9Ex"); - if (!d3d9create) { - return false; +#ifdef USE_D3D9EX + if (d3d9CreateEx) { + hr = d3d9CreateEx(D3D_SDK_VERSION, getter_AddRefs(mD3D9Ex)); + if (SUCCEEDED(hr)) { + mD3D9 = mD3D9Ex; + } } +#endif - mD3D9 = dont_AddRef(d3d9create(D3D_SDK_VERSION)); + if (!mD3D9) { + if (!d3d9Create) { + return false; + } + + mD3D9 = dont_AddRef(d3d9Create(D3D_SDK_VERSION)); + + if (!mD3D9) { + return false; + } + } D3DPRESENT_PARAMETERS pp; memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); @@ -218,18 +246,47 @@ DeviceManagerD3D9::Init() pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; pp.hDeviceWindow = mFocusWnd; - HRESULT hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, - D3DDEVTYPE_HAL, - mFocusWnd, - D3DCREATE_FPU_PRESERVE | - D3DCREATE_MULTITHREADED | - D3DCREATE_MIXED_VERTEXPROCESSING, - &pp, - getter_AddRefs(mDevice)); + if (mD3D9Ex) { + hr = mD3D9Ex->CreateDeviceEx(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + NULL, + getter_AddRefs(mDeviceEx)); + if (SUCCEEDED(hr)) { + mDevice = mDeviceEx; + } - if (FAILED(hr)) { - NS_WARNING("Failed to create Device for DeviceManagerD3D9."); - return false; + D3DCAPS9 caps; + if (mDeviceEx->GetDeviceCaps(&caps)) { + if (LACKS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + // XXX - Should we actually hit this we'll need a CanvasLayer that + // supports static D3DPOOL_DEFAULT textures. + NS_WARNING("D3D9Ex device not used because of lack of support for \ + dynamic textures. This is unexpected."); + mDevice = nsnull; + mDeviceEx = nsnull; + } + } + } + + if (!mDevice) { + hr = mD3D9->CreateDevice(D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + mFocusWnd, + D3DCREATE_FPU_PRESERVE | + D3DCREATE_MULTITHREADED | + D3DCREATE_MIXED_VERTEXPROCESSING, + &pp, + getter_AddRefs(mDevice)); + + if (FAILED(hr)) { + NS_WARNING("Failed to create Device for DeviceManagerD3D9."); + return false; + } } if (!VerifyCaps()) { @@ -265,9 +322,9 @@ DeviceManagerD3D9::Init() } hr = mDevice->CreateVertexBuffer(sizeof(vertex) * 4, + D3DUSAGE_WRITEONLY, 0, - 0, - D3DPOOL_MANAGED, + D3DPOOL_DEFAULT, getter_AddRefs(mVB), NULL); @@ -385,6 +442,27 @@ DeviceManagerD3D9::VerifyReadyForRendering() HRESULT hr = mDevice->TestCooperativeLevel(); if (SUCCEEDED(hr)) { + if (IsD3D9Ex()) { + hr = mDeviceEx->CheckDeviceState(mFocusWnd); + if (FAILED(hr)) { + D3DPRESENT_PARAMETERS pp; + memset(&pp, 0, sizeof(D3DPRESENT_PARAMETERS)); + + pp.BackBufferWidth = 1; + pp.BackBufferHeight = 1; + pp.BackBufferFormat = D3DFMT_A8R8G8B8; + pp.SwapEffect = D3DSWAPEFFECT_DISCARD; + pp.Windowed = TRUE; + pp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; + pp.hDeviceWindow = mFocusWnd; + + hr = mDeviceEx->ResetEx(&pp, NULL); + // Handle D3DERR_DEVICEREMOVED! + if (FAILED(hr)) { + return false; + } + } + } return true; } @@ -473,6 +551,10 @@ DeviceManagerD3D9::VerifyCaps() return false; } + if (HAS_CAP(caps.Caps2, D3DCAPS2_DYNAMICTEXTURES)) { + mHasDynamicTextures = true; + } + return true; } diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.h b/gfx/layers/d3d9/DeviceManagerD3D9.h index ae92fa651b27..b3b921142447 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.h +++ b/gfx/layers/d3d9/DeviceManagerD3D9.h @@ -124,6 +124,10 @@ public: IDirect3DDevice9 *device() { return mDevice; } + bool IsD3D9Ex() { return mDeviceEx; } + + bool HasDynamicTextures() { return mHasDynamicTextures; } + enum ShaderMode { RGBLAYER, YCBCRLAYER, @@ -153,9 +157,15 @@ private: /* The D3D device we use */ nsRefPtr mDevice; + /* The D3D9Ex device - only valid on Vista+ with WDDM */ + nsRefPtr mDeviceEx; + /* An instance of the D3D9 object */ nsRefPtr mD3D9; + /* An instance of the D3D9Ex object - only valid on Vista+ with WDDM */ + nsRefPtr mD3D9Ex; + /* Vertex shader used for layer quads */ nsRefPtr mLayerVS; @@ -179,6 +189,9 @@ private: */ HWND mFocusWnd; + /* If this device supports dynamic textures */ + bool mHasDynamicTextures; + nsAutoRefCnt mRefCnt; NS_DECL_OWNINGTHREAD diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index 1bb1de06e166..eedbd1d1847a 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -244,6 +244,7 @@ PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9(mozilla::layers::LayerManagerD3D9* aM void PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData) { + // XXX - For D3D9Ex we really should just copy to systemmem surfaces here. // For now, we copy the data int width_shift = 0; int height_shift = 0; @@ -309,76 +310,116 @@ PlanarYCbCrImageD3D9::AllocateTextures() { - D3DLOCKED_RECT lockrect; + D3DLOCKED_RECT lockrectY; + D3DLOCKED_RECT lockrectCb; + D3DLOCKED_RECT lockrectCr; PRUint8* src; PRUint8* dest; - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mYTexture), NULL); + nsRefPtr tmpSurfaceY; + nsRefPtr tmpSurfaceCb; + nsRefPtr tmpSurfaceCr; - /* lock the entire texture */ - mYTexture->LockRect(0, &lockrect, NULL, 0); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex does not support the managed pool, could use dynamic textures + // here. But since an Image is immutable static textures are probably a + // better idea. + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(mCrTexture), NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mYSize.width, + mData.mYSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceY), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCb), + NULL); + mManager->device()->CreateOffscreenPlainSurface(mData.mCbCrSize.width, + mData.mCbCrSize.height, + D3DFMT_L8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpSurfaceCr), + NULL); + tmpSurfaceY->LockRect(&lockrectY, NULL, 0); + tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0); + tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0); + } else { + mManager->device()->CreateTexture(mData.mYSize.width, mData.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mYTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCbTexture), NULL); + mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(mCrTexture), NULL); + + /* lock the entire texture */ + mYTexture->LockRect(0, &lockrectY, NULL, 0); + mCbTexture->LockRect(0, &lockrectCb, NULL, 0); + mCrTexture->LockRect(0, &lockrectCr, NULL, 0); + } src = mData.mYChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectY.pBits; // copy over data for (int h=0; hUnlockRect(0); - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCbTexture), NULL); - - - /* lock the entire texture */ - mCbTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCbChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCb.pBits; // copy over data for (int h=0; hUnlockRect(0); - - - //XXX: ensure correct usage flags - mManager->device()->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCrTexture), NULL); - - - /* lock the entire texture */ - mCrTexture->LockRect(0, &lockrect, NULL, 0); - src = mData.mCrChannel; //FIX cast - dest = (PRUint8*)lockrect.pBits; + dest = (PRUint8*)lockrectCr.pBits; // copy over data for (int h=0; hUnlockRect(0); - + if (mManager->deviceManager()->IsD3D9Ex()) { + tmpSurfaceY->UnlockRect(); + tmpSurfaceCb->UnlockRect(); + tmpSurfaceCr->UnlockRect(); + nsRefPtr dstSurface; + mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL); + mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL); + mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL); + } else { + mYTexture->UnlockRect(0); + mCbTexture->UnlockRect(0); + mCrTexture->UnlockRect(0); + } } void @@ -426,31 +467,49 @@ CairoImageD3D9::SetData(const CairoImage::Data &aData) context->SetSource(aData.mSurface); context->Paint(); - //XXX: make sure we're using the correct usage flags - mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, - 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, - getter_AddRefs(mTexture), NULL); + if (mManager->deviceManager()->IsD3D9Ex()) { + // D3D9Ex doesn't support managed textures. We could use dynamic textures + // here but since Images are immutable that probably isn't such a great + // idea. + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, + getter_AddRefs(mTexture), NULL); + nsRefPtr surface; + mManager->device()->CreateOffscreenPlainSurface(aData.mSize.width, + aData.mSize.height, + D3DFMT_A8R8G8B8, + D3DPOOL_SYSTEMMEM, + getter_AddRefs(surface), + NULL); + D3DLOCKED_RECT lockedRect; + surface->LockRect(&lockedRect, NULL, 0); + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockedRect.pBits + lockedRect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } + surface->UnlockRect(); + nsRefPtr dstSurface; + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + mManager->device()->UpdateSurface(surface, NULL, dstSurface, NULL); + } else { + mManager->device()->CreateTexture(aData.mSize.width, aData.mSize.height, + 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, + getter_AddRefs(mTexture), NULL); + D3DLOCKED_RECT lockrect; + /* lock the entire texture */ + mTexture->LockRect(0, &lockrect, NULL, 0); - D3DLOCKED_RECT lockrect; - /* lock the entire texture */ - mTexture->LockRect(0, &lockrect, NULL, 0); + // copy over data. If we don't need to do any swaping we can + // use memcpy + for (int y = 0; y < aData.mSize.height; y++) { + memcpy((char*)lockrect.pBits + lockrect.Pitch * y, + imageSurface->Data() + imageSurface->Stride() * y, + aData.mSize.width * 4); + } - PRUint8* src = imageSurface->Data(); - //FIX cast - PRUint8* dest = (PRUint8*)lockrect.pBits; - - // copy over data. If we don't need to do any swaping we can - // use memcpy - for (int i=0; iUnlockRect(0); } - - mTexture->UnlockRect(0); } already_AddRefed From 6d53e198b0387176ece5ce9fb705716c4ef26b30 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:39:49 +0200 Subject: [PATCH 242/369] Bug 580109: Optimize operations that can be executed as a GPU memcpy. r=jrmuizel a=dougt --- gfx/cairo/cairo/src/cairo-d2d-surface.cpp | 173 +++++++++++++++++++++- gfx/cairo/cairo/src/cairo-rectangle.c | 20 +++ gfx/cairo/cairo/src/cairoint.h | 4 + 3 files changed, 194 insertions(+), 3 deletions(-) diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index aa720ad8e898..cb62dc5bd0a6 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -2168,7 +2168,152 @@ _cairo_d2d_flush(void *surface) return CAIRO_STATUS_SUCCESS; } +static cairo_int_status_t +_cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, + cairo_d2d_surface_t *src, + cairo_point_int_t *translation, + cairo_region_t *region) +{ + RefPtr dstSurface; + dst->surface->QueryInterface(&dstSurface); + RefPtr srcSurface; + src->surface->QueryInterface(&srcSurface); + DXGI_SURFACE_DESC srcDesc, dstDesc; + srcSurface->GetDesc(&srcDesc); + dstSurface->GetDesc(&dstDesc); + + cairo_rectangle_int_t clip_rect; + clip_rect.x = 0; + clip_rect.y = 0; + clip_rect.width = dstDesc.Width; + clip_rect.height = dstDesc.Height; + + cairo_int_status_t rv = CAIRO_INT_STATUS_SUCCESS; + + _cairo_d2d_flush(dst); + ID3D10Resource *srcResource = src->surface; + if (src->surface.get() == dst->surface.get()) { + // Self-copy + srcResource = _cairo_d2d_get_buffer_texture(dst); + D3D10Factory::Device()->CopyResource(srcResource, src->surface); + } else { + // Need to flush the source too if it's a different surface. + _cairo_d2d_flush(src); + } + + // One copy for each rectangle in the final clipping region. + for (int i = 0; i < cairo_region_num_rectangles(region); i++) { + D3D10_BOX rect; + cairo_rectangle_int_t area_to_copy; + + cairo_region_get_rectangle(region, i, &area_to_copy); + + cairo_rectangle_int_t transformed_rect = { area_to_copy.x + translation->x, + area_to_copy.y + translation->y, + area_to_copy.width, area_to_copy.height }; + cairo_rectangle_int_t surface_rect = { 0, 0, srcDesc.Width, srcDesc.Height }; + + + if (!_cairo_rectangle_contains(&surface_rect, &transformed_rect)) { + /* We cannot do any sort of extend, in the future a little bit of extra code could + * allow us to support EXTEND_NONE. + */ + rv = CAIRO_INT_STATUS_UNSUPPORTED; + break; + } + + rect.front = 0; + rect.back = 1; + rect.left = transformed_rect.x; + rect.top = transformed_rect.y; + rect.right = transformed_rect.x + transformed_rect.width; + rect.bottom = transformed_rect.y + transformed_rect.height; + + D3D10Factory::Device()->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); + } + + return rv; +} + +/** + * This function will text if we can use GPU mem cpy to execute an operation with + * a surface pattern. If box is NULL it will operate on the entire dst surface. + */ +static cairo_int_status_t +_cairo_d2d_try_copy(cairo_d2d_surface_t *dst, + cairo_surface_t *src, + cairo_box_t *box, + const cairo_matrix_t *matrix, + cairo_clip_t *clip, + cairo_operator_t op) +{ + if (op != CAIRO_OPERATOR_SOURCE && + !(op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_point_int_t translation; + if ((box && !box_is_integer(box)) || + !_cairo_matrix_is_integer_translation(matrix, &translation.x, &translation.y)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + /* For now we do only D2D sources */ + if (src->type != CAIRO_SURFACE_TYPE_D2D) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + cairo_rectangle_int_t rect; + if (box) { + _cairo_box_round_to_rectangle(box, &rect); + } else { + rect.x = rect.y = 0; + rect.width = dst->rt->GetPixelSize().width; + rect.height = dst->rt->GetPixelSize().height; + } + + cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + + /* Region we need to clip this operation to */ + cairo_region_t *clipping_region = NULL; + cairo_region_t *region; + if (clip) { + _cairo_clip_get_region(clip, &clipping_region); + + if (!clipping_region) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + region = cairo_region_copy(clipping_region); + + cairo_region_intersect_rectangle(region, &rect); + + if (cairo_region_is_empty(region)) { + // Nothing to do. + return CAIRO_INT_STATUS_SUCCESS; + } + } else { + region = cairo_region_create_rectangle(&rect); + // Areas outside of the surface do not matter. + cairo_rectangle_int_t surface_rect = { 0, 0, + dst->rt->GetPixelSize().width, + dst->rt->GetPixelSize().height }; + cairo_region_intersect_rectangle(region, &surface_rect); + } + + cairo_int_status_t rv = _cairo_d2d_copy_surface(dst, d2dsrc, &translation, region); + + cairo_region_destroy(region); + + return rv; +} static cairo_int_status_t _cairo_d2d_paint(void *surface, @@ -2185,6 +2330,17 @@ _cairo_d2d_paint(void *surface, return _cairo_d2d_clear(d2dsurf, clip); } + if (source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + + status = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + NULL, &source->matrix, clip, op); + + if (status != CAIRO_INT_STATUS_UNSUPPORTED) { + return status; + } + } _begin_draw_state(d2dsurf); status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); @@ -2380,6 +2536,19 @@ _cairo_d2d_fill(void *surface, cairo_int_status_t status; cairo_d2d_surface_t *d2dsurf = static_cast(surface); + cairo_box_t box; + bool is_box = _cairo_path_fixed_is_box(path, &box); + + if (is_box && source->type == CAIRO_PATTERN_TYPE_SURFACE) { + const cairo_surface_pattern_t *surf_pattern = + reinterpret_cast(source); + cairo_int_status_t rv = _cairo_d2d_try_copy(d2dsurf, surf_pattern->surface, + &box, &source->matrix, clip, op); + + if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { + return rv; + } + } op = _cairo_d2d_simplify_operator(op, source); @@ -2406,8 +2575,6 @@ _cairo_d2d_fill(void *surface, d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } - cairo_box_t box; - if (op == CAIRO_OPERATOR_CLEAR) { if (_cairo_path_fixed_is_box(path, &box)) { return _cairo_d2d_clear_box (d2dsurf, clip, &box); @@ -2416,7 +2583,7 @@ _cairo_d2d_fill(void *surface, } } - if (_cairo_path_fixed_is_box(path, &box)) { + if (is_box) { float x1 = _cairo_fixed_to_float(box.p1.x); float y1 = _cairo_fixed_to_float(box.p1.y); float x2 = _cairo_fixed_to_float(box.p2.x); diff --git a/gfx/cairo/cairo/src/cairo-rectangle.c b/gfx/cairo/cairo/src/cairo-rectangle.c index e887c75134aa..40a8bc95c4ba 100644 --- a/gfx/cairo/cairo/src/cairo-rectangle.c +++ b/gfx/cairo/cairo/src/cairo-rectangle.c @@ -94,6 +94,26 @@ _cairo_boxes_get_extents (const cairo_box_t *boxes, } } +/* This function will return 'true' if the containing_rectangle contains the + * contained_rectangle, and false otherwise. + */ +cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle) +{ + if (containing_rectangle->x > contained_rectangle->x || + containing_rectangle->y > contained_rectangle->y) + return FALSE; + + if (containing_rectangle->x + containing_rectangle->width < + contained_rectangle->x + contained_rectangle->width || + containing_rectangle->y + containing_rectangle->height < + contained_rectangle->y + contained_rectangle->height) + return FALSE; + + return TRUE; +} + /* XXX We currently have a confusing mix of boxes and rectangles as * exemplified by this function. A #cairo_box_t is a rectangular area * represented by the coordinates of the upper left and lower right diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 28861d73ef4b..63612e63ae35 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -271,6 +271,10 @@ cairo_private void _cairo_box_from_rectangle (cairo_box_t *box, const cairo_rectangle_int_t *rectangle); +cairo_private cairo_bool_t +_cairo_rectangle_contains (const cairo_rectangle_int_t *containing_rectangle, + const cairo_rectangle_int_t *contained_rectangle); + cairo_private void _cairo_box_round_to_rectangle (const cairo_box_t *box, cairo_rectangle_int_t *rectangle); From 9c925ac10563e9ffca6747a3a36ecd5fac89dde5 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:57:11 +0200 Subject: [PATCH 243/369] Bug 580765: Allow const dereferencing for our cairo ref ptr. r=jrmuizel --- gfx/cairo/cairo/src/cairo-win32-refptr.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gfx/cairo/cairo/src/cairo-win32-refptr.h b/gfx/cairo/cairo/src/cairo-win32-refptr.h index 11fc0252eea5..6dd5ac323431 100644 --- a/gfx/cairo/cairo/src/cairo-win32-refptr.h +++ b/gfx/cairo/cairo/src/cairo-win32-refptr.h @@ -107,6 +107,11 @@ public: return mPtr; } + T* operator->() const + { + return mPtr; + } + operator bool() { return (mPtr ? true : false); From d39a572aa8f93dfe1a8440e8ae3cc00d330942b1 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 01:59:51 +0200 Subject: [PATCH 244/369] Bug 580765: Support different compositing operations with D2D. Add cairo_d2d_device_t to make handling this easier. r=jrmuizel --- gfx/cairo/cairo/src/cairo-d2d-private-fx.h | 546 ++++++++++++++ gfx/cairo/cairo/src/cairo-d2d-private.fx | 57 ++ gfx/cairo/cairo/src/cairo-d2d-private.h | 139 ++-- gfx/cairo/cairo/src/cairo-d2d-surface.cpp | 812 +++++++++++++++++---- gfx/cairo/cairo/src/cairo-dwrite-font.cpp | 78 +- gfx/cairo/cairo/src/cairo-win32.h | 47 +- gfx/thebes/gfxD2DSurface.cpp | 11 +- gfx/thebes/gfxWindowsPlatform.cpp | 9 +- gfx/thebes/gfxWindowsPlatform.h | 6 + 9 files changed, 1433 insertions(+), 272 deletions(-) create mode 100644 gfx/cairo/cairo/src/cairo-d2d-private-fx.h create mode 100644 gfx/cairo/cairo/src/cairo-d2d-private.fx diff --git a/gfx/cairo/cairo/src/cairo-d2d-private-fx.h b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h new file mode 100644 index 000000000000..15945339ff3a --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private-fx.h @@ -0,0 +1,546 @@ +#if 0 +// +// FX Version: fx_4_0 +// Child effect (requires effect pool): false +// +// 1 local buffer(s) +// +cbuffer cb0 +{ + float4 QuadDesc; // Offset: 0, size: 16 + float4 TexCoords; // Offset: 16, size: 16 +} + +// +// 2 local object(s) +// +Texture2D tex; +SamplerState sSampler +{ + Texture = tex; + AddressU = uint(CLAMP /* 3 */); + AddressV = uint(CLAMP /* 3 */); +}; + +// +// 1 technique(s) +// +technique10 SampleTexture +{ + pass P0 + { + VertexShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Buffer Definitions: + // + // cbuffer cb0 + // { + // + // float4 QuadDesc; // Offset: 0 Size: 16 + // float4 TexCoords; // Offset: 16 Size: 16 + // + // } + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // cb0 cbuffer NA NA 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // POSITION 0 xyz 0 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float xyzw + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Constant buffer to DX9 shader constant mappings: + // + // Target Reg Buffer Start Reg # of Regs Data Conversion + // ---------- ------- --------- --------- ---------------------- + // c1 cb0 0 2 ( FLT, FLT, FLT, FLT) + // + // + // Runtime generated constant mappings: + // + // Target Reg Constant Description + // ---------- -------------------------------------------------- + // c0 Vertex Shader position offset + // + // + // Level9 shader bytecode: + // + vs_2_x + def c3, 0, 1, 0, 0 + dcl_texcoord v0 + mad oT0.xy, v0, c2.zwzw, c2 + mad r0.x, v0.x, c1.z, c1.x + mad r0.y, v0.y, c1.w, c1.y + add oPos.xy, r0, c0 + mov oPos.zw, c3.xyxy + + // approximately 5 instruction slots used + vs_4_0 + dcl_constantbuffer cb0[2], immediateIndexed + dcl_input v0.xy + dcl_output_siv o0.xyzw, position + dcl_output o1.xy + mad o0.xy, v0.xyxx, cb0[0].zwzz, cb0[0].xyxx + mov o0.zw, l(0,0,0,1.000000) + mad o1.xy, v0.xyxx, cb0[1].zwzz, cb0[1].xyxx + ret + // Approximately 4 instruction slots used + + }; + GeometryShader = NULL; + PixelShader = asm { + // + // Generated by Microsoft (R) HLSL Shader Compiler 9.27.952.3022 + // + // + // Resource Bindings: + // + // Name Type Format Dim Slot Elements + // ------------------------------ ---------- ------- ----------- ---- -------- + // sSampler sampler NA NA 0 1 + // tex texture float4 2d 0 1 + // + // + // + // Input signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Position 0 xyzw 0 POS float + // TEXCOORD 0 xy 1 NONE float xy + // + // + // Output signature: + // + // Name Index Mask Register SysValue Format Used + // -------------------- ----- ------ -------- -------- ------ ------ + // SV_Target 0 xyzw 0 TARGET float xyzw + // + // + // Sampler/Resource to DX9 shader sampler mappings: + // + // Target Sampler Source Sampler Source Resource + // -------------- --------------- ---------------- + // s0 s0 t0 + // + // + // Level9 shader bytecode: + // + ps_2_x + dcl t0.xy + dcl_2d s0 + texld r0, t0, s0 + mov oC0, r0 + + // approximately 2 instruction slots used (1 texture, 1 arithmetic) + ps_4_0 + dcl_sampler s0, mode_default + dcl_resource_texture2d (float,float,float,float) t0 + dcl_input_ps linear v1.xy + dcl_output o0.xyzw + sample o0.xyzw, v1.xyxx, t0.xyzw, s0 + ret + // Approximately 2 instruction slots used + + }; + } + +} + +#endif + +const BYTE g_main[] = +{ + 68, 88, 66, 67, 235, 24, + 238, 6, 37, 230, 191, 228, + 58, 61, 41, 219, 70, 130, + 61, 51, 1, 0, 0, 0, + 187, 8, 0, 0, 1, 0, + 0, 0, 36, 0, 0, 0, + 70, 88, 49, 48, 143, 8, + 0, 0, 1, 16, 255, 254, + 1, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 79, 7, + 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 2, 0, + 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 99, 98, + 48, 0, 102, 108, 111, 97, + 116, 52, 0, 8, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 16, + 0, 0, 0, 10, 33, 0, + 0, 81, 117, 97, 100, 68, + 101, 115, 99, 0, 84, 101, + 120, 67, 111, 111, 114, 100, + 115, 0, 84, 101, 120, 116, + 117, 114, 101, 50, 68, 0, + 62, 0, 0, 0, 2, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 12, 0, 0, 0, 116, 101, + 120, 0, 83, 97, 109, 112, + 108, 101, 114, 83, 116, 97, + 116, 101, 0, 104, 0, 0, + 0, 2, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 21, 0, 0, + 0, 115, 83, 97, 109, 112, + 108, 101, 114, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 1, 0, + 0, 0, 2, 0, 0, 0, + 3, 0, 0, 0, 83, 97, + 109, 112, 108, 101, 84, 101, + 120, 116, 117, 114, 101, 0, + 80, 48, 0, 172, 3, 0, + 0, 68, 88, 66, 67, 247, + 105, 31, 113, 120, 95, 58, + 12, 207, 141, 45, 76, 175, + 59, 223, 25, 1, 0, 0, + 0, 172, 3, 0, 0, 6, + 0, 0, 0, 56, 0, 0, + 0, 248, 0, 0, 0, 188, + 1, 0, 0, 56, 2, 0, + 0, 32, 3, 0, 0, 84, + 3, 0, 0, 65, 111, 110, + 57, 184, 0, 0, 0, 184, + 0, 0, 0, 0, 2, 254, + 255, 132, 0, 0, 0, 52, + 0, 0, 0, 1, 0, 36, + 0, 0, 0, 48, 0, 0, + 0, 48, 0, 0, 0, 36, + 0, 1, 0, 48, 0, 0, + 0, 0, 0, 2, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 2, 254, + 255, 81, 0, 0, 5, 3, + 0, 15, 160, 0, 0, 0, + 0, 0, 0, 128, 63, 0, + 0, 0, 0, 0, 0, 0, + 0, 31, 0, 0, 2, 5, + 0, 0, 128, 0, 0, 15, + 144, 4, 0, 0, 4, 0, + 0, 3, 224, 0, 0, 228, + 144, 2, 0, 238, 160, 2, + 0, 228, 160, 4, 0, 0, + 4, 0, 0, 1, 128, 0, + 0, 0, 144, 1, 0, 170, + 160, 1, 0, 0, 160, 4, + 0, 0, 4, 0, 0, 2, + 128, 0, 0, 85, 144, 1, + 0, 255, 160, 1, 0, 85, + 160, 2, 0, 0, 3, 0, + 0, 3, 192, 0, 0, 228, + 128, 0, 0, 228, 160, 1, + 0, 0, 2, 0, 0, 12, + 192, 3, 0, 68, 160, 255, + 255, 0, 0, 83, 72, 68, + 82, 188, 0, 0, 0, 64, + 0, 1, 0, 47, 0, 0, + 0, 89, 0, 0, 4, 70, + 142, 32, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 95, + 0, 0, 3, 50, 16, 16, + 0, 0, 0, 0, 0, 103, + 0, 0, 4, 242, 32, 16, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 50, 32, 16, 0, 1, + 0, 0, 0, 50, 0, 0, + 11, 50, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 0, 0, 0, 0, 230, + 138, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 70, + 128, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 54, + 0, 0, 8, 194, 32, 16, + 0, 0, 0, 0, 0, 2, + 64, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 128, + 63, 50, 0, 0, 11, 50, + 32, 16, 0, 1, 0, 0, + 0, 70, 16, 16, 0, 0, + 0, 0, 0, 230, 138, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 70, 128, 32, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 4, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 224, 0, 0, + 0, 1, 0, 0, 0, 64, + 0, 0, 0, 1, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 254, 255, 0, 129, 0, + 0, 174, 0, 0, 0, 60, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 99, 98, 48, 0, 60, + 0, 0, 0, 2, 0, 0, + 0, 88, 0, 0, 0, 32, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 136, + 0, 0, 0, 0, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 164, + 0, 0, 0, 16, 0, 0, + 0, 16, 0, 0, 0, 2, + 0, 0, 0, 148, 0, 0, + 0, 0, 0, 0, 0, 81, + 117, 97, 100, 68, 101, 115, + 99, 0, 171, 171, 171, 1, + 0, 3, 0, 1, 0, 4, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 84, 101, 120, + 67, 111, 111, 114, 100, 115, + 0, 77, 105, 99, 114, 111, + 115, 111, 102, 116, 32, 40, + 82, 41, 32, 72, 76, 83, + 76, 32, 83, 104, 97, 100, + 101, 114, 32, 67, 111, 109, + 112, 105, 108, 101, 114, 32, + 57, 46, 50, 55, 46, 57, + 53, 50, 46, 51, 48, 50, + 50, 0, 171, 73, 83, 71, + 78, 44, 0, 0, 0, 1, + 0, 0, 0, 8, 0, 0, + 0, 32, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 7, 3, 0, + 0, 80, 79, 83, 73, 84, + 73, 79, 78, 0, 171, 171, + 171, 79, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 12, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 195, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 2, + 0, 0, 0, 0, 0, 0, + 0, 188, 2, 0, 0, 68, + 88, 66, 67, 90, 17, 243, + 62, 104, 14, 0, 40, 49, + 70, 150, 92, 77, 1, 115, + 141, 1, 0, 0, 0, 188, + 2, 0, 0, 6, 0, 0, + 0, 56, 0, 0, 0, 164, + 0, 0, 0, 16, 1, 0, + 0, 140, 1, 0, 0, 48, + 2, 0, 0, 136, 2, 0, + 0, 65, 111, 110, 57, 100, + 0, 0, 0, 100, 0, 0, + 0, 0, 2, 255, 255, 60, + 0, 0, 0, 40, 0, 0, + 0, 0, 0, 40, 0, 0, + 0, 40, 0, 0, 0, 40, + 0, 1, 0, 36, 0, 0, + 0, 40, 0, 0, 0, 0, + 0, 1, 2, 255, 255, 31, + 0, 0, 2, 0, 0, 0, + 128, 0, 0, 3, 176, 31, + 0, 0, 2, 0, 0, 0, + 144, 0, 8, 15, 160, 66, + 0, 0, 3, 0, 0, 15, + 128, 0, 0, 228, 176, 0, + 8, 228, 160, 1, 0, 0, + 2, 0, 8, 15, 128, 0, + 0, 228, 128, 255, 255, 0, + 0, 83, 72, 68, 82, 100, + 0, 0, 0, 64, 0, 0, + 0, 25, 0, 0, 0, 90, + 0, 0, 3, 0, 96, 16, + 0, 0, 0, 0, 0, 88, + 24, 0, 4, 0, 112, 16, + 0, 0, 0, 0, 0, 85, + 85, 0, 0, 98, 16, 0, + 3, 50, 16, 16, 0, 1, + 0, 0, 0, 101, 0, 0, + 3, 242, 32, 16, 0, 0, + 0, 0, 0, 69, 0, 0, + 9, 242, 32, 16, 0, 0, + 0, 0, 0, 70, 16, 16, + 0, 1, 0, 0, 0, 70, + 126, 16, 0, 0, 0, 0, + 0, 0, 96, 16, 0, 0, + 0, 0, 0, 62, 0, 0, + 1, 83, 84, 65, 84, 116, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 82, + 68, 69, 70, 156, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 2, 0, 0, + 0, 28, 0, 0, 0, 0, + 4, 255, 255, 0, 129, 0, + 0, 105, 0, 0, 0, 92, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 0, 0, + 0, 101, 0, 0, 0, 2, + 0, 0, 0, 5, 0, 0, + 0, 4, 0, 0, 0, 255, + 255, 255, 255, 0, 0, 0, + 0, 1, 0, 0, 0, 12, + 0, 0, 0, 115, 83, 97, + 109, 112, 108, 101, 114, 0, + 116, 101, 120, 0, 77, 105, + 99, 114, 111, 115, 111, 102, + 116, 32, 40, 82, 41, 32, + 72, 76, 83, 76, 32, 83, + 104, 97, 100, 101, 114, 32, + 67, 111, 109, 112, 105, 108, + 101, 114, 32, 57, 46, 50, + 55, 46, 57, 53, 50, 46, + 51, 48, 50, 50, 0, 171, + 171, 73, 83, 71, 78, 80, + 0, 0, 0, 2, 0, 0, + 0, 8, 0, 0, 0, 56, + 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, + 0, 15, 0, 0, 0, 68, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 3, + 0, 0, 0, 1, 0, 0, + 0, 3, 3, 0, 0, 83, + 86, 95, 80, 111, 115, 105, + 116, 105, 111, 110, 0, 84, + 69, 88, 67, 79, 79, 82, + 68, 0, 171, 171, 171, 79, + 83, 71, 78, 44, 0, 0, + 0, 1, 0, 0, 0, 8, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 3, 0, 0, + 0, 0, 0, 0, 0, 15, + 0, 0, 0, 83, 86, 95, + 84, 97, 114, 103, 101, 116, + 0, 171, 171, 135, 4, 0, + 0, 0, 0, 0, 0, 4, + 0, 0, 0, 32, 0, 0, + 0, 0, 0, 0, 0, 2, + 0, 0, 0, 255, 255, 255, + 255, 0, 0, 0, 0, 43, + 0, 0, 0, 15, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 52, 0, 0, + 0, 15, 0, 0, 0, 0, + 0, 0, 0, 16, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, + 0, 100, 0, 0, 0, 72, + 0, 0, 0, 0, 0, 0, + 0, 255, 255, 255, 255, 0, + 0, 0, 0, 145, 0, 0, + 0, 117, 0, 0, 0, 0, + 0, 0, 0, 255, 255, 255, + 255, 3, 0, 0, 0, 55, + 0, 0, 0, 0, 0, 0, + 0, 2, 0, 0, 0, 100, + 0, 0, 0, 46, 0, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 0, 154, 0, 0, + 0, 47, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 166, 0, 0, 0, 0, + 0, 0, 0, 178, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 192, 0, 0, + 0, 3, 0, 0, 0, 0, + 0, 0, 0, 6, 0, 0, + 0, 0, 0, 0, 0, 7, + 0, 0, 0, 115, 4, 0, + 0, 8, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, + 0, 123, 4, 0, 0, 7, + 0, 0, 0, 0, 0, 0, + 0, 7, 0, 0, 0, 71, + 7, 0, 0 +}; diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.fx b/gfx/cairo/cairo/src/cairo-d2d-private.fx new file mode 100644 index 000000000000..8f3236a4cda3 --- /dev/null +++ b/gfx/cairo/cairo/src/cairo-d2d-private.fx @@ -0,0 +1,57 @@ +// We store vertex coordinates and the quad shape in a constant buffer, this is +// easy to update and allows us to use a single call to set the x, y, w, h of +// the quad. +// The QuadDesc and TexCoords both work as follows: +// The x component is the quad left point, the y component is the top point +// the z component is the width, and the w component is the height. The quad +// are specified in viewport coordinates, i.e. { -1.0f, 1.0f, 2.0f, -2.0f } +// would cover the entire viewport (which runs from <-1.0f, 1.0f> left to right +// and <-1.0f, 1.0f> -bottom- to top. The TexCoords desc is specified in texture +// space <0, 1.0f> left to right and top to bottom. The input vertices of the +// shader stage always form a rectangle from {0, 0} - {1, 1} +cbuffer cb0 +{ + float4 QuadDesc; + float4 TexCoords; +} + +struct VS_OUTPUT +{ + float4 Position : SV_Position; + float2 TexCoord : TEXCOORD0; +}; + +Texture2D tex; + +sampler sSampler = sampler_state { + Texture = tex; + AddressU = Clamp; + AddressV = Clamp; +}; + +VS_OUTPUT SampleTextureVS(float3 pos : POSITION) +{ + VS_OUTPUT Output; + Output.Position.w = 1.0f; + Output.Position.x = pos.x * QuadDesc.z + QuadDesc.x; + Output.Position.y = pos.y * QuadDesc.w + QuadDesc.y; + Output.Position.z = 0; + Output.TexCoord.x = pos.x * TexCoords.z + TexCoords.x; + Output.TexCoord.y = pos.y * TexCoords.w + TexCoords.y; + return Output; +} + +float4 SampleTexturePS( VS_OUTPUT In) : SV_Target +{ + return tex.Sample(sSampler, In.TexCoord); +}; + +technique10 SampleTexture +{ + pass P0 + { + SetVertexShader(CompileShader(vs_4_0_level_9_3, SampleTextureVS())); + SetGeometryShader(NULL); + SetPixelShader(CompileShader(ps_4_0_level_9_3, SampleTexturePS())); + } +} diff --git a/gfx/cairo/cairo/src/cairo-d2d-private.h b/gfx/cairo/cairo/src/cairo-d2d-private.h index 3244bb42dbf9..da60e4168a1a 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-private.h +++ b/gfx/cairo/cairo/src/cairo-d2d-private.h @@ -49,10 +49,28 @@ extern "C" { } #include "cairo-win32-refptr.h" +#include "cairo-d2d-private-fx.h" +#include "cairo-win32.h" /* describes the type of the currently applied clip so that we can pop it */ struct d2d_clip; +#define MAX_OPERATORS CAIRO_OPERATOR_HSL_LUMINOSITY + 1 + +struct _cairo_d2d_device +{ + cairo_device_t base; + + HMODULE mD3D10_1; + RefPtr mD3D10Device; + RefPtr mSampleEffect; + RefPtr mInputLayout; + RefPtr mQuadBuffer; + RefPtr mRasterizerState; + RefPtr mBlendStates[MAX_OPERATORS]; +}; +typedef struct _cairo_d2d_device cairo_d2d_device_t; + struct _cairo_d2d_surface { _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), textRenderingInit(true) @@ -61,6 +79,10 @@ struct _cairo_d2d_surface { } cairo_surface_t base; + /* Device used by this surface + * NOTE: In upstream cairo this is in the surface base class */ + cairo_d2d_device_t *device; + /** Render target of the texture we render to */ RefPtr rt; /** Surface containing our backstore */ @@ -103,6 +125,10 @@ struct _cairo_d2d_surface { /** Indicates if text rendering is initialized */ bool textRenderingInit; + RefPtr buffer_rt_view; + RefPtr buffer_sr_view; + + //cairo_surface_clipper_t clipper; }; typedef struct _cairo_d2d_surface cairo_d2d_surface_t; @@ -124,104 +150,14 @@ typedef HRESULT (WINAPI*D3D10CreateDevice1Func)( ID3D10Device1 **ppDevice ); -class D2DSurfFactory -{ -public: - static ID2D1Factory *Instance() - { - if (!mFactoryInstance) { - D2D1CreateFactoryFunc createD2DFactory = (D2D1CreateFactoryFunc) - GetProcAddress(LoadLibraryW(L"d2d1.dll"), "D2D1CreateFactory"); - if (createD2DFactory) { - D2D1_FACTORY_OPTIONS options; -#ifdef DEBUG - options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; -#else - options.debugLevel = D2D1_DEBUG_LEVEL_NONE; -#endif - createD2DFactory( - D2D1_FACTORY_TYPE_SINGLE_THREADED, - __uuidof(ID2D1Factory), - &options, - (void**)&mFactoryInstance); - } - } - return mFactoryInstance; - } -private: - static ID2D1Factory *mFactoryInstance; -}; - -/** - * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: - * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - * can be misleading. In fact, that flag gives no such indication. I pointed this - * out to Bas in my email. However, Microsoft is in fact using this flag to - * indicate "light weight" DX applications. By light weight they are essentially - * referring to applications that are not games. The idea is that when you create - * a DX game, the driver assumes that you will pretty much have a single instance - * and therefore it doesn't try to hold back when it comes to GPU resource - * allocation as long as it can crank out performance. In other words, the - * priority in regular DX applications is to make that one application run as fast - * as you can. For "light weight" applications, including D2D applications, the - * priorities are a bit different. Now you are no longer going to have a single - * (or very few) instances. You can have a lot of them (say, for example, a - * separate DX context/device per browser tab). In such cases, the GPU resource - * allocation scheme changes. - */ -class D3D10Factory -{ -public: - static ID3D10Device1 *Device() - { - if (!mDeviceInstance) { - D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) - GetProcAddress(LoadLibraryA("d3d10_1.dll"), "D3D10CreateDevice1"); - if (createD3DDevice) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_1, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_10_0, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - if (FAILED(hr)) { - /* TODO: D3D10Level9 might be slower than GDI */ - HRESULT hr = createD3DDevice( - NULL, - D3D10_DRIVER_TYPE_HARDWARE, - NULL, - D3D10_CREATE_DEVICE_BGRA_SUPPORT | - D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - D3D10_FEATURE_LEVEL_9_3, - D3D10_1_SDK_VERSION, - &mDeviceInstance); - - } - } - if (SUCCEEDED(hr)) { - mDeviceInstance->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); - } - } - } - return mDeviceInstance; - } -private: - static ID3D10Device1 *mDeviceInstance; -}; - +typedef HRESULT (WINAPI*D3D10CreateEffectFromMemoryFunc)( + void *pData, + SIZE_T DataLength, + UINT FXFlags, + ID3D10Device *pDevice, + ID3D10EffectPool *pEffectPool, + ID3D10Effect **ppEffect +); RefPtr _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, @@ -229,8 +165,15 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf, bool unique = false); void _cairo_d2d_begin_draw_state(cairo_d2d_surface_t *d2dsurf); + cairo_status_t _cairo_d2d_set_clip(cairo_d2d_surface_t *d2dsurf, cairo_clip_t *clip); +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds = NULL); + +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip); + +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, const cairo_pattern_t *source); + #endif /* CAIRO_HAS_D2D_SURFACE */ #endif /* CAIRO_D2D_PRIVATE_H */ diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index cb62dc5bd0a6..ea173b93faa9 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -47,11 +47,295 @@ extern "C" { // Required for using placement new. #include -ID2D1Factory *D2DSurfFactory::mFactoryInstance = NULL; -ID3D10Device1 *D3D10Factory::mDeviceInstance = NULL; - #define CAIRO_INT_STATUS_SUCCESS (cairo_int_status_t)CAIRO_STATUS_SUCCESS +struct Vertex +{ + float position[2]; +}; + +// This factory is not device dependent, we can store it. But will clear it +// if there are no devices left needing it. +static ID2D1Factory *sD2DFactory = NULL; +static HMODULE sD2DModule; + +static void +_cairo_d2d_release_factory() +{ + int refcnt = sD2DFactory->Release(); + if (!refcnt) { + // Once the last reference goes, free the library. + sD2DFactory = NULL; + FreeLibrary(sD2DModule); + } +} + +/** + * Set a blending mode for an operator. This will also return a boolean that + * reports if for this blend mode the entire surface needs to be blended. This + * is true whenever the DEST blend is not ONE when src alpha is 0. + */ +static cairo_int_status_t +_cairo_d2d_set_operator(cairo_d2d_device_t *device, + cairo_operator_t op) +{ + assert(op < MAX_OPERATORS); + if (op >= MAX_OPERATORS) { + // Eep! Someone forgot to update MAX_OPERATORS probably. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + + if (device->mBlendStates[op]) { + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; + } + + D3D10_BLEND_DESC desc; + memset(&desc, 0, sizeof(desc)); + desc.BlendEnable[0] = TRUE; + desc.AlphaToCoverageEnable = FALSE; + desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; + + switch (op) { + case CAIRO_OPERATOR_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_ADD: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; + break; + case CAIRO_OPERATOR_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OVER: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_DEST_IN: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_OUT: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; + break; + case CAIRO_OPERATOR_DEST_ATOP: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + case CAIRO_OPERATOR_XOR: + desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; + desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; + desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; + break; + default: + return CAIRO_INT_STATUS_UNSUPPORTED; + }; + device->mD3D10Device->CreateBlendState(&desc, &device->mBlendStates[op]); + + device->mD3D10Device->OMSetBlendState(device->mBlendStates[op], NULL, 0xffffffff); + return CAIRO_INT_STATUS_SUCCESS; +} + +cairo_device_t * +cairo_d2d_create_device() +{ + D3D10_RASTERIZER_DESC rastDesc; + D3D10_INPUT_ELEMENT_DESC layout[] = + { + { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, + }; + D3D10_PASS_DESC passDesc; + ID3D10EffectTechnique *technique; + Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; + CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); + D3D10_SUBRESOURCE_DATA data; + + cairo_d2d_device_t *device = new cairo_d2d_device_t; + device->mD3D10_1 = LoadLibraryA("d3d10_1.dll"); + D3D10CreateDevice1Func createD3DDevice = (D3D10CreateDevice1Func) + GetProcAddress(device->mD3D10_1, "D3D10CreateDevice1"); + D3D10CreateEffectFromMemoryFunc createEffect = (D3D10CreateEffectFromMemoryFunc) + GetProcAddress(device->mD3D10_1, "D3D10CreateEffectFromMemory"); + D2D1CreateFactoryFunc createD2DFactory; + + if (!createD3DDevice || !createEffect) { + goto FAILED; + } + + /** + * On usage of D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS: + * documentation on D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + * can be misleading. In fact, that flag gives no such indication. I pointed this + * out to Bas in my email. However, Microsoft is in fact using this flag to + * indicate "light weight" DX applications. By light weight they are essentially + * referring to applications that are not games. The idea is that when you create + * a DX game, the driver assumes that you will pretty much have a single instance + * and therefore it doesn't try to hold back when it comes to GPU resource + * allocation as long as it can crank out performance. In other words, the + * priority in regular DX applications is to make that one application run as fast + * as you can. For "light weight" applications, including D2D applications, the + * priorities are a bit different. Now you are no longer going to have a single + * (or very few) instances. You can have a lot of them (say, for example, a + * separate DX context/device per browser tab). In such cases, the GPU resource + * allocation scheme changes. + */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_1, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_10_0, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + if (FAILED(hr)) { + /* This is not guaranteed to be too fast! */ + HRESULT hr = createD3DDevice( + NULL, + D3D10_DRIVER_TYPE_HARDWARE, + NULL, + D3D10_CREATE_DEVICE_BGRA_SUPPORT | + D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + D3D10_FEATURE_LEVEL_9_3, + D3D10_1_SDK_VERSION, + &device->mD3D10Device); + + } + } + if (FAILED(hr)) { + goto FAILED; + } + + if (!sD2DFactory) { + sD2DModule = LoadLibraryW(L"d2d1.dll"); + createD2DFactory = (D2D1CreateFactoryFunc) + GetProcAddress(sD2DModule, "D2D1CreateFactory"); + if (!createD2DFactory) { + goto FAILED; + } + D2D1_FACTORY_OPTIONS options; +#ifdef DEBUG + options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION; +#else + options.debugLevel = D2D1_DEBUG_LEVEL_NONE; +#endif + hr = createD2DFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, + __uuidof(ID2D1Factory), + &options, + (void**)&sD2DFactory); + if (FAILED(hr)) { + goto FAILED; + } + } else { + sD2DFactory->AddRef(); + } + + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINESTRIP); + createEffect((void*)g_main, sizeof(g_main), 0, device->mD3D10Device, NULL, &device->mSampleEffect); + + technique = device->mSampleEffect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->GetDesc(&passDesc); + + + hr = device->mD3D10Device->CreateInputLayout(layout, + sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), + passDesc.pIAInputSignature, + passDesc.IAInputSignatureSize, + &device->mInputLayout); + if (FAILED(hr)) { + goto FAILED; + } + + data.pSysMem = (void*)vertices; + hr = device->mD3D10Device->CreateBuffer(&bufferDesc, &data, &device->mQuadBuffer); + if (FAILED(hr)) { + goto FAILED; + } + + memset(&rastDesc, 0, sizeof(rastDesc)); + rastDesc.CullMode = D3D10_CULL_NONE; + rastDesc.FillMode = D3D10_FILL_SOLID; + hr = device->mD3D10Device->CreateRasterizerState(&rastDesc, &device->mRasterizerState); + if (FAILED(hr)) { + goto FAILED; + } + device->base.refcount = 1; + + return &device->base; +FAILED: + delete &device->base; + return NULL; +} + +int +cairo_release_device(cairo_device_t *device) +{ + int newrefcnt = --device->refcount; + if (!newrefcnt) { + // Call the correct destructor + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + FreeLibrary(d2d_device->mD3D10_1); + delete d2d_device; + _cairo_d2d_release_factory(); + } + return newrefcnt; +} + +int +cairo_addref_device(cairo_device_t *device) +{ + return ++device->refcount; +} + +static void +_cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) +{ + device->mD3D10Device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + device->mD3D10Device->IASetInputLayout(device->mInputLayout); + + UINT stride = sizeof(Vertex); + UINT offset = 0; + ID3D10Buffer *buff = device->mQuadBuffer; + device->mD3D10Device->IASetVertexBuffers(0, 1, &buff, &stride, &offset); + + device->mD3D10Device->RSSetState(device->mRasterizerState); +} + // Contains our cache usage - perhaps this should be made threadsafe. static int cache_usage = 0; @@ -347,6 +631,15 @@ _cairo_d2d_color_from_cairo_color(const cairo_color_t &color) (FLOAT)color.alpha); } +static void +_cairo_d2d_round_out_to_int_rect(cairo_rectangle_int_t *rect, double x1, double y1, double x2, double y2) +{ + rect->x = (int)floor(x1); + rect->y = (int)floor(y1); + rect->width = (int)ceil(x2) - rect->x; + rect->height = (int)ceil(y2) - rect->y; +} + /** * Gets the surface buffer texture for window surfaces whose backbuffer * is not directly usable as a bitmap. @@ -366,7 +659,7 @@ _cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface) softDesc.MipLevels = 1; softDesc.Usage = D3D10_USAGE_DEFAULT; softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); + surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); } return surface->bufferTexture; } @@ -414,7 +707,7 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface) _cairo_d2d_flush(d2dsurf); if (d2dsurf->dxgiChain) { d2dsurf->dxgiChain->Present(0, 0); - D3D10Factory::Device()->Flush(); + d2dsurf->device->mD3D10Device->Flush(); } } @@ -743,16 +1036,16 @@ _cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style } RefPtr strokeStyle; - D2DSurfFactory::Instance()->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, - line_cap, - line_cap, - line_join, - (FLOAT)style->miter_limit, - dashStyle, - (FLOAT)style->dash_offset), - dashes, - style->num_dashes, - &strokeStyle); + sD2DFactory->CreateStrokeStyle(D2D1::StrokeStyleProperties(line_cap, + line_cap, + line_cap, + line_join, + (FLOAT)style->miter_limit, + dashStyle, + (FLOAT)style->dash_offset), + dashes, + style->num_dashes, + &strokeStyle); delete [] dashes; return strokeStyle; } @@ -769,6 +1062,18 @@ cairo_user_data_key_t bitmap_key_extend; cairo_user_data_key_t bitmap_key_snapshot; struct cached_bitmap { + cached_bitmap() + { + sD2DFactory->AddRef(); + } + + ~cached_bitmap() + { + // Clear bitmap out first because it depends on the factory. + bitmap = NULL; + _cairo_d2d_release_factory(); + } + /** The cached bitmap */ RefPtr bitmap; /** The cached bitmap is dirty and needs its data refreshed */ @@ -1724,7 +2029,7 @@ _cairo_d2d_create_path_geometry_for_path(cairo_path_fixed_t *path, D2D1_FIGURE_BEGIN type) { RefPtr d2dpath; - D2DSurfFactory::Instance()->CreatePathGeometry(&d2dpath); + sD2DFactory->CreatePathGeometry(&d2dpath); RefPtr sink; d2dpath->Open(&sink); D2D1_FILL_MODE fillMode = D2D1_FILL_MODE_WINDING; @@ -1844,8 +2149,8 @@ _cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf, return CAIRO_INT_STATUS_SUCCESS; } -static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, - const cairo_pattern_t *source) +cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, + const cairo_pattern_t *source) { if (op == CAIRO_OPERATOR_SOURCE) { /** Operator over is easier for D2D! If the source if opaque, change */ @@ -1935,7 +2240,7 @@ _cairo_d2d_create_similar(void *surface, RefPtr texture; RefPtr dxgiSurface; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; } @@ -1952,14 +2257,14 @@ _cairo_d2d_create_similar(void *surface, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE : D2D1_RENDER_TARGET_USAGE_NONE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, - D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, - alpha), - dpiX, - dpiY, - usage), - &newSurf->rt); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha), + dpiX, + dpiY, + usage), + &newSurf->rt); if (FAILED(hr)) { goto FAIL_CREATESIMILAR; @@ -1982,6 +2287,9 @@ _cairo_d2d_create_similar(void *surface, _d2d_clear_surface(newSurf); + newSurf->device = d2dsurf->device; + cairo_addref_device(&newSurf->device->base); + return reinterpret_cast(newSurf); FAIL_CREATESIMILAR: @@ -1998,6 +2306,7 @@ _cairo_d2d_finish(void *surface) reset_clip(d2dsurf); + cairo_release_device(&d2dsurf->device->base); d2dsurf->~cairo_d2d_surface_t(); return CAIRO_STATUS_SUCCESS; } @@ -2032,12 +2341,12 @@ _cairo_d2d_acquire_source_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2105,11 +2414,11 @@ _cairo_d2d_acquire_dest_image(void *abstract_surface, softDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ; softDesc.Usage = D3D10_USAGE_STAGING; softDesc.BindFlags = 0; - hr = D3D10Factory::Device()->CreateTexture2D(&softDesc, NULL, &softTexture); + hr = d2dsurf->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &softTexture); if (FAILED(hr)) { return CAIRO_STATUS_NO_MEMORY; } - D3D10Factory::Device()->CopyResource(softTexture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopyResource(softTexture, d2dsurf->surface); D3D10_MAPPED_TEXTURE2D data; hr = softTexture->Map(0, D3D10_MAP_READ_WRITE, 0, &data); @@ -2149,7 +2458,7 @@ _cairo_d2d_release_dest_image(void *abstract_surface, cairo_surface_destroy(&image->base); softTexture->Unmap(0); - D3D10Factory::Device()->CopyResource(d2dsurf->surface, softTexture); + d2dsurf->device->mD3D10Device->CopyResource(d2dsurf->surface, softTexture); softTexture->Release(); } @@ -2196,7 +2505,7 @@ _cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, if (src->surface.get() == dst->surface.get()) { // Self-copy srcResource = _cairo_d2d_get_buffer_texture(dst); - D3D10Factory::Device()->CopyResource(srcResource, src->surface); + src->device->mD3D10Device->CopyResource(srcResource, src->surface); } else { // Need to flush the source too if it's a different surface. _cairo_d2d_flush(src); @@ -2230,14 +2539,14 @@ _cairo_d2d_copy_surface(cairo_d2d_surface_t *dst, rect.right = transformed_rect.x + transformed_rect.width; rect.bottom = transformed_rect.y + transformed_rect.height; - D3D10Factory::Device()->CopySubresourceRegion(dst->surface, - 0, - area_to_copy.x, - area_to_copy.y, - 0, - srcResource, - 0, - &rect); + src->device->mD3D10Device->CopySubresourceRegion(dst->surface, + 0, + area_to_copy.x, + area_to_copy.y, + 0, + srcResource, + 0, + &rect); } return rv; @@ -2282,6 +2591,11 @@ _cairo_d2d_try_copy(cairo_d2d_surface_t *dst, cairo_d2d_surface_t *d2dsrc = reinterpret_cast(src); + if (d2dsrc->device != dst->device) { + // This doesn't work between different devices. + return CAIRO_INT_STATUS_UNSUPPORTED; + } + /* Region we need to clip this operation to */ cairo_region_t *clipping_region = NULL; cairo_region_t *region; @@ -2315,6 +2629,191 @@ _cairo_d2d_try_copy(cairo_d2d_surface_t *dst, return rv; } +RefPtr _cairo_d2d_get_temp_rt(cairo_d2d_surface_t *surf, cairo_clip_t *clip) +{ + RefPtr texture = _cairo_d2d_get_buffer_texture(surf); + RefPtr new_rt; + RefPtr dxgiSurface; + texture->QueryInterface(&dxgiSurface); + HRESULT hr; + + _cairo_d2d_flush(surf); + + if (!surf) { + return NULL; + } + + D2D1_RENDER_TARGET_PROPERTIES props = + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED)); + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &new_rt); + + if (FAILED(hr)) { + return NULL; + } + + new_rt->BeginDraw(); + new_rt->Clear(D2D1::ColorF(0, 0)); + + // Since this is a fresh surface there's no point in doing clever things to + // keep the clip path around until a certain depth. So we just do a straight- + // forward push of all clip paths in the tree, similar to what the normal + // clip code does, but a little less clever. + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + cairo_box_t clip_box; + if (_cairo_path_fixed_is_box(&path->path, &clip_box)) { + // If this does not have a region it could be none-pixel aligned. + D2D1_ANTIALIAS_MODE aaMode = D2D1_ANTIALIAS_MODE_PER_PRIMITIVE; + if (box_is_integer(&clip_box)) { + aaMode = D2D1_ANTIALIAS_MODE_ALIASED; + } + new_rt->PushAxisAlignedClip(D2D1::RectF(_cairo_fixed_to_float(clip_box.p1.x), + _cairo_fixed_to_float(clip_box.p1.y), + _cairo_fixed_to_float(clip_box.p2.x), + _cairo_fixed_to_float(clip_box.p2.y)), + aaMode); + } else { + HRESULT hr; + RefPtr geom = _cairo_d2d_create_path_geometry_for_path (&path->path, + path->fill_rule, + D2D1_FIGURE_BEGIN_FILLED); + RefPtr layer; + + hr = new_rt->CreateLayer (&layer); + + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + new_rt->PushLayer(D2D1::LayerParameters( + D2D1::InfiniteRect(), + geom, + D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, + D2D1::IdentityMatrix(), + 1.0, + 0, + options), + layer); + } + path = path->prev; + } + } + return new_rt; +} + +cairo_int_status_t _cairo_d2d_blend_temp_surface(cairo_d2d_surface_t *surf, cairo_operator_t op, ID2D1RenderTarget *rt, cairo_clip_t *clip, const cairo_rectangle_int_t *bounds) +{ + int numPaths = 0; + if (clip) { + cairo_clip_path_t *path = clip->path; + while (path) { + numPaths++; + path = path->prev; + } + + cairo_clip_path_t **paths = new cairo_clip_path_t*[numPaths]; + + numPaths = 0; + path = clip->path; + while (path) { + paths[numPaths++] = path; + path = path->prev; + } + + for (int i = numPaths - 1; i >= 0; i--) { + if (paths[i]->flags & CAIRO_CLIP_PATH_IS_BOX) { + rt->PopAxisAlignedClip(); + } else { + rt->PopLayer(); + } + } + delete [] paths; + } + rt->EndDraw(); + HRESULT hr; + + RefPtr srcTexture = _cairo_d2d_get_buffer_texture(surf); + RefPtr dstTexture; + + surf->surface->QueryInterface(&dstTexture); + ID3D10Device *device = surf->device->mD3D10Device; + + if (!surf->buffer_rt_view) { + hr = device->CreateRenderTargetView(dstTexture, NULL, &surf->buffer_rt_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + if (!surf->buffer_sr_view) { + hr = device->CreateShaderResourceView(srcTexture, NULL, &surf->buffer_sr_view); + if (FAILED(hr)) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + } + + cairo_int_status_t status; + + status = _cairo_d2d_set_operator(surf->device, op); + + if (unlikely(status)) { + return status; + } + + D3D10_TEXTURE2D_DESC tDesc; + dstTexture->GetDesc(&tDesc); + D3D10_VIEWPORT vp; + vp.Height = tDesc.Height; + vp.MinDepth = 0; + vp.MaxDepth = 1.0; + vp.TopLeftX = 0; + vp.TopLeftY = 0; + vp.Width = tDesc.Width; + device->RSSetViewports(1, &vp); + + ID3D10Effect *effect = surf->device->mSampleEffect; + + ID3D10RenderTargetView *rtViewPtr = surf->buffer_rt_view; + device->OMSetRenderTargets(1, &rtViewPtr, 0); + ID3D10EffectVectorVariable *quadDesc = effect->GetVariableByName("QuadDesc")->AsVector(); + ID3D10EffectVectorVariable *texCoords = effect->GetVariableByName("TexCoords")->AsVector(); + + float quadDescVal[] = { -1.0f, 1.0f, 2.0f, -2.0f }; + float texCoordsVal[] = { 0.0, 0.0, 1.0f, 1.0f }; + if (bounds && _cairo_operator_bounded_by_mask(op)) { + quadDescVal[0] = -1.0f + ((float)bounds->x / (float)tDesc.Width) * 2.0f; + quadDescVal[1] = 1.0f - ((float)bounds->y / (float)tDesc.Height) * 2.0f; + quadDescVal[2] = ((float)bounds->width / (float)tDesc.Width) * 2.0f; + quadDescVal[3] = -((float)bounds->height / (float)tDesc.Height) * 2.0f; + texCoordsVal[0] = (float)bounds->x / (float)tDesc.Width; + texCoordsVal[1] = (float)bounds->y / (float)tDesc.Height; + texCoordsVal[2] = (float)bounds->width / (float)tDesc.Width; + texCoordsVal[3] = (float)bounds->height / (float)tDesc.Height; + } + quadDesc->SetFloatVector(quadDescVal); + texCoords->SetFloatVector(texCoordsVal); + + _cairo_d2d_setup_for_blend(surf->device); + ID3D10EffectTechnique *technique = effect->GetTechniqueByName("SampleTexture"); + technique->GetPassByIndex(0)->Apply(0); + + ID3D10ShaderResourceView *srViewPtr = surf->buffer_sr_view; + device->PSSetShaderResources(0, 1, &srViewPtr); + + device->Draw(4, 0); + +#ifdef DEBUG + // Quiet down some info messages from D3D10 debug layer + srViewPtr = NULL; + device->PSSetShaderResources(0, 1, &srViewPtr); + rtViewPtr = NULL; + device->OMSetRenderTargets(1, &rtViewPtr, 0); +#endif + return CAIRO_INT_STATUS_SUCCESS; +} + static cairo_int_status_t _cairo_d2d_paint(void *surface, cairo_operator_t op, @@ -2326,6 +2825,10 @@ _cairo_d2d_paint(void *surface, op = _cairo_d2d_simplify_operator(op, source); + if (op == CAIRO_OPERATOR_SOURCE) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } + if (op == CAIRO_OPERATOR_CLEAR) { return _cairo_d2d_clear(d2dsurf, clip); } @@ -2341,14 +2844,25 @@ _cairo_d2d_paint(void *surface, return status; } } + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + if (unlikely(status)) + return status; + } +#endif - if (unlikely(status)) - return status; - - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2356,23 +2870,16 @@ _cairo_d2d_paint(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - if (op == CAIRO_OPERATOR_OVER) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else if (op == CAIRO_OPERATOR_SOURCE) { - D2D1_SIZE_F size = d2dsurf->rt->GetSize(); - d2dsurf->rt->Clear(D2D1::ColorF(0, 0)); - d2dsurf->rt->FillRectangle(D2D1::RectF((FLOAT)0, - (FLOAT)0, - (FLOAT)size.width, - (FLOAT)size.height), - brush); - } else { - return CAIRO_INT_STATUS_UNSUPPORTED; + + D2D1_SIZE_F size = target_rt->GetSize(); + target_rt->FillRectangle(D2D1::RectF((FLOAT)0, + (FLOAT)0, + (FLOAT)size.width, + (FLOAT)size.height), + brush); + + if (target_rt.get() != d2dsurf->rt.get()) { + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip); } return CAIRO_INT_STATUS_SUCCESS; @@ -2473,27 +2980,32 @@ _cairo_d2d_stroke(void *surface, op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely (status)) - return status; + RefPtr target_rt = d2dsurf->rt; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + if (unlikely(status)) + return status; + } +#endif if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); } RefPtr strokeStyle = _cairo_d2d_create_strokestyle_for_stroke_style(style); @@ -2507,9 +3019,9 @@ _cairo_d2d_stroke(void *surface, D2D1::Matrix3x2F inverse_mat = _cairo_d2d_invert_matrix(mat); RefPtr trans_geom; - D2DSurfFactory::Instance()->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); + sD2DFactory->CreateTransformedGeometry(d2dpath, &inverse_mat, &trans_geom); - d2dsurf->rt->SetTransform(mat); + target_rt->SetTransform(mat); RefPtr brush = _cairo_d2d_create_brush_for_pattern(d2dsurf, source); @@ -2517,9 +3029,18 @@ _cairo_d2d_stroke(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + target_rt->DrawGeometry(trans_geom, brush, (FLOAT)style->line_width, strokeStyle); + + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); + + if (target_rt.get() != d2dsurf->rt.get()) { + D2D1_RECT_F bounds; + trans_geom->GetWidenedBounds((FLOAT)style->line_width, strokeStyle, D2D1::IdentityMatrix(), &bounds); + cairo_rectangle_int_t bound_rect; + _cairo_d2d_round_out_to_int_rect(&bound_rect, bounds.left, bounds.top, bounds.right, bounds.bottom); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bound_rect); + } - d2dsurf->rt->SetTransform(D2D1::Matrix3x2F::Identity()); return CAIRO_INT_STATUS_SUCCESS; } @@ -2552,37 +3073,49 @@ _cairo_d2d_fill(void *surface, op = _cairo_d2d_simplify_operator(op, source); - if (op != CAIRO_OPERATOR_OVER && op != CAIRO_OPERATOR_ADD && - op != CAIRO_OPERATOR_CLEAR) { - /** - * We don't really support ADD yet. True ADD support requires getting - * the tesselated mesh from D2D, and blending that using D3D which has - * an add operator available. - */ + if (op == CAIRO_OPERATOR_SOURCE) { return CAIRO_INT_STATUS_UNSUPPORTED; } - _begin_draw_state(d2dsurf); - status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); - - if (unlikely(status)) - return status; - - - if (antialias == CAIRO_ANTIALIAS_NONE) { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); - } else { - d2dsurf->rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); - } - if (op == CAIRO_OPERATOR_CLEAR) { if (_cairo_path_fixed_is_box(path, &box)) { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + return _cairo_d2d_clear_box (d2dsurf, clip, &box); } else { return CAIRO_INT_STATUS_UNSUPPORTED; } } + RefPtr target_rt = d2dsurf->rt; + +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(d2dsurf, clip); + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _begin_draw_state(d2dsurf); + status = (cairo_int_status_t)_cairo_d2d_set_clip (d2dsurf, clip); + + if (unlikely(status)) + return status; + } +#endif + + if (antialias == CAIRO_ANTIALIAS_NONE) { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); + } else { + target_rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); + } + if (is_box) { float x1 = _cairo_fixed_to_float(box.p1.x); float y1 = _cairo_fixed_to_float(box.p1.y); @@ -2594,11 +3127,11 @@ _cairo_d2d_fill(void *surface, return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillRectangle(D2D1::RectF(x1, - y1, - x2, - y2), - brush); + target_rt->FillRectangle(D2D1::RectF(x1, + y1, + x2, + y2), + brush); } else { RefPtr d2dpath = _cairo_d2d_create_path_geometry_for_path(path, fill_rule, D2D1_FIGURE_BEGIN_FILLED); @@ -2607,8 +3140,17 @@ _cairo_d2d_fill(void *surface, if (!brush) { return CAIRO_INT_STATUS_UNSUPPORTED; } - d2dsurf->rt->FillGeometry(d2dpath, brush); + target_rt->FillGeometry(d2dpath, brush); } + + if (target_rt.get() != d2dsurf->rt.get()) { + double x1, y1, x2, y2; + _cairo_path_fixed_bounds(path, &x1, &y1, &x2, &y2); + cairo_rectangle_int_t bounds; + _cairo_d2d_round_out_to_int_rect(&bounds, x1, y1, x2, y2); + return _cairo_d2d_blend_temp_surface(d2dsurf, op, target_rt, clip, &bounds); + } + return CAIRO_INT_STATUS_SUCCESS; } @@ -2660,17 +3202,11 @@ _cairo_d2d_getextents(void *surface, /** Helper functions. */ cairo_surface_t* -cairo_d2d_surface_create_for_hwnd(HWND wnd, +cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device, + HWND wnd, cairo_content_t content) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } - + cairo_d2d_device_t *d2d_device = reinterpret_cast(cairo_device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2700,7 +3236,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, if (!sizePixels.height) { sizePixels.height = 1; } - ID3D10Device1 *device = D3D10Factory::Device(); + ID3D10Device1 *device = d2d_device->mD3D10Device; RefPtr dxgiDevice; RefPtr dxgiAdapter; RefPtr dxgiFactory; @@ -2762,7 +3298,7 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, dpiX, dpiY, D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE); - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(newSurf->backBuf, props, &newSurf->rt); if (FAILED(hr)) { @@ -2776,6 +3312,9 @@ cairo_d2d_surface_create_for_hwnd(HWND wnd, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(cairo_device); + return reinterpret_cast(newSurf); FAIL_HWND: @@ -2785,17 +3324,12 @@ FAIL_HWND: } cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height) { - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - /** - * FIXME: In the near future we can use cairo_device_t to pass in a - * device. - */ - return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); - } + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); new (newSurf) cairo_d2d_surface_t(); @@ -2838,7 +3372,7 @@ cairo_d2d_surface_create(cairo_format_t format, D2D1_BITMAP_PROPERTIES bitProps; D2D1_RENDER_TARGET_PROPERTIES props; - hr = D3D10Factory::Device()->CreateTexture2D(&desc, NULL, &texture); + hr = d2d_device->mD3D10Device->CreateTexture2D(&desc, NULL, &texture); if (FAILED(hr)) { goto FAIL_CREATE; @@ -2858,7 +3392,7 @@ cairo_d2d_surface_create(cairo_format_t format, if (desc.MiscFlags & D3D10_RESOURCE_MISC_GDI_COMPATIBLE) props.usage = D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE; - hr = D2DSurfFactory::Instance()->CreateDxgiSurfaceRenderTarget(dxgiSurface, + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, props, &newSurf->rt); @@ -2887,6 +3421,9 @@ cairo_d2d_surface_create(cairo_format_t format, _d2d_clear_surface(newSurf); + newSurf->device = d2d_device; + cairo_addref_device(device); + return reinterpret_cast(newSurf); FAIL_CREATE: @@ -2945,8 +3482,8 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } ID3D10Texture2D *texture = _cairo_d2d_get_buffer_texture(d2dsurf); - D3D10Factory::Device()->CopyResource(texture, d2dsurf->surface); - D3D10Factory::Device()->CopySubresourceRegion(d2dsurf->surface, + d2dsurf->device->mD3D10Device->CopyResource(texture, d2dsurf->surface); + d2dsurf->device->mD3D10Device->CopySubresourceRegion(d2dsurf->surface, 0, point.x, point.y, @@ -2957,19 +3494,6 @@ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t } -cairo_bool_t -cairo_d2d_has_support() -{ - /** - * FIXME: We should be able to fix this in the near future when we pass in - * a cairo_device_t to our surface creation functions. - */ - if (!D3D10Factory::Device() || !D2DSurfFactory::Instance()) { - return false; - } - return true; -} - HDC cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) { diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp index 271f5e321d4b..238539b1938a 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp @@ -1262,32 +1262,52 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, if (cairo_scaled_font_get_type (scaled_font) != CAIRO_FONT_TYPE_DWRITE) return CAIRO_INT_STATUS_UNSUPPORTED; + op = _cairo_d2d_simplify_operator(op, source); - /* We can only handle operator SOURCE or OVER with the destination - * having no alpha */ - if (op != CAIRO_OPERATOR_SOURCE && op != CAIRO_OPERATOR_OVER) - return CAIRO_INT_STATUS_UNSUPPORTED; + /* We cannot handle operator SOURCE or CLEAR */ + if (op == CAIRO_OPERATOR_SOURCE || op == CAIRO_OPERATOR_CLEAR) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } - _cairo_d2d_begin_draw_state (dst); - _cairo_d2d_set_clip (dst, clip); + RefPtr target_rt = dst->rt; + cairo_rectangle_int_t fontArea; +#ifndef ALWAYS_MANUAL_COMPOSITE + if (op != CAIRO_OPERATOR_OVER) { +#endif + target_rt = _cairo_d2d_get_temp_rt(dst, clip); + + if (!target_rt) { + return CAIRO_INT_STATUS_UNSUPPORTED; + } +#ifndef ALWAYS_MANUAL_COMPOSITE + } else { + _cairo_d2d_begin_draw_state(dst); + status = (cairo_int_status_t)_cairo_d2d_set_clip (dst, clip); + + if (unlikely(status)) + return status; + } +#endif - D2D1_TEXT_ANTIALIAS_MODE cleartype = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; + D2D1_TEXT_ANTIALIAS_MODE highest_quality = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; - if (dst->base.content != CAIRO_CONTENT_COLOR) { - cleartype = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; + // If we're rendering to a temporary surface we cannot do sub-pixel AA. + if (dst->base.content != CAIRO_CONTENT_COLOR || dst->rt.get() != target_rt.get()) { + highest_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; } + switch (scaled_font->options.antialias) { case CAIRO_ANTIALIAS_DEFAULT: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; case CAIRO_ANTIALIAS_NONE: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED); break; case CAIRO_ANTIALIAS_GRAY: - dst->rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); + target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); break; case CAIRO_ANTIALIAS_SUBPIXEL: - dst->rt->SetTextAntialiasMode(cleartype); + target_rt->SetTextAntialiasMode(highest_quality); break; } @@ -1349,7 +1369,29 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, D2D1::Matrix3x2F mat = _cairo_d2d_matrix_from_matrix(&dwritesf->mat); if (transform) { - dst->rt->SetTransform(mat); + target_rt->SetTransform(mat); + } + + if (dst->rt.get() != target_rt.get()) { + RefPtr analysis; + DWRITE_MATRIX dwmat = _cairo_dwrite_matrix_from_matrix(&dwritesf->mat); + DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, + 1.0f, + transform ? &dwmat : 0, + DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, + DWRITE_MEASURING_MODE_NATURAL, + 0, + 0, + &analysis); + + RECT bounds; + analysis->GetAlphaTextureBounds(scaled_font->options.antialias == CAIRO_ANTIALIAS_NONE ? + DWRITE_TEXTURE_ALIASED_1x1 : DWRITE_TEXTURE_CLEARTYPE_3x1, + &bounds); + fontArea.x = bounds.left; + fontArea.y = bounds.top; + fontArea.width = bounds.right - bounds.left; + fontArea.height = bounds.bottom - bounds.top; } RefPtr brush = _cairo_d2d_create_brush_for_pattern(dst, @@ -1374,15 +1416,19 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface, brush->SetTransform(&mat_brush); } - dst->rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); + target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush); if (transform) { - dst->rt->SetTransform(D2D1::Matrix3x2F::Identity()); + target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); } delete [] indices; delete [] offsets; delete [] advances; + + if (target_rt.get() != dst->rt.get()) { + return _cairo_d2d_blend_temp_surface(dst, op, target_rt, clip, &fontArea); + } return CAIRO_INT_STATUS_SUCCESS; } diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h index 53fed4d03ef7..b7302c0c8de7 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -126,27 +126,61 @@ cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrit #endif /* CAIRO_HAS_DWRITE_FONT */ #if CAIRO_HAS_D2D_SURFACE + +struct _cairo_device +{ + int type; + int refcount; +}; +typedef struct _cairo_device cairo_device_t; + +/** + * Create a D2D device + * + * \return New D2D device, NULL if creation failed. + */ +cairo_device_t * +cairo_d2d_create_device(); + +/** + * Releases a D2D device. + * + * \return References left to the device + */ +int +cairo_release_device(cairo_device_t *device); + +/** + * Addrefs a D2D device. + * + * \return References to the device + */ +int +cairo_addref_device(cairo_device_t *device); /** * Create a D2D surface for an HWND * + * \param device Device used to create the surface * \param wnd Handle for the window * \param content Content of the window, should be COLOR_ALPHA for transparent windows * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create_for_hwnd(HWND wnd, cairo_content_t content); +cairo_d2d_surface_create_for_hwnd(cairo_device_t *device, HWND wnd, cairo_content_t content); /** * Create a D2D surface of a certain size. * + * \param device Device used to create the surface * \param format Cairo format of the surface * \param width Width of the surface * \param height Height of the surface * \return New cairo surface */ cairo_public cairo_surface_t * -cairo_d2d_surface_create(cairo_format_t format, +cairo_d2d_surface_create(cairo_device_t *device, + cairo_format_t format, int width, int height); @@ -172,15 +206,6 @@ void cairo_d2d_present_backbuffer(cairo_surface_t *surface); */ void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip); -/** - * Verify if D2D surfaces are actually supported. This will confirm the needed - * hardware is available. - * - * \return True if the support is available. If false surface creation will - * return error surfaces. - */ -cairo_bool_t cairo_d2d_has_support(); - /** * Get a DC for the current render target. When selecting the retention option this * call can be relatively slow, since it may require reading back contents from the diff --git a/gfx/thebes/gfxD2DSurface.cpp b/gfx/thebes/gfxD2DSurface.cpp index dc0a92fec3be..bdc1ed887eaf 100644 --- a/gfx/thebes/gfxD2DSurface.cpp +++ b/gfx/thebes/gfxD2DSurface.cpp @@ -36,10 +36,14 @@ #include "gfxD2DSurface.h" #include "cairo.h" #include "cairo-win32.h" +#include "gfxWindowsPlatform.h" gfxD2DSurface::gfxD2DSurface(HWND aWnd, gfxContentType aContent) { - Init(cairo_d2d_surface_create_for_hwnd(aWnd, (cairo_content_t)aContent)); + Init(cairo_d2d_surface_create_for_hwnd( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + aWnd, + (cairo_content_t)aContent)); } gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) @@ -50,7 +54,10 @@ gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) gfxD2DSurface::gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat) { - Init(cairo_d2d_surface_create((cairo_format_t)imageFormat, size.width, size.height)); + Init(cairo_d2d_surface_create( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + (cairo_format_t)imageFormat, + size.width, size.height)); } gfxD2DSurface::~gfxD2DSurface() diff --git a/gfx/thebes/gfxWindowsPlatform.cpp b/gfx/thebes/gfxWindowsPlatform.cpp index c3b1b6249189..ae50a3969917 100644 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -170,6 +170,7 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifdef CAIRO_HAS_D2D_SURFACE NS_RegisterMemoryReporter(new D2DCacheReporter()); + mD2DDevice = NULL; #endif #ifdef CAIRO_HAS_DWRITE_FONT nsresult rv; @@ -213,7 +214,8 @@ gfxWindowsPlatform::gfxWindowsPlatform() #ifndef CAIRO_HAS_D2D_SURFACE return; #else - if (!cairo_d2d_has_support()) { + mD2DDevice = cairo_d2d_create_device(); + if (!mD2DDevice) { return; } #ifdef CAIRO_HAS_DWRITE_FONT @@ -235,6 +237,11 @@ gfxWindowsPlatform::~gfxWindowsPlatform() { // not calling FT_Done_FreeType because cairo may still hold references to // these FT_Faces. See bug 458169. +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DDevice) { + cairo_release_device(mD2DDevice); + } +#endif } gfxPlatformFontList* diff --git a/gfx/thebes/gfxWindowsPlatform.h b/gfx/thebes/gfxWindowsPlatform.h index 10d1756afb73..9b8d6230de39 100644 --- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -214,6 +214,9 @@ public: #ifdef CAIRO_HAS_DWRITE_FONT IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *GetD2DDevice() { return mD2DDevice; } +#endif #ifdef MOZ_FT2_FONTS FT_Library GetFTLibrary(); @@ -233,6 +236,9 @@ private: #ifdef CAIRO_HAS_DWRITE_FONT nsRefPtr mDWriteFactory; #endif +#ifdef CAIRO_HAS_D2D_SURFACE + cairo_device_t *mD2DDevice; +#endif virtual qcms_profile* GetPlatformCMSOutputProfile(); From dcd0ee87d8aee460185850fa9bda994458d31070 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 02:36:01 +0200 Subject: [PATCH 245/369] Bug 584539: Part 1 - Allow creating a D2D surface from a DirectX SharedHandle. r=jrmuizel --- gfx/cairo/cairo/src/cairo-d2d-surface.cpp | 135 ++++++++++++++++++++++ gfx/cairo/cairo/src/cairo-win32.h | 24 ++++ 2 files changed, 159 insertions(+) diff --git a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp index ea173b93faa9..71834be76bb0 100644 --- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp +++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp @@ -322,6 +322,39 @@ cairo_addref_device(cairo_device_t *device) return ++device->refcount; } +void +cairo_d2d_finish_device(cairo_device_t *device) +{ + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + // Here it becomes interesting, this flush method is generally called when + // interop is going on between our device and another device. The + // synchronisation between these devices is not always that great. The + // device flush method may flush the device's command queue, but it gives + // no guarantee that the device will actually be done with those commands, + // and so the surface may still not be complete when the external device + // chooses to use it. The EVENT query will actually tell us when the GPU + // is completely done with our commands. + D3D10_QUERY_DESC queryDesc; + queryDesc.MiscFlags = 0; + queryDesc.Query = D3D10_QUERY_EVENT; + RefPtr query; + + d2d_device->mD3D10Device->CreateQuery(&queryDesc, &query); + + // QUERY_EVENT does not use Begin(). It's disabled. + query->End(); + + BOOL done = FALSE; + while (!done) { + // This will return S_OK and done = FALSE when the GPU is not done, and + // S_OK and done = TRUE when the GPU is done. Any other return value + // means we need to break out or risk an infinite loop. + if (FAILED(query->GetData(&done, sizeof(BOOL), 0))) { + break; + } + } +} + static void _cairo_d2d_setup_for_blend(cairo_d2d_device_t *device) { @@ -3432,6 +3465,108 @@ FAIL_CREATE: return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_MEMORY)); } +cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content) +{ + if (!device) { + return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_NO_DEVICE)); + } + + cairo_d2d_device_t *d2d_device = reinterpret_cast(device); + cairo_d2d_surface_t *newSurf = static_cast(malloc(sizeof(cairo_d2d_surface_t))); + new (newSurf) cairo_d2d_surface_t(); + + cairo_status_t status = CAIRO_STATUS_NO_MEMORY; + HRESULT hr; + RefPtr texture; + RefPtr dxgiSurface; + D2D1_BITMAP_PROPERTIES bitProps; + D2D1_RENDER_TARGET_PROPERTIES props; + DXGI_FORMAT format; + DXGI_SURFACE_DESC desc; + + hr = d2d_device->mD3D10Device->OpenSharedResource(handle, + __uuidof(ID3D10Resource), + (void**)&newSurf->surface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + hr = newSurf->surface->QueryInterface(&dxgiSurface); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + dxgiSurface->GetDesc(&desc); + format = desc.Format; + + D2D1_ALPHA_MODE alpha = D2D1_ALPHA_MODE_PREMULTIPLIED; + if (format == DXGI_FORMAT_B8G8R8A8_UNORM) { + if (content == CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, content); + if (content == CAIRO_CONTENT_COLOR) { + alpha = D2D1_ALPHA_MODE_IGNORE; + } + } else if (format == DXGI_FORMAT_A8_UNORM) { + if (content != CAIRO_CONTENT_ALPHA) { + status = CAIRO_STATUS_INVALID_CONTENT; + goto FAIL_CREATEHANDLE; + } + _cairo_surface_init(&newSurf->base, &cairo_d2d_surface_backend, CAIRO_CONTENT_ALPHA); + } else { + status = CAIRO_STATUS_INVALID_FORMAT; + // We don't know how to support this format! + goto FAIL_CREATEHANDLE; + } + + props = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, + D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, alpha)); + + hr = sD2DFactory->CreateDxgiSurfaceRenderTarget(dxgiSurface, + props, + &newSurf->rt); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + + bitProps = D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, + alpha)); + + if (format != DXGI_FORMAT_A8_UNORM) { + /* For some reason creation of shared bitmaps for A8 UNORM surfaces + * doesn't work even though the documentation suggests it does. The + * function will return an error if we try */ + hr = newSurf->rt->CreateSharedBitmap(IID_IDXGISurface, + dxgiSurface, + &bitProps, + &newSurf->surfaceBitmap); + + if (FAILED(hr)) { + goto FAIL_CREATEHANDLE; + } + } + + newSurf->rt->CreateSolidColorBrush(D2D1::ColorF(0, 1.0), &newSurf->solidColorBrush); + + _d2d_clear_surface(newSurf); + + newSurf->device = d2d_device; + cairo_addref_device(device); + + return &newSurf->base; + +FAIL_CREATEHANDLE: + newSurf->~cairo_d2d_surface_t(); + free(newSurf); + return _cairo_surface_create_in_error(_cairo_error(status)); +} + void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) { diff --git a/gfx/cairo/cairo/src/cairo-win32.h b/gfx/cairo/cairo/src/cairo-win32.h index b7302c0c8de7..e6e7feea0106 100644 --- a/gfx/cairo/cairo/src/cairo-win32.h +++ b/gfx/cairo/cairo/src/cairo-win32.h @@ -158,6 +158,16 @@ cairo_release_device(cairo_device_t *device); int cairo_addref_device(cairo_device_t *device); +/** + * Flushes a D3D device. In most cases the surface backend will do this + * internally, but when using a surfaces created from a shared handle this + * should be executed manually when a different device is going to be accessing + * the same surface data. This will also block until the device is finished + * processing all work. + */ +void +cairo_d2d_finish_device(cairo_device_t *device); + /** * Create a D2D surface for an HWND * @@ -184,6 +194,20 @@ cairo_d2d_surface_create(cairo_device_t *device, int width, int height); +/** + * Create a D3D surface from a Texture SharedHandle, this is obtained from a + * CreateTexture call on a D3D9 device. This has to be an A8R8G8B8 format + * or an A8 format, the treatment of the alpha channel can be indicated using + * the content parameter. + * + * \param device Device used to create the surface + * \param handle Shared handle to the texture we want to wrap + * \param content Content of the texture, COLOR_ALPHA for ARGB + * \return New cairo surface + */ +cairo_public cairo_surface_t * +cairo_d2d_surface_create_for_handle(cairo_device_t *device, HANDLE handle, cairo_content_t content); + /** * Present the backbuffer for a surface create for an HWND. This needs * to be called when the owner of the original window surface wants to From 7167044f1f628a3227acc2bae5a5842b1b801a54 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 02:36:03 +0200 Subject: [PATCH 246/369] Bug 584539: Part 2 - Expose D2D surface creation from handle. r=jrmuizel --- gfx/thebes/gfxD2DSurface.cpp | 8 ++++++++ gfx/thebes/gfxD2DSurface.h | 1 + 2 files changed, 9 insertions(+) diff --git a/gfx/thebes/gfxD2DSurface.cpp b/gfx/thebes/gfxD2DSurface.cpp index bdc1ed887eaf..dfa1ef1884f5 100644 --- a/gfx/thebes/gfxD2DSurface.cpp +++ b/gfx/thebes/gfxD2DSurface.cpp @@ -46,6 +46,14 @@ gfxD2DSurface::gfxD2DSurface(HWND aWnd, gfxContentType aContent) (cairo_content_t)aContent)); } +gfxD2DSurface::gfxD2DSurface(HANDLE handle, gfxContentType aContent) +{ + Init(cairo_d2d_surface_create_for_handle( + gfxWindowsPlatform::GetPlatform()->GetD2DDevice(), + handle, + (cairo_content_t)aContent)); +} + gfxD2DSurface::gfxD2DSurface(cairo_surface_t *csurf) { Init(csurf, PR_TRUE); diff --git a/gfx/thebes/gfxD2DSurface.h b/gfx/thebes/gfxD2DSurface.h index 6d3748cc705f..1a3d641fdc57 100644 --- a/gfx/thebes/gfxD2DSurface.h +++ b/gfx/thebes/gfxD2DSurface.h @@ -51,6 +51,7 @@ public: gfxD2DSurface(const gfxIntSize& size, gfxImageFormat imageFormat = ImageFormatRGB24); + gfxD2DSurface(HANDLE handle, gfxContentType aContent); gfxD2DSurface(cairo_surface_t *csurf); From 3ef26ae8e59f49e44e65b6eebb7773ad7cb9b7c9 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 11 Aug 2010 02:36:05 +0200 Subject: [PATCH 247/369] Bug 584539: Part 3 - Use D2D interop when using the D3D9 layer manager. r=vlad a=dougt --- gfx/layers/d3d9/ThebesLayerD3D9.cpp | 271 ++++++++++++++++++---------- gfx/layers/d3d9/ThebesLayerD3D9.h | 10 +- 2 files changed, 184 insertions(+), 97 deletions(-) diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.cpp b/gfx/layers/d3d9/ThebesLayerD3D9.cpp index b8c0c3567855..786b2fb5aad0 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp +++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp @@ -39,6 +39,9 @@ #include "gfxPlatform.h" #include "gfxWindowsPlatform.h" +#ifdef CAIRO_HAS_D2D_SURFACE +#include "gfxD2DSurface.h" +#endif namespace mozilla { namespace layers { @@ -101,7 +104,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) return; } - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -116,10 +120,8 @@ ThebesLayerD3D9::SetVisibleRegion(const nsIntRegion &aRegion) nsIntRect oldBounds = oldVisibleRegion.GetBounds(); nsIntRect newBounds = mVisibleRegion.GetBounds(); - - device()->CreateTexture(newBounds.width, newBounds.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + + CreateNewTexture(gfxIntSize(newBounds.width, newBounds.height)); // Old visible region will become the region that is covered by both the // old and the new visible region. @@ -191,13 +193,14 @@ ThebesLayerD3D9::RenderLayer() if (mVisibleRegion.IsEmpty()) { return; } - HRESULT hr; nsIntRect visibleRect = mVisibleRegion.GetBounds(); // We differentiate between these formats since D3D9 will only allow us to // call GetDC on an opaque surface. - D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + D3DFORMAT fmt = (UseOpaqueSurface(this) && !mD2DSurface) ? + D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + if (mTexture) { D3DSURFACE_DESC desc; mTexture->GetLevelDesc(0, &desc); @@ -211,103 +214,16 @@ ThebesLayerD3D9::RenderLayer() } if (!mTexture) { - device()->CreateTexture(visibleRect.width, visibleRect.height, 1, - D3DUSAGE_RENDERTARGET, fmt, - D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + CreateNewTexture(gfxIntSize(visibleRect.width, visibleRect.height)); mValidRegion.SetEmpty(); } if (!mValidRegion.IsEqual(mVisibleRegion)) { nsIntRegion region; region.Sub(mVisibleRegion, mValidRegion); - nsIntRect bounds = region.GetBounds(); - gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; - nsRefPtr destinationSurface; - nsRefPtr context; + DrawRegion(region); - nsRefPtr tmpTexture; - device()->CreateTexture(bounds.width, bounds.height, 1, - 0, fmt, - D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); - - nsRefPtr surf; - HDC dc; - if (UseOpaqueSurface(this)) { - hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); - - if (FAILED(hr)) { - // Uh-oh, bail. - NS_WARNING("Failed to get texture surface level."); - return; - } - - hr = surf->GetDC(&dc); - - if (FAILED(hr)) { - NS_WARNING("Failed to get device context for texture surface."); - return; - } - - destinationSurface = new gfxWindowsSurface(dc); - } else { - // XXX - We may consider retaining a SYSTEMMEM texture texture the size - // of our DEFAULT texture and then use UpdateTexture and add dirty rects - // to update in a single call. - destinationSurface = - gfxPlatform::GetPlatform()-> - CreateOffscreenSurface(gfxIntSize(bounds.width, - bounds.height), - imageFormat); - } - - context = new gfxContext(destinationSurface); - context->Translate(gfxPoint(-bounds.x, -bounds.y)); - LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); - cbInfo.Callback(this, context, region, nsIntRegion(), cbInfo.CallbackData); - - if (UseOpaqueSurface(this)) { - surf->ReleaseDC(dc); - } else { - D3DLOCKED_RECT r; - tmpTexture->LockRect(0, &r, NULL, 0); - - nsRefPtr imgSurface = - new gfxImageSurface((unsigned char *)r.pBits, - gfxIntSize(bounds.width, - bounds.height), - r.Pitch, - imageFormat); - - context = new gfxContext(imgSurface); - context->SetSource(destinationSurface); - context->SetOperator(gfxContext::OPERATOR_SOURCE); - context->Paint(); - - imgSurface = NULL; - - tmpTexture->UnlockRect(0); - } - - nsRefPtr srcSurface; - nsRefPtr dstSurface; - - mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); - - nsIntRegionRectIterator iter(region); - const nsIntRect *iterRect; - while ((iterRect = iter.Next())) { - RECT rect; - rect.left = iterRect->x - bounds.x; - rect.top = iterRect->y - bounds.y; - rect.right = rect.left + iterRect->width; - rect.bottom = rect.top + iterRect->height; - POINT point; - point.x = iterRect->x - visibleRect.x; - point.y = iterRect->y - visibleRect.y; - device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); - } mValidRegion = mVisibleRegion; } @@ -362,5 +278,168 @@ ThebesLayerD3D9::IsEmpty() return !mTexture; } +void +ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion) +{ + HRESULT hr; + nsIntRect visibleRect = mVisibleRegion.GetBounds(); + nsRefPtr context; + +#ifdef CAIRO_HAS_D2D_SURFACE + if (mD2DSurface) { + context = new gfxContext(mD2DSurface); + nsIntRegionRectIterator iter(aRegion); + context->Translate(gfxPoint(-visibleRect.x, -visibleRect.y)); + context->NewPath(); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + context->Rectangle(gfxRect(iterRect->x, iterRect->y, iterRect->width, iterRect->height)); + } + context->Clip(); + if (mD2DSurface->GetContentType() != gfxASurface::CONTENT_COLOR) { + context->SetOperator(gfxContext::OPERATOR_CLEAR); + context->Paint(); + context->SetOperator(gfxContext::OPERATOR_OVER); + } + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + mD2DSurface->Flush(); + + // XXX - This call is quite expensive, we may want to consider doing our + // drawing in a seperate 'validation' iteration. And then flushing once for + // all the D2D surfaces we might have drawn, before doing our D3D9 rendering + // loop. + cairo_d2d_finish_device(gfxWindowsPlatform::GetPlatform()->GetD2DDevice()); + return; + } +#endif + + D3DFORMAT fmt = UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8; + nsIntRect bounds = aRegion.GetBounds(); + + gfxASurface::gfxImageFormat imageFormat = gfxASurface::ImageFormatARGB32; + nsRefPtr destinationSurface; + + nsRefPtr tmpTexture; + device()->CreateTexture(bounds.width, bounds.height, 1, + 0, fmt, + D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL); + + nsRefPtr surf; + HDC dc; + if (UseOpaqueSurface(this)) { + hr = tmpTexture->GetSurfaceLevel(0, getter_AddRefs(surf)); + + if (FAILED(hr)) { + // Uh-oh, bail. + NS_WARNING("Failed to get texture surface level."); + return; + } + + hr = surf->GetDC(&dc); + + if (FAILED(hr)) { + NS_WARNING("Failed to get device context for texture surface."); + return; + } + + destinationSurface = new gfxWindowsSurface(dc); + } else { + // XXX - We may consider retaining a SYSTEMMEM texture texture the size + // of our DEFAULT texture and then use UpdateTexture and add dirty rects + // to update in a single call. + destinationSurface = + gfxPlatform::GetPlatform()-> + CreateOffscreenSurface(gfxIntSize(bounds.width, + bounds.height), + imageFormat); + } + + context = new gfxContext(destinationSurface); + context->Translate(gfxPoint(-bounds.x, -bounds.y)); + LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo(); + cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData); + + if (UseOpaqueSurface(this)) { + surf->ReleaseDC(dc); + } else { + D3DLOCKED_RECT r; + tmpTexture->LockRect(0, &r, NULL, 0); + + nsRefPtr imgSurface = + new gfxImageSurface((unsigned char *)r.pBits, + gfxIntSize(bounds.width, + bounds.height), + r.Pitch, + imageFormat); + + context = new gfxContext(imgSurface); + context->SetSource(destinationSurface); + context->SetOperator(gfxContext::OPERATOR_SOURCE); + context->Paint(); + + imgSurface = NULL; + + tmpTexture->UnlockRect(0); + } + + nsRefPtr srcSurface; + nsRefPtr dstSurface; + + mTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + tmpTexture->GetSurfaceLevel(0, getter_AddRefs(srcSurface)); + + nsIntRegionRectIterator iter(aRegion); + const nsIntRect *iterRect; + while ((iterRect = iter.Next())) { + RECT rect; + rect.left = iterRect->x - bounds.x; + rect.top = iterRect->y - bounds.y; + rect.right = rect.left + iterRect->width; + rect.bottom = rect.top + iterRect->height; + POINT point; + point.x = iterRect->x - visibleRect.x; + point.y = iterRect->y - visibleRect.y; + device()->UpdateSurface(srcSurface, &rect, dstSurface, &point); + } +} + +void +ThebesLayerD3D9::CreateNewTexture(const gfxIntSize &aSize) +{ + if (aSize.width == 0 | aSize.height == 0) { + // Nothing to do. + return; + } + + mTexture = nsnull; +#ifdef CAIRO_HAS_D2D_SURFACE + if (gfxWindowsPlatform::GetPlatform()->GetRenderMode() == + gfxWindowsPlatform::RENDER_DIRECT2D) { + if (mD3DManager->deviceManager()->IsD3D9Ex()) { + // We should have D3D9Ex where we have D2D. + HANDLE sharedHandle = 0; + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), &sharedHandle); + + mD2DSurface = new gfxD2DSurface(sharedHandle, UseOpaqueSurface(this) ? + gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA); + + // If there's an error, go on and do what we always do. + if (mD2DSurface->CairoStatus()) { + mD2DSurface = nsnull; + mTexture = nsnull; + } + } + } +#endif + if (!mTexture) { + device()->CreateTexture(aSize.width, aSize.height, 1, + D3DUSAGE_RENDERTARGET, UseOpaqueSurface(this) ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8, + D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL); + } +} + } /* namespace layers */ } /* namespace mozilla */ diff --git a/gfx/layers/d3d9/ThebesLayerD3D9.h b/gfx/layers/d3d9/ThebesLayerD3D9.h index 13bb29f3da17..d51a9a86d4c5 100644 --- a/gfx/layers/d3d9/ThebesLayerD3D9.h +++ b/gfx/layers/d3d9/ThebesLayerD3D9.h @@ -42,7 +42,6 @@ #include "LayerManagerD3D9.h" #include "gfxImageSurface.h" - namespace mozilla { namespace layers { @@ -70,6 +69,15 @@ private: * D3D9 texture */ nsRefPtr mTexture; + + /* This contains the D2D surface if we have one */ + nsRefPtr mD2DSurface; + + /* Have a region of our layer drawn */ + void DrawRegion(const nsIntRegion &aRegion); + + /* Create a new texture */ + void CreateNewTexture(const gfxIntSize &aSize); }; } /* layers */ From 588533b9fe4a1af8f4f23e61c8e2ab5b9b0635c8 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 10 Aug 2010 11:52:32 -0400 Subject: [PATCH 248/369] Bug 572290 - value of HTML signature file shows up in signature; r=bzbarsky a=blocking2.0+ --- .../src/nsHTMLFragmentContentSink.cpp | 6 +- .../libeditor/html/tests/test_bug520189.html | 69 ++++++++++++++----- 2 files changed, 56 insertions(+), 19 deletions(-) diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 823ca70b9cd7..996f2f6a1089 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1100,6 +1100,10 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag) { nsresult rv = NS_OK; + if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) { + mIgnoreNextCloseHead = PR_FALSE; + return NS_OK; + } if (mSkip) { mSkip = PR_FALSE; return rv; @@ -1229,7 +1233,7 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - if (mSkip) { + if (mSkip || mIgnoreNextCloseHead) { return rv; } diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 8bf41534ba09..d62a9db220f1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -52,6 +52,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=520182 <div id="jj" contenteditable="true"></div> <iframe id="kk" src="about:blank"></iframe> <div id="ll" contenteditable="true"></div> + <iframe id="mm" src="about:blank"></iframe> + <div id="nn" contenteditable="true"></div> </div> <pre id="test"> <script type="application/javascript"> @@ -73,6 +75,7 @@ const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz"; const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz"; const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz"; const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz"; +const invalidStyle7Payload = "<html><head><title>xxxfoo"; const nestedStylePayload = "foo#bar2{-moz-binding:url('data:text/xml,');baz"; const validImgSrc1Payload = "foobaz"; const validImgSrc2Payload = "foobaz"; @@ -333,6 +336,27 @@ var tests = [ payload: invalidStyle6Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") + }, + { + id: "mm", + isIFrame: true, + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("mm").contentDocument.documentElement, + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } + }, + { + id: "nn", + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("nn"), + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } } ]; @@ -357,28 +381,37 @@ function runTest(test) { } else elem.focus(); - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if ("insertHTML" in test) { + if ("isIFrame" in test) { + elem.contentDocument.execCommand("inserthtml", false, test.payload); + } else { + getSelection().collapse(elem, 0); + document.execCommand("inserthtml", false, test.payload); + } + } else { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); + var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] + .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); - clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); - var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); + var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); - mainWindow.goDoCommand("cmd_paste"); + mainWindow.goDoCommand("cmd_paste"); + } if ("checkResult" in test) { if ("isIFrame" in test) { From 9f2adea5a43333cc3b3770c52c61d8575329343a Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 10 Aug 2010 11:52:32 -0400 Subject: [PATCH 249/369] Bug 572290 - value of HTML signature file shows up in signature; r=bzbarsky a=blocking2.0+ --HG-- extra : rebase_source : 7085d047498e0509f056c8a33d9189e52aaeb077 --- .../src/nsHTMLFragmentContentSink.cpp | 9 ++- .../libeditor/html/tests/test_bug520189.html | 69 ++++++++++++++----- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 823ca70b9cd7..918d33ec4f2e 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1100,6 +1100,10 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag) { nsresult rv = NS_OK; + if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) { + mIgnoreNextCloseHead = PR_FALSE; + return NS_OK; + } if (mSkip) { mSkip = PR_FALSE; return rv; @@ -1229,7 +1233,10 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - if (mSkip) { + // We need to explicitly skip adding leaf nodes in the paranoid sink, + // otherwise things like the textnode under <title> get appended to + // the fragment itself, and won't be popped off in CloseContainer. + if (mSkip || mIgnoreNextCloseHead) { return rv; } diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 8bf41534ba09..d62a9db220f1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -52,6 +52,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=520182 <div id="jj" contenteditable="true"></div> <iframe id="kk" src="about:blank"></iframe> <div id="ll" contenteditable="true"></div> + <iframe id="mm" src="about:blank"></iframe> + <div id="nn" contenteditable="true"></div> </div> <pre id="test"> <script type="application/javascript"> @@ -73,6 +75,7 @@ const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz"; const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz"; const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz"; const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz"; +const invalidStyle7Payload = "<html><head><title>xxxfoo"; const nestedStylePayload = "foo#bar2{-moz-binding:url('data:text/xml,');baz"; const validImgSrc1Payload = "foobaz"; const validImgSrc2Payload = "foobaz"; @@ -333,6 +336,27 @@ var tests = [ payload: invalidStyle6Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") + }, + { + id: "mm", + isIFrame: true, + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("mm").contentDocument.documentElement, + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } + }, + { + id: "nn", + insertHTML: true, + payload: invalidStyle7Payload, + rootElement: function() document.getElementById("nn"), + checkResult: function(html) { + is(html.indexOf("xxx"), -1, "Should not have retained the title text"); + isnot(html.indexOf("foo"), -1, "Should have retained the body text"); + } } ]; @@ -357,28 +381,37 @@ function runTest(test) { } else elem.focus(); - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + if ("insertHTML" in test) { + if ("isIFrame" in test) { + elem.contentDocument.execCommand("inserthtml", false, test.payload); + } else { + getSelection().collapse(elem, 0); + document.execCommand("inserthtml", false, test.payload); + } + } else { + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); + var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] + .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); - clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); - var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); + var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); - mainWindow.goDoCommand("cmd_paste"); + mainWindow.goDoCommand("cmd_paste"); + } if ("checkResult" in test) { if ("isIFrame" in test) { From 0b3664b8433382fc30b0e4f775dae360c63f5cfe Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Tue, 10 Aug 2010 18:16:57 +0200 Subject: [PATCH 250/369] Bug 557589 - code audit and create unit test plan for service.js [r=mconnor] Part 4 followup: Fix a reference error in Weave.Service._checkServerError and improve test coverage for verifyLogin() to exercise that code path. --- services/sync/modules/service.js | 2 +- .../tests/unit/test_service_verifyLogin.js | 38 +++++++++++++++++-- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index feeca3850923..aef2258403de 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -1524,7 +1524,7 @@ WeaveSvc.prototype = { if (Utils.checkStatus(resp.status, null, [500, [502, 504]])) { Status.enforceBackoff = true; if (resp.status == 503 && resp.headers["retry-after"]) - Observers.notify("weave:service:backoff:interval", parseInt(resp.headers["retry-after"], 10)); + Svc.Obs.notify("weave:service:backoff:interval", parseInt(resp.headers["retry-after"], 10)); } }, /** diff --git a/services/sync/tests/unit/test_service_verifyLogin.js b/services/sync/tests/unit/test_service_verifyLogin.js index aa311ea48902..8cfcabf63b73 100644 --- a/services/sync/tests/unit/test_service_verifyLogin.js +++ b/services/sync/tests/unit/test_service_verifyLogin.js @@ -18,18 +18,33 @@ function login_handler(request, response) { response.bodyOutputStream.write(body, body.length); } +function send(statusCode, status, body) { + return function(request, response) { + response.setStatusLine(request.httpVersion, statusCode, status); + response.bodyOutputStream.write(body, body.length); + }; +} + +function service_unavailable(request, response) { + let body = "Service Unavailable"; + response.setStatusLine(request.httpVersion, 503, "Service Unavailable"); + response.setHeader("Retry-After", "42"); + response.bodyOutputStream.write(body, body.length); +} + function run_test() { let logger = Log4Moz.repository.rootLogger; Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender()); do_test_pending(); let server = httpd_setup({ - "/1.0/johndoe/info/collections": login_handler + "/api/1.0/johndoe/info/collections": login_handler, + "/api/1.0/janedoe/info/collections": service_unavailable, + "/user/1.0/johndoe/node/weave": send(200, "OK", "http://localhost:8080/api/") }); try { Weave.Service.serverURL = "http://localhost:8080/"; - Weave.Service.clusterURL = "http://localhost:8080/"; _("Force the initial state."); Status.service = STATUS_OK; @@ -47,12 +62,27 @@ function run_test() { do_check_eq(Status.service, CLIENT_NOT_CONFIGURED); do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE); + _("verifyLogin() has found out the user's cluster URL, though."); + do_check_eq(Weave.Service.clusterURL, "http://localhost:8080/api/"); + _("Success if passphrase is set."); Weave.Service.passphrase = "foo"; - Weave.Service.login(); + do_check_true(Weave.Service.verifyLogin()); do_check_eq(Status.service, STATUS_OK); do_check_eq(Status.login, LOGIN_SUCCEEDED); - do_check_true(Weave.Service.isLoggedIn); + + _("If verifyLogin() encounters a server error, it flips on the backoff flag and notifies observers on a 503 with Retry-After."); + Weave.Service.username = "janedoe"; + do_check_false(Status.enforceBackoff); + let backoffInterval; + Svc.Obs.add("weave:service:backoff:interval", function(subject, data) { + backoffInterval = subject; + }); + do_check_false(Weave.Service.verifyLogin()); + do_check_true(Status.enforceBackoff); + do_check_eq(backoffInterval, 42); + do_check_eq(Status.service, LOGIN_FAILED); + do_check_eq(Status.login, LOGIN_FAILED_SERVER_ERROR); } finally { Svc.Prefs.resetBranch(""); From 092e9e0518cc615092c796a33e60b7343406bb6d Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 10 Aug 2010 11:13:10 -0700 Subject: [PATCH 251/369] + reversed the argument order for Utils.assert and Utils.assertThrow, per Dao's review --- browser/base/content/tabview/iq.js | 43 ++++++++++--------- .../base/content/tabview/modules/utils.jsm | 34 +++++++-------- 2 files changed, 39 insertions(+), 38 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index f361dcefe67c..4750ad06357c 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -117,13 +117,13 @@ let iQClass = function(selector, context) { if (ret) { if (Utils.isPlainObject(context)) { - Utils.assert('does not support HTML creation with context', false); + Utils.assert(false, 'does not support HTML creation with context'); } else { selector = [doc.createElement(ret[1])]; } } else { - Utils.assert('does not support complex HTML creation', false); + Utils.assert(false, 'does not support complex HTML creation'); } return Utils.merge(this, selector); @@ -198,7 +198,7 @@ iQClass.prototype = { // Execute a callback for every element in the matched set. each: function(callback) { if (typeof callback != "function") { - Utils.assert("each's argument must be a function", false); + Utils.assert(false, "each's argument must be a function"); return null; } for (let i = 0; this[i] != null; i++) { @@ -212,7 +212,7 @@ iQClass.prototype = { // Adds the given class(es) to the receiver. addClass: function(value) { if (typeof value != "string" || !value) { - Utils.assert('requires a valid string argument', false); + Utils.assert(false, 'requires a valid string argument'); return null; } @@ -234,7 +234,7 @@ iQClass.prototype = { // Removes the given class(es) from the receiver. removeClass: function(value) { if (typeof value != "string" || !value) { - Utils.assert('does not support function argument', false); + Utils.assert(false, 'does not support function argument'); return null; } @@ -352,7 +352,7 @@ iQClass.prototype = { // Function: bounds // Returns a with the receiver's bounds. bounds: function() { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); let rect = this[0].getBoundingClientRect(); return new Rect(Math.floor(rect.left), Math.floor(rect.top), Math.floor(rect.width), Math.floor(rect.height)); @@ -365,7 +365,7 @@ iQClass.prototype = { data: function(key, value) { let data = null; if (typeof value === "undefined") { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); data = this[0].iQData; if (data) return data[key]; @@ -391,7 +391,7 @@ iQClass.prototype = { // Given a value, sets the receiver's innerHTML to it; otherwise returns // what's already there. html: function(value) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); if (typeof value === "undefined") return this[0].innerHTML; @@ -404,7 +404,7 @@ iQClass.prototype = { // Given a value, sets the receiver's textContent to it; otherwise returns // what's already there. text: function(value) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); if (typeof value === "undefined") { return this[0].textContent; } @@ -416,7 +416,7 @@ iQClass.prototype = { // Function: val // Given a value, sets the receiver's value to it; otherwise returns what's already there. val: function(value) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); if (typeof value === "undefined") { return this[0].value; } @@ -429,7 +429,7 @@ iQClass.prototype = { // Function: appendTo // Appends the receiver to the result of iQ(selector). appendTo: function(selector) { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); iQ(selector).append(this); return this; }, @@ -439,7 +439,8 @@ iQClass.prototype = { // Appends the result of iQ(selector) to the receiver. append: function(selector) { let object = iQ(selector); - Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1 && this.length == 1); + Utils.assert(object.length == 1 && this.length == 1, + 'does not yet support multi-objects (or null objects)'); this[0].appendChild(object[0]); return this; }, @@ -449,9 +450,9 @@ iQClass.prototype = { // Sets or gets an attribute on the element(s). attr: function(key, value) { try { - Utils.assert('string key', typeof key === 'string'); + Utils.assert(typeof key === 'string', 'string key'); if (typeof value === "undefined") { - Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); return this[0].getAttribute(key); } for (let i = 0; this[i] != null; i++) { @@ -479,7 +480,7 @@ iQClass.prototype = { if (typeof a === 'string') { let key = a; if (typeof b === "undefined") { - Utils.assert('retrieval does not support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); return window.getComputedStyle(this[0], null).getPropertyValue(key); } @@ -533,7 +534,7 @@ iQClass.prototype = { // in, but "this" is set to the element that was animated. animate: function(css, options) { try { - Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); if (!options) options = {}; @@ -591,8 +592,8 @@ iQClass.prototype = { // Function: fadeOut // Animates the receiver to full transparency. Calls callback on completion. fadeOut: function(callback) { - Utils.assert('does not yet support duration', typeof callback == "function" - || typeof callback === "undefined"); + Utils.assert(typeof callback == "function" || typeof callback === "undefined", + 'does not yet support duration'); this.animate({ opacity: 0 @@ -657,7 +658,7 @@ iQClass.prototype = { // Binds the given function to the given event type. Also wraps the function // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { - Utils.assert('does not support eventData argument', typeof func == "function"); + Utils.assert(typeof func == "function", 'does not support eventData argument'); let handler = function(event) { try { @@ -691,7 +692,7 @@ iQClass.prototype = { // Binds the given function to the given event type, but only for one call; // automatically unbinds after the event fires once. one: function(type, func) { - Utils.assert('does not support eventData argument', typeof func == "function"); + Utils.assert(typeof func == "function", 'does not support eventData argument'); let handler = function(e) { iQ(this).unbind(type, handler); @@ -705,7 +706,7 @@ iQClass.prototype = { // Function: unbind // Unbinds the given function from the given event type. unbind: function(type, func) { - Utils.assert('Must provide a function', typeof func == "function"); + Utils.assert(typeof func == "function", 'Must provide a function'); for (let i = 0; this[i] != null; i++) { let elem = this[i]; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index f7691d3a8cce..d5a8e79726ff 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -384,10 +384,10 @@ Subscribable.prototype = { // The refObject is used to facilitate removal if necessary. addSubscriber: function(refObject, eventName, callback) { try { - Utils.assertThrow("refObject", refObject); - Utils.assertThrow("callback must be a function", typeof callback == "function"); - Utils.assertThrow("eventName must be a non-empty string", - eventName && typeof(eventName) == "string"); + Utils.assertThrow(refObject, "refObject"); + Utils.assertThrow(typeof callback == "function", "callback must be a function"); + Utils.assertThrow(eventName && typeof(eventName) == "string", + "eventName must be a non-empty string"); if (!this.subscribers) this.subscribers = {}; @@ -401,7 +401,7 @@ Subscribable.prototype = { }); if (existing.length) { - Utils.assert('should only ever be one', existing.length == 1); + Utils.assert(existing.length == 1, 'should only ever be one'); existing[0].callback = callback; } else { subs.push({ @@ -419,9 +419,9 @@ Subscribable.prototype = { // Removes the callback associated with refObject for the given event. removeSubscriber: function(refObject, eventName) { try { - Utils.assertThrow("refObject", refObject); - Utils.assertThrow("eventName must be a non-empty string", - eventName && typeof(eventName) == "string"); + Utils.assertThrow(refObject, "refObject"); + Utils.assertThrow(eventName && typeof(eventName) == "string", + "eventName must be a non-empty string"); if (!this.subscribers || !this.subscribers[eventName]) return; @@ -439,8 +439,8 @@ Subscribable.prototype = { // Internal routine. Used by the Subscribable to fire events. _sendToSubscribers: function(eventName, eventInfo) { try { - Utils.assertThrow("eventName must be a non-empty string", - eventName && typeof(eventName) == "string"); + Utils.assertThrow(eventName && typeof(eventName) == "string", + "eventName must be a non-empty string"); if (!this.subscribers || !this.subscribers[eventName]) return; @@ -498,10 +498,10 @@ let Utils = { // ---------- // Function: assert // Prints a stack trace along with label (as a console message) if condition is false. - assert: function Utils_assert(label, condition) { + assert: function Utils_assert(condition, label) { if (!condition) { let text; - if (typeof(label) == 'undefined') + if (typeof(label) != 'string') text = 'badly formed assert'; else text = "tabview assert: " + label; @@ -513,10 +513,10 @@ let Utils = { // ---------- // Function: assertThrow // Throws label as an exception if condition is false. - assertThrow: function(label, condition) { + assertThrow: function(condition, label) { if (!condition) { let text; - if (typeof(label) == 'undefined') + if (typeof(label) != 'string') text = 'badly formed assert'; else text = "tabview assert: " + label; @@ -682,15 +682,15 @@ let Utils = { // Deep copy is not supported if (typeof target === "boolean") { - this.assert("The first argument of extend cannot be a boolean." - +"Deep copy is not supported.", false); + this.assert(false, "The first argument of extend cannot be a boolean." + + "Deep copy is not supported."); return target; } // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it. // This is no longer supported. if (length === 1) { - this.assert("Extending the iQ prototype using extend is not supported.", false); + this.assert(false, "Extending the iQ prototype using extend is not supported."); return target; } From 8a238ad491fc4ed18851b85fdf255e69b3cb9ebe Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Tue, 10 Aug 2010 19:20:05 -0400 Subject: [PATCH 252/369] Bug 582023 cleanup based on comments from Dao on things that are not iQ: - typeof() is not a function - formatting of && and || at the end of the line - declaring variables more locally using let in Utils.extend - the Initial Developer is the Mozilla Foundation --HG-- extra : rebase_source : d1f9699fb0014c95e336c34d777ce354b141739a --- .../base/content/tabview/modules/utils.jsm | 89 ++++++++++--------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index d5a8e79726ff..d8bc449fd28f 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -13,12 +13,12 @@ * * The Original Code is utils.js. * - * The Initial Developer of the Original Code is - * Aza Raskin + * The Initial Developer of the Original Code is the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Aza Raskin * Ian Gilman * Michael Yoshitaka Erlewine * @@ -136,10 +136,10 @@ Rect.prototype = { // Function: intersects // Returns true if this rectangle intersects the given . intersects: function(rect) { - return (rect.right > this.left - && rect.left < this.right - && rect.bottom > this.top - && rect.top < this.bottom); + return (rect.right > this.left && + rect.left < this.right && + rect.bottom > this.top && + rect.top < this.bottom); }, // ---------- @@ -164,10 +164,10 @@ Rect.prototype = { // Paramaters // - A contains: function(rect) { - return(rect.left > this.left - && rect.right < this.right - && rect.top > this.top - && rect.bottom < this.bottom) + return (rect.left > this.left && + rect.right < this.right && + rect.top > this.top && + rect.bottom < this.bottom); }, // ---------- @@ -237,10 +237,10 @@ Rect.prototype = { // Function: equals // Returns true if this rectangle is identical to the given . equals: function(rect) { - return (rect.left == this.left - && rect.top == this.top - && rect.width == this.width - && rect.height == this.height); + return (rect.left == this.left && + rect.top == this.top && + rect.width == this.width && + rect.height == this.height); }, // ---------- @@ -386,7 +386,7 @@ Subscribable.prototype = { try { Utils.assertThrow(refObject, "refObject"); Utils.assertThrow(typeof callback == "function", "callback must be a function"); - Utils.assertThrow(eventName && typeof(eventName) == "string", + Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); if (!this.subscribers) @@ -420,7 +420,7 @@ Subscribable.prototype = { removeSubscriber: function(refObject, eventName) { try { Utils.assertThrow(refObject, "refObject"); - Utils.assertThrow(eventName && typeof(eventName) == "string", + Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); if (!this.subscribers || !this.subscribers[eventName]) @@ -439,7 +439,7 @@ Subscribable.prototype = { // Internal routine. Used by the Subscribable to fire events. _sendToSubscribers: function(eventName, eventInfo) { try { - Utils.assertThrow(eventName && typeof(eventName) == "string", + Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); if (!this.subscribers || !this.subscribers[eventName]) @@ -501,7 +501,7 @@ let Utils = { assert: function Utils_assert(condition, label) { if (!condition) { let text; - if (typeof(label) != 'string') + if (typeof label != 'string') text = 'badly formed assert'; else text = "tabview assert: " + label; @@ -516,7 +516,7 @@ let Utils = { assertThrow: function(condition, label) { if (!condition) { let text; - if (typeof(label) != 'string') + if (typeof label != 'string') text = 'badly formed assert'; else text = "tabview assert: " + label; @@ -542,9 +542,9 @@ let Utils = { } s += prop + ': '; - if (typeof(value) == 'string') + if (typeof value == 'string') s += '\'' + value + '\''; - else if (typeof(value) == 'function') + else if (typeof value == 'function') s += 'function'; else s += value; @@ -560,7 +560,7 @@ let Utils = { expandArgumentsForLog: function(args) { var that = this; return Array.map(args, function(arg) { - return typeof(arg) == 'object' ? that.expandObject(arg) : arg; + return typeof arg == 'object' ? that.expandObject(arg) : arg; }).join('; '); }, @@ -584,27 +584,27 @@ let Utils = { // Function: isNumber // Returns true if the argument is a valid number. isNumber: function(n) { - return (typeof(n) == 'number' && !isNaN(n)); + return typeof n == 'number' && !isNaN(n); }, // ---------- // Function: isRect // Returns true if the given object (r) looks like a . isRect: function(r) { - return (r - && this.isNumber(r.left) - && this.isNumber(r.top) - && this.isNumber(r.width) - && this.isNumber(r.height)); + return (r && + this.isNumber(r.left) && + this.isNumber(r.top) && + this.isNumber(r.width) && + this.isNumber(r.height)); }, // ---------- // Function: isRange // Returns true if the given object (r) looks like a . isRange: function(r) { - return (r - && this.isNumber(r.min) - && this.isNumber(r.max)); + return (r && + this.isNumber(r.min) && + this.isNumber(r.max)); }, // ---------- @@ -620,17 +620,17 @@ let Utils = { isPlainObject: function(obj) { // Must be an Object. // Make sure that DOM nodes and window objects don't pass through, as well - if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" - || obj.nodeType || obj.setInterval) { + if (!obj || Object.prototype.toString.call(obj) !== "[object Object]" || + obj.nodeType || obj.setInterval) { return false; } // Not own constructor property must be Object const hasOwnProperty = Object.prototype.hasOwnProperty; - if (obj.constructor - && !hasOwnProperty.call(obj, "constructor") - && !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) { + if (obj.constructor && + !hasOwnProperty.call(obj, "constructor") && + !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf")) { return false; } @@ -657,7 +657,7 @@ let Utils = { // Returns a copy of the argument. Note that this is a shallow copy; if the argument // has properties that are themselves objects, those properties will be copied by reference. copy: function(value) { - if (value && typeof(value) == 'object') { + if (value && typeof value == 'object') { if (Array.isArray(value)) return this.extend([], value); return this.extend({}, value); @@ -677,9 +677,9 @@ let Utils = { // Function: extend // Pass several objects in and it will combine them all into the first object and return it. extend: function() { - // copy reference to target object - var target = arguments[0] || {}, i = 1, length = arguments.length, options, name, src, copy; + // copy reference to target object + let target = arguments[0] || {}; // Deep copy is not supported if (typeof target === "boolean") { this.assert(false, "The first argument of extend cannot be a boolean." + @@ -689,6 +689,7 @@ let Utils = { // Back when this was in iQ + iQ.fn, so you could extend iQ objects with it. // This is no longer supported. + let length = arguments.length; if (length === 1) { this.assert(false, "Extending the iQ prototype using extend is not supported."); return target; @@ -699,13 +700,13 @@ let Utils = { target = {}; } - for (; i < length; i++) { + for (let i = 1; i < length; i++) { // Only deal with non-null/undefined values - if ((options = arguments[i]) != null) { + let options = arguments[i]; + if (options != null) { // Extend the base object - for (name in options) { - src = target[name]; - copy = options[name]; + for (let name in options) { + let copy = options[name]; // Prevent never-ending loop if (target === copy) From 6d08603c79e8836b2fdaae7152c8df0b84d3d82c Mon Sep 17 00:00:00 2001 From: Ian Gilman Date: Tue, 10 Aug 2010 16:31:12 -0700 Subject: [PATCH 253/369] + addressing dao's comments in iQ --HG-- extra : rebase_source : 9fd229f468514fb08326fe292434de45e6167bfb --- browser/base/content/tabview/iq.js | 61 ++++++++++-------------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 4750ad06357c..9066189019e9 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -14,11 +14,12 @@ * The Original Code is iq.js. * * The Initial Developer of the Original Code is - * Ian Gilman . + * the Mozilla Foundation * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Ian Gilman * Aza Raskin * Michael Yoshitaka Erlewine * @@ -181,15 +182,13 @@ let iQClass = function(selector, context) { } } return ret; -} +}; + iQClass.prototype = { // Start with an empty selector selector: "", - // The current version of iQ being used - iq: "1.4.2", - // The default length of a iQ object is 0 length: 0, @@ -328,7 +327,7 @@ iQClass.prototype = { // Returns the width of the receiver. width: function() { let bounds = this.bounds(); - return bounds.width + return bounds.width; }, // ---------- @@ -449,19 +448,15 @@ iQClass.prototype = { // Function: attr // Sets or gets an attribute on the element(s). attr: function(key, value) { - try { - Utils.assert(typeof key === 'string', 'string key'); - if (typeof value === "undefined") { - Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); - return this[0].getAttribute(key); - } - for (let i = 0; this[i] != null; i++) { - this[i].setAttribute(key, value); - } - } catch(e) { - Utils.log(e); + Utils.assert(typeof key === 'string', 'string key'); + if (typeof value === "undefined") { + Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)'); + return this[0].getAttribute(key); } + for (let i = 0; this[i] != null; i++) + this[i].setAttribute(key, value); + return this; }, @@ -503,7 +498,7 @@ iQClass.prototype = { let elem = this[i]; for (let key in properties) { let value = properties[key]; - if (pixels[key] && typeof(value) != 'string') + if (pixels[key] && typeof value != 'string') value += 'px'; if (value == null) { @@ -613,16 +608,12 @@ iQClass.prototype = { // Function: fadeIn // Animates the receiver to full opacity. fadeIn: function() { - try { - this.css({display: ''}); - this.animate({ - opacity: 1 - }, { - duration: 400 - }); - } catch(e) { - Utils.log(e); - } + this.css({display: ''}); + this.animate({ + opacity: 1 + }, { + duration: 400 + }); return this; }, @@ -631,12 +622,7 @@ iQClass.prototype = { // Function: hide // Hides the receiver. hide: function() { - try { - this.css({display: 'none', opacity: 0}); - } catch(e) { - Utils.log(e); - } - + this.css({display: 'none', opacity: 0}); return this; }, @@ -644,12 +630,7 @@ iQClass.prototype = { // Function: show // Shows the receiver. show: function() { - try { - this.css({display: '', opacity: 1}); - } catch(e) { - Utils.log(e); - } - + this.css({display: '', opacity: 1}); return this; }, From 280a8f839d93388034a3af4b1f4e68e73e86b7cf Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 11 Aug 2010 01:54:46 +0200 Subject: [PATCH 254/369] Bug 585291 - FormEngine tests leak [r=mconnor] Nuke service references on xpcom-shutdown, particularly Svc.Form (nsIFormHistory2) which doesn't clean up after itself as it was only used by nsIFormAutComplete so far. --- services/sync/modules/util.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/services/sync/modules/util.js b/services/sync/modules/util.js index c1b1d24c3e51..93edf619e757 100644 --- a/services/sync/modules/util.js +++ b/services/sync/modules/util.js @@ -918,3 +918,8 @@ Svc.Obs = Observers; let Str = {}; ["errors", "sync"] .forEach(function(lazy) Utils.lazy2(Str, lazy, Utils.lazyStrings(lazy))); + +Svc.Obs.add("xpcom-shutdown", function () { + for (let name in Svc) + delete Svc[name]; +}); From 2add9c99386965f75b2a461b110f77a7ed13ebd7 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 10 Aug 2010 21:20:04 -0400 Subject: [PATCH 255/369] Bug 572290 - comment addition; a=NPOTB --- content/html/document/src/nsHTMLFragmentContentSink.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 996f2f6a1089..918d33ec4f2e 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1233,6 +1233,9 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; + // We need to explicitly skip adding leaf nodes in the paranoid sink, + // otherwise things like the textnode under get appended to + // the fragment itself, and won't be popped off in CloseContainer. if (mSkip || mIgnoreNextCloseHead) { return rv; } From df0ee9e34af6cd0ad40b71090fb6007a50d3f043 Mon Sep 17 00:00:00 2001 From: Honza Bambas <honzab.moz@firemni.cz> Date: Tue, 10 Aug 2010 20:11:57 -0700 Subject: [PATCH 256/369] Bug 536294 - e10s HTTP: redirects. r=jduell --HG-- rename : netwerk/protocol/http/HttpChannelParent.cpp => netwerk/protocol/http/HttpChannelParentListener.cpp rename : netwerk/protocol/http/HttpChannelParent.h => netwerk/protocol/http/HttpChannelParentListener.h --- netwerk/base/public/Makefile.in | 1 + .../base/public/nsIRedirectResultListener.idl | 55 ++++ netwerk/ipc/NeckoChild.cpp | 15 +- netwerk/ipc/NeckoParent.cpp | 4 +- netwerk/ipc/NeckoParent.h | 2 +- netwerk/ipc/PNecko.ipdl | 4 +- netwerk/protocol/http/HttpBaseChannel.cpp | 138 +++++++++ netwerk/protocol/http/HttpBaseChannel.h | 22 +- netwerk/protocol/http/HttpChannelChild.cpp | 178 ++++++++++- netwerk/protocol/http/HttpChannelChild.h | 22 +- netwerk/protocol/http/HttpChannelParent.cpp | 98 ++---- netwerk/protocol/http/HttpChannelParent.h | 31 +- .../http/HttpChannelParentListener.cpp | 289 ++++++++++++++++++ .../protocol/http/HttpChannelParentListener.h | 94 ++++++ netwerk/protocol/http/Makefile.in | 1 + netwerk/protocol/http/PHttpChannel.ipdl | 18 +- netwerk/protocol/http/nsHttpChannel.cpp | 169 ++++------ netwerk/protocol/http/nsHttpChannel.h | 8 +- netwerk/test/unit_ipc/test_event_sink_wrap.js | 7 + .../test_redirect-caching_canceled_wrap.js | 7 + .../test_redirect-caching_failure_wrap.js | 7 + .../test_redirect-caching_passing_wrap.js | 7 + .../unit_ipc/test_redirect_canceled_wrap.js | 7 + .../unit_ipc/test_redirect_failure_wrap.js | 7 + .../unit_ipc/test_redirect_passing_wrap.js | 7 + 25 files changed, 968 insertions(+), 230 deletions(-) create mode 100644 netwerk/base/public/nsIRedirectResultListener.idl create mode 100644 netwerk/protocol/http/HttpChannelParentListener.cpp create mode 100644 netwerk/protocol/http/HttpChannelParentListener.h create mode 100644 netwerk/test/unit_ipc/test_event_sink_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect_canceled_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect_failure_wrap.js create mode 100644 netwerk/test/unit_ipc/test_redirect_passing_wrap.js diff --git a/netwerk/base/public/Makefile.in b/netwerk/base/public/Makefile.in index 0ffd4a73c099..191f97869823 100644 --- a/netwerk/base/public/Makefile.in +++ b/netwerk/base/public/Makefile.in @@ -139,6 +139,7 @@ XPIDLSRCS = \ nsIRandomGenerator.idl \ nsIURIWithPrincipal.idl \ nsIURIClassifier.idl \ + nsIRedirectResultListener.idl \ $(NULL) EXPORTS = \ diff --git a/netwerk/base/public/nsIRedirectResultListener.idl b/netwerk/base/public/nsIRedirectResultListener.idl new file mode 100644 index 000000000000..26697cef4ca5 --- /dev/null +++ b/netwerk/base/public/nsIRedirectResultListener.idl @@ -0,0 +1,55 @@ +/* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * + * ***** 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 nsIApplicationCache.idl. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Honza Bambas <honzab@firemni.cz> + * + * 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 ***** */ + +#include "nsISupports.idl" + +[scriptable, uuid(85cd2640-e91e-41ac-bdca-1dbf10dc131e)] +interface nsIRedirectResultListener : nsISupports +{ + /** + * When an HTTP redirect has been processed (either successfully or not) + * nsIHttpChannel will call this function if its callbacks implement this + * interface. + * + * @param proceeding + * Indicated whether the redirect will be proceeding, or not (i.e. + * has been canceled, or failed). + */ + void onRedirectResult(in PRBool proceeding); +}; diff --git a/netwerk/ipc/NeckoChild.cpp b/netwerk/ipc/NeckoChild.cpp index 1b88f69ab32d..14612b3a0ff4 100644 --- a/netwerk/ipc/NeckoChild.cpp +++ b/netwerk/ipc/NeckoChild.cpp @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -87,11 +88,17 @@ void NeckoChild::DestroyNeckoChild() } PHttpChannelChild* -NeckoChild::AllocPHttpChannel(PBrowserChild* iframeEmbedding) +NeckoChild::AllocPHttpChannel(PBrowserChild* browser) { - // We don't allocate here: see HttpChannelChild::AsyncOpen() - NS_RUNTIMEABORT("AllocPHttpChannel should not be called"); - return nsnull; + // This constructor is only used when PHttpChannel is constructed by + // the parent process, e.g. during a redirect. (Normally HttpChannelChild is + // created by nsHttpHandler::NewProxiedChannel(), and then creates the + // PHttpChannel in HttpChannelChild::AsyncOpen().) + + // No need to store PBrowser. It is only needed by the parent. + HttpChannelChild* httpChannel = new HttpChannelChild(); + httpChannel->AddIPDLReference(); + return httpChannel; } bool diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index c896653c1422..e5c1d2daecbd 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -58,9 +58,9 @@ NeckoParent::~NeckoParent() } PHttpChannelParent* -NeckoParent::AllocPHttpChannel(PBrowserParent* iframeEmbedding) +NeckoParent::AllocPHttpChannel(PBrowserParent* browser) { - HttpChannelParent *p = new HttpChannelParent(iframeEmbedding); + HttpChannelParent *p = new HttpChannelParent(browser); p->AddRef(); return p; } diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index 55438f7b20aa..23a1f51f4a0b 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -56,7 +56,7 @@ public: virtual ~NeckoParent(); protected: - virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent* iframeEmbedding); + virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent* browser); virtual bool DeallocPHttpChannel(PHttpChannelParent*); virtual PCookieServiceParent* AllocPCookieService(); virtual bool DeallocPCookieService(PCookieServiceParent*); diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index e68f38f4db66..b308e1fe0b29 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -57,10 +57,12 @@ sync protocol PNecko parent: __delete__(); - PHttpChannel(nullable PBrowser iframeEmbedding); PCookieService(); HTMLDNSPrefetch(nsString hostname, PRUint16 flags); + +both: + PHttpChannel(nullable PBrowser browser); }; diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index 5d1df25c4c40..c3fd73f2f5ad 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -46,6 +46,12 @@ #include "nsMimeTypes.h" #include "nsNetUtil.h" +#include "nsICachingChannel.h" +#include "nsISeekableStream.h" +#include "nsIEncodedChannel.h" +#include "nsIResumableChannel.h" +#include "nsIApplicationCacheChannel.h" + namespace mozilla { namespace net { @@ -62,6 +68,9 @@ HttpBaseChannel::HttpBaseChannel() , mAllowPipelining(PR_TRUE) , mForceAllowThirdPartyCookie(PR_FALSE) , mUploadStreamHasHeaders(PR_FALSE) + , mInheritApplicationCache(PR_TRUE) + , mChooseApplicationCache(PR_FALSE) + , mLoadedFromApplicationCache(PR_FALSE) { LOG(("Creating HttpBaseChannel @%x\n", this)); @@ -980,6 +989,135 @@ HttpBaseChannel::AddCookiesToRequest() SetRequestHeader(nsDependentCString(nsHttp::Cookie), cookie, PR_FALSE); } +static PLDHashOperator +CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure) +{ + nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*> + (aClosure); + bag->SetProperty(aKey, aData); + return PL_DHASH_NEXT; +} + +nsresult +HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, + nsIChannel *newChannel, + PRBool preserveMethod) +{ + LOG(("HttpBaseChannel::SetupReplacementChannel " + "[this=%p newChannel=%p preserveMethod=%d]", + this, newChannel, preserveMethod)); + PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE; + // if the original channel was using SSL and this channel is not using + // SSL, then no need to inhibit persistent caching. however, if the + // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING + // set, then allow the flag to apply to the redirected channel as well. + // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, + // we only need to check if the original channel was using SSL. + if (mConnectionInfo->UsingSSL()) + newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; + + // Do not pass along LOAD_CHECK_OFFLINE_CACHE + newLoadFlags &= ~nsICachingChannel::LOAD_CHECK_OFFLINE_CACHE; + + newChannel->SetLoadGroup(mLoadGroup); + newChannel->SetNotificationCallbacks(mCallbacks); + newChannel->SetLoadFlags(newLoadFlags); + + nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); + if (!httpChannel) + return NS_OK; // no other options to set + + if (preserveMethod) { + nsCOMPtr<nsIUploadChannel> uploadChannel = + do_QueryInterface(httpChannel); + nsCOMPtr<nsIUploadChannel2> uploadChannel2 = + do_QueryInterface(httpChannel); + if (mUploadStream && (uploadChannel2 || uploadChannel)) { + // rewind upload stream + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); + if (seekable) + seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + + // replicate original call to SetUploadStream... + if (uploadChannel2) { + const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type); + if (!ctype) + ctype = ""; + const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length); + PRInt64 len = clen ? nsCRT::atoll(clen) : -1; + uploadChannel2->ExplicitSetUploadStream( + mUploadStream, + nsDependentCString(ctype), + len, + nsDependentCString(mRequestHead.Method()), + mUploadStreamHasHeaders); + } else { + if (mUploadStreamHasHeaders) { + uploadChannel->SetUploadStream(mUploadStream, EmptyCString(), + -1); + } else { + const char *ctype = + mRequestHead.PeekHeader(nsHttp::Content_Type); + const char *clen = + mRequestHead.PeekHeader(nsHttp::Content_Length); + if (!ctype) { + ctype = "application/octet-stream"; + } + if (clen) { + uploadChannel->SetUploadStream(mUploadStream, + nsDependentCString(ctype), + atoi(clen)); + } + } + } + } + // since preserveMethod is true, we need to ensure that the appropriate + // request method gets set on the channel, regardless of whether or not + // we set the upload stream above. This means SetRequestMethod() will + // be called twice if ExplicitSetUploadStream() gets called above. + + httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method())); + } + // convey the referrer if one was used for this channel to the next one + if (mReferrer) + httpChannel->SetReferrer(mReferrer); + // convey the mAllowPipelining flag + httpChannel->SetAllowPipelining(mAllowPipelining); + // convey the new redirection limit + httpChannel->SetRedirectionLimit(mRedirectionLimit - 1); + + nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel); + if (httpInternal) { + // convey the mForceAllowThirdPartyCookie flag + httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie); + + // update the DocumentURI indicator since we are being redirected. + // if this was a top-level document channel, then the new channel + // should have its mDocumentURI point to newURI; otherwise, we + // just need to pass along our mDocumentURI to the new channel. + if (newURI && (mURI == mDocumentURI)) + httpInternal->SetDocumentURI(newURI); + else + httpInternal->SetDocumentURI(mDocumentURI); + } + + // transfer application cache information + nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = + do_QueryInterface(newChannel); + if (appCacheChannel) { + appCacheChannel->SetApplicationCache(mApplicationCache); + appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache); + // We purposely avoid transfering mChooseApplicationCache. + } + + // transfer any properties + nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel)); + if (bag) + mPropertyHash.EnumerateRead(CopyProperties, bag.get()); + + return NS_OK; +} + //------------------------------------------------------------------------------ } // namespace net diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index 1018c1a8a166..acf6dbee182e 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -55,6 +55,7 @@ #include "nsIProgressEventSink.h" #include "nsIURI.h" #include "nsISupportsPriority.h" +#include "nsIApplicationCache.h" #define DIE_WITH_ASYNC_OPEN_MSG() \ do { \ @@ -169,6 +170,9 @@ public: protected: void AddCookiesToRequest(); + virtual nsresult SetupReplacementChannel(nsIURI *, + nsIChannel *, + PRBool preserveMethod); // Helper function to simplify getting notification callbacks. template <class T> @@ -189,6 +193,7 @@ protected: nsCOMPtr<nsIInterfaceRequestor> mCallbacks; nsCOMPtr<nsIProgressEventSink> mProgressSink; nsCOMPtr<nsIURI> mReferrer; + nsCOMPtr<nsIApplicationCache> mApplicationCache; nsHttpRequestHead mRequestHead; nsCOMPtr<nsIInputStream> mUploadStream; @@ -206,13 +211,16 @@ protected: PRUint8 mCaps; PRUint8 mRedirectionLimit; - PRUint8 mCanceled : 1; - PRUint8 mIsPending : 1; - PRUint8 mWasOpened : 1; - PRUint8 mResponseHeadersModified : 1; - PRUint8 mAllowPipelining : 1; - PRUint8 mForceAllowThirdPartyCookie : 1; - PRUint8 mUploadStreamHasHeaders : 1; + PRUint32 mCanceled : 1; + PRUint32 mIsPending : 1; + PRUint32 mWasOpened : 1; + PRUint32 mResponseHeadersModified : 1; + PRUint32 mAllowPipelining : 1; + PRUint32 mForceAllowThirdPartyCookie : 1; + PRUint32 mUploadStreamHasHeaders : 1; + PRUint32 mInheritApplicationCache : 1; + PRUint32 mChooseApplicationCache : 1; + PRUint32 mLoadedFromApplicationCache : 1; }; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index edf67be49a4d..2aeb10e0ace3 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -24,6 +24,7 @@ * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> * Daniel Witte <dwitte@mozilla.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -75,7 +76,11 @@ private: HttpChannelChild *mChannel; }; -// C++ file contents + +//----------------------------------------------------------------------------- +// HttpChannelChild +//----------------------------------------------------------------------------- + HttpChannelChild::HttpChannelChild() : mIsFromCache(PR_FALSE) , mCacheEntryAvailable(PR_FALSE) @@ -113,6 +118,7 @@ NS_INTERFACE_MAP_BEGIN(HttpChannelChild) NS_INTERFACE_MAP_ENTRY(nsITraceableChannel) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer) NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel) + NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback) NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel) //----------------------------------------------------------------------------- @@ -473,6 +479,160 @@ HttpChannelChild::OnStatus(const nsresult& status, } } +class Redirect1Event : public ChildChannelEvent +{ + public: + Redirect1Event(HttpChannelChild* child, + PHttpChannelChild* newChannel, + const IPC::URI& newURI, + const PRUint32& redirectFlags, + const nsHttpResponseHead& responseHead) + : mChild(child) + , mNewChannel(newChannel) + , mNewURI(newURI) + , mRedirectFlags(redirectFlags) + , mResponseHead(responseHead) {} + + void Run() + { + mChild->Redirect1Begin(mNewChannel, mNewURI, mRedirectFlags, + mResponseHead); + } + private: + HttpChannelChild* mChild; + PHttpChannelChild* mNewChannel; + IPC::URI mNewURI; + PRUint32 mRedirectFlags; + nsHttpResponseHead mResponseHead; +}; + +bool +HttpChannelChild::RecvRedirect1Begin(PHttpChannelChild* newChannel, + const IPC::URI& newURI, + const PRUint32& redirectFlags, + const nsHttpResponseHead& responseHead) +{ + if (ShouldEnqueue()) { + EnqueueEvent(new Redirect1Event(this, newChannel, newURI, redirectFlags, + responseHead)); + } else { + Redirect1Begin(newChannel, newURI, redirectFlags, responseHead); + } + return true; +} + +void +HttpChannelChild::Redirect1Begin(PHttpChannelChild* newChannel, + const IPC::URI& newURI, + const PRUint32& redirectFlags, + const nsHttpResponseHead& responseHead) +{ + HttpChannelChild* + newHttpChannelChild = static_cast<HttpChannelChild*>(newChannel); + nsCOMPtr<nsIURI> uri(newURI); + + nsresult rv = + newHttpChannelChild->HttpBaseChannel::Init(uri, mCaps, + mConnectionInfo->ProxyInfo()); + if (NS_FAILED(rv)) + return; // TODO Bug 536317 + + // We won't get OnStartRequest, set cookies here. + mResponseHead = new nsHttpResponseHead(responseHead); + SetCookie(mResponseHead->PeekHeader(nsHttp::Set_Cookie)); + + PRBool preserveMethod = (mResponseHead->Status() == 307); + rv = SetupReplacementChannel(uri, newHttpChannelChild, preserveMethod); + if (NS_FAILED(rv)) + return; // TODO Bug 536317 + + mRedirectChannelChild = newHttpChannelChild; + + nsresult result = gHttpHandler->AsyncOnChannelRedirect(this, + newHttpChannelChild, + redirectFlags); + if (NS_FAILED(result)) + OnRedirectVerifyCallback(result); +} + +class Redirect3Event : public ChildChannelEvent +{ + public: + Redirect3Event(HttpChannelChild* child) : mChild(child) {} + void Run() { mChild->Redirect3Complete(); } + private: + HttpChannelChild* mChild; +}; + +bool +HttpChannelChild::RecvRedirect3Complete() +{ + if (ShouldEnqueue()) { + EnqueueEvent(new Redirect3Event(this)); + } else { + Redirect3Complete(); + } + return true; +} + +void +HttpChannelChild::Redirect3Complete() +{ + nsresult rv; + + // Redirecting to new channel: shut this down and init new channel + if (mLoadGroup) + mLoadGroup->RemoveRequest(this, nsnull, NS_BINDING_ABORTED); + + // Chrome channel has been AsyncOpen'd. Reflect this in child. + rv = mRedirectChannelChild->CompleteRedirectSetup(mListener, + mListenerContext); + if (NS_FAILED(rv)) + ; // TODO Cancel: Bug 536317 +} + +nsresult +HttpChannelChild::CompleteRedirectSetup(nsIStreamListener *listener, + nsISupports *aContext) +{ + LOG(("HttpChannelChild::FinishRedirectSetup [this=%x]\n", this)); + + NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS); + NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED); + + // notify "http-on-modify-request" observers + gHttpHandler->OnModifyRequest(this); + + mIsPending = PR_TRUE; + mWasOpened = PR_TRUE; + mListener = listener; + mListenerContext = aContext; + + // add ourselves to the load group. + if (mLoadGroup) + mLoadGroup->AddRequest(this, nsnull); + + // TODO: may have been canceled by on-modify-request observers: bug 536317 + + mState = HCC_OPENED; + return NS_OK; +} + +//----------------------------------------------------------------------------- +// HttpChannelChild::nsIAsyncVerifyRedirectCallback +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelChild::OnRedirectVerifyCallback(nsresult result) +{ + // Cookies may have been changed by redirect observers + mRedirectChannelChild->AddCookiesToRequest(); + // Must not be called until after redirect observers called. + mRedirectChannelChild->SetOriginalURI(mRedirectOriginalURI); + + return SendRedirect2Result(result, mRedirectChannelChild->mRequestHeaders); +} + //----------------------------------------------------------------------------- // HttpChannelChild::nsIRequest //----------------------------------------------------------------------------- @@ -600,16 +760,16 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) tabChild = static_cast<mozilla::dom::TabChild*>(iTabChild.get()); } - // The socket transport layer in the chrome process now has a logical ref to - // us, until either OnStopRequest or OnRedirect is called. + // The socket transport in the chrome process now holds a logical ref to us + // until OnStopRequest, or we do a redirect, or we hit an IPDL error. AddIPDLReference(); gNeckoChild->SendPHttpChannelConstructor(this, tabChild); - SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI), IPC::URI(mDocumentURI), - IPC::URI(mReferrer), mLoadFlags, mRequestHeaders, - mRequestHead.Method(), uploadStreamData, - uploadStreamInfo, mPriority, mRedirectionLimit, + SendAsyncOpen(IPC::URI(mURI), IPC::URI(mOriginalURI), + IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags, + mRequestHeaders, mRequestHead.Method(), uploadStreamData, + uploadStreamInfo, mPriority, mRedirectionLimit, mAllowPipelining, mForceAllowThirdPartyCookie); mState = HCC_OPENED; @@ -784,7 +944,8 @@ HttpChannelChild::GetApplicationCache(nsIApplicationCache **aApplicationCache) NS_IMETHODIMP HttpChannelChild::SetApplicationCache(nsIApplicationCache *aApplicationCache) { - DROP_DEAD(); + // FIXME: redirects call. so stub OK for now. Fix in bug 536295. + return NS_ERROR_NOT_IMPLEMENTED; } //----------------------------------------------------------------------------- @@ -825,5 +986,6 @@ HttpChannelChild::SetChooseApplicationCache(PRBool aChooseApplicationCache) //------------------------------------------------------------------------------ + }} // mozilla::net diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index faf30eb0466f..8cb60fb9e73a 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -24,6 +24,7 @@ * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> * Daniel Witte <dwitte@mozilla.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -58,6 +59,7 @@ #include "nsIResumableChannel.h" #include "nsIProxiedChannel.h" #include "nsITraceableChannel.h" +#include "nsIAsyncVerifyRedirectCallback.h" namespace mozilla { namespace net { @@ -73,7 +75,6 @@ enum HttpChannelChildState { HCC_ONSTOP }; -// Header file contents class HttpChannelChild : public PHttpChannelChild , public HttpBaseChannel , public nsICacheInfoChannel @@ -82,6 +83,7 @@ class HttpChannelChild : public PHttpChannelChild , public nsIProxiedChannel , public nsITraceableChannel , public nsIApplicationCacheChannel + , public nsIAsyncVerifyRedirectCallback { public: NS_DECL_ISUPPORTS_INHERITED @@ -92,6 +94,7 @@ public: NS_DECL_NSITRACEABLECHANNEL NS_DECL_NSIAPPLICATIONCACHECONTAINER NS_DECL_NSIAPPLICATIONCACHECHANNEL + NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK HttpChannelChild(); virtual ~HttpChannelChild(); @@ -114,6 +117,10 @@ public: // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); + // Final setup when redirect has proceeded successfully in chrome + nsresult CompleteRedirectSetup(nsIStreamListener *listener, + nsISupports *aContext); + // IPDL holds a reference while the PHttpChannel protocol is live (starting at // AsyncOpen, and ending at either OnStopRequest or any IPDL error, either of // which call NeckoChild::DeallocPHttpChannel()). @@ -133,9 +140,16 @@ protected: bool RecvOnStopRequest(const nsresult& statusCode); bool RecvOnProgress(const PRUint64& progress, const PRUint64& progressMax); bool RecvOnStatus(const nsresult& status, const nsString& statusArg); + bool RecvRedirect1Begin(PHttpChannelChild* newChannel, + const URI& newURI, + const PRUint32& redirectFlags, + const nsHttpResponseHead& responseHead); + bool RecvRedirect3Complete(); private: RequestHeaderTuples mRequestHeaders; + nsRefPtr<HttpChannelChild> mRedirectChannelChild; + nsCOMPtr<nsIURI> mRedirectOriginalURI; PRPackedBool mIsFromCache; PRPackedBool mCacheEntryAvailable; @@ -175,6 +189,10 @@ private: void OnStopRequest(const nsresult& statusCode); void OnProgress(const PRUint64& progress, const PRUint64& progressMax); void OnStatus(const nsresult& status, const nsString& statusArg); + void Redirect1Begin(PHttpChannelChild* newChannel, const URI& newURI, + const PRUint32& redirectFlags, + const nsHttpResponseHead& responseHead); + void Redirect3Complete(); friend class AutoEventEnqueuer; friend class StartRequestEvent; @@ -182,6 +200,8 @@ private: friend class DataAvailableEvent; friend class ProgressEvent; friend class StatusEvent; + friend class Redirect1Event; + friend class Redirect3Event; }; //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 57a9f16d1c1b..7e11fd7cb25f 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -40,6 +41,8 @@ #include "mozilla/net/HttpChannelParent.h" #include "mozilla/dom/TabParent.h" +#include "mozilla/net/NeckoParent.h" +#include "HttpChannelParentListener.h" #include "nsHttpChannel.h" #include "nsHttpHandler.h" #include "nsNetUtil.h" @@ -52,7 +55,6 @@ namespace mozilla { namespace net { -// C++ file contents HttpChannelParent::HttpChannelParent(PBrowserParent* iframeEmbedding) : mIPCClosed(false) { @@ -82,11 +84,8 @@ HttpChannelParent::ActorDestroy(ActorDestroyReason why) // HttpChannelParent::nsISupports //----------------------------------------------------------------------------- -NS_IMPL_ISUPPORTS4(HttpChannelParent, - nsIRequestObserver, - nsIStreamListener, - nsIInterfaceRequestor, - nsIProgressEventSink); +NS_IMPL_ISUPPORTS1(HttpChannelParent, + nsIProgressEventSink) //----------------------------------------------------------------------------- // HttpChannelParent::PHttpChannelParent @@ -145,7 +144,9 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, requestHeaders[i].mMerge); } - httpChan->SetNotificationCallbacks(this); + mChannelListener = new HttpChannelParentListener(this); + + httpChan->SetNotificationCallbacks(mChannelListener); httpChan->SetRequestMethod(nsDependentCString(requestMethod.get())); @@ -167,7 +168,7 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, httpChan->SetAllowPipelining(allowPipelining); httpChan->SetForceAllowThirdPartyCookie(forceAllowThirdPartyCookie); - rv = httpChan->AsyncOpen(this, nsnull); + rv = httpChan->AsyncOpen(mChannelListener, nsnull); if (NS_FAILED(rv)) return false; // TODO: cancel request (bug 536317), return true @@ -191,15 +192,28 @@ HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset) return true; } +bool +HttpChannelParent::RecvRedirect2Result(const nsresult& result, + const RequestHeaderTuples& changedHeaders) +{ + if (mChannelListener) + mChannelListener->OnContentRedirectResultReceived(result, changedHeaders); + return true; +} + //----------------------------------------------------------------------------- -// HttpChannelParent::nsIRequestObserver +// nsIRequestObserver and nsIStreamListener methods equivalents //----------------------------------------------------------------------------- -NS_IMETHODIMP +nsresult HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) { LOG(("HttpChannelParent::OnStartRequest [this=%x]\n", this)); + // We need this member only to call OnContentRedirectResultReceived on it, + // that will for sure not happen when we get here. Throw it away ASAP. + mChannelListener = nsnull; + nsHttpChannel *chan = static_cast<nsHttpChannel *>(aRequest); nsHttpResponseHead *responseHead = chan->GetResponseHead(); @@ -224,7 +238,7 @@ HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) return NS_OK; } -NS_IMETHODIMP +nsresult HttpChannelParent::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext, nsresult aStatusCode) @@ -237,11 +251,7 @@ HttpChannelParent::OnStopRequest(nsIRequest *aRequest, return NS_OK; } -//----------------------------------------------------------------------------- -// HttpChannelParent::nsIStreamListener -//----------------------------------------------------------------------------- - -NS_IMETHODIMP +nsresult HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, nsISupports *aContext, nsIInputStream *aInputStream, @@ -267,63 +277,10 @@ HttpChannelParent::OnDataAvailable(nsIRequest *aRequest, return NS_OK; } -//----------------------------------------------------------------------------- -// HttpChannelParent::nsIInterfaceRequestor -//----------------------------------------------------------------------------- - -NS_IMETHODIMP -HttpChannelParent::GetInterface(const nsIID& aIID, void **result) -{ - if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) { - if (!mTabParent) - return NS_NOINTERFACE; - return mTabParent->QueryInterface(aIID, result); - } - - // TODO: 575494: once we're confident we're handling all needed interfaces, - // remove all code below and simply "return QueryInterface(aIID, result)" - if (// Known interface calls: - - // FIXME: HTTP Authorization (bug 537782): - // nsHttpChannel first tries to get this as an nsIAuthPromptProvider; if that - // fails, it tries as an nsIAuthPrompt2, and if that fails, an nsIAuthPrompt. - // See nsHttpChannel::GetAuthPrompt(). So if we can return any one of these, - // HTTP auth should be all set. The other two if checks can be eventually - // deleted. - aIID.Equals(NS_GET_IID(nsIAuthPrompt2)) || - aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || - // FIXME: redirects (bug 536294): - // The likely solution here is for this class to implement nsIChannelEventSink - // and nsIHttpEventSink (and forward calls to any real sinks in the child), in - // which case QueryInterface() will do the work here and these if statements - // can be eventually discarded. - aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || - aIID.Equals(NS_GET_IID(nsIHttpEventSink)) || - // FIXME: application cache (bug 536295): - aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer)) || - aIID.Equals(NS_GET_IID(nsIProgressEventSink)) || - // FIXME: bug 561830: when fixed, we shouldn't be asked for this interface - aIID.Equals(NS_GET_IID(nsIDocShellTreeItem)) || - // Let this return NS_ERROR_NO_INTERFACE: it's OK to not provide it. - aIID.Equals(NS_GET_IID(nsIBadCertListener2))) - { - return QueryInterface(aIID, result); - } else { - nsPrintfCString msg(2000, - "HttpChannelParent::GetInterface: interface UUID=%s not yet supported! " - "Use 'grep -ri UUID <mozilla_src>' to find the name of the interface, " - "check http://tinyurl.com/255ojvu to see if a bug has already been " - "filed, and if not, add one and make it block bug 516730. Thanks!", - aIID.ToString()); - NECKO_MAYBE_ABORT(msg); - return NS_NOINTERFACE; - } -} - //----------------------------------------------------------------------------- // HttpChannelParent::nsIProgressEventSink //----------------------------------------------------------------------------- - + NS_IMETHODIMP HttpChannelParent::OnProgress(nsIRequest *aRequest, nsISupports *aContext, @@ -346,6 +303,5 @@ HttpChannelParent::OnStatus(nsIRequest *aRequest, return NS_OK; } - }} // mozilla::net diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index d64277a9464d..95b2df3a436c 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -45,8 +46,6 @@ #include "mozilla/dom/PBrowserParent.h" #include "mozilla/net/PHttpChannelParent.h" #include "mozilla/net/NeckoCommon.h" -#include "nsIStreamListener.h" -#include "nsIInterfaceRequestor.h" #include "nsIProgressEventSink.h" #include "nsITabParent.h" @@ -57,19 +56,27 @@ class nsICacheEntryDescriptor; namespace mozilla { namespace net { -// Header file contents +class HttpChannelParentListener; + class HttpChannelParent : public PHttpChannelParent - , public nsIStreamListener - , public nsIInterfaceRequestor , public nsIProgressEventSink { public: NS_DECL_ISUPPORTS - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - NS_DECL_NSIINTERFACEREQUESTOR NS_DECL_NSIPROGRESSEVENTSINK + // Make these non-virtual for a little performance benefit + nsresult OnStartRequest(nsIRequest *aRequest, + nsISupports *aContext); + nsresult OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode); + nsresult OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + PRUint32 aOffset, + PRUint32 aCount); + HttpChannelParent(PBrowserParent* iframeEmbedding); virtual ~HttpChannelParent(); @@ -90,12 +97,18 @@ protected: virtual bool RecvSetPriority(const PRUint16& priority); virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset); + virtual bool RecvRedirect2Result(const nsresult& result, + const RequestHeaderTuples& changedHeaders); virtual void ActorDestroy(ActorDestroyReason why); -private: +protected: + friend class mozilla::net::HttpChannelParentListener; nsCOMPtr<nsITabParent> mTabParent; + +private: nsCOMPtr<nsIChannel> mChannel; + nsRefPtr<HttpChannelParentListener> mChannelListener; nsCOMPtr<nsICacheEntryDescriptor> mCacheDescriptor; bool mIPCClosed; // PHttpChannel actor has been Closed() }; diff --git a/netwerk/protocol/http/HttpChannelParentListener.cpp b/netwerk/protocol/http/HttpChannelParentListener.cpp new file mode 100644 index 000000000000..24551b4a6bb7 --- /dev/null +++ b/netwerk/protocol/http/HttpChannelParentListener.cpp @@ -0,0 +1,289 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ + +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> + * + * 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 ***** */ + +#include "HttpChannelParentListener.h" +#include "mozilla/net/HttpChannelParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/net/NeckoParent.h" +#include "nsHttpChannel.h" +#include "nsHttpHandler.h" +#include "nsNetUtil.h" +#include "nsISupportsPriority.h" +#include "nsIAuthPromptProvider.h" +#include "nsIDocShellTreeItem.h" +#include "nsIBadCertListener2.h" +#include "nsICacheEntryDescriptor.h" +#include "nsSerializationHelper.h" +#include "nsISerializable.h" +#include "nsIAssociatedContentSecurity.h" +#include "nsISecureBrowserUI.h" + +namespace mozilla { +namespace net { + +HttpChannelParentListener::HttpChannelParentListener(HttpChannelParent* aInitialChannel) + : mActiveChannel(aInitialChannel) +{ +} + +HttpChannelParentListener::~HttpChannelParentListener() +{ +} + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsISupports +//----------------------------------------------------------------------------- + +NS_IMPL_ISUPPORTS5(HttpChannelParentListener, + nsIInterfaceRequestor, + nsIStreamListener, + nsIRequestObserver, + nsIChannelEventSink, + nsIRedirectResultListener) + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsIRequestObserver +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelParentListener::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext) +{ + if (!mActiveChannel) + return NS_ERROR_UNEXPECTED; + + LOG(("HttpChannelParentListener::OnStartRequest [this=%x]\n", this)); + return mActiveChannel->OnStartRequest(aRequest, aContext); +} + +NS_IMETHODIMP +HttpChannelParentListener::OnStopRequest(nsIRequest *aRequest, + nsISupports *aContext, + nsresult aStatusCode) +{ + if (!mActiveChannel) + return NS_ERROR_UNEXPECTED; + + LOG(("HttpChannelParentListener::OnStopRequest: [this=%x status=%ul]\n", + this, aStatusCode)); + nsresult rv = mActiveChannel->OnStopRequest(aRequest, aContext, aStatusCode); + + mActiveChannel = nsnull; + return rv; +} + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsIStreamListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelParentListener::OnDataAvailable(nsIRequest *aRequest, + nsISupports *aContext, + nsIInputStream *aInputStream, + PRUint32 aOffset, + PRUint32 aCount) +{ + if (!mActiveChannel) + return NS_ERROR_UNEXPECTED; + + LOG(("HttpChannelParentListener::OnDataAvailable [this=%x]\n", this)); + return mActiveChannel->OnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount); +} + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsIInterfaceRequestor +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelParentListener::GetInterface(const nsIID& aIID, void **result) +{ + if (aIID.Equals(NS_GET_IID(nsIAuthPromptProvider))) { + if (!mActiveChannel || !mActiveChannel->mTabParent) + return NS_NOINTERFACE; + return mActiveChannel->mTabParent->QueryInterface(aIID, result); + } + + if (aIID.Equals(NS_GET_IID(nsIProgressEventSink))) { + if (!mActiveChannel) + return NS_NOINTERFACE; + return mActiveChannel->QueryInterface(aIID, result); + } + + // TODO: 575494: once we're confident we're handling all needed interfaces, + // remove all code below and simply "return QueryInterface(aIID, result)" + if (// Known interface calls: + + // FIXME: HTTP Authorization (bug 537782): + // nsHttpChannel first tries to get this as an nsIAuthPromptProvider; if that + // fails, it tries as an nsIAuthPrompt2, and if that fails, an nsIAuthPrompt. + // See nsHttpChannel::GetAuthPrompt(). So if we can return any one of these, + // HTTP auth should be all set. The other two if checks can be eventually + // deleted. + aIID.Equals(NS_GET_IID(nsIAuthPrompt2)) || + aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || + // FIXME: redirects (bug 536294): + // The likely solution here is for this class to implement nsIChannelEventSink + // and nsIHttpEventSink (and forward calls to any real sinks in the child), in + // which case QueryInterface() will do the work here and these if statements + // can be eventually discarded. + aIID.Equals(NS_GET_IID(nsIChannelEventSink)) || + aIID.Equals(NS_GET_IID(nsIHttpEventSink)) || + aIID.Equals(NS_GET_IID(nsIRedirectResultListener)) || + // FIXME: application cache (bug 536295): + aIID.Equals(NS_GET_IID(nsIApplicationCacheContainer)) || + // FIXME: bug 561830: when fixed, we shouldn't be asked for this interface + aIID.Equals(NS_GET_IID(nsIDocShellTreeItem)) || + // Let this return NS_ERROR_NO_INTERFACE: it's OK to not provide it. + aIID.Equals(NS_GET_IID(nsIBadCertListener2))) + { + return QueryInterface(aIID, result); + } else { + nsPrintfCString msg(2000, + "HttpChannelParentListener::GetInterface: interface UUID=%s not yet supported! " + "Use 'grep -ri UUID <mozilla_src>' to find the name of the interface, " + "check http://tinyurl.com/255ojvu to see if a bug has already been " + "filed, and if not, add one and make it block bug 516730. Thanks!", + aIID.ToString()); + NECKO_MAYBE_ABORT(msg); + return NS_NOINTERFACE; + } +} + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsIChannelEventSink +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelParentListener::AsyncOnChannelRedirect( + nsIChannel *oldChannel, + nsIChannel *newChannel, + PRUint32 redirectFlags, + nsIAsyncVerifyRedirectCallback* callback) +{ + if (mActiveChannel->mIPCClosed) + return NS_BINDING_ABORTED; + + // Create new PHttpChannel + PBrowserParent* browser = mActiveChannel->mTabParent ? + static_cast<TabParent*>(mActiveChannel->mTabParent.get()) : nsnull; + mRedirectChannel = static_cast<HttpChannelParent *> + (mActiveChannel->Manager()->SendPHttpChannelConstructor(browser)); + + // Join it with the correct channel + mRedirectChannel->mChannel = newChannel; + + // Let the new channel also keep the wrapper in case we get another redirect + // response, it wouldn't be able to send back the redirect result. + mRedirectChannel->mChannelListener = this; + + // And finally, let the content process decide to redirect or not. + mRedirectCallback = callback; + + nsCOMPtr<nsIURI> newURI; + newChannel->GetURI(getter_AddRefs(newURI)); + + nsHttpChannel *oldHttpChannel = static_cast<nsHttpChannel *>(oldChannel); + // TODO: check mActiveChannel->mIPCClosed and return val from Send function + mActiveChannel->SendRedirect1Begin(mRedirectChannel, + IPC::URI(newURI), + redirectFlags, + *oldHttpChannel->GetResponseHead()); + + // mActiveChannel gets the response in RecvRedirect2Result and forwards it + // to this wrapper through OnContentRedirectResultReceived + + return NS_OK; +} + +void +HttpChannelParentListener::OnContentRedirectResultReceived( + const nsresult result, + const RequestHeaderTuples& changedHeaders) +{ + nsHttpChannel* newHttpChannel = + static_cast<nsHttpChannel*>(mRedirectChannel->mChannel.get()); + + if (NS_SUCCEEDED(result)) { + for (PRUint32 i = 0; i < changedHeaders.Length(); i++) { + newHttpChannel->SetRequestHeader(changedHeaders[i].mHeader, + changedHeaders[i].mValue, + changedHeaders[i].mMerge); + } + } + + mRedirectCallback->OnRedirectVerifyCallback(result); + mRedirectCallback = nsnull; +} + +//----------------------------------------------------------------------------- +// HttpChannelParentListener::nsIRedirectResultListener +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpChannelParentListener::OnRedirectResult(PRBool succeeded) +{ + if (!mRedirectChannel) { + // Redirect might get canceled before we got AsyncOnChannelRedirect + return NS_OK; + } + + if (succeeded && !mActiveChannel->mIPCClosed) { + // TODO: check return value: assume child dead if failed + mActiveChannel->SendRedirect3Complete(); + } + + HttpChannelParent* channelToDelete; + if (succeeded) { + // Switch to redirect channel and delete the old one. + channelToDelete = mActiveChannel; + mActiveChannel = mRedirectChannel; + } else { + // Delete the redirect target channel: continue using old channel + channelToDelete = mRedirectChannel; + } + + if (!channelToDelete->mIPCClosed) + HttpChannelParent::Send__delete__(channelToDelete); + mRedirectChannel = nsnull; + + return NS_OK; +} + +}} // mozilla::net diff --git a/netwerk/protocol/http/HttpChannelParentListener.h b/netwerk/protocol/http/HttpChannelParentListener.h new file mode 100644 index 000000000000..ee678362d22e --- /dev/null +++ b/netwerk/protocol/http/HttpChannelParentListener.h @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ + +/* ***** 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.org code. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> + * + * 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 ***** */ + +#ifndef mozilla_net_HttpChannelCallbackWrapper_h +#define mozilla_net_HttpChannelCallbackWrapper_h + +#include "nsHttp.h" +#include "mozilla/net/NeckoCommon.h" +#include "PHttpChannelParams.h" +#include "nsIStreamListener.h" +#include "nsIInterfaceRequestor.h" +#include "nsIChannelEventSink.h" +#include "nsIRedirectResultListener.h" +#include "nsIProgressEventSink.h" + +using namespace mozilla::dom; + +class nsICacheEntryDescriptor; + +namespace mozilla { +namespace net { + +class HttpChannelParent; + +class HttpChannelParentListener : public nsIInterfaceRequestor + , public nsIChannelEventSink + , public nsIRedirectResultListener + , public nsIStreamListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIINTERFACEREQUESTOR + NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIREDIRECTRESULTLISTENER + NS_DECL_NSIREQUESTOBSERVER + NS_DECL_NSISTREAMLISTENER + + HttpChannelParentListener(HttpChannelParent* aInitialChannel); + virtual ~HttpChannelParentListener(); + +protected: + friend class HttpChannelParent; + void OnContentRedirectResultReceived( + const nsresult result, + const RequestHeaderTuples& changedHeaders); + +private: + nsRefPtr<HttpChannelParent> mActiveChannel; + nsRefPtr<HttpChannelParent> mRedirectChannel; + nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback; +}; + +} // namespace net +} // namespace mozilla + +#endif // mozilla_net_HttpChannelParent_h diff --git a/netwerk/protocol/http/Makefile.in b/netwerk/protocol/http/Makefile.in index 4258bc917a1a..e5f1a2b0c37d 100644 --- a/netwerk/protocol/http/Makefile.in +++ b/netwerk/protocol/http/Makefile.in @@ -114,6 +114,7 @@ ifdef MOZ_IPC CPPSRCS += \ HttpChannelParent.cpp \ HttpChannelChild.cpp \ + HttpChannelParentListener.cpp \ $(NULL) endif diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index 77ebf1827243..d3f42539d511 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -23,6 +23,7 @@ * * Contributor(s): * Jason Duell <jduell.mcbugs@gmail.com> + * Honza Bambas <honzab@firemni.cz> * * 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 @@ -57,8 +58,6 @@ protocol PHttpChannel manager PNecko; parent: - __delete__(); - AsyncOpen(URI uri, // - TODO: bug 571161: unclear if any HTTP channel clients ever // set originalURI != uri (about:credits?); also not clear if @@ -80,6 +79,9 @@ parent: SetCacheTokenCachedCharset(nsCString charset); + // Reports approval/veto of redirect by child process redirect observers + Redirect2Result(nsresult result, RequestHeaderTuples changedHeaders); + child: OnStartRequest(nsHttpResponseHead responseHead, PRBool useResponseHead, @@ -97,6 +99,18 @@ child: OnProgress(PRUint64 progress, PRUint64 progressMax); OnStatus(nsresult status, nsString statusArg); + + // Called to initiate content channel redirect, starts talking to sinks + // on the content process and reports result via OnRedirect2Result above + Redirect1Begin(PHttpChannel newChannel, + URI newUri, + PRUint32 redirectFlags, + nsHttpResponseHead responseHead); + // Called if redirect successful so that child can complete setup. + Redirect3Complete(); + +both: + __delete__(); }; diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index a5d7ac77ae68..65d05117a3f8 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -69,6 +69,7 @@ #include "nsICacheService.h" #include "nsDNSPrefetch.h" #include "nsChannelClassifier.h" +#include "nsIRedirectResultListener.h" // True if the local cache should be bypassed when processing a request. #define BYPASS_LOCAL_CACHE(loadFlags) \ @@ -77,6 +78,34 @@ static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID); +class AutoRedirectVetoNotifier +{ +public: + AutoRedirectVetoNotifier(nsHttpChannel* channel) : mChannel(channel) {} + ~AutoRedirectVetoNotifier() {ReportRedirectResult(false);} + void DontReport() {mChannel = nsnull;} + void RedirectSucceeded() {ReportRedirectResult(true);} + +private: + nsHttpChannel* mChannel; + void ReportRedirectResult(bool succeeded); +}; + +void +AutoRedirectVetoNotifier::ReportRedirectResult(bool succeeded) +{ + if (!mChannel) + return; + + nsCOMPtr<nsIRedirectResultListener> vetoHook; + NS_QueryNotificationCallbacks(mChannel, + NS_GET_IID(nsIRedirectResultListener), + getter_AddRefs(vetoHook)); + mChannel = nsnull; + if (vetoHook) + vetoHook->OnRedirectResult(succeeded); +} + //----------------------------------------------------------------------------- // nsHttpChannel <public> //----------------------------------------------------------------------------- @@ -99,9 +128,6 @@ nsHttpChannel::nsHttpChannel() , mCacheForOfflineUse(PR_FALSE) , mCachingOpportunistically(PR_FALSE) , mFallbackChannel(PR_FALSE) - , mInheritApplicationCache(PR_TRUE) - , mChooseApplicationCache(PR_FALSE) - , mLoadedFromApplicationCache(PR_FALSE) , mTracingEnabled(PR_TRUE) , mCustomConditionalRequest(PR_FALSE) , mFallingBack(PR_FALSE) @@ -1230,6 +1256,7 @@ nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) rv = WaitForRedirectCallback(); if (NS_FAILED(rv)) { + AutoRedirectVetoNotifier notifier(this); PopRedirectAsyncFunc(&nsHttpChannel::ContinueDoReplaceWithProxy); mRedirectChannel = nsnull; } @@ -1240,6 +1267,8 @@ nsHttpChannel::AsyncDoReplaceWithProxy(nsIProxyInfo* pi) nsresult nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) { + AutoRedirectVetoNotifier notifier(this); + if (NS_FAILED(rv)) return rv; @@ -1256,6 +1285,8 @@ nsHttpChannel::ContinueDoReplaceWithProxy(nsresult rv) mStatus = NS_BINDING_REDIRECTED; + notifier.RedirectSucceeded(); + // disconnect from the old listeners... mListener = nsnull; mListenerContext = nsnull; @@ -1593,6 +1624,7 @@ nsHttpChannel::ProcessFallback(PRBool *waitingForRedirectCallback) rv = WaitForRedirectCallback(); if (NS_FAILED(rv)) { + AutoRedirectVetoNotifier notifier(this); PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessFallback); mRedirectChannel = nsnull; return rv; @@ -1607,6 +1639,8 @@ nsHttpChannel::ProcessFallback(PRBool *waitingForRedirectCallback) nsresult nsHttpChannel::ContinueProcessFallback(nsresult rv) { + AutoRedirectVetoNotifier notifier(this); + if (NS_FAILED(rv)) return rv; @@ -1617,14 +1651,18 @@ nsHttpChannel::ContinueProcessFallback(nsresult rv) rv = mRedirectChannel->AsyncOpen(mListener, mListenerContext); mRedirectChannel = nsnull; - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(rv)) + return rv; // close down this channel Cancel(NS_BINDING_REDIRECTED); + notifier.RedirectSucceeded(); + // disconnect from our listener mListener = 0; mListenerContext = 0; + // and from our callbacks mCallbacks = nsnull; mProgressSink = nsnull; @@ -2752,15 +2790,6 @@ nsHttpChannel::ClearBogusContentEncodingIfNeeded() // nsHttpChannel <redirect> //----------------------------------------------------------------------------- -static PLDHashOperator -CopyProperties(const nsAString& aKey, nsIVariant *aData, void *aClosure) -{ - nsIWritablePropertyBag* bag = static_cast<nsIWritablePropertyBag*> - (aClosure); - bag->SetProperty(aKey, aData); - return PL_DHASH_NEXT; -} - nsresult nsHttpChannel::SetupReplacementChannel(nsIURI *newURI, nsIChannel *newChannel, @@ -2769,105 +2798,19 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI, LOG(("nsHttpChannel::SetupReplacementChannel " "[this=%p newChannel=%p preserveMethod=%d]", this, newChannel, preserveMethod)); - PRUint32 newLoadFlags = mLoadFlags | LOAD_REPLACE; - // if the original channel was using SSL and this channel is not using - // SSL, then no need to inhibit persistent caching. however, if the - // original channel was not using SSL and has INHIBIT_PERSISTENT_CACHING - // set, then allow the flag to apply to the redirected channel as well. - // since we force set INHIBIT_PERSISTENT_CACHING on all HTTPS channels, - // we only need to check if the original channel was using SSL. - if (mConnectionInfo->UsingSSL()) - newLoadFlags &= ~INHIBIT_PERSISTENT_CACHING; - // Do not pass along LOAD_CHECK_OFFLINE_CACHE - newLoadFlags &= ~LOAD_CHECK_OFFLINE_CACHE; - - newChannel->SetLoadGroup(mLoadGroup); - newChannel->SetNotificationCallbacks(mCallbacks); - newChannel->SetLoadFlags(newLoadFlags); + nsresult rv = HttpBaseChannel::SetupReplacementChannel(newURI, newChannel, preserveMethod); + if (NS_FAILED(rv)) + return rv; nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel); if (!httpChannel) return NS_OK; // no other options to set - if (preserveMethod) { - nsCOMPtr<nsIUploadChannel> uploadChannel = - do_QueryInterface(httpChannel); - nsCOMPtr<nsIUploadChannel2> uploadChannel2 = - do_QueryInterface(httpChannel); - if (mUploadStream && (uploadChannel2 || uploadChannel)) { - // rewind upload stream - nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream); - if (seekable) - seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); - - // replicate original call to SetUploadStream... - if (uploadChannel2) { - const char *ctype = mRequestHead.PeekHeader(nsHttp::Content_Type); - if (!ctype) - ctype = ""; - const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length); - PRInt64 len = clen ? nsCRT::atoll(clen) : -1; - uploadChannel2->ExplicitSetUploadStream( - mUploadStream, - nsDependentCString(ctype), - len, - nsDependentCString(mRequestHead.Method()), - mUploadStreamHasHeaders); - } - else { - if (mUploadStreamHasHeaders) - uploadChannel->SetUploadStream(mUploadStream, EmptyCString(), - -1); - else { - const char *ctype = - mRequestHead.PeekHeader(nsHttp::Content_Type); - const char *clen = - mRequestHead.PeekHeader(nsHttp::Content_Length); - if (!ctype) { - ctype = "application/octet-stream"; - } - if (clen) { - uploadChannel->SetUploadStream(mUploadStream, - nsDependentCString(ctype), - atoi(clen)); - } - } - } - } - // since preserveMethod is true, we need to ensure that the appropriate - // request method gets set on the channel, regardless of whether or not - // we set the upload stream above. This means SetRequestMethod() will - // be called twice if ExplicitSetUploadStream() gets called above. - - httpChannel->SetRequestMethod(nsDependentCString(mRequestHead.Method())); - } - // convey the referrer if one was used for this channel to the next one - if (mReferrer) - httpChannel->SetReferrer(mReferrer); - // convey the mAllowPipelining flag - httpChannel->SetAllowPipelining(mAllowPipelining); - // convey the new redirection limit - httpChannel->SetRedirectionLimit(mRedirectionLimit - 1); - + // transfer the remote flag nsHttpChannel *httpChannelImpl = static_cast<nsHttpChannel*>(httpChannel.get()); httpChannelImpl->SetRemoteChannel(mRemoteChannel); - nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel); - if (httpInternal) { - // convey the mForceAllowThirdPartyCookie flag - httpInternal->SetForceAllowThirdPartyCookie(mForceAllowThirdPartyCookie); - - // update the DocumentURI indicator since we are being redirected. - // if this was a top-level document channel, then the new channel - // should have its mDocumentURI point to newURI; otherwise, we - // just need to pass along our mDocumentURI to the new channel. - if (newURI && (mURI == mDocumentURI)) - httpInternal->SetDocumentURI(newURI); - else - httpInternal->SetDocumentURI(mDocumentURI); - } - // convey the mApplyConversion flag (bug 91862) nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel); if (encodedChannel) @@ -2883,20 +2826,6 @@ nsHttpChannel::SetupReplacementChannel(nsIURI *newURI, resumableChannel->ResumeAt(mStartPos, mEntityID); } - // transfer application cache information - nsCOMPtr<nsIApplicationCacheChannel> appCacheChannel = - do_QueryInterface(newChannel); - if (appCacheChannel) { - appCacheChannel->SetApplicationCache(mApplicationCache); - appCacheChannel->SetInheritApplicationCache(mInheritApplicationCache); - // We purposely avoid transfering mChooseApplicationCache. - } - - // transfer any properties - nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel)); - if (bag) - mPropertyHash.EnumerateRead(CopyProperties, bag.get()); - return NS_OK; } @@ -3034,6 +2963,7 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) rv = WaitForRedirectCallback(); if (NS_FAILED(rv)) { + AutoRedirectVetoNotifier notifier(this); PopRedirectAsyncFunc(&nsHttpChannel::ContinueProcessRedirection); mRedirectChannel = nsnull; } @@ -3044,6 +2974,8 @@ nsHttpChannel::ContinueProcessRedirectionAfterFallback(nsresult rv) nsresult nsHttpChannel::ContinueProcessRedirection(nsresult rv) { + AutoRedirectVetoNotifier notifier(this); + LOG(("ContinueProcessRedirection [rv=%x]\n", rv)); if (NS_FAILED(rv)) return rv; @@ -3076,9 +3008,12 @@ nsHttpChannel::ContinueProcessRedirection(nsresult rv) // close down this channel Cancel(NS_BINDING_REDIRECTED); + notifier.RedirectSucceeded(); + // disconnect from our listener mListener = 0; mListenerContext = 0; + // and from our callbacks mCallbacks = nsnull; mProgressSink = nsnull; diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index e461ed0d6bf9..f69432ff9385 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -54,7 +54,6 @@ #include "nsICachingChannel.h" #include "nsICacheEntryDescriptor.h" #include "nsICacheListener.h" -#include "nsIApplicationCache.h" #include "nsIApplicationCacheChannel.h" #include "nsIEncodedChannel.h" #include "nsIStringEnumerator.h" @@ -206,7 +205,7 @@ private: void HandleAsyncFallback(); nsresult ContinueHandleAsyncFallback(nsresult); nsresult PromptTempRedirect(); - nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod); + virtual nsresult SetupReplacementChannel(nsIURI *, nsIChannel *, PRBool preserveMethod); // proxy specific methods nsresult ProxyFailover(); @@ -269,8 +268,6 @@ private: nsCacheAccessMode mOfflineCacheAccess; nsCString mOfflineCacheClientID; - nsCOMPtr<nsIApplicationCache> mApplicationCache; - // auth specific data nsCOMPtr<nsIHttpChannelAuthProvider> mAuthProvider; @@ -314,9 +311,6 @@ private: // True if we are loading a fallback cache entry from the // application cache. PRUint32 mFallbackChannel : 1; - PRUint32 mInheritApplicationCache : 1; - PRUint32 mChooseApplicationCache : 1; - PRUint32 mLoadedFromApplicationCache : 1; PRUint32 mTracingEnabled : 1; // True if consumer added its own If-None-Match or If-Modified-Since // headers. In such a case we must not override them in the cache code diff --git a/netwerk/test/unit_ipc/test_event_sink_wrap.js b/netwerk/test/unit_ipc/test_event_sink_wrap.js new file mode 100644 index 000000000000..908c971f8ab1 --- /dev/null +++ b/netwerk/test/unit_ipc/test_event_sink_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// + +function run_test() { + run_test_in_child("../unit/test_event_sink.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js b/netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js new file mode 100644 index 000000000000..a1b8adfb9b55 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect-caching_canceled_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// +// +function run_test() { + run_test_in_child("../unit/test_redirect-caching_canceled.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js b/netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js new file mode 100644 index 000000000000..4fea2fdf6523 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect-caching_failure_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// +// +function run_test() { + run_test_in_child("../unit/test_redirect-caching_failure.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js b/netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js new file mode 100644 index 000000000000..8519501865a2 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect-caching_passing_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// +// +function run_test() { + run_test_in_child("../unit/test_redirect-caching_passing.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect_canceled_wrap.js b/netwerk/test/unit_ipc/test_redirect_canceled_wrap.js new file mode 100644 index 000000000000..546e05141094 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect_canceled_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// +// +function run_test() { + run_test_in_child("../unit/test_redirect_canceled.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect_failure_wrap.js b/netwerk/test/unit_ipc/test_redirect_failure_wrap.js new file mode 100644 index 000000000000..fbe27697f0a8 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect_failure_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// + +function run_test() { + run_test_in_child("../unit/test_redirect_failure.js"); +} diff --git a/netwerk/test/unit_ipc/test_redirect_passing_wrap.js b/netwerk/test/unit_ipc/test_redirect_passing_wrap.js new file mode 100644 index 000000000000..597ac35fb443 --- /dev/null +++ b/netwerk/test/unit_ipc/test_redirect_passing_wrap.js @@ -0,0 +1,7 @@ +// +// Run test script in content process instead of chrome (xpcshell's default) +// + +function run_test() { + run_test_in_child("../unit/test_redirect_passing.js"); +} From 044d1a38e2011d477563f44749ff376411d4486c Mon Sep 17 00:00:00 2001 From: Kyle Huey <me@kylehuey.com> Date: Tue, 10 Aug 2010 21:10:36 -0700 Subject: [PATCH 257/369] Backed out changeset 1b12ed9d1c48 --- content/html/document/src/nsHTMLFragmentContentSink.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 918d33ec4f2e..996f2f6a1089 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1233,9 +1233,6 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - // We need to explicitly skip adding leaf nodes in the paranoid sink, - // otherwise things like the textnode under <title> get appended to - // the fragment itself, and won't be popped off in CloseContainer. if (mSkip || mIgnoreNextCloseHead) { return rv; } From 73aa6b4ba36e7c48e6889efbaf91ddee971ce7e0 Mon Sep 17 00:00:00 2001 From: Kyle Huey <me@kylehuey.com> Date: Tue, 10 Aug 2010 21:12:06 -0700 Subject: [PATCH 258/369] Backed out changeset 40ad71a60486 --- .../libeditor/html/tests/test_bug520189.html | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index 3b244b5f45e1..d62a9db220f1 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -202,26 +202,26 @@ var tests = [ { id: "s", isIFrame: true, - payload: invalidStyle3Payload, + payload: invalidStyle1Payload, rootElement: function() document.getElementById("s").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "t", - payload: invalidStyle3Payload, + payload: invalidStyle1Payload, rootElement: function() document.getElementById("t"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "u", isIFrame: true, - payload: invalidStyle4Payload, + payload: invalidStyle2Payload, rootElement: function() document.getElementById("u").contentDocument.documentElement, checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, { id: "v", - payload: invalidStyle4Payload, + payload: invalidStyle2Payload, rootElement: function() document.getElementById("v"), checkResult: function(html) is(html.indexOf("xxx"), -1, "Should not have retained the import style") }, @@ -301,39 +301,39 @@ var tests = [ { id: "gg", isIFrame: true, - payload: validImgSrc1Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("gg").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "hh", - payload: validImgSrc1Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("hh"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ii", isIFrame: true, - payload: validImgSrc2Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("ii").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "jj", - payload: validImgSrc2Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("jj"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "kk", isIFrame: true, - payload: validImgSrc3Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("kk").contentDocument.documentElement, checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, { id: "ll", - payload: validImgSrc3Payload, + payload: invalidStyle6Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") }, From ee8098277d3a6c0f3a1e4cfd2c95a8aa02091ca2 Mon Sep 17 00:00:00 2001 From: Kyle Huey <me@kylehuey.com> Date: Tue, 10 Aug 2010 21:14:51 -0700 Subject: [PATCH 259/369] Backed out changeset 0aa4ad290221 --- .../src/nsHTMLFragmentContentSink.cpp | 6 +- .../libeditor/html/tests/test_bug520189.html | 69 +++++-------------- 2 files changed, 19 insertions(+), 56 deletions(-) diff --git a/content/html/document/src/nsHTMLFragmentContentSink.cpp b/content/html/document/src/nsHTMLFragmentContentSink.cpp index 996f2f6a1089..823ca70b9cd7 100644 --- a/content/html/document/src/nsHTMLFragmentContentSink.cpp +++ b/content/html/document/src/nsHTMLFragmentContentSink.cpp @@ -1100,10 +1100,6 @@ nsHTMLParanoidFragmentSink::CloseContainer(const nsHTMLTag aTag) { nsresult rv = NS_OK; - if (mIgnoreNextCloseHead && aTag == eHTMLTag_head) { - mIgnoreNextCloseHead = PR_FALSE; - return NS_OK; - } if (mSkip) { mSkip = PR_FALSE; return rv; @@ -1233,7 +1229,7 @@ nsHTMLParanoidFragmentSink::AddLeaf(const nsIParserNode& aNode) nsresult rv = NS_OK; - if (mSkip || mIgnoreNextCloseHead) { + if (mSkip) { return rv; } diff --git a/editor/libeditor/html/tests/test_bug520189.html b/editor/libeditor/html/tests/test_bug520189.html index d62a9db220f1..8bf41534ba09 100644 --- a/editor/libeditor/html/tests/test_bug520189.html +++ b/editor/libeditor/html/tests/test_bug520189.html @@ -52,8 +52,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=520182 <div id="jj" contenteditable="true"></div> <iframe id="kk" src="about:blank"></iframe> <div id="ll" contenteditable="true"></div> - <iframe id="mm" src="about:blank"></iframe> - <div id="nn" contenteditable="true"></div> </div> <pre id="test"> <script type="application/javascript"> @@ -75,7 +73,6 @@ const invalidStyle3Payload = "foo<style>@import 'xxx.css';</style>baz"; const invalidStyle4Payload = "foo<span style=\"@import 'xxx.css';\">bar</span>baz"; const invalidStyle5Payload = "foo<span style=\"@font-face{font-family:xxx;src:'xxx.ttf';}\">bar</span>baz"; const invalidStyle6Payload = "foo<span style=\"@namespace xxx url(http://example.com/);\">bar</span>baz"; -const invalidStyle7Payload = "<html><head><title>xxxfoo"; const nestedStylePayload = "foo#bar2{-moz-binding:url('data:text/xml,');baz"; const validImgSrc1Payload = "foobaz"; const validImgSrc2Payload = "foobaz"; @@ -336,27 +333,6 @@ var tests = [ payload: invalidStyle6Payload, rootElement: function() document.getElementById("ll"), checkResult: function(html) isnot(html.indexOf("bar"), -1, "Should have retained the src attribute for the image") - }, - { - id: "mm", - isIFrame: true, - insertHTML: true, - payload: invalidStyle7Payload, - rootElement: function() document.getElementById("mm").contentDocument.documentElement, - checkResult: function(html) { - is(html.indexOf("xxx"), -1, "Should not have retained the title text"); - isnot(html.indexOf("foo"), -1, "Should have retained the body text"); - } - }, - { - id: "nn", - insertHTML: true, - payload: invalidStyle7Payload, - rootElement: function() document.getElementById("nn"), - checkResult: function(html) { - is(html.indexOf("xxx"), -1, "Should not have retained the title text"); - isnot(html.indexOf("foo"), -1, "Should have retained the body text"); - } } ]; @@ -381,37 +357,28 @@ function runTest(test) { } else elem.focus(); - if ("insertHTML" in test) { - if ("isIFrame" in test) { - elem.contentDocument.execCommand("inserthtml", false, test.payload); - } else { - getSelection().collapse(elem, 0); - document.execCommand("inserthtml", false, test.payload); - } - } else { - netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); - var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] - .getService(Components.interfaces.nsIClipboard); + var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"] + .getService(Components.interfaces.nsIClipboard); - var trans = Components.classes["@mozilla.org/widget/transferable;1"] - .createInstance(Components.interfaces.nsITransferable); - var data = Components.classes["@mozilla.org/supports-string;1"] - .createInstance(Components.interfaces.nsISupportsString); - data.data = test.payload; - trans.addDataFlavor("text/html"); - trans.setTransferData("text/html", data, data.data.length * 2); - clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); + var trans = Components.classes["@mozilla.org/widget/transferable;1"] + .createInstance(Components.interfaces.nsITransferable); + var data = Components.classes["@mozilla.org/supports-string;1"] + .createInstance(Components.interfaces.nsISupportsString); + data.data = test.payload; + trans.addDataFlavor("text/html"); + trans.setTransferData("text/html", data, data.data.length * 2); + clipboard.setData(trans, null, Components.interfaces.nsIClipboard.kGlobalClipboard); - var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIWebNavigation) - .QueryInterface(Components.interfaces.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Components.interfaces.nsIInterfaceRequestor) - .getInterface(Components.interfaces.nsIDOMWindow); + var mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); - mainWindow.goDoCommand("cmd_paste"); - } + mainWindow.goDoCommand("cmd_paste"); if ("checkResult" in test) { if ("isIFrame" in test) { From db31d255835e0adac9c8c1998ebcc6e766251590 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Wed, 11 Aug 2010 00:09:50 -0700 Subject: [PATCH 260/369] Minor formatting fixes to redirects patch (bug 536294) --- netwerk/protocol/http/HttpBaseChannel.cpp | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index c3fd73f2f5ad..c811c47f3827 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -905,10 +905,7 @@ HttpBaseChannel::SetCookie(const char *aCookieHeader) nsICookieService *cs = gHttpHandler->GetCookieService(); NS_ENSURE_TRUE(cs, NS_ERROR_FAILURE); - return cs->SetCookieStringFromHttp(mURI, - nsnull, - nsnull, - aCookieHeader, + return cs->SetCookieStringFromHttp(mURI, nsnull, nsnull, aCookieHeader, mResponseHead->PeekHeader(nsHttp::Date), this); } @@ -972,8 +969,7 @@ HttpBaseChannel::AddCookiesToRequest() if (cs) { cs->GetCookieStringFromHttp(mURI, mDocumentURI ? mDocumentURI : mOriginalURI, - this, - getter_Copies(cookie)); + this, getter_Copies(cookie)); } if (cookie.IsEmpty()) { @@ -1046,11 +1042,9 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, const char *clen = mRequestHead.PeekHeader(nsHttp::Content_Length); PRInt64 len = clen ? nsCRT::atoll(clen) : -1; uploadChannel2->ExplicitSetUploadStream( - mUploadStream, - nsDependentCString(ctype), - len, - nsDependentCString(mRequestHead.Method()), - mUploadStreamHasHeaders); + mUploadStream, nsDependentCString(ctype), len, + nsDependentCString(mRequestHead.Method()), + mUploadStreamHasHeaders); } else { if (mUploadStreamHasHeaders) { uploadChannel->SetUploadStream(mUploadStream, EmptyCString(), @@ -1065,8 +1059,8 @@ HttpBaseChannel::SetupReplacementChannel(nsIURI *newURI, } if (clen) { uploadChannel->SetUploadStream(mUploadStream, - nsDependentCString(ctype), - atoi(clen)); + nsDependentCString(ctype), + atoi(clen)); } } } From 00afa443f6931a1a354ff814df96103a123e34bf Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Tue, 10 Aug 2010 23:07:09 -0400 Subject: [PATCH 261/369] Bug 536321 - e10s HTTP: suspend/resume. r=dwitte --- netwerk/protocol/http/HttpBaseChannel.cpp | 53 ++++++++- netwerk/protocol/http/HttpBaseChannel.h | 9 ++ netwerk/protocol/http/HttpChannelChild.cpp | 56 +++++++--- netwerk/protocol/http/HttpChannelChild.h | 25 ++++- netwerk/protocol/http/HttpChannelParent.cpp | 22 +++- netwerk/protocol/http/HttpChannelParent.h | 7 +- netwerk/protocol/http/PHttpChannel.ipdl | 8 +- netwerk/protocol/http/nsHttpChannel.cpp | 46 -------- netwerk/protocol/http/nsHttpChannel.h | 8 +- netwerk/test/unit/head_channels.js | 25 +++++ netwerk/test/unit/test_httpsuspend.js | 72 +++++++++++++ netwerk/test/unit/test_reentrancy.js | 101 ++++++++++++++++++ netwerk/test/unit/test_resumable_channel.js | 24 ++++- .../test/unit_ipc/test_httpsuspend_wrap.js | 3 + netwerk/test/unit_ipc/test_reentrancy_wrap.js | 3 + .../unit_ipc/test_resumable_channel_wrap.js | 3 + 16 files changed, 388 insertions(+), 77 deletions(-) create mode 100644 netwerk/test/unit/test_httpsuspend.js create mode 100644 netwerk/test/unit/test_reentrancy.js create mode 100644 netwerk/test/unit_ipc/test_httpsuspend_wrap.js create mode 100644 netwerk/test/unit_ipc/test_reentrancy_wrap.js create mode 100644 netwerk/test/unit_ipc/test_resumable_channel_wrap.js diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index c811c47f3827..7cb49d120e98 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -51,12 +51,14 @@ #include "nsIEncodedChannel.h" #include "nsIResumableChannel.h" #include "nsIApplicationCacheChannel.h" +#include "nsEscape.h" namespace mozilla { namespace net { HttpBaseChannel::HttpBaseChannel() - : mStatus(NS_OK) + : mStartPos(LL_MAXUINT) + , mStatus(NS_OK) , mLoadFlags(LOAD_NORMAL) , mPriority(PRIORITY_NORMAL) , mCaps(0) @@ -950,6 +952,55 @@ HttpBaseChannel::AdjustPriority(PRInt32 delta) return SetPriority(mPriority + delta); } +//----------------------------------------------------------------------------- +// HttpBaseChannel::nsIResumableChannel +//----------------------------------------------------------------------------- + +NS_IMETHODIMP +HttpBaseChannel::GetEntityID(nsACString& aEntityID) +{ + // Don't return an entity ID for Non-GET requests which require + // additional data + if (mRequestHead.Method() != nsHttp::Get) { + return NS_ERROR_NOT_RESUMABLE; + } + + // Don't return an entity if the server sent the following header: + // Accept-Ranges: none + // Not sending the Accept-Ranges header means we can still try + // sending range requests. + const char* acceptRanges = + mResponseHead->PeekHeader(nsHttp::Accept_Ranges); + if (acceptRanges && + !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) { + return NS_ERROR_NOT_RESUMABLE; + } + + PRUint64 size = LL_MAXUINT; + nsCAutoString etag, lastmod; + if (mResponseHead) { + size = mResponseHead->TotalEntitySize(); + const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified); + if (cLastMod) + lastmod = cLastMod; + const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag); + if (cEtag) + etag = cEtag; + } + nsCString entityID; + NS_EscapeURL(etag.BeginReading(), etag.Length(), esc_AlwaysCopy | + esc_FileBaseName | esc_Forced, entityID); + entityID.Append('/'); + entityID.AppendInt(PRInt64(size)); + entityID.Append('/'); + entityID.Append(lastmod); + // NOTE: Appending lastmod as the last part avoids having to escape it + + aEntityID = entityID; + + return NS_OK; +} + //------------------------------------------------------------------------------ //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpBaseChannel.h b/netwerk/protocol/http/HttpBaseChannel.h index acf6dbee182e..180018da9429 100644 --- a/netwerk/protocol/http/HttpBaseChannel.h +++ b/netwerk/protocol/http/HttpBaseChannel.h @@ -56,6 +56,7 @@ #include "nsIURI.h" #include "nsISupportsPriority.h" #include "nsIApplicationCache.h" +#include "nsIResumableChannel.h" #define DIE_WITH_ASYNC_OPEN_MSG() \ do { \ @@ -95,6 +96,7 @@ class HttpBaseChannel : public nsHashPropertyBag , public nsIUploadChannel , public nsIUploadChannel2 , public nsISupportsPriority + , public nsIResumableChannel { public: NS_DECL_ISUPPORTS_INHERITED @@ -168,6 +170,9 @@ public: NS_IMETHOD GetPriority(PRInt32 *value); NS_IMETHOD AdjustPriority(PRInt32 delta); + // nsIResumableChannel + NS_IMETHOD GetEntityID(nsACString& aEntityID); + protected: void AddCookiesToRequest(); virtual nsresult SetupReplacementChannel(nsIURI *, @@ -205,6 +210,10 @@ protected: nsCString mContentCharsetHint; nsCString mUserSetCookieHeader; + // Resumable channel specific data + nsCString mEntityID; + PRUint64 mStartPos; + nsresult mStatus; PRUint32 mLoadFlags; PRInt16 mPriority; diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 2aeb10e0ace3..2765016cc94c 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -56,6 +56,8 @@ namespace net { class ChildChannelEvent { public: + ChildChannelEvent() { MOZ_COUNT_CTOR(Callback); } + virtual ~ChildChannelEvent() { MOZ_COUNT_DTOR(Callback); } virtual void Run() = 0; }; @@ -70,6 +72,7 @@ public: } ~AutoEventEnqueuer() { + mChannel->EndEventQueueing(); mChannel->FlushEventQueue(); } private: @@ -85,6 +88,8 @@ HttpChannelChild::HttpChannelChild() : mIsFromCache(PR_FALSE) , mCacheEntryAvailable(PR_FALSE) , mCacheExpirationTime(nsICache::NO_EXPIRATION_TIME) + , mSendResumeAt(false) + , mSuspendCount(0) , mState(HCC_NEW) , mIPCOpen(false) , mQueuePhase(PHASE_UNQUEUED) @@ -147,8 +152,8 @@ HttpChannelChild::FlushEventQueue() NS_ABORT_IF_FALSE(mQueuePhase != PHASE_UNQUEUED, "Queue flushing should not occur if PHASE_UNQUEUED"); - // Queue already being flushed. - if (mQueuePhase != PHASE_QUEUEING) + // Queue already being flushed, or the channel's suspended. + if (mQueuePhase != PHASE_FINISHED_QUEUEING || mSuspendCount) return; if (mEventQueue.Length() > 0) { @@ -157,14 +162,25 @@ HttpChannelChild::FlushEventQueue() // all callbacks have run. mQueuePhase = PHASE_FLUSHING; - nsCOMPtr kungFuDeathGrip(this); - for (PRUint32 i = 0; i < mEventQueue.Length(); i++) { + nsRefPtr kungFuDeathGrip(this); + PRUint32 i; + for (i = 0; i < mEventQueue.Length(); i++) { mEventQueue[i]->Run(); + // If the callback ended up suspending us, abort all further flushing. + if (mSuspendCount) + break; } - mEventQueue.Clear(); + // We will always want to remove at least one finished callback. + if (i < mEventQueue.Length()) + i++; + + mEventQueue.RemoveElementsAt(0, i); } - mQueuePhase = PHASE_UNQUEUED; + if (mSuspendCount) + mQueuePhase = PHASE_QUEUEING; + else + mQueuePhase = PHASE_UNQUEUED; } class StartRequestEvent : public ChildChannelEvent @@ -647,13 +663,22 @@ HttpChannelChild::Cancel(nsresult status) NS_IMETHODIMP HttpChannelChild::Suspend() { - DROP_DEAD(); + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + SendSuspend(); + mSuspendCount++; + return NS_OK; } NS_IMETHODIMP HttpChannelChild::Resume() { - DROP_DEAD(); + NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); + SendResume(); + mSuspendCount--; + if (!mSuspendCount) + FlushEventQueue(); + return NS_OK; } //----------------------------------------------------------------------------- @@ -770,7 +795,8 @@ HttpChannelChild::AsyncOpen(nsIStreamListener *listener, nsISupports *aContext) IPC::URI(mDocumentURI), IPC::URI(mReferrer), mLoadFlags, mRequestHeaders, mRequestHead.Method(), uploadStreamData, uploadStreamInfo, mPriority, mRedirectionLimit, - mAllowPipelining, mForceAllowThirdPartyCookie); + mAllowPipelining, mForceAllowThirdPartyCookie, mSendResumeAt, + mStartPos, mEntityID); mState = HCC_OPENED; return NS_OK; @@ -886,14 +912,14 @@ HttpChannelChild::SetApplyConversion(PRBool aApplyConversion) NS_IMETHODIMP HttpChannelChild::ResumeAt(PRUint64 startPos, const nsACString& entityID) { - DROP_DEAD(); + ENSURE_CALLED_BEFORE_ASYNC_OPEN(); + mStartPos = startPos; + mEntityID = entityID; + mSendResumeAt = true; + return NS_OK; } -NS_IMETHODIMP -HttpChannelChild::GetEntityID(nsACString& aEntityID) -{ - DROP_DEAD(); -} +// GetEntityID is shared in HttpBaseChannel //----------------------------------------------------------------------------- // HttpChannelChild::nsISupportsPriority diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 8cb60fb9e73a..7c4812f6a18f 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -79,7 +79,6 @@ class HttpChannelChild : public PHttpChannelChild , public HttpBaseChannel , public nsICacheInfoChannel , public nsIEncodedChannel - , public nsIResumableChannel , public nsIProxiedChannel , public nsITraceableChannel , public nsIApplicationCacheChannel @@ -89,7 +88,6 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSICACHEINFOCHANNEL NS_DECL_NSIENCODEDCHANNEL - NS_DECL_NSIRESUMABLECHANNEL NS_DECL_NSIPROXIEDCHANNEL NS_DECL_NSITRACEABLECHANNEL NS_DECL_NSIAPPLICATIONCACHECONTAINER @@ -116,6 +114,8 @@ public: NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); + // nsIResumableChannel + NS_IMETHOD ResumeAt(PRUint64 startPos, const nsACString& entityID); // Final setup when redirect has proceeded successfully in chrome nsresult CompleteRedirectSetup(nsIStreamListener *listener, @@ -156,6 +156,11 @@ private: PRUint32 mCacheExpirationTime; nsCString mCachedCharset; + // If ResumeAt is called before AsyncOpen, we need to send extra data upstream + bool mSendResumeAt; + // Current suspension depth for this channel object + PRUint32 mSuspendCount; + // FIXME: replace with IPDL states (bug 536319) enum HttpChannelChildState mState; bool mIPCOpen; @@ -166,6 +171,7 @@ private: // event loop (ex: IPDL rpc) could cause listener->OnDataAvailable (for // instance) to be called before mListener->OnStartRequest has completed. void BeginEventQueueing(); + void EndEventQueueing(); void FlushEventQueue(); void EnqueueEvent(ChildChannelEvent* callback); bool ShouldEnqueue(); @@ -174,6 +180,7 @@ private: enum { PHASE_UNQUEUED, PHASE_QUEUEING, + PHASE_FINISHED_QUEUEING, PHASE_FLUSHING } mQueuePhase; @@ -211,16 +218,26 @@ private: inline void HttpChannelChild::BeginEventQueueing() { - if (mQueuePhase == PHASE_FLUSHING) + if (mQueuePhase != PHASE_UNQUEUED) return; // Store incoming IPDL messages for later. mQueuePhase = PHASE_QUEUEING; } +inline void +HttpChannelChild::EndEventQueueing() +{ + if (mQueuePhase != PHASE_QUEUEING) + return; + + mQueuePhase = PHASE_FINISHED_QUEUEING; +} + + inline bool HttpChannelChild::ShouldEnqueue() { - return mQueuePhase != PHASE_UNQUEUED; + return mQueuePhase != PHASE_UNQUEUED || mSuspendCount; } inline void diff --git a/netwerk/protocol/http/HttpChannelParent.cpp b/netwerk/protocol/http/HttpChannelParent.cpp index 7e11fd7cb25f..923289da8c18 100644 --- a/netwerk/protocol/http/HttpChannelParent.cpp +++ b/netwerk/protocol/http/HttpChannelParent.cpp @@ -104,7 +104,10 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, const PRUint16& priority, const PRUint8& redirectionLimit, const PRBool& allowPipelining, - const PRBool& forceAllowThirdPartyCookie) + const PRBool& forceAllowThirdPartyCookie, + const bool& doResumeAt, + const PRUint64& startPos, + const nsCString& entityID) { nsCOMPtr uri(aURI); nsCOMPtr originalUri(aOriginalURI); @@ -129,6 +132,9 @@ HttpChannelParent::RecvAsyncOpen(const IPC::URI& aURI, nsHttpChannel *httpChan = static_cast(mChannel.get()); httpChan->SetRemoteChannel(true); + if (doResumeAt) + httpChan->ResumeAt(startPos, entityID); + if (originalUri) httpChan->SetOriginalURI(originalUri); if (docUri) @@ -183,6 +189,20 @@ HttpChannelParent::RecvSetPriority(const PRUint16& priority) return true; } +bool +HttpChannelParent::RecvSuspend() +{ + mChannel->Suspend(); + return true; +} + +bool +HttpChannelParent::RecvResume() +{ + mChannel->Resume(); + return true; +} + bool HttpChannelParent::RecvSetCacheTokenCachedCharset(const nsCString& charset) { diff --git a/netwerk/protocol/http/HttpChannelParent.h b/netwerk/protocol/http/HttpChannelParent.h index 95b2df3a436c..d0ea1fd62869 100644 --- a/netwerk/protocol/http/HttpChannelParent.h +++ b/netwerk/protocol/http/HttpChannelParent.h @@ -93,10 +93,15 @@ protected: const PRUint16& priority, const PRUint8& redirectionLimit, const PRBool& allowPipelining, - const PRBool& forceAllowThirdPartyCookie); + const PRBool& forceAllowThirdPartyCookie, + const bool& doResumeAt, + const PRUint64& startPos, + const nsCString& entityID); virtual bool RecvSetPriority(const PRUint16& priority); virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset); + virtual bool RecvSuspend(); + virtual bool RecvResume(); virtual bool RecvRedirect2Result(const nsresult& result, const RequestHeaderTuples& changedHeaders); diff --git a/netwerk/protocol/http/PHttpChannel.ipdl b/netwerk/protocol/http/PHttpChannel.ipdl index d3f42539d511..640cff0a9ab5 100644 --- a/netwerk/protocol/http/PHttpChannel.ipdl +++ b/netwerk/protocol/http/PHttpChannel.ipdl @@ -73,12 +73,18 @@ parent: PRUint16 priority, PRUint8 redirectionLimit, PRBool allowPipelining, - PRBool forceAllowThirdPartyCookie); + PRBool forceAllowThirdPartyCookie, + bool resumeAt, + PRUint64 startPos, + nsCString entityID); SetPriority(PRUint16 priority); SetCacheTokenCachedCharset(nsCString charset); + Suspend(); + Resume(); + // Reports approval/veto of redirect by child process redirect observers Redirect2Result(nsresult result, RequestHeaderTuples changedHeaders); diff --git a/netwerk/protocol/http/nsHttpChannel.cpp b/netwerk/protocol/http/nsHttpChannel.cpp index 65d05117a3f8..472e5918202c 100644 --- a/netwerk/protocol/http/nsHttpChannel.cpp +++ b/netwerk/protocol/http/nsHttpChannel.cpp @@ -115,7 +115,6 @@ nsHttpChannel::nsHttpChannel() , mCacheAccess(0) , mPostID(0) , mRequestTime(0) - , mStartPos(LL_MAXUINT) , mPendingAsyncCallOnResume(nsnull) , mSuspendCount(0) , mApplyConversion(PR_TRUE) @@ -4073,51 +4072,6 @@ nsHttpChannel::ResumeAt(PRUint64 aStartPos, return NS_OK; } -NS_IMETHODIMP -nsHttpChannel::GetEntityID(nsACString& aEntityID) -{ - // Don't return an entity ID for Non-GET requests which require - // additional data - if (mRequestHead.Method() != nsHttp::Get) { - return NS_ERROR_NOT_RESUMABLE; - } - - // Don't return an entity if the server sent the following header: - // Accept-Ranges: none - // Not sending the Accept-Ranges header means we can still try - // sending range requests. - const char* acceptRanges = - mResponseHead->PeekHeader(nsHttp::Accept_Ranges); - if (acceptRanges && - !nsHttp::FindToken(acceptRanges, "bytes", HTTP_HEADER_VALUE_SEPS)) { - return NS_ERROR_NOT_RESUMABLE; - } - - PRUint64 size = LL_MAXUINT; - nsCAutoString etag, lastmod; - if (mResponseHead) { - size = mResponseHead->TotalEntitySize(); - const char* cLastMod = mResponseHead->PeekHeader(nsHttp::Last_Modified); - if (cLastMod) - lastmod = cLastMod; - const char* cEtag = mResponseHead->PeekHeader(nsHttp::ETag); - if (cEtag) - etag = cEtag; - } - nsCString entityID; - NS_EscapeURL(etag.BeginReading(), etag.Length(), esc_AlwaysCopy | - esc_FileBaseName | esc_Forced, entityID); - entityID.Append('/'); - entityID.AppendInt(PRInt64(size)); - entityID.Append('/'); - entityID.Append(lastmod); - // NOTE: Appending lastmod as the last part avoids having to escape it - - aEntityID = entityID; - - return NS_OK; -} - //----------------------------------------------------------------------------- // nsHttpChannel::nsICacheListener //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpChannel.h b/netwerk/protocol/http/nsHttpChannel.h index f69432ff9385..088bebf2443f 100644 --- a/netwerk/protocol/http/nsHttpChannel.h +++ b/netwerk/protocol/http/nsHttpChannel.h @@ -80,7 +80,6 @@ class nsHttpChannel : public HttpBaseChannel , public nsICacheListener , public nsIEncodedChannel , public nsITransportEventSink - , public nsIResumableChannel , public nsIProtocolProxyCallback , public nsIHttpAuthenticableChannel , public nsITraceableChannel @@ -96,7 +95,6 @@ public: NS_DECL_NSICACHELISTENER NS_DECL_NSIENCODEDCHANNEL NS_DECL_NSITRANSPORTEVENTSINK - NS_DECL_NSIRESUMABLECHANNEL NS_DECL_NSIPROTOCOLPROXYCALLBACK NS_DECL_NSIPROXIEDCHANNEL NS_DECL_NSITRACEABLECHANNEL @@ -143,6 +141,8 @@ public: NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); + // nsIResumableChannel + NS_IMETHOD ResumeAt(PRUint64 startPos, const nsACString& entityID); public: /* internal necko use only */ typedef void (nsHttpChannel:: *nsAsyncCallback)(void); @@ -271,10 +271,6 @@ private: // auth specific data nsCOMPtr mAuthProvider; - // Resumable channel specific data - nsCString mEntityID; - PRUint64 mStartPos; - // Function pointer that can be set to indicate that we got suspended while // waiting on an AsyncCall. When we get resumed we should AsyncCall this // function. diff --git a/netwerk/test/unit/head_channels.js b/netwerk/test/unit/head_channels.js index 0261cba64de1..f92d6f872afb 100644 --- a/netwerk/test/unit/head_channels.js +++ b/netwerk/test/unit/head_channels.js @@ -23,6 +23,10 @@ function read_stream(stream, count) { const CL_EXPECT_FAILURE = 0x1; const CL_EXPECT_GZIP = 0x2; +const CL_EXPECT_3S_DELAY = 0x4; +const CL_SUSPEND = 0x8; + +const SUSPEND_DELAY = 3000; /** * A stream listener that calls a callback function with a specified @@ -50,6 +54,7 @@ ChannelListener.prototype = { _got_onstartrequest: false, _got_onstoprequest: false, _contentLen: -1, + _lastEvent: 0, QueryInterface: function(iid) { if (iid.equals(Components.interfaces.nsIStreamListener) || @@ -64,6 +69,7 @@ ChannelListener.prototype = { if (this._got_onstartrequest) do_throw("Got second onStartRequest event!"); this._got_onstartrequest = true; + this._lastEvent = Date.now(); request.QueryInterface(Components.interfaces.nsIChannel); try { @@ -75,6 +81,12 @@ ChannelListener.prototype = { } if (this._contentLen == -1 && !(this._flags & CL_EXPECT_FAILURE)) do_throw("Content length is unknown in onStartRequest!"); + + if (this._flags & CL_SUSPEND) { + request.suspend(); + do_timeout(SUSPEND_DELAY, function() { request.resume(); }); + } + } catch (ex) { do_throw("Error in onStartRequest: " + ex); } @@ -82,6 +94,8 @@ ChannelListener.prototype = { onDataAvailable: function(request, context, stream, offset, count) { try { + let current = Date.now(); + if (!this._got_onstartrequest) do_throw("onDataAvailable without onStartRequest event!"); if (this._got_onstoprequest) @@ -91,7 +105,18 @@ ChannelListener.prototype = { if (this._flags & CL_EXPECT_FAILURE) do_throw("Got data despite expecting a failure"); + if (current - this._lastEvent >= SUSPEND_DELAY && + !(this._flags & CL_EXPECT_3S_DELAY)) + do_throw("Data received after significant unexpected delay"); + else if (current - this._lastEvent < SUSPEND_DELAY && + this._flags & CL_EXPECT_3S_DELAY) + do_throw("Data received sooner than expected"); + else if (current - this._lastEvent >= SUSPEND_DELAY && + this._flags & CL_EXPECT_3S_DELAY) + this._flags &= ~CL_EXPECT_3S_DELAY; // No more delays expected + this._buffer = this._buffer.concat(read_stream(stream, count)); + this._lastEvent = current; } catch (ex) { do_throw("Error in onDataAvailable: " + ex); } diff --git a/netwerk/test/unit/test_httpsuspend.js b/netwerk/test/unit/test_httpsuspend.js new file mode 100644 index 000000000000..0459c637aaeb --- /dev/null +++ b/netwerk/test/unit/test_httpsuspend.js @@ -0,0 +1,72 @@ +// This file ensures that suspending a channel directly after opening it +// suspends future notifications correctly. + +do_load_httpd_js(); + +const MIN_TIME_DIFFERENCE = 3000; +const RESUME_DELAY = 5000; + +var listener = { + _lastEvent: 0, + _gotData: false, + + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIStreamListener) || + iid.equals(Components.interfaces.nsIRequestObserver) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + onStartRequest: function(request, ctx) { + this._lastEvent = Date.now(); + request.QueryInterface(Ci.nsIRequest); + + // Insert a delay between this and the next callback to ensure message buffering + // works correctly + request.suspend(); + do_timeout(RESUME_DELAY, function() request.resume()); + }, + + onDataAvailable: function(request, context, stream, offset, count) { + do_check_true(Date.now() - this._lastEvent >= MIN_TIME_DIFFERENCE); + read_stream(stream, count); + + // Ensure that suspending and resuming inside a callback works correctly + request.suspend(); + request.resume(); + + this._gotData = true; + }, + + onStopRequest: function(request, ctx, status) { + do_check_true(this._gotData); + httpserv.stop(do_test_finished); + } +}; + +function makeChan(url) { + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); + return chan; +} + +var httpserv = null; + +function run_test() { + httpserv = new nsHttpServer(); + httpserv.registerPathHandler("/woo", data); + httpserv.start(4444); + + var chan = makeChan("http://localhost:4444/woo"); + chan.QueryInterface(Ci.nsIRequest); + chan.asyncOpen(listener, null); + + do_test_pending(); +} + +function data(metadata, response) { + let httpbody = "0123456789"; + response.setHeader("Content-Type", "text/plain", false); + response.bodyOutputStream.write(httpbody, httpbody.length); +} diff --git a/netwerk/test/unit/test_reentrancy.js b/netwerk/test/unit/test_reentrancy.js new file mode 100644 index 000000000000..aecdfddf5050 --- /dev/null +++ b/netwerk/test/unit/test_reentrancy.js @@ -0,0 +1,101 @@ +do_load_httpd_js(); + +var httpserver = new nsHttpServer(); +var testpath = "/simple"; +var httpbody = "0123456789"; + +function syncXHR() +{ + var xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Ci.nsIXMLHttpRequest); + xhr.open("GET", "http://localhost:4444" + testpath, false); + xhr.send(null); +} + +const MAX_TESTS = 2; + +var listener = { + _done_onStart: false, + _done_onData: false, + _test: 0, + + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIStreamListener) || + iid.equals(Components.interfaces.nsIRequestObserver) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + onStartRequest: function(request, ctx) { + switch(this._test) { + case 0: + request.suspend(); + syncXHR(); + request.resume(); + break; + case 1: + request.suspend(); + syncXHR(); + do_execute_soon(function() request.resume()); + break; + case 2: + do_execute_soon(function() request.suspend()); + do_execute_soon(function() request.resume()); + syncXHR(); + break; + } + + this._done_onStart = true; + }, + + onDataAvailable: function(request, context, stream, offset, count) { + do_check_true(this._done_onStart); + read_stream(stream, count); + this._done_onData = true; + }, + + onStopRequest: function(request, ctx, status) { + do_check_true(this._done_onData); + this._reset(); + if (this._test <= MAX_TESTS) + next_test(); + else + httpserver.stop(do_test_finished); + }, + + _reset: function() { + this._done_onStart = false; + this._done_onData = false; + this._test++; + } +}; + +function makeChan(url) { + var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); + var chan = ios.newChannel(url, null, null).QueryInterface(Ci.nsIHttpChannel); + return chan; +} + +function next_test() +{ + var chan = makeChan("http://localhost:4444" + testpath); + chan.QueryInterface(Ci.nsIRequest); + chan.asyncOpen(listener, null); +} + +function run_test() +{ + httpserver.registerPathHandler(testpath, serverHandler); + httpserver.start(4444); + + next_test(); + + do_test_pending(); +} + +function serverHandler(metadata, response) +{ + response.setHeader("Content-Type", "text/xml", false); + response.bodyOutputStream.write(httpbody, httpbody.length); +} diff --git a/netwerk/test/unit/test_resumable_channel.js b/netwerk/test/unit/test_resumable_channel.js index 8aacdb47cc08..f58513d1cef8 100644 --- a/netwerk/test/unit/test_resumable_channel.js +++ b/netwerk/test/unit/test_resumable_channel.js @@ -188,6 +188,18 @@ function run_test() { do_check_true(request.nsIHttpChannel.requestSucceeded); do_check_eq(data, rangeBody); + // Try a successful suspend/resume from 0 + var chan = make_channel("http://localhost:4444/range"); + chan.nsIResumableChannel.resumeAt(0, entityID); + chan.asyncOpen(new ChannelListener(try_suspend_resume, null, + CL_SUSPEND | CL_EXPECT_3S_DELAY), null); + } + + function try_suspend_resume(request, data, ctx) { + dump("*** try_suspend_resume()\n"); + do_check_true(request.nsIHttpChannel.requestSucceeded); + do_check_eq(data, rangeBody); + // Try a successful resume from 0 var chan = make_channel("http://localhost:4444/range"); chan.nsIResumableChannel.resumeAt(0, entityID); @@ -199,12 +211,20 @@ function run_test() { do_check_true(request.nsIHttpChannel.requestSucceeded); do_check_eq(data, rangeBody); + // XXX skip all authentication tests for now, as they're busted on e10s (bug 537782) + // Authentication (no password; working resume) // (should not give us any data) - var chan = make_channel("http://localhost:4444/range"); + /*var chan = make_channel("http://localhost:4444/range"); chan.nsIResumableChannel.resumeAt(1, entityID); chan.nsIHttpChannel.setRequestHeader("X-Need-Auth", "true", false); - chan.asyncOpen(new ChannelListener(test_auth_nopw, null, CL_EXPECT_FAILURE), null); + chan.asyncOpen(new ChannelListener(test_auth_nopw, null, CL_EXPECT_FAILURE), null);*/ + + // 404 page (same content length as real content) + var chan = make_channel("http://localhost:4444/range"); + chan.nsIResumableChannel.resumeAt(1, entityID); + chan.nsIHttpChannel.setRequestHeader("X-Want-404", "true", false); + chan.asyncOpen(new ChannelListener(test_404, null, CL_EXPECT_FAILURE), null); } function test_auth_nopw(request, data, ctx) { diff --git a/netwerk/test/unit_ipc/test_httpsuspend_wrap.js b/netwerk/test/unit_ipc/test_httpsuspend_wrap.js new file mode 100644 index 000000000000..348541283a45 --- /dev/null +++ b/netwerk/test/unit_ipc/test_httpsuspend_wrap.js @@ -0,0 +1,3 @@ +function run_test() { + run_test_in_child("../unit/test_httpsuspend.js"); +} \ No newline at end of file diff --git a/netwerk/test/unit_ipc/test_reentrancy_wrap.js b/netwerk/test/unit_ipc/test_reentrancy_wrap.js new file mode 100644 index 000000000000..43d72dd0598c --- /dev/null +++ b/netwerk/test/unit_ipc/test_reentrancy_wrap.js @@ -0,0 +1,3 @@ +function run_test() { + run_test_in_child("../unit/test_reentrancy.js"); +} \ No newline at end of file diff --git a/netwerk/test/unit_ipc/test_resumable_channel_wrap.js b/netwerk/test/unit_ipc/test_resumable_channel_wrap.js new file mode 100644 index 000000000000..573ab25b64e6 --- /dev/null +++ b/netwerk/test/unit_ipc/test_resumable_channel_wrap.js @@ -0,0 +1,3 @@ +function run_test() { + run_test_in_child("../unit/test_resumable_channel.js"); +} From d6f58bdf36970f0d77611044fe7f31ab8ac2446d Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Wed, 11 Aug 2010 01:33:42 -0700 Subject: [PATCH 262/369] Disable perma-orange xpcshell test for redirects (Bug 586205). --- netwerk/test/unit/test_event_sink.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/netwerk/test/unit/test_event_sink.js b/netwerk/test/unit/test_event_sink.js index 34a64ee1664f..49564ff00ff3 100644 --- a/netwerk/test/unit/test_event_sink.js +++ b/netwerk/test/unit/test_event_sink.js @@ -105,6 +105,12 @@ function makeChan(url) { var httpserv = null; function run_test() { +// DISABLE TEST: bug 586205 +_dump('FIXME/bug 586205: disabled to avoid perma-orange\n'); +} +function never() { + + httpserv = new nsHttpServer(); httpserv.registerPathHandler("/redirect", redirect); httpserv.registerPathHandler("/redirectfile", redirectfile); From 28997f6ec8e90af3f06ee9262fd12cc78790a878 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 11 Aug 2010 17:50:03 +0900 Subject: [PATCH 263/369] Bug 581576 hung up or too slow when press Enter key on Gmail editor which has a lot of misspelled words r=ehsan, a=dbaron --- editor/libeditor/base/nsEditor.cpp | 20 +++++++++++++------- editor/libeditor/base/nsEditor.h | 7 +++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index 1ddfff6074cb..a7bf85162c26 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -368,9 +368,7 @@ nsEditor::GetDesiredSpellCheckState() return PR_FALSE; // Spellchecking forced off globally } - // Check for password/readonly/disabled, which are not spellchecked - // regardless of DOM - if (IsPasswordEditor() || IsReadonly() || IsDisabled()) { + if (CanEnableSpellCheck()) { return PR_FALSE; } @@ -443,6 +441,12 @@ nsEditor::GetFlags(PRUint32 *aFlags) NS_IMETHODIMP nsEditor::SetFlags(PRUint32 aFlags) { + // XXX Don't return even if aFlags == mFlags because when this editor is + // initializing, SetFlags() returns before all jobs are finished and + // PostCreate() will call this with same flags, then, this method needs to + // do the all jobs. + + PRBool spellcheckerWasEnabled = CanEnableSpellCheck(); mFlags = aFlags; if (!mDocWeak || !mPresShellWeak) { @@ -452,9 +456,11 @@ nsEditor::SetFlags(PRUint32 aFlags) return NS_OK; } - // Changing the flags can change whether spellchecking is on, so re-sync it - nsresult rv = SyncRealTimeSpell(); - NS_ENSURE_SUCCESS(rv, rv); + // The flag change may cause the spellchecker state change + if (CanEnableSpellCheck() != spellcheckerWasEnabled) { + nsresult rv = SyncRealTimeSpell(); + NS_ENSURE_SUCCESS(rv, rv); + } // Might be changing editable state, so, we need to reset current IME state // if we're focused and the flag change causes IME state change. @@ -462,7 +468,7 @@ nsEditor::SetFlags(PRUint32 aFlags) // Use "enable" for the default value because if IME is disabled // unexpectedly, it makes serious a11y problem. PRUint32 newState = nsIContent::IME_STATUS_ENABLE; - rv = GetPreferredIMEState(&newState); + nsresult rv = GetPreferredIMEState(&newState); if (NS_SUCCEEDED(rv)) { // NOTE: When the enabled state isn't going to be modified, this method // is going to do nothing. diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index 83e3178da6e8..126039168f62 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -360,6 +360,13 @@ protected: nsKeyEvent* GetNativeKeyEvent(nsIDOMKeyEvent* aDOMKeyEvent); + PRBool CanEnableSpellCheck() + { + // Check for password/readonly/disabled, which are not spellchecked + // regardless of DOM + return !IsPasswordEditor() && !IsReadonly() && !IsDisabled(); + } + public: /** All editor operations which alter the doc should be prefaced From 674565afd953629122c9ed165ddf3c9e2737a34f Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 11 Aug 2010 18:19:34 +0900 Subject: [PATCH 264/369] Bug 581576 fix a nit of previous check-in, r+a=pending --- editor/libeditor/base/nsEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index a7bf85162c26..bd9feabbc9d1 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -368,7 +368,7 @@ nsEditor::GetDesiredSpellCheckState() return PR_FALSE; // Spellchecking forced off globally } - if (CanEnableSpellCheck()) { + if (!CanEnableSpellCheck()) { return PR_FALSE; } From 3a85986c744b0d10905500fce29268dea23be672 Mon Sep 17 00:00:00 2001 From: Johnny Stenback Date: Wed, 11 Aug 2010 04:13:25 -0700 Subject: [PATCH 265/369] Disabling failing tests from bug 536321, followup bug 586238 filed to track this. --- netwerk/test/unit/test_httpsuspend.js | 4 ++++ netwerk/test/unit/test_resumable_channel.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/netwerk/test/unit/test_httpsuspend.js b/netwerk/test/unit/test_httpsuspend.js index 0459c637aaeb..0176770c17cc 100644 --- a/netwerk/test/unit/test_httpsuspend.js +++ b/netwerk/test/unit/test_httpsuspend.js @@ -54,6 +54,10 @@ function makeChan(url) { var httpserv = null; function run_test() { +// DISABLE TEST: bug 586238 + _dump('FIXME/bug 586238: disabled to avoid perma-orange\n'); +} +function never() { httpserv = new nsHttpServer(); httpserv.registerPathHandler("/woo", data); httpserv.start(4444); diff --git a/netwerk/test/unit/test_resumable_channel.js b/netwerk/test/unit/test_resumable_channel.js index f58513d1cef8..409d67cbbfd5 100644 --- a/netwerk/test/unit/test_resumable_channel.js +++ b/netwerk/test/unit/test_resumable_channel.js @@ -68,6 +68,10 @@ Requestor.prototype = { }; function run_test() { +// DISABLE TEST: bug 586238 + _dump('FIXME/bug 586238: disabled to avoid perma-orange\n'); +} +function never() { dump("*** run_test\n"); httpserver = new nsHttpServer(); httpserver.registerPathHandler("/auth", authHandler); From 471bd528400ab90f53241c08df9b059fe23bf03b Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Wed, 11 Aug 2010 20:52:36 +0900 Subject: [PATCH 266/369] Bug 581576 backout due to test_contextmenu failure --- editor/libeditor/base/nsEditor.cpp | 20 +++++++------------- editor/libeditor/base/nsEditor.h | 7 ------- 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index bd9feabbc9d1..1ddfff6074cb 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -368,7 +368,9 @@ nsEditor::GetDesiredSpellCheckState() return PR_FALSE; // Spellchecking forced off globally } - if (!CanEnableSpellCheck()) { + // Check for password/readonly/disabled, which are not spellchecked + // regardless of DOM + if (IsPasswordEditor() || IsReadonly() || IsDisabled()) { return PR_FALSE; } @@ -441,12 +443,6 @@ nsEditor::GetFlags(PRUint32 *aFlags) NS_IMETHODIMP nsEditor::SetFlags(PRUint32 aFlags) { - // XXX Don't return even if aFlags == mFlags because when this editor is - // initializing, SetFlags() returns before all jobs are finished and - // PostCreate() will call this with same flags, then, this method needs to - // do the all jobs. - - PRBool spellcheckerWasEnabled = CanEnableSpellCheck(); mFlags = aFlags; if (!mDocWeak || !mPresShellWeak) { @@ -456,11 +452,9 @@ nsEditor::SetFlags(PRUint32 aFlags) return NS_OK; } - // The flag change may cause the spellchecker state change - if (CanEnableSpellCheck() != spellcheckerWasEnabled) { - nsresult rv = SyncRealTimeSpell(); - NS_ENSURE_SUCCESS(rv, rv); - } + // Changing the flags can change whether spellchecking is on, so re-sync it + nsresult rv = SyncRealTimeSpell(); + NS_ENSURE_SUCCESS(rv, rv); // Might be changing editable state, so, we need to reset current IME state // if we're focused and the flag change causes IME state change. @@ -468,7 +462,7 @@ nsEditor::SetFlags(PRUint32 aFlags) // Use "enable" for the default value because if IME is disabled // unexpectedly, it makes serious a11y problem. PRUint32 newState = nsIContent::IME_STATUS_ENABLE; - nsresult rv = GetPreferredIMEState(&newState); + rv = GetPreferredIMEState(&newState); if (NS_SUCCEEDED(rv)) { // NOTE: When the enabled state isn't going to be modified, this method // is going to do nothing. diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index 126039168f62..83e3178da6e8 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -360,13 +360,6 @@ protected: nsKeyEvent* GetNativeKeyEvent(nsIDOMKeyEvent* aDOMKeyEvent); - PRBool CanEnableSpellCheck() - { - // Check for password/readonly/disabled, which are not spellchecked - // regardless of DOM - return !IsPasswordEditor() && !IsReadonly() && !IsDisabled(); - } - public: /** All editor operations which alter the doc should be prefaced From 83666a4b0d771c84e72110fefc39f124513b0c23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 11 Aug 2010 14:02:02 +0200 Subject: [PATCH 267/369] Bug 583745 - Adjust toolbar gradients for when the navbar is hidden. r=mano --- browser/themes/winstripe/browser/browser.css | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index e7c2bef61674..1d36a148c1d7 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -81,7 +81,11 @@ } #nav-bar:not(:-moz-lwtheme), -#navigator-toolbox[tabsontop="true"] > #nav-bar { +#nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme), +#nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme), +#navigator-toolbox[tabsontop="true"] > #nav-bar, +#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar, +#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar { background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0)); } From 614ec4a44c57fd54f9e76847e276606ec8f277fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 11 Aug 2010 14:02:49 +0200 Subject: [PATCH 268/369] Bug 585361 - Handle requests for synchronously removing an already asynchronously closing tab. r=gavin --- browser/base/content/tabbrowser.xml | 13 +++++++++++-- browser/base/content/test/browser_bug380960.js | 5 +++++ testing/mochitest/browser-test.js | 4 ---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index eb4eb2c9df34..622140b69bde 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1323,10 +1323,19 @@ -1) { + this._endRemoveTab(aTab); + return; + } + + var isLastTab = (this.tabs.length - this._removingTabs.length == 1); + if (!this._beginRemoveTab(aTab, false, null, true)) return; @@ -2404,7 +2413,7 @@ var tabs = document.getBindingParent(this); tabs.removeAttribute("overflow"); - tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser._endRemoveTab, + tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab, tabs.tabbrowser); tabs._positionPinnedTabs(); diff --git a/browser/base/content/test/browser_bug380960.js b/browser/base/content/test/browser_bug380960.js index 4ba0c87c87e4..a754935a7436 100644 --- a/browser/base/content/test/browser_bug380960.js +++ b/browser/base/content/test/browser_bug380960.js @@ -5,6 +5,11 @@ function test() { gBrowser.removeTab(tab); is(tab.parentNode, null, "tab removed immediately"); + tab = gBrowser.addTab("about:blank", { skipAnimation: true }); + gBrowser.removeTab(tab, { animate: true }); + gBrowser.removeTab(tab); + is(tab.parentNode, null, "tab removed immediately when calling removeTab again after the animation was kicked off"); + waitForExplicitFinish(); Services.prefs.setBoolPref("browser.tabs.animate", true); diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index f6be12d32781..bcd2d22c159c 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -83,10 +83,6 @@ Tester.prototype = { let msg = baseMsg.replace("{elt}", "tab") + ": " + lastTab.linkedBrowser.currentURI.spec; this.currentTest.addResult(new testResult(false, msg, "", false)); - if (gBrowser._removingTabs && gBrowser._removingTabs.indexOf(lastTab) > -1) { - gBrowser._endRemoveTab(lastTab); - continue; - } gBrowser.removeTab(lastTab); } } From a9cb9079f4077e9ac658319eb8663813907d1126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 11 Aug 2010 14:03:26 +0200 Subject: [PATCH 269/369] Bug 585021 - Animate when opening the Home page in a new tab. r=gavin --- browser/base/content/tabbrowser.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 622140b69bde..f92ac59d718c 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1016,7 +1016,8 @@ // == 1 false YES // == 1 true NO // > 1 false/true NO - var owner = (aURIs.length > 1) || aLoadInBackground ? null : this.selectedTab; + var multiple = aURIs.length > 1; + var owner = multiple || aLoadInBackground ? null : this.selectedTab; var firstTabAdded = null; if (aReplace) { @@ -1028,7 +1029,7 @@ } } else - firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: true}); + firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple}); var tabNum = this.tabContainer.selectedIndex; for (let i = 1; i < aURIs.length; ++i) { From 023fed832619fbbfe2dd97d68ea20ec5aa661e49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Wed, 11 Aug 2010 14:03:44 +0200 Subject: [PATCH 270/369] Bug 586114 - Consolidate toolbox bottom border styling. r=mano --- browser/themes/winstripe/browser/browser.css | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index 1d36a148c1d7..0338d3f53a6e 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -60,11 +60,8 @@ #navigator-toolbox { -moz-appearance: none; - border-top: none; background-color: transparent; -} - -#navigator-toolbox[tabsontop="true"] { + border-top: none; border-bottom: 1px solid ThreeDShadow; } @@ -1119,10 +1116,6 @@ richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-i -moz-box-shadow: 0 -1px ThreeDShadow inset; } -#TabsToolbar[tabsontop="false"] { - border-bottom: 1px solid ThreeDShadow !important; -} - .tabbrowser-tabs:-moz-system-metric(touch-enabled) { min-height: .73cm; } From 6c2eb3acea56baa972b0f034b3eef1c2a0b8a692 Mon Sep 17 00:00:00 2001 From: Benjamin Smedberg Date: Wed, 11 Aug 2010 09:00:33 -0400 Subject: [PATCH 271/369] Back out all of dgottwald's push from Wed Aug 11 05:04:25 2010 PDT because the tree is in a state of chaos and the push was obviously against tree rules. --- browser/base/content/tabbrowser.xml | 18 ++++-------------- browser/base/content/test/browser_bug380960.js | 5 ----- browser/themes/winstripe/browser/browser.css | 15 +++++++++------ testing/mochitest/browser-test.js | 4 ++++ 4 files changed, 17 insertions(+), 25 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index f92ac59d718c..eb4eb2c9df34 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -1016,8 +1016,7 @@ // == 1 false YES // == 1 true NO // > 1 false/true NO - var multiple = aURIs.length > 1; - var owner = multiple || aLoadInBackground ? null : this.selectedTab; + var owner = (aURIs.length > 1) || aLoadInBackground ? null : this.selectedTab; var firstTabAdded = null; if (aReplace) { @@ -1029,7 +1028,7 @@ } } else - firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: multiple}); + firstTabAdded = this.addTab(aURIs[0], {ownerTab: owner, skipAnimation: true}); var tabNum = this.tabContainer.selectedIndex; for (let i = 1; i < aURIs.length; ++i) { @@ -1324,19 +1323,10 @@ -1) { - this._endRemoveTab(aTab); - return; - } - - var isLastTab = (this.tabs.length - this._removingTabs.length == 1); - if (!this._beginRemoveTab(aTab, false, null, true)) return; @@ -2414,7 +2404,7 @@ var tabs = document.getBindingParent(this); tabs.removeAttribute("overflow"); - tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser.removeTab, + tabs.tabbrowser._removingTabs.forEach(tabs.tabbrowser._endRemoveTab, tabs.tabbrowser); tabs._positionPinnedTabs(); diff --git a/browser/base/content/test/browser_bug380960.js b/browser/base/content/test/browser_bug380960.js index a754935a7436..4ba0c87c87e4 100644 --- a/browser/base/content/test/browser_bug380960.js +++ b/browser/base/content/test/browser_bug380960.js @@ -5,11 +5,6 @@ function test() { gBrowser.removeTab(tab); is(tab.parentNode, null, "tab removed immediately"); - tab = gBrowser.addTab("about:blank", { skipAnimation: true }); - gBrowser.removeTab(tab, { animate: true }); - gBrowser.removeTab(tab); - is(tab.parentNode, null, "tab removed immediately when calling removeTab again after the animation was kicked off"); - waitForExplicitFinish(); Services.prefs.setBoolPref("browser.tabs.animate", true); diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index 0338d3f53a6e..e7c2bef61674 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -60,8 +60,11 @@ #navigator-toolbox { -moz-appearance: none; - background-color: transparent; border-top: none; + background-color: transparent; +} + +#navigator-toolbox[tabsontop="true"] { border-bottom: 1px solid ThreeDShadow; } @@ -78,11 +81,7 @@ } #nav-bar:not(:-moz-lwtheme), -#nav-bar[collapsed="true"] + toolbar:not(:-moz-lwtheme), -#nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme), -#navigator-toolbox[tabsontop="true"] > #nav-bar, -#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + toolbar, -#navigator-toolbox[tabsontop="true"]:not([customizing]) > #nav-bar[collapsed="true"] + #customToolbars + #PersonalToolbar { +#navigator-toolbox[tabsontop="true"] > #nav-bar { background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0)); } @@ -1116,6 +1115,10 @@ richlistitem[type="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-i -moz-box-shadow: 0 -1px ThreeDShadow inset; } +#TabsToolbar[tabsontop="false"] { + border-bottom: 1px solid ThreeDShadow !important; +} + .tabbrowser-tabs:-moz-system-metric(touch-enabled) { min-height: .73cm; } diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js index bcd2d22c159c..f6be12d32781 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js @@ -83,6 +83,10 @@ Tester.prototype = { let msg = baseMsg.replace("{elt}", "tab") + ": " + lastTab.linkedBrowser.currentURI.spec; this.currentTest.addResult(new testResult(false, msg, "", false)); + if (gBrowser._removingTabs && gBrowser._removingTabs.indexOf(lastTab) > -1) { + gBrowser._endRemoveTab(lastTab); + continue; + } gBrowser.removeTab(lastTab); } } From a96635422e37f40ef0dcf832bd1acd3f5618578a Mon Sep 17 00:00:00 2001 From: Robert Kaiser Date: Wed, 11 Aug 2010 18:05:08 +0200 Subject: [PATCH 272/369] bug 570788 - Incorporate improvements from SeaMonkey places review into Firefox code, round 3 (PUIU, PC, sbUtils), r=mak a=gavin --- .../components/places/content/controller.js | 131 +++++++++--------- .../components/places/content/sidebarUtils.js | 2 +- .../components/places/src/PlacesUIUtils.jsm | 60 ++++---- 3 files changed, 97 insertions(+), 96 deletions(-) diff --git a/browser/components/places/content/controller.js b/browser/components/places/content/controller.js index afdd501447be..04f3dc57d7ad 100644 --- a/browser/components/places/content/controller.js +++ b/browser/components/places/content/controller.js @@ -50,7 +50,7 @@ const RELOAD_ACTION_NOTHING = 0; const RELOAD_ACTION_INSERT = 1; // Removing items from the view, select the first item after the last selected const RELOAD_ACTION_REMOVE = 2; -// Moving items within a view, don't treat the dropped items as additional +// Moving items within a view, don't treat the dropped items as additional // rows. const RELOAD_ACTION_MOVE = 3; @@ -67,7 +67,7 @@ const REMOVE_PAGES_MAX_SINGLEREMOVES = 10; /** * Represents an insertion point within a container where we can insert - * items. + * items. * @param aItemId * The identifier of the parent container * @param aIndex @@ -139,7 +139,7 @@ PlacesController.prototype = { return true; } - // All other Places Commands are prefixed with "placesCmd_" ... this + // All other Places Commands are prefixed with "placesCmd_" ... this // filters out other commands that we do _not_ support (see 329587). const CMD_PREFIX = "placesCmd_"; return (aCommand.substr(0, CMD_PREFIX.length) == CMD_PREFIX); @@ -178,7 +178,7 @@ PlacesController.prototype = { if (this._view.selType != "single") { let rootNode = this._view.result.root; if (rootNode.containerOpen && rootNode.childCount > 0) - return true; + return true; } return false; case "placesCmd_open": @@ -308,9 +308,9 @@ PlacesController.prototype = { onEvent: function PC_onEvent(eventName) { }, - + /** - * Determine whether or not the selection can be removed, either by the + * Determine whether or not the selection can be removed, either by the * delete or cut operations based on whether or not any of its contents * are non-removable. We don't need to worry about recursion here since it * is a policy decision that a removable item not be placed inside a non- @@ -373,14 +373,14 @@ PlacesController.prototype = { var root = this._view.result.root; for (var i = 0; i < nodes.length; ++i) { if (nodes[i] == root) - return true; + return true; } return false; }, /** - * Looks at the data on the clipboard to see if it is paste-able. + * Looks at the data on the clipboard to see if it is paste-able. * Paste-able data is: * - in a format that the view can receive * @returns true if: - clipboard data is of a TYPE_X_MOZ_PLACE_* flavor, @@ -427,7 +427,7 @@ PlacesController.prototype = { } }, - /** + /** * Gathers information about the selected nodes according to the following * rules: * "link" node is a URI @@ -442,7 +442,7 @@ PlacesController.prototype = { * * @returns an array of objects corresponding the selected nodes. Each * object has each of the properties above set if its corresponding - * node matches the rule. In addition, the annotations names for each + * node matches the rule. In addition, the annotations names for each * node are set on its corresponding object as properties. * Notes: * 1) This can be slow, so don't call it anywhere performance critical! @@ -456,7 +456,7 @@ PlacesController.prototype = { if (nodes.length == 0) nodes.push(root); // See the second note above - for (var i=0; i < nodes.length; i++) { + for (var i = 0; i < nodes.length; i++) { var nodeData = {}; var node = nodes[i]; var nodeType = node.type; @@ -514,16 +514,16 @@ PlacesController.prototype = { // annotations if (uri) { - var names = PlacesUtils.annotations.getPageAnnotationNames(uri); - for (var j = 0; j < names.length; ++j) + let names = PlacesUtils.annotations.getPageAnnotationNames(uri); + for (let j = 0; j < names.length; ++j) nodeData[names[j]] = true; } // For items also include the item-specific annotations if (node.itemId != -1) { - names = PlacesUtils.annotations - .getItemAnnotationNames(node.itemId); - for (j = 0; j < names.length; ++j) + let names = PlacesUtils.annotations + .getItemAnnotationNames(node.itemId); + for (let j = 0; j < names.length; ++j) nodeData[names[j]] = true; } metadata.push(nodeData); @@ -532,14 +532,14 @@ PlacesController.prototype = { return metadata; }, - /** + /** * Determines if a context-menu item should be shown * @param aMenuItem - * the context menu item + * the context menu item * @param aMetaData * meta data about the selection * @returns true if the conditions (see buildContextMenu) are satisfied - * and the item can be displayed, false otherwise. + * and the item can be displayed, false otherwise. */ _shouldShowMenuItem: function PC__shouldShowMenuItem(aMenuItem, aMetaData) { var selectiontype = aMenuItem.getAttribute("selectiontype"); @@ -551,8 +551,8 @@ PlacesController.prototype = { var forceHideAttr = aMenuItem.getAttribute("forcehideselection"); if (forceHideAttr) { var forceHideRules = forceHideAttr.split("|"); - for (var i = 0; i < aMetaData.length; ++i) { - for (var j=0; j < forceHideRules.length; ++j) { + for (let i = 0; i < aMetaData.length; ++i) { + for (let j = 0; j < forceHideRules.length; ++j) { if (forceHideRules[j] in aMetaData[i]) return false; } @@ -682,7 +682,7 @@ PlacesController.prototype = { }, /** - * Select all links in the current view. + * Select all links in the current view. */ selectAll: function PC_selectAll() { this._view.selectAll(); @@ -691,7 +691,7 @@ PlacesController.prototype = { /** * Opens the bookmark properties for the selected URI Node. */ - showBookmarkPropertiesForSelection: + showBookmarkPropertiesForSelection: function PC_showBookmarkPropertiesForSelection() { var node = this._view.selectedNode; if (!node) @@ -717,7 +717,7 @@ PlacesController.prototype = { * receive a string instead of an nsIURI object. */ _assertURINotString: function PC__assertURINotString(value) { - NS_ASSERT((typeof(value) == "object") && !(value instanceof String), + NS_ASSERT((typeof(value) == "object") && !(value instanceof String), "This method should be passed a URI as a nsIURI object, not as a string."); }, @@ -759,16 +759,15 @@ PlacesController.prototype = { var messageKey = "tabs.openWarningMultipleBranded"; var openKey = "tabs.openButtonMultiple"; - var strings = document.getElementById("placeBundle"); const BRANDING_BUNDLE_URI = "chrome://branding/locale/brand.properties"; var brandShortName = Cc["@mozilla.org/intl/stringbundle;1"]. getService(Ci.nsIStringBundleService). createBundle(BRANDING_BUNDLE_URI). GetStringFromName("brandShortName"); - + var buttonPressed = promptService.confirmEx(window, PlacesUIUtils.getString("tabs.openWarningTitle"), - PlacesUIUtils.getFormattedString(messageKey, + PlacesUIUtils.getFormattedString(messageKey, [numTabsToOpen, brandShortName]), (promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0) + (promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1), @@ -788,7 +787,7 @@ PlacesController.prototype = { }, /** - * Opens the links in the selected folder, or the selected links in new tabs. + * Opens the links in the selected folder, or the selected links in new tabs. */ openSelectionInTabs: function PC_openLinksInTabs(aEvent) { var node = this._view.selectedNode; @@ -828,7 +827,7 @@ PlacesController.prototype = { /** * Create a new Bookmark folder somewhere. Prompts the user for the name - * of the folder. + * of the folder. */ newFolder: function PC_newFolder() { var ip = this._view.insertionPoint; @@ -880,17 +879,17 @@ PlacesController.prototype = { /** * Walk the list of folders we're removing in this delete operation, and - * see if the selected node specified is already implicitly being removed - * because it is a child of that folder. + * see if the selected node specified is already implicitly being removed + * because it is a child of that folder. * @param node - * Node to check for containment. + * Node to check for containment. * @param pastFolders * List of folders the calling function has already traversed - * @returns true if the node should be skipped, false otherwise. + * @returns true if the node should be skipped, false otherwise. */ _shouldSkipNode: function PC_shouldSkipNode(node, pastFolders) { /** - * Determines if a node is contained by another node within a resultset. + * Determines if a node is contained by another node within a resultset. * @param node * The node to check for containment for * @param parent @@ -906,7 +905,7 @@ PlacesController.prototype = { } return false; } - + for (var j = 0; j < pastFolders.length; ++j) { if (isContainedBy(node, pastFolders[j])) return true; @@ -915,10 +914,10 @@ PlacesController.prototype = { }, /** - * Creates a set of transactions for the removal of a range of items. + * Creates a set of transactions for the removal of a range of items. * A range is an array of adjacent nodes in a view. * @param [in] range - * An array of nodes to remove. Should all be adjacent. + * An array of nodes to remove. Should all be adjacent. * @param [out] transactions * An array of transactions. * @param [optional] removedFolders @@ -1090,7 +1089,7 @@ PlacesController.prototype = { var root = this._view.result.root; - if (PlacesUtils.nodeIsFolder(root)) + if (PlacesUtils.nodeIsFolder(root)) this._removeRowsFromBookmarks(aTxnName); else if (PlacesUtils.nodeIsQuery(root)) { var queryType = PlacesUtils.asQuery(root).queryOptions.queryType; @@ -1136,7 +1135,7 @@ PlacesController.prototype = { addData(PlacesUtils.TYPE_HTML, index, overrideURI); } - // This order is _important_! It controls how this and other + // This order is _important_! It controls how this and other // applications select data to be inserted based on type. addData(PlacesUtils.TYPE_X_MOZ_PLACE, i); @@ -1166,8 +1165,8 @@ PlacesController.prototype = { try { let nodes = this._view.selectedNodes; - let xferable = Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); + let xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); let foundFolder = false, foundLink = false; let copiedFolders = []; let placeString, mozURLString, htmlString, unicodeString; @@ -1179,11 +1178,11 @@ PlacesController.prototype = { continue; if (PlacesUtils.nodeIsFolder(node)) copiedFolders.push(node); - + function generateChunk(type, overrideURI) { let suffix = i < (nodes.length - 1) ? PlacesUtils.endl : ""; let uri = overrideURI; - + if (PlacesUtils.nodeIsLivemarkContainer(node)) uri = PlacesUtils.livemarks.getFeedURI(node.itemId).spec @@ -1194,8 +1193,8 @@ PlacesController.prototype = { htmlString += (PlacesUtils.wrapNode(node, PlacesUtils.TYPE_HTML, uri) + suffix); - var placeSuffix = i < (nodes.length - 1) ? "," : ""; - var resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node); + let placeSuffix = i < (nodes.length - 1) ? "," : ""; + let resolveShortcuts = !PlacesControllerDragHelper.canMoveNode(node); return PlacesUtils.wrapNode(node, type, overrideURI, resolveShortcuts) + placeSuffix; } @@ -1207,7 +1206,7 @@ PlacesController.prototype = { xferable.addDataFlavor(type); xferable.setTransferData(type, PlacesUIUtils._wrapString(data), data.length * 2); } - // This order is _important_! It controls how this and other applications + // This order is _important_! It controls how this and other applications // select data to be inserted based on type. if (placeString) addData(PlacesUtils.TYPE_X_MOZ_PLACE, placeString); @@ -1241,11 +1240,11 @@ PlacesController.prototype = { */ paste: function PC_paste() { // Strategy: - // - // There can be data of various types (folder, separator, link) on the + // + // There can be data of various types (folder, separator, link) on the // clipboard. We need to get all of that data and build edit transactions - // for them. This means asking the clipboard once for each type and - // aggregating the results. + // for them. This means asking the clipboard once for each type and + // aggregating the results. /** * Constructs a transferable that can receive data of specific types. @@ -1255,10 +1254,9 @@ PlacesController.prototype = { * @returns The transferable. */ function makeXferable(types) { - var xferable = - Cc["@mozilla.org/widget/transferable;1"]. - createInstance(Ci.nsITransferable); - for (var i = 0; i < types.length; ++i) + var xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + for (var i = 0; i < types.length; ++i) xferable.addDataFlavor(types[i]); return xferable; } @@ -1291,10 +1289,10 @@ PlacesController.prototype = { if (ip.isTag) { var uri = PlacesUtils._uri(items[i].uri); txn = PlacesUIUtils.ptm.tagURI(uri, [ip.itemId]); - } + } else { // adjusted to make sure that items are given the correct index - // transactions insert differently if index == -1 + // transactions insert differently if index == -1 // transaction will enqueue the item. if (ip.index > -1) index = ip.index + i; @@ -1307,18 +1305,18 @@ PlacesController.prototype = { } catch (e) { // getAnyTransferData will throw if there is no data of the specified - // type on the clipboard. + // type on the clipboard. // unwrapNodes will throw if the data that is present is malformed in - // some way. + // some way. // In either case, don't fail horribly, just return no data. } return []; } // Get transactions to paste any folders, separators or links that might - // be on the clipboard, aggregate them and execute them. + // be on the clipboard, aggregate them and execute them. var transactions = getTransactions([PlacesUtils.TYPE_X_MOZ_PLACE, - PlacesUtils.TYPE_X_MOZ_URL, + PlacesUtils.TYPE_X_MOZ_URL, PlacesUtils.TYPE_UNICODE]); var txn = PlacesUIUtils.ptm.aggregateTransactions("Paste", transactions); PlacesUIUtils.ptm.doTransaction(txn); @@ -1336,8 +1334,8 @@ PlacesController.prototype = { /** * Handles drag and drop operations for views. Note that this is view agnostic! * You should not use PlacesController._view within these methods, since - * the view that the item(s) have been dropped on was not necessarily active. - * Drop functions are passed the view that is being dropped on. + * the view that the item(s) have been dropped on was not necessarily active. + * Drop functions are passed the view that is being dropped on. */ let PlacesControllerDragHelper = { /** @@ -1416,7 +1414,8 @@ let PlacesControllerDragHelper = { let dragged; try { dragged = PlacesUtils.unwrapNodes(data, flavor)[0]; - } catch (e) { + } + catch (e) { return false; } @@ -1442,10 +1441,10 @@ let PlacesControllerDragHelper = { return true; }, - + /** * Determines if a node can be moved. - * + * * @param aNode * A nsINavHistoryResultNode node. * @returns True if the node can be moved, false otherwise. @@ -1477,7 +1476,7 @@ let PlacesControllerDragHelper = { /** * Determines if a container node can be moved. - * + * * @param aId * A bookmark folder id. * @param [optional] aParentId diff --git a/browser/components/places/content/sidebarUtils.js b/browser/components/places/content/sidebarUtils.js index 13ac90b2801d..2a97cb75960d 100644 --- a/browser/components/places/content/sidebarUtils.js +++ b/browser/components/places/content/sidebarUtils.js @@ -132,6 +132,6 @@ var SidebarUtils = { }, clearURLFromStatusBar: function SU_clearURLFromStatusBar() { - window.top.XULBrowserWindow.setOverLink("", null); + window.top.XULBrowserWindow.setOverLink("", null); } }; diff --git a/browser/components/places/src/PlacesUIUtils.jsm b/browser/components/places/src/PlacesUIUtils.jsm index 6841f0bcfb26..5aa0db777125 100644 --- a/browser/components/places/src/PlacesUIUtils.jsm +++ b/browser/components/places/src/PlacesUIUtils.jsm @@ -72,7 +72,7 @@ var PlacesUIUtils = { * @returns A URI object for the spec. */ createFixedURI: function PUIU_createFixedURI(aSpec) { - return URIFixup.createFixupURI(aSpec, 0); + return URIFixup.createFixupURI(aSpec, Ci.nsIURIFixup.FIXUP_FLAG_NONE); }, /** @@ -128,7 +128,7 @@ var PlacesUIUtils = { */ _getBookmarkItemCopyTransaction: function PUIU__getBookmarkItemCopyTransaction(aData, aContainer, aIndex, - aExcludeAnnotations) { + aExcludeAnnotations) { var itemURL = PlacesUtils._uri(aData.uri); var itemTitle = aData.title; var keyword = aData.keyword || null; @@ -278,7 +278,7 @@ var PlacesUIUtils = { * the move/insert. */ makeTransaction: function PUIU_makeTransaction(data, type, container, - index, copy) { + index, copy) { switch (data.type) { case PlacesUtils.TYPE_X_MOZ_PLACE_CONTAINER: if (copy) @@ -289,7 +289,7 @@ var PlacesUIUtils = { case PlacesUtils.TYPE_X_MOZ_PLACE: if (data.id == -1) // Not bookmarked. return this._getURIItemCopyTransaction(data, container, index); - + if (copy) return this._getBookmarkItemCopyTransaction(data, container, index); // Otherwise move the item. @@ -361,14 +361,14 @@ var PlacesUIUtils = { * bookmarks root folder. */ showAddBookmarkUI: function PUIU_showAddBookmarkUI(aURI, - aTitle, - aDescription, - aDefaultInsertionPoint, - aShowPicker, - aLoadInSidebar, - aKeyword, - aPostData, - aCharSet) { + aTitle, + aDescription, + aDefaultInsertionPoint, + aShowPicker, + aLoadInSidebar, + aKeyword, + aPostData, + aCharSet) { var info = { action: "add", type: "bookmark" @@ -417,9 +417,9 @@ var PlacesUIUtils = { */ showMinimalAddBookmarkUI: function PUIU_showMinimalAddBookmarkUI(aURI, aTitle, aDescription, - aDefaultInsertionPoint, aShowPicker, - aLoadInSidebar, aKeyword, aPostData, - aCharSet) { + aDefaultInsertionPoint, aShowPicker, + aLoadInSidebar, aKeyword, aPostData, + aCharSet) { var info = { action: "add", type: "bookmark", @@ -485,11 +485,11 @@ var PlacesUIUtils = { * bookmarks root folder. */ showAddLivemarkUI: function PUIU_showAddLivemarkURI(aFeedURI, - aSiteURI, - aTitle, - aDescription, - aDefaultInsertionPoint, - aShowPicker) { + aSiteURI, + aTitle, + aDescription, + aDefaultInsertionPoint, + aShowPicker) { var info = { action: "add", type: "livemark" @@ -525,8 +525,8 @@ var PlacesUIUtils = { */ showMinimalAddLivemarkUI: function PUIU_showMinimalAddLivemarkURI(aFeedURI, aSiteURI, aTitle, - aDescription, aDefaultInsertionPoint, - aShowPicker) { + aDescription, aDefaultInsertionPoint, + aShowPicker) { var info = { action: "add", type: "livemark", @@ -706,7 +706,7 @@ var PlacesUIUtils = { /** * By calling this before visiting an URL, the visit will be associated to a * TRANSITION_BOOKMARK transition. - * This is used when visiting pages from the bookmarks menu, + * This is used when visiting pages from the bookmarks menu, * personal toolbar, and bookmarks from within the places organizer. * If this is not called visits will be marked as TRANSITION_LINK. */ @@ -730,6 +730,8 @@ var PlacesUIUtils = { * bookmarked (see bug 224521). * @param aURINode * a URI node + * @param aWindow + * a window on which a potential error alert is shown on. * @return true if it's safe to open the node in the browser, false otherwise. * */ @@ -890,7 +892,7 @@ var PlacesUIUtils = { openNodeWithEvent: function PUIU_openNodeWithEvent(aNode, aEvent) { this.openNodeIn(aNode, this._getCurrentActiveWin().whereToOpenLink(aEvent)); }, - + /** * Loads the node's URL in the appropriate tab or window or as a * web panel. @@ -927,12 +929,12 @@ var PlacesUIUtils = { * Used to avoid nsIURI overhead in frequently called UI functions. * * @param aUrlString the url to guess the scheme from. - * + * * @return guessed scheme for this url string. * * @note this is not supposed be perfect, so use it only for UI purposes. */ - guessUrlSchemeForUI: function PUU_guessUrlSchemeForUI(aUrlString) { + guessUrlSchemeForUI: function PUIU_guessUrlSchemeForUI(aUrlString) { return aUrlString.substr(0, aUrlString.indexOf(":")); }, @@ -961,7 +963,7 @@ var PlacesUIUtils = { return title || this.getString("noTitle"); }, - get leftPaneQueries() { + get leftPaneQueries() { // build the map this.leftPaneFolderId; return this.leftPaneQueries; @@ -1239,7 +1241,7 @@ var PlacesUIUtils = { queryName = name; } } - return queryName; + return queryName; } }; @@ -1380,7 +1382,7 @@ XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ptm", function() { /** * Transaction for editing a the description of a bookmark or a folder. - * + * * @param aItemId * id of the item to edit. * @param aDescription From 6c816884ba8ad4877a8ae71c0ed5502be1c35e50 Mon Sep 17 00:00:00 2001 From: Robert Kaiser Date: Wed, 11 Aug 2010 18:05:10 +0200 Subject: [PATCH 273/369] bug 570788 - Incorporate improvements from SeaMonkey places review into Firefox code, round 4 (tree), r=mak a=gavin --- browser/components/places/content/tree.xml | 1 - browser/components/places/content/treeView.js | 48 ++++++++----------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/browser/components/places/content/tree.xml b/browser/components/places/content/tree.xml index ad46c1e753cc..eed2cb61f66f 100644 --- a/browser/components/places/content/tree.xml +++ b/browser/components/places/content/tree.xml @@ -780,4 +780,3 @@ - diff --git a/browser/components/places/content/treeView.js b/browser/components/places/content/treeView.js index a4d97fbeffe3..970824a89762 100644 --- a/browser/components/places/content/treeView.js +++ b/browser/components/places/content/treeView.js @@ -37,11 +37,21 @@ * * ***** END LICENSE BLOCK ***** */ +function PlacesTreeView(aFlatList, aOnOpenFlatContainer) { + this._tree = null; + this._result = null; + this._selection = null; + this._rootNode = null; + this._rows = []; + this._flatList = aFlatList; + this._openContainerCallback = aOnOpenFlatContainer; +} + PlacesTreeView.prototype = { _makeAtom: function PTV__makeAtom(aString) { - return Cc["@mozilla.org/atom-service;1"]. - getService(Ci.nsIAtomService). - getAtom(aString); + return Cc["@mozilla.org/atom-service;1"]. + getService(Ci.nsIAtomService). + getAtom(aString); }, _atoms: [], @@ -122,8 +132,7 @@ PlacesTreeView.prototype = { if (!(aContainer instanceof Ci.nsINavHistoryQueryResultNode)) return aContainer._plainContainer = false; - let resultType = aContainer.queryOptions.resultType; - switch (resultType) { + switch (aContainer.queryOptions.resultType) { case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_QUERY: case Ci.nsINavHistoryQueryOptions.RESULTS_AS_SITE_QUERY: case Ci.nsINavHistoryQueryOptions.RESULTS_AS_DATE_SITE_QUERY: @@ -602,8 +611,7 @@ PlacesTreeView.prototype = { return; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let parentRow; @@ -632,7 +640,7 @@ PlacesTreeView.prototype = { // children themselves that would be in the way. let cc = aParentNode.childCount; let separatorsAreHidden = PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE; + this.isSorted(); for (let i = aNewIndex + 1; i < cc; i++) { let node = aParentNode.getChild(i); if (!separatorsAreHidden || PlacesUtils.nodeIsSeparator(node)) { @@ -679,8 +687,7 @@ PlacesTreeView.prototype = { throw Cr.NS_ERROR_NOT_IMPLEMENTED; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let parentRow = aParentNode == this._rootNode ? @@ -728,8 +735,7 @@ PlacesTreeView.prototype = { return; // Bail out for hidden separators. - if (PlacesUtils.nodeIsSeparator(aNode) && - this._result.sortingMode != Ci.nsINavHistoryQueryOptions.SORT_BY_NONE) + if (PlacesUtils.nodeIsSeparator(aNode) && this.isSorted()) return; let oldRow = this._getRowForNode(aNode, true); @@ -1204,7 +1210,7 @@ PlacesTreeView.prototype = { else { // Use the last-selected node's container. container = lastSelected.parent; - + // During its Drag & Drop operation, the tree code closes-and-opens // containers very often (part of the XUL "spring-loaded folders" // implementation). And in certain cases, we may reach a closed @@ -1309,8 +1315,7 @@ PlacesTreeView.prototype = { getCellText: function PTV_getCellText(aRow, aColumn) { let node = this._getNodeForRow(aRow); - let columnType = this._getColumnType(aColumn); - switch (columnType) { + switch (this._getColumnType(aColumn)) { case this.COLUMN_TYPE_TITLE: // normally, this is just the title, but we don't want empty items in // the tree view so return a special string if the title is empty. @@ -1428,8 +1433,7 @@ PlacesTreeView.prototype = { let newSort; let newSortingAnnotation = ""; const NHQO = Ci.nsINavHistoryQueryOptions; - let columnType = this._getColumnType(aColumn); - switch (columnType) { + switch (this._getColumnType(aColumn)) { case this.COLUMN_TYPE_TITLE: if (oldSort == NHQO.SORT_BY_TITLE_ASCENDING) newSort = NHQO.SORT_BY_TITLE_DESCENDING; @@ -1576,13 +1580,3 @@ PlacesTreeView.prototype = { performActionOnRow: function(aAction, aRow) { }, performActionOnCell: function(aAction, aRow, aColumn) { } }; - -function PlacesTreeView(aFlatList, aOnOpenFlatContainer) { - this._tree = null; - this._result = null; - this._selection = null; - this._rootNode = null; - this._rows = []; - this._flatList = aFlatList; - this._openContainerCallback = aOnOpenFlatContainer; -} From b073c9e6a30529eef57149e3ed0063fd4949fe26 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Wed, 11 Aug 2010 18:05:12 +0200 Subject: [PATCH 274/369] bug 586183 - Fix for embedding/ when --disable-ipc is used. r+a=bsmedberg --- embedding/browser/webBrowser/nsWebBrowser.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embedding/browser/webBrowser/nsWebBrowser.cpp b/embedding/browser/webBrowser/nsWebBrowser.cpp index 365eb1338479..bea786f9dd31 100644 --- a/embedding/browser/webBrowser/nsWebBrowser.cpp +++ b/embedding/browser/webBrowser/nsWebBrowser.cpp @@ -1233,7 +1233,10 @@ NS_IMETHODIMP nsWebBrowser::Create() } mDocShellAsNav->SetSessionHistory(mInitInfo->sessionHistory); - if (XRE_GetProcessType() == GeckoProcessType_Default) { +#ifdef MOZ_IPC + if (XRE_GetProcessType() == GeckoProcessType_Default) +#endif + { // Hook up global history. Do not fail if we can't - just warn. rv = EnableGlobalHistory(mShouldEnableHistory); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "EnableGlobalHistory() failed"); From 0b585cc0a708817adab9851664598995bc113ccb Mon Sep 17 00:00:00 2001 From: Justin Wood Date: Wed, 11 Aug 2010 18:05:14 +0200 Subject: [PATCH 275/369] bug 586187 - Build failure in LayerManagerD3D9 due to --disable-ipc not also defining Unicode, r=khuey a=bustage --- gfx/layers/d3d9/DeviceManagerD3D9.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gfx/layers/d3d9/DeviceManagerD3D9.cpp b/gfx/layers/d3d9/DeviceManagerD3D9.cpp index bf04df6ae4a1..051f199c853b 100644 --- a/gfx/layers/d3d9/DeviceManagerD3D9.cpp +++ b/gfx/layers/d3d9/DeviceManagerD3D9.cpp @@ -199,9 +199,9 @@ DeviceManagerD3D9::Init() } } - mFocusWnd = CreateWindow(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW, - CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, - NULL, GetModuleHandle(NULL), NULL); + mFocusWnd = ::CreateWindowW(kClassName, L"D3D9Window", WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, + NULL, GetModuleHandle(NULL), NULL); if (!mFocusWnd) { NS_WARNING("Failed to create DeviceManagerD3D9 Window."); From 984c9dfea0bad308c37f176d81b1299d4a4cd2aa Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 17:52:21 +0100 Subject: [PATCH 276/369] bug 553963 - part 1 - fix handling of overlong text runs in the uniscribe shaper. r=jdaggett --- gfx/thebes/gfxFont.cpp | 1 + gfx/thebes/gfxUniscribeShaper.cpp | 116 +++++++++++++++--------------- 2 files changed, 59 insertions(+), 58 deletions(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index daca44bd8fed..fc791731e7b4 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -3627,6 +3627,7 @@ PRUint32 gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset) { NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun"); + NS_ASSERTION(mGlyphRuns.Length() > 0, "no glyph runs present!"); if (aOffset == mCharacterCount) return mGlyphRuns.Length(); PRUint32 start = 0; diff --git a/gfx/thebes/gfxUniscribeShaper.cpp b/gfx/thebes/gfxUniscribeShaper.cpp index 63dda2c7d6d2..84163527a96f 100644 --- a/gfx/thebes/gfxUniscribeShaper.cpp +++ b/gfx/thebes/gfxUniscribeShaper.cpp @@ -394,48 +394,15 @@ private: PRPackedBool mFontSelected; }; -#define MAX_ITEM_LENGTH 32768 - -static PRUint32 FindNextItemStart(int aOffset, int aLimit, - nsTArray &aLogAttr, - const PRUnichar *aString) -{ - if (aOffset + MAX_ITEM_LENGTH >= aLimit) { - // The item starting at aOffset can't be longer than the max length, - // so starting the next item at aLimit won't cause ScriptShape() to fail. - return aLimit; - } - - // Try to start the next item before or after a space, since spaces - // don't kern or ligate. - PRUint32 off; - int boundary = -1; - for (off = MAX_ITEM_LENGTH; off > 1; --off) { - if (aLogAttr[off].fCharStop) { - if (off > boundary) { - boundary = off; - } - if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ') - return aOffset+off; - } - } - - // Try to start the next item at the last cluster boundary in the range. - if (boundary > 0) { - return aOffset+boundary; - } - - // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break - // on the size limit. It won't be visually plesaing, but at least it - // won't cause ScriptShape() to fail. - return aOffset + MAX_ITEM_LENGTH; -} +#define MAX_ITEM_LENGTH 16384 class Uniscribe { public: - Uniscribe(const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) : - mString(aString), mLength(aLength), mIsRTL(aIsRTL) + Uniscribe(const PRUnichar *aString, + PRUint32 aLength, + gfxTextRun *aTextRun): + mString(aString), mLength(aLength), mTextRun(aTextRun) { } ~Uniscribe() { @@ -446,44 +413,79 @@ public: memset(&mState, 0, sizeof(SCRIPT_STATE)); // Lock the direction. Don't allow the itemizer to change directions // based on character type. - mState.uBidiLevel = mIsRTL; + mState.uBidiLevel = mTextRun->IsRightToLeft() ? 1 : 0; mState.fOverrideDirection = PR_TRUE; } private: +// We try to avoid calling Uniscribe with text runs that may generate +// more than this many glyphs, because of the possibility of arithmetic +// overflow of 16-bit variables. If long runs need to be split because +// of this, we'll look for whitespace to break on so that shaping needed +// (e.g. for complex scripts) should be unaffected. +#define MAX_UNISCRIBE_GLYPHS 32767 + // Append mItems[aIndex] to aDest, adding extra items to aDest to ensure // that no item is too long for ScriptShape() to handle. See bug 366643. nsresult CopyItemSplitOversize(int aIndex, nsTArray &aDest) { aDest.AppendElement(mItems[aIndex]); - const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; - if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) { - // This items length would cause ScriptShape() to fail. We need to - // add extra items here so that no item's length could cause the fail. - - // Get cluster boundaries, so we can break cleanly if possible. - nsTArray logAttr; - if (!logAttr.SetLength(itemLength)) - return NS_ERROR_FAILURE; - HRESULT rv = ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength, - &mItems[aIndex].a, logAttr.Elements()); - if (FAILED(rv)) - return NS_ERROR_FAILURE; + const int itemLength = + mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; + if (ESTIMATE_MAX_GLYPHS(itemLength) > MAX_UNISCRIBE_GLYPHS) { + // This item's length would cause ScriptShape() to fail. + // We need to add extra items here so that no item's length + // could cause the fail. + // We break on whitespace or cluster boundaries if possible. const int nextItemStart = mItems[aIndex+1].iCharPos; int start = FindNextItemStart(mItems[aIndex].iCharPos, - nextItemStart, logAttr, mString); + nextItemStart); while (start < nextItemStart) { SCRIPT_ITEM item = mItems[aIndex]; item.iCharPos = start; aDest.AppendElement(item); - start = FindNextItemStart(start, nextItemStart, logAttr, mString); + start = FindNextItemStart(start, nextItemStart); } } return NS_OK; } + PRUint32 FindNextItemStart(int aOffset, int aLimit) { + if (aOffset + MAX_ITEM_LENGTH >= aLimit) { + // The item starting at aOffset can't be longer than max length, + // so starting the next item at aLimit won't cause ScriptShape() + // to fail. + return aLimit; + } + // Try to start the next item before or after a space, since spaces + // don't kern or ligate. + PRInt32 off; + int boundary = -1; + for (off = MAX_ITEM_LENGTH; off > 1; --off) { + if (mTextRun->IsClusterStart(off)) { + if (off > boundary) { + boundary = off; + } + if (mString[aOffset+off] == ' ' || + mString[aOffset+off - 1] == ' ') { + return aOffset+off; + } + } + } + + // Try to start the next item at last cluster boundary in the range. + if (boundary > 0) { + return aOffset+boundary; + } + + // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break + // on the size limit. It won't be visually pleasing, but at least it + // won't cause ScriptShape() to fail. + return aOffset + MAX_ITEM_LENGTH; + } + public: int Itemize() { @@ -537,7 +539,7 @@ public: private: const PRUnichar *mString; const PRUint32 mLength; - const PRBool mIsRTL; + gfxTextRun *mTextRun; SCRIPT_CONTROL mControl; SCRIPT_STATE mState; @@ -556,15 +558,13 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, { DCFromContext aDC(aContext); - const PRBool isRTL = aTextRun->IsRightToLeft(); - PRBool result = PR_TRUE; HRESULT rv; gfxGDIFont *font = static_cast(mFont); AutoSelectFont fs(aDC, font->GetHFONT()); - Uniscribe us(aString + aRunStart, aRunLength, isRTL); + Uniscribe us(aString + aRunStart, aRunLength, aTextRun); /* itemize the string */ int numItems = us.Itemize(); From e15470b88aaf25ddb973ba060f056eed2cfd8320 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 17:52:23 +0100 Subject: [PATCH 277/369] bug 553963 - part 2 - make gfxFont handle text-shaping failure more robustly. r=roc --- gfx/thebes/gfxFont.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index fc791731e7b4..2aa550fb8c9c 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2235,17 +2235,19 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, PRUint32 matchedLength = range.Length(); gfxFont *matchedFont = (range.font ? range.font.get() : nsnull); + // create the glyph run for this range + aTextRun->AddGlyphRun(matchedFont ? matchedFont : mainFont, + runStart, (matchedLength > 0)); if (matchedFont) { - // create the glyph run for this range - aTextRun->AddGlyphRun(matchedFont, runStart, (matchedLength > 0)); - // do glyph layout and record the resulting positioned glyphs - matchedFont->InitTextRun(aContext, aTextRun, aString, - runStart, matchedLength, aRunScript); - } else { - // create the glyph run before calling SetMissing Glyph - aTextRun->AddGlyphRun(mainFont, runStart, matchedLength); - + if (!matchedFont->InitTextRun(aContext, aTextRun, aString, + runStart, matchedLength, + aRunScript)) { + // glyph layout failed! treat as missing glyphs + matchedFont = nsnull; + } + } + if (!matchedFont) { for (PRUint32 index = runStart; index < runStart + matchedLength; index++) { // Record the char code so we can draw a box with the Unicode value if (NS_IS_HIGH_SURROGATE(aString[index]) && From 1270fcec82681d8bd9416a3342fc3530812b868f Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 17:52:26 +0100 Subject: [PATCH 278/369] bug 553963 - part 3 - don't reset glyph runs when falling back from uniscribe to GDI text shaping. r=jdaggett --- gfx/thebes/gfxUniscribeShaper.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gfx/thebes/gfxUniscribeShaper.cpp b/gfx/thebes/gfxUniscribeShaper.cpp index 84163527a96f..a9785160580a 100644 --- a/gfx/thebes/gfxUniscribeShaper.cpp +++ b/gfx/thebes/gfxUniscribeShaper.cpp @@ -633,10 +633,9 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, } if (FAILED(rv)) { - aTextRun->ResetGlyphRuns(); // Uniscribe doesn't like this font for some reason. - // Returning FALSE will make the gfxGDIFont discard this - // shaper and replace it with a "dumb" GDI one. + // Returning FALSE will make the gfxGDIFont retry with the + // "dumb" GDI one, unless useUniscribeOnly was set. result = PR_FALSE; break; } From 907d28a252d4e8626ec7e5809d0af77a094c50e9 Mon Sep 17 00:00:00 2001 From: Michael Yoshitaka Erlewine Date: Wed, 11 Aug 2010 13:06:49 -0400 Subject: [PATCH 279/369] Bug 582023: making changes requested by Dao. Now iQ + Utils are ready for m-c with r=dao! --- browser/base/content/tabview/iq.js | 107 +++++++-------- .../base/content/tabview/modules/utils.jsm | 122 +++++++++--------- 2 files changed, 109 insertions(+), 120 deletions(-) diff --git a/browser/base/content/tabview/iq.js b/browser/base/content/tabview/iq.js index 9066189019e9..690874d51280 100644 --- a/browser/base/content/tabview/iq.js +++ b/browser/base/content/tabview/iq.js @@ -13,8 +13,7 @@ * * The Original Code is iq.js. * - * The Initial Developer of the Original Code is - * the Mozilla Foundation + * The Initial Developer of the Original Code is the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * @@ -124,7 +123,7 @@ let iQClass = function(selector, context) { } } else { - Utils.assert(false, 'does not support complex HTML creation'); + Utils.assert(false, 'does not support complex HTML creation'); } return Utils.merge(this, selector); @@ -210,10 +209,8 @@ iQClass.prototype = { // Function: addClass // Adds the given class(es) to the receiver. addClass: function(value) { - if (typeof value != "string" || !value) { - Utils.assert(false, 'requires a valid string argument'); - return null; - } + Utils.assertThrow(typeof value == "string" && value, + 'requires a valid string argument'); let length = this.length; for (let i = 0; i < length; i++) { @@ -528,57 +525,53 @@ iQClass.prototype = { // complete - function to call once the animation is done, takes nothing // in, but "this" is set to the element that was animated. animate: function(css, options) { - try { - Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); + Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)'); - if (!options) - options = {}; + if (!options) + options = {}; - let easings = { - tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)", - // TODO: change 1.0 above to 1.29 after bug 575672 is fixed - - easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care - fast: 'cubic-bezier(0.7,0,1,1)' - }; + let easings = { + tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.0)", + // TODO: change 1.0 above to 1.29 after bug 575672 is fixed + + easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care + fast: 'cubic-bezier(0.7,0,1,1)' + }; - let duration = (options.duration || 400); - let easing = (easings[options.easing] || 'ease'); + let duration = (options.duration || 400); + let easing = (easings[options.easing] || 'ease'); - // The latest versions of Firefox do not animate from a non-explicitly - // set css properties. So for each element to be animated, go through - // and explicitly define 'em. - let rupper = /([A-Z])/g; - this.each(function(elem) { - let cStyle = window.getComputedStyle(elem, null); - for (let prop in css) { - prop = prop.replace(rupper, "-$1").toLowerCase(); - iQ(elem).css(prop, cStyle.getPropertyValue(prop)); - } + // The latest versions of Firefox do not animate from a non-explicitly + // set css properties. So for each element to be animated, go through + // and explicitly define 'em. + let rupper = /([A-Z])/g; + this.each(function(elem) { + let cStyle = window.getComputedStyle(elem, null); + for (let prop in css) { + prop = prop.replace(rupper, "-$1").toLowerCase(); + iQ(elem).css(prop, cStyle.getPropertyValue(prop)); + } + }); + + this.css({ + '-moz-transition-property': 'all', // TODO: just animate the properties we're changing + '-moz-transition-duration': (duration / 1000) + 's', + '-moz-transition-timing-function': easing + }); + + this.css(css); + + let self = this; + setTimeout(function() { + self.css({ + '-moz-transition-property': 'none', + '-moz-transition-duration': '', + '-moz-transition-timing-function': '' }); - this.css({ - '-moz-transition-property': 'all', // TODO: just animate the properties we're changing - '-moz-transition-duration': (duration / 1000) + 's', - '-moz-transition-timing-function': easing - }); - - this.css(css); - - let self = this; - setTimeout(function() { - self.css({ - '-moz-transition-property': 'none', - '-moz-transition-duration': '', - '-moz-transition-timing-function': '' - }); - - if (typeof options.complete == "function") - options.complete.apply(self); - }, duration); - } catch(e) { - Utils.log(e); - } + if (typeof options.complete == "function") + options.complete.apply(self); + }, duration); return this; }, @@ -639,15 +632,7 @@ iQClass.prototype = { // Binds the given function to the given event type. Also wraps the function // in a try/catch block that does a Utils.log on any errors. bind: function(type, func) { - Utils.assert(typeof func == "function", 'does not support eventData argument'); - - let handler = function(event) { - try { - return func.apply(this, [event]); - } catch(e) { - Utils.log(e); - } - }; + let handler = function(event) func.apply(this, [event]); for (let i = 0; this[i] != null; i++) { let elem = this[i]; diff --git a/browser/base/content/tabview/modules/utils.jsm b/browser/base/content/tabview/modules/utils.jsm index d8bc449fd28f..0a0bcdef3e27 100644 --- a/browser/base/content/tabview/modules/utils.jsm +++ b/browser/base/content/tabview/modules/utils.jsm @@ -50,10 +50,8 @@ let EXPORTED_SYMBOLS = ["Point", "Rect", "Range", "Subscribable", "Utils"]; // ######### -const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; -const Cr = Components.results; Cu.import("resource://gre/modules/Services.jsm"); @@ -137,9 +135,9 @@ Rect.prototype = { // Returns true if this rectangle intersects the given . intersects: function(rect) { return (rect.right > this.left && - rect.left < this.right && - rect.bottom > this.top && - rect.top < this.bottom); + rect.left < this.right && + rect.bottom > this.top && + rect.top < this.bottom); }, // ---------- @@ -165,9 +163,9 @@ Rect.prototype = { // - A contains: function(rect) { return (rect.left > this.left && - rect.right < this.right && - rect.top > this.top && - rect.bottom < this.bottom); + rect.right < this.right && + rect.top > this.top && + rect.bottom < this.bottom); }, // ---------- @@ -238,9 +236,9 @@ Rect.prototype = { // Returns true if this rectangle is identical to the given . equals: function(rect) { return (rect.left == this.left && - rect.top == this.top && - rect.width == this.width && - rect.height == this.height); + rect.top == this.top && + rect.width == this.width && + rect.height == this.height); }, // ---------- @@ -317,11 +315,11 @@ Range.prototype = { // Paramaters // - a number or contains: function(value) { - return Utils.isNumber(value) ? - value >= this.min && value <= this.max : - Utils.isRange(value) ? - (value.min <= this.max && this.min <= value.max) : - false; + if (Utils.isNumber(value)) + return value >= this.min && value <= this.max; + if (Utils.isRange(value)) + return value.min >= this.min && value.max <= this.max; + return false; }, // ---------- @@ -388,29 +386,30 @@ Subscribable.prototype = { Utils.assertThrow(typeof callback == "function", "callback must be a function"); Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); - - if (!this.subscribers) - this.subscribers = {}; - - if (!this.subscribers[eventName]) - this.subscribers[eventName] = []; - - var subs = this.subscribers[eventName]; - var existing = subs.filter(function(element) { - return element.refObject == refObject; - }); - - if (existing.length) { - Utils.assert(existing.length == 1, 'should only ever be one'); - existing[0].callback = callback; - } else { - subs.push({ - refObject: refObject, - callback: callback - }); - } } catch(e) { Utils.log(e); + return; + } + + if (!this.subscribers) + this.subscribers = {}; + + if (!this.subscribers[eventName]) + this.subscribers[eventName] = []; + + var subs = this.subscribers[eventName]; + var existing = subs.filter(function(element) { + return element.refObject == refObject; + }); + + if (existing.length) { + Utils.assert(existing.length == 1, 'should only ever be one'); + existing[0].callback = callback; + } else { + subs.push({ + refObject: refObject, + callback: callback + }); } }, @@ -422,16 +421,17 @@ Subscribable.prototype = { Utils.assertThrow(refObject, "refObject"); Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); - - if (!this.subscribers || !this.subscribers[eventName]) - return; - - this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { - return element.refObject != refObject; - }); } catch(e) { Utils.log(e); + return; } + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) { + return element.refObject != refObject; + }); }, // ---------- @@ -441,18 +441,22 @@ Subscribable.prototype = { try { Utils.assertThrow(eventName && typeof eventName == "string", "eventName must be a non-empty string"); - - if (!this.subscribers || !this.subscribers[eventName]) - return; - - var self = this; - var subsCopy = this.subscribers[eventName].concat(); - subsCopy.forEach(function(object) { - object.callback(self, eventInfo); - }); } catch(e) { Utils.log(e); + return; } + + if (!this.subscribers || !this.subscribers[eventName]) + return; + + var subsCopy = this.subscribers[eventName].concat(); + subsCopy.forEach(function(object) { + try { + object.callback(this, eventInfo); + } catch(e) { + Utils.log(e); + } + }, this); } }; @@ -592,10 +596,10 @@ let Utils = { // Returns true if the given object (r) looks like a . isRect: function(r) { return (r && - this.isNumber(r.left) && - this.isNumber(r.top) && - this.isNumber(r.width) && - this.isNumber(r.height)); + this.isNumber(r.left) && + this.isNumber(r.top) && + this.isNumber(r.width) && + this.isNumber(r.height)); }, // ---------- @@ -603,8 +607,8 @@ let Utils = { // Returns true if the given object (r) looks like a . isRange: function(r) { return (r && - this.isNumber(r.min) && - this.isNumber(r.max)); + this.isNumber(r.min) && + this.isNumber(r.max)); }, // ---------- From a709e19cc8547f2fb198c3b61490551878e59543 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 18:47:48 +0100 Subject: [PATCH 280/369] Backed out changeset fc3b32b2f050 --- gfx/thebes/gfxFont.cpp | 1 - gfx/thebes/gfxUniscribeShaper.cpp | 116 +++++++++++++++--------------- 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index fc791731e7b4..daca44bd8fed 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -3627,7 +3627,6 @@ PRUint32 gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset) { NS_ASSERTION(aOffset <= mCharacterCount, "Bad offset looking for glyphrun"); - NS_ASSERTION(mGlyphRuns.Length() > 0, "no glyph runs present!"); if (aOffset == mCharacterCount) return mGlyphRuns.Length(); PRUint32 start = 0; diff --git a/gfx/thebes/gfxUniscribeShaper.cpp b/gfx/thebes/gfxUniscribeShaper.cpp index 84163527a96f..63dda2c7d6d2 100644 --- a/gfx/thebes/gfxUniscribeShaper.cpp +++ b/gfx/thebes/gfxUniscribeShaper.cpp @@ -394,15 +394,48 @@ private: PRPackedBool mFontSelected; }; -#define MAX_ITEM_LENGTH 16384 +#define MAX_ITEM_LENGTH 32768 + +static PRUint32 FindNextItemStart(int aOffset, int aLimit, + nsTArray &aLogAttr, + const PRUnichar *aString) +{ + if (aOffset + MAX_ITEM_LENGTH >= aLimit) { + // The item starting at aOffset can't be longer than the max length, + // so starting the next item at aLimit won't cause ScriptShape() to fail. + return aLimit; + } + + // Try to start the next item before or after a space, since spaces + // don't kern or ligate. + PRUint32 off; + int boundary = -1; + for (off = MAX_ITEM_LENGTH; off > 1; --off) { + if (aLogAttr[off].fCharStop) { + if (off > boundary) { + boundary = off; + } + if (aString[aOffset+off] == ' ' || aString[aOffset+off - 1] == ' ') + return aOffset+off; + } + } + + // Try to start the next item at the last cluster boundary in the range. + if (boundary > 0) { + return aOffset+boundary; + } + + // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break + // on the size limit. It won't be visually plesaing, but at least it + // won't cause ScriptShape() to fail. + return aOffset + MAX_ITEM_LENGTH; +} class Uniscribe { public: - Uniscribe(const PRUnichar *aString, - PRUint32 aLength, - gfxTextRun *aTextRun): - mString(aString), mLength(aLength), mTextRun(aTextRun) + Uniscribe(const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) : + mString(aString), mLength(aLength), mIsRTL(aIsRTL) { } ~Uniscribe() { @@ -413,79 +446,44 @@ public: memset(&mState, 0, sizeof(SCRIPT_STATE)); // Lock the direction. Don't allow the itemizer to change directions // based on character type. - mState.uBidiLevel = mTextRun->IsRightToLeft() ? 1 : 0; + mState.uBidiLevel = mIsRTL; mState.fOverrideDirection = PR_TRUE; } private: -// We try to avoid calling Uniscribe with text runs that may generate -// more than this many glyphs, because of the possibility of arithmetic -// overflow of 16-bit variables. If long runs need to be split because -// of this, we'll look for whitespace to break on so that shaping needed -// (e.g. for complex scripts) should be unaffected. -#define MAX_UNISCRIBE_GLYPHS 32767 - // Append mItems[aIndex] to aDest, adding extra items to aDest to ensure // that no item is too long for ScriptShape() to handle. See bug 366643. nsresult CopyItemSplitOversize(int aIndex, nsTArray &aDest) { aDest.AppendElement(mItems[aIndex]); - const int itemLength = - mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; - if (ESTIMATE_MAX_GLYPHS(itemLength) > MAX_UNISCRIBE_GLYPHS) { - // This item's length would cause ScriptShape() to fail. - // We need to add extra items here so that no item's length - // could cause the fail. + const int itemLength = mItems[aIndex+1].iCharPos - mItems[aIndex].iCharPos; + if (ESTIMATE_MAX_GLYPHS(itemLength) > 65535) { + // This items length would cause ScriptShape() to fail. We need to + // add extra items here so that no item's length could cause the fail. + + // Get cluster boundaries, so we can break cleanly if possible. + nsTArray logAttr; + if (!logAttr.SetLength(itemLength)) + return NS_ERROR_FAILURE; + HRESULT rv = ScriptBreak(mString+mItems[aIndex].iCharPos, itemLength, + &mItems[aIndex].a, logAttr.Elements()); + if (FAILED(rv)) + return NS_ERROR_FAILURE; - // We break on whitespace or cluster boundaries if possible. const int nextItemStart = mItems[aIndex+1].iCharPos; int start = FindNextItemStart(mItems[aIndex].iCharPos, - nextItemStart); + nextItemStart, logAttr, mString); while (start < nextItemStart) { SCRIPT_ITEM item = mItems[aIndex]; item.iCharPos = start; aDest.AppendElement(item); - start = FindNextItemStart(start, nextItemStart); + start = FindNextItemStart(start, nextItemStart, logAttr, mString); } } return NS_OK; } - PRUint32 FindNextItemStart(int aOffset, int aLimit) { - if (aOffset + MAX_ITEM_LENGTH >= aLimit) { - // The item starting at aOffset can't be longer than max length, - // so starting the next item at aLimit won't cause ScriptShape() - // to fail. - return aLimit; - } - // Try to start the next item before or after a space, since spaces - // don't kern or ligate. - PRInt32 off; - int boundary = -1; - for (off = MAX_ITEM_LENGTH; off > 1; --off) { - if (mTextRun->IsClusterStart(off)) { - if (off > boundary) { - boundary = off; - } - if (mString[aOffset+off] == ' ' || - mString[aOffset+off - 1] == ' ') { - return aOffset+off; - } - } - } - - // Try to start the next item at last cluster boundary in the range. - if (boundary > 0) { - return aOffset+boundary; - } - - // No nice cluster boundaries inside MAX_ITEM_LENGTH characters, break - // on the size limit. It won't be visually pleasing, but at least it - // won't cause ScriptShape() to fail. - return aOffset + MAX_ITEM_LENGTH; - } - public: int Itemize() { @@ -539,7 +537,7 @@ public: private: const PRUnichar *mString; const PRUint32 mLength; - gfxTextRun *mTextRun; + const PRBool mIsRTL; SCRIPT_CONTROL mControl; SCRIPT_STATE mState; @@ -558,13 +556,15 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, { DCFromContext aDC(aContext); + const PRBool isRTL = aTextRun->IsRightToLeft(); + PRBool result = PR_TRUE; HRESULT rv; gfxGDIFont *font = static_cast(mFont); AutoSelectFont fs(aDC, font->GetHFONT()); - Uniscribe us(aString + aRunStart, aRunLength, aTextRun); + Uniscribe us(aString + aRunStart, aRunLength, isRTL); /* itemize the string */ int numItems = us.Itemize(); From 3df506e39117ae152a4551656fec1dd86d858cb9 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 19:04:16 +0100 Subject: [PATCH 281/369] Backed out changeset 444328d96da2 --- gfx/thebes/gfxFont.cpp | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 2aa550fb8c9c..fc791731e7b4 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2235,19 +2235,17 @@ gfxFontGroup::InitTextRun(gfxContext *aContext, PRUint32 matchedLength = range.Length(); gfxFont *matchedFont = (range.font ? range.font.get() : nsnull); - // create the glyph run for this range - aTextRun->AddGlyphRun(matchedFont ? matchedFont : mainFont, - runStart, (matchedLength > 0)); if (matchedFont) { + // create the glyph run for this range + aTextRun->AddGlyphRun(matchedFont, runStart, (matchedLength > 0)); + // do glyph layout and record the resulting positioned glyphs - if (!matchedFont->InitTextRun(aContext, aTextRun, aString, - runStart, matchedLength, - aRunScript)) { - // glyph layout failed! treat as missing glyphs - matchedFont = nsnull; - } - } - if (!matchedFont) { + matchedFont->InitTextRun(aContext, aTextRun, aString, + runStart, matchedLength, aRunScript); + } else { + // create the glyph run before calling SetMissing Glyph + aTextRun->AddGlyphRun(mainFont, runStart, matchedLength); + for (PRUint32 index = runStart; index < runStart + matchedLength; index++) { // Record the char code so we can draw a box with the Unicode value if (NS_IS_HIGH_SURROGATE(aString[index]) && From d93d77926ade2d13c733965aa87d2b3b21f4bd64 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Wed, 11 Aug 2010 19:06:46 +0100 Subject: [PATCH 282/369] Backed out changeset 153bd6dc88be --- gfx/thebes/gfxUniscribeShaper.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gfx/thebes/gfxUniscribeShaper.cpp b/gfx/thebes/gfxUniscribeShaper.cpp index a9785160580a..84163527a96f 100644 --- a/gfx/thebes/gfxUniscribeShaper.cpp +++ b/gfx/thebes/gfxUniscribeShaper.cpp @@ -633,9 +633,10 @@ gfxUniscribeShaper::InitTextRun(gfxContext *aContext, } if (FAILED(rv)) { + aTextRun->ResetGlyphRuns(); // Uniscribe doesn't like this font for some reason. - // Returning FALSE will make the gfxGDIFont retry with the - // "dumb" GDI one, unless useUniscribeOnly was set. + // Returning FALSE will make the gfxGDIFont discard this + // shaper and replace it with a "dumb" GDI one. result = PR_FALSE; break; } From b606ca2090f82f0da66cdef7e18fd2bfb6e1ccb8 Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Wed, 11 Aug 2010 09:46:55 -0700 Subject: [PATCH 283/369] Bug 576735: If an add-ons directory is removed when it is already pending uninstall startup fails badly. r=Unfocused --- toolkit/mozapps/extensions/XPIProvider.jsm | 7 +- .../test/xpcshell/test_bug576735.js | 70 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index 24db8d37efc2..d1cd34b96e9e 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -5692,8 +5692,11 @@ DirectoryInstallLocation.prototype = { delete this._DirToIDMap[dir.path]; delete this._IDToDirMap[aId]; - if (!dir.exists()) - throw new Error("Attempt to uninstall unknown add-on " + aId); + if (!dir.exists()) { + WARN("Attempted to remove the directory for " + aId + " from " + + this._name + " but it was already gone"); + return; + } dir.remove(true); }, diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js b/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js new file mode 100644 index 000000000000..be4cf52fab85 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug576735.js @@ -0,0 +1,70 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tests that we recover gracefully from an extension directory disappearing +// when we were expecting to uninstall it. + +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +var addon2 = { + id: "addon2@tests.mozilla.org", + version: "2.0", + name: "Test 2", + targetApplications: [{ + id: "toolkit@mozilla.org", + minVersion: "1", + maxVersion: "1" + }] +}; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "2"); + + var dest = profileDir.clone(); + dest.append("addon1@tests.mozilla.org"); + writeInstallRDFToDir(addon1, dest); + + startupManager(); + + AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) { + a1.uninstall(); + + shutdownManager(); + + var dest = profileDir.clone(); + dest.append("addon1@tests.mozilla.org"); + dest.remove(true); + + dest = profileDir.clone(); + dest.append("addon2@tests.mozilla.org"); + writeInstallRDFToDir(addon2, dest); + + startupManager(); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org"], + function([a1, a2]) { + // Addon1 should no longer be installed + do_check_eq(a1, null); + + // Addon2 should have been detected + do_check_neq(a2, null); + + do_test_finished(); + }); + }); +} From 2e90a0c453c47dff213e79146d544db322d1ce29 Mon Sep 17 00:00:00 2001 From: Ben Parr Date: Wed, 11 Aug 2010 09:55:12 -0700 Subject: [PATCH 284/369] Bug 577990: Visible delay in showing the Language category when opening the Add-Ons Manager. r+a=dtownsend --- .../mozapps/extensions/content/extensions.js | 27 +++- .../mozapps/extensions/content/extensions.xul | 6 +- .../extensions/test/browser/Makefile.in | 1 + .../test/browser/browser_bug577990.js | 127 ++++++++++++++++++ .../mozapps/extensions/test/browser/head.js | 16 ++- 5 files changed, 168 insertions(+), 9 deletions(-) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_bug577990.js diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index 94347db62c5f..6a3a2eb07f99 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -133,6 +133,7 @@ function notifyInitialized() { } function shutdown() { + gCategories.shutdown(); gSearchView.shutdown(); gEventManager.shutdown(); gViewController.shutdown(); @@ -793,6 +794,7 @@ function getAddonsAndInstalls(aType, aCallback) { var gCategories = { node: null, _search: null, + _maybeHidden: null, initialize: function() { this.node = document.getElementById("categories"); @@ -820,13 +822,21 @@ var gCategories = { } }, false); - var maybeHidden = ["addons://list/locale", "addons://list/searchengine"]; - gPendingInitializations += maybeHidden.length; - maybeHidden.forEach(function(aId) { + this._maybeHidden = ["addons://list/locale", "addons://list/searchengine"]; + gPendingInitializations += this._maybeHidden.length; + this._maybeHidden.forEach(function(aId) { var type = gViewController.parseViewId(aId).param; getAddonsAndInstalls(type, function(aAddonsList, aInstallsList) { + var hidden = (aAddonsList.length == 0 && aInstallsList.length == 0); + var item = self.get(aId); + + // Don't load view that is becoming hidden + if (hidden && aId == gViewController.currentViewId) + gViewController.loadView(VIEW_DEFAULT); + + item.hidden = hidden; + if (aAddonsList.length > 0 || aInstallsList.length > 0) { - self.get(aId).hidden = false; notifyInitialized(); return; } @@ -861,6 +871,15 @@ var gCategories = { }); }, + shutdown: function() { + // Force persist of hidden state. See bug 15232 + var self = this; + this._maybeHidden.forEach(function(aId) { + var item = self.get(aId); + item.setAttribute("hidden", !!item.hidden); + }); + }, + select: function(aId) { if (this.node.selectedItem && this.node.selectedItem.value == aId) diff --git a/toolkit/mozapps/extensions/content/extensions.xul b/toolkit/mozapps/extensions/content/extensions.xul index b26f52ef3750..bdcabb238b13 100644 --- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -114,11 +114,13 @@ name="&view.discover.label;"/>