From 3e2d11b401536bfb0d9cdf28bbfb7a2dcc11df38 Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Thu, 30 Jul 2009 15:04:14 +1200 Subject: [PATCH 001/175] Bug 467988. Merge canvas mochitests into one file to speed up mochitest running. r=roc --HG-- extra : rebase_source : 9a4f90fdbe07759e3970dbf665d753b7320b345a --- content/canvas/test/Makefile.in | 687 +- content/canvas/test/test_canvas.html | 24276 +++++++++++++++++++++++++ 2 files changed, 24285 insertions(+), 678 deletions(-) create mode 100644 content/canvas/test/test_canvas.html diff --git a/content/canvas/test/Makefile.in b/content/canvas/test/Makefile.in index 96b52e9f87e..571d677c1c2 100644 --- a/content/canvas/test/Makefile.in +++ b/content/canvas/test/Makefile.in @@ -45,656 +45,7 @@ relativesrcdir = content/canvas/test include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _TEST_FILES_0 = \ - test_fallback.basic.html \ - test_fallback.multiple.html \ - test_fallback.nested.html \ - test_type.name.html \ - test_type.exists.html \ - test_type.delete.html \ - test_type.prototype.html \ - test_type.replace.html \ - test_type.extend.html \ - test_size.attributes.html \ - test_size.attributes.type.get.html \ - test_size.attributes.type.set.html \ - test_size.attributes.default.html \ - test_size.attributes.reflect.1.html \ - test_size.attributes.reflect.2.html \ - test_size.attributes.removed.html \ - test_size.attributes.parse.whitespace.html \ - test_size.attributes.parse.nonnumber.html \ - test_size.attributes.parse.zero.html \ - test_size.attributes.parse.negative.html \ - test_size.attributes.parse.zerosuffix.html \ - test_size.attributes.parse.floatsuffix.html \ - test_size.attributes.parse.badsuffix.html \ - test_size.attributes.parse.percentsuffix.html \ - test_size.attributes.setAttribute.whitespace.html \ - test_size.attributes.setAttribute.nonnumber.html \ - test_size.attributes.setAttribute.zero.html \ - test_size.attributes.setAttribute.negative.html \ - test_size.attributes.setAttribute.zerosuffix.html \ - test_size.attributes.setAttribute.floatsuffix.html \ - test_size.attributes.setAttribute.badsuffix.html \ - test_size.attributes.setAttribute.percentsuffix.html \ - test_size.attributes.style.html \ - test_initial.colour.html \ - test_initial.reset.different.html \ - test_initial.reset.same.html \ - test_initial.reset.path.html \ - test_initial.reset.clip.html \ - test_initial.reset.transform.html \ - test_initial.reset.gradient.html \ - test_initial.reset.pattern.html \ - test_context.emptystring.html \ - test_context.unrecognised.badname.html \ - test_context.unrecognised.badsuffix.html \ - test_context.unrecognised.nullsuffix.html \ - test_context.unrecognised.unicode.html \ - test_context.casesensitive.html \ - test_context.arguments.missing.html \ - test_context.arguments.extra.html \ - test_toDataURL.default.html \ - test_toDataURL.png.html \ - test_toDataURL.unrecognised.html \ - test_toDataURL.lowercase.html \ - test_toDataURL.arguments.1.html \ - test_toDataURL.arguments.2.html \ - test_toDataURL.arguments.3.html \ - test_toDataURL.nocontext.html \ - test_toDataURL.zerosize.html \ - test_toDataURL.primarycolours.html \ - test_toDataURL.complexcolours.html \ - test_2d.getcontext.exists.html \ - test_2d.type.exists.html \ - test_2d.type.delete.html \ - test_2d.type.prototype.html \ - test_2d.type.replace.html \ - test_2d.type.extend.html \ - test_2d.getcontext.unique.html \ - test_2d.getcontext.shared.html \ - test_2d.voidreturn.html \ - test_2d.missingargs.html \ - test_2d.scaled.html \ - test_2d.canvas.reference.html \ - test_2d.canvas.readonly.html \ - test_2d.state.saverestore.strokeStyle.html \ - test_2d.state.saverestore.fillStyle.html \ - test_2d.state.saverestore.globalAlpha.html \ - test_2d.state.saverestore.lineWidth.html \ - test_2d.state.saverestore.lineCap.html \ - test_2d.state.saverestore.lineJoin.html \ - test_2d.state.saverestore.miterLimit.html \ - test_2d.state.saverestore.shadowOffsetX.html \ - test_2d.state.saverestore.shadowOffsetY.html \ - test_2d.state.saverestore.shadowBlur.html \ - test_2d.state.saverestore.shadowColor.html \ - test_2d.state.saverestore.globalCompositeOperation.html \ - test_initial.reset.2dstate.html \ - test_2d.state.saverestore.transformation.html \ - test_2d.state.saverestore.clip.html \ - test_2d.state.saverestore.path.html \ - test_2d.state.saverestore.bitmap.html \ - test_2d.state.saverestore.stack.html \ - test_2d.state.saverestore.stackdepth.html \ - test_2d.state.saverestore.underflow.html \ - test_2d.transformation.order.html \ - test_2d.transformation.scale.basic.html \ - test_2d.transformation.scale.zero.html \ - test_2d.transformation.scale.negative.html \ - test_2d.transformation.scale.large.html \ - test_2d.transformation.scale.nonfinite.html \ - test_2d.transformation.scale.multiple.html \ - $(NULL) - -_TEST_FILES_1 = \ - test_2d.transformation.rotate.zero.html \ - test_2d.transformation.rotate.radians.html \ - test_2d.transformation.rotate.direction.html \ - test_2d.transformation.rotate.wrap.html \ - test_2d.transformation.rotate.wrapnegative.html \ - test_2d.transformation.rotate.nonfinite.html \ - test_2d.transformation.translate.basic.html \ - test_2d.transformation.translate.nonfinite.html \ - test_2d.transformation.transform.identity.html \ - test_2d.transformation.transform.skewed.html \ - test_2d.transformation.transform.multiply.html \ - test_2d.transformation.transform.nonfinite.html \ - test_2d.transformation.setTransform.skewed.html \ - test_2d.transformation.setTransform.multiple.html \ - test_2d.transformation.setTransform.nonfinite.html \ - test_2d.composite.globalAlpha.range.html \ - test_2d.composite.globalAlpha.invalid.html \ - test_2d.composite.globalAlpha.default.html \ - test_2d.composite.globalAlpha.fill.html \ - test_2d.composite.globalAlpha.image.html \ - test_2d.composite.globalAlpha.canvas.html \ - test_2d.composite.globalAlpha.imagepattern.html \ - test_2d.composite.globalAlpha.canvaspattern.html \ - test_2d.composite.solid.source-over.html \ - test_2d.composite.solid.destination-over.html \ - test_2d.composite.solid.source-in.html \ - test_2d.composite.solid.destination-in.html \ - test_2d.composite.solid.source-out.html \ - test_2d.composite.solid.destination-out.html \ - test_2d.composite.solid.source-atop.html \ - test_2d.composite.solid.destination-atop.html \ - test_2d.composite.solid.copy.html \ - test_2d.composite.transparent.source-over.html \ - test_2d.composite.transparent.destination-over.html \ - test_2d.composite.transparent.source-in.html \ - test_2d.composite.transparent.destination-in.html \ - test_2d.composite.transparent.source-out.html \ - test_2d.composite.transparent.destination-out.html \ - test_2d.composite.transparent.source-atop.html \ - test_2d.composite.transparent.destination-atop.html \ - test_2d.composite.transparent.copy.html \ - test_2d.composite.image.source-over.html \ - test_2d.composite.image.destination-over.html \ - test_2d.composite.image.destination-out.html \ - test_2d.composite.image.source-atop.html \ - test_2d.composite.image.copy.html \ - test_2d.composite.canvas.source-over.html \ - test_2d.composite.canvas.destination-over.html \ - test_2d.composite.canvas.destination-out.html \ - test_2d.composite.canvas.source-atop.html \ - test_2d.composite.canvas.copy.html \ - test_2d.composite.uncovered.fill.copy.html \ - test_2d.composite.uncovered.image.copy.html \ - test_2d.composite.uncovered.pattern.copy.html \ - test_2d.composite.clip.source-over.html \ - test_2d.composite.clip.destination-over.html \ - test_2d.composite.clip.source-in.html \ - test_2d.composite.clip.destination-in.html \ - test_2d.composite.clip.source-out.html \ - test_2d.composite.clip.destination-out.html \ - test_2d.composite.clip.source-atop.html \ - test_2d.composite.clip.destination-atop.html \ - test_2d.composite.clip.copy.html \ - test_2d.composite.operation.get.html \ - test_2d.composite.operation.unrecognised.html \ - test_2d.composite.operation.darker.html \ - test_2d.composite.operation.over.html \ - test_2d.composite.operation.clear.html \ - test_2d.composite.operation.highlight.html \ - test_2d.composite.operation.nullsuffix.html \ - $(NULL) - -_TEST_FILES_2 = \ - test_2d.composite.operation.casesensitive.html \ - test_2d.composite.operation.default.html \ - test_2d.fillStyle.parse.html4.html \ - test_2d.fillStyle.parse.hex3.html \ - test_2d.fillStyle.parse.hex6.html \ - test_2d.fillStyle.parse.rgb-num.html \ - test_2d.fillStyle.parse.rgb-clamp-1.html \ - test_2d.fillStyle.parse.rgb-clamp-2.html \ - test_2d.fillStyle.parse.rgb-clamp-3.html \ - test_2d.fillStyle.parse.rgb-clamp-4.html \ - test_2d.fillStyle.parse.rgb-clamp-5.html \ - test_2d.fillStyle.parse.rgb-percent.html \ - test_2d.fillStyle.parse.rgba-solid-1.html \ - test_2d.fillStyle.parse.rgba-solid-2.html \ - test_2d.fillStyle.parse.rgba-num-1.html \ - test_2d.fillStyle.parse.rgba-num-2.html \ - test_2d.fillStyle.parse.rgba-percent.html \ - test_2d.fillStyle.parse.rgba-clamp-1.html \ - test_2d.fillStyle.parse.rgba-clamp-2.html \ - test_2d.fillStyle.parse.transparent-1.html \ - test_2d.fillStyle.parse.transparent-2.html \ - test_2d.fillStyle.parse.hsl-1.html \ - test_2d.fillStyle.parse.hsl-2.html \ - test_2d.fillStyle.parse.hsl-3.html \ - test_2d.fillStyle.parse.hsl-4.html \ - test_2d.fillStyle.parse.hsl-5.html \ - test_2d.fillStyle.parse.hsl-clamp-1.html \ - test_2d.fillStyle.parse.hsl-clamp-2.html \ - test_2d.fillStyle.parse.hsl-clamp-3.html \ - test_2d.fillStyle.parse.hsl-clamp-4.html \ - test_2d.fillStyle.parse.hsla-1.html \ - test_2d.fillStyle.parse.hsla-2.html \ - test_2d.fillStyle.parse.hsla-clamp-1.html \ - test_2d.fillStyle.parse.hsla-clamp-2.html \ - test_2d.fillStyle.parse.hsla-clamp-3.html \ - test_2d.fillStyle.parse.hsla-clamp-4.html \ - test_2d.fillStyle.parse.hsla-clamp-5.html \ - test_2d.fillStyle.parse.hsla-clamp-6.html \ - test_2d.fillStyle.parse.svg-1.html \ - test_2d.fillStyle.parse.svg-2.html \ - test_2d.fillStyle.parse.invalid.hex3.html \ - test_2d.fillStyle.parse.invalid.hex6.html \ - test_2d.fillStyle.parse.invalid.rgb-1.html \ - test_2d.fillStyle.parse.invalid.rgb-2.html \ - test_2d.fillStyle.parse.invalid.rgb-3.html \ - test_2d.fillStyle.parse.invalid.rgb-4.html \ - test_2d.fillStyle.parse.invalid.rgb-5.html \ - test_2d.fillStyle.parse.invalid.rgb-6.html \ - test_2d.fillStyle.parse.invalid.rgb-7.html \ - test_2d.fillStyle.parse.invalid.rgba-1.html \ - test_2d.fillStyle.parse.invalid.rgba-2.html \ - test_2d.fillStyle.parse.invalid.rgba-3.html \ - test_2d.fillStyle.parse.invalid.rgba-4.html \ - test_2d.fillStyle.parse.invalid.rgba-5.html \ - test_2d.fillStyle.parse.invalid.hsl-1.html \ - test_2d.fillStyle.parse.invalid.hsl-2.html \ - test_2d.fillStyle.parse.invalid.hsl-3.html \ - test_2d.fillStyle.parse.invalid.hsl-4.html \ - test_2d.fillStyle.parse.invalid.hsl-5.html \ - test_2d.fillStyle.parse.invalid.hsla-1.html \ - test_2d.fillStyle.parse.invalid.hsla-2.html \ - test_2d.fillStyle.parse.system.html \ - test_2d.fillStyle.parse.current.basic.html \ - test_2d.fillStyle.parse.current.changed.html \ - test_2d.fillStyle.parse.current.removed.html \ - test_2d.fillStyle.invalidstring.html \ - test_2d.fillStyle.invalidtype.html \ - test_2d.fillStyle.get.solid.html \ - test_2d.fillStyle.get.semitransparent.html \ - test_2d.fillStyle.get.transparent.html \ - test_2d.fillStyle.default.html \ - test_2d.strokeStyle.default.html \ - test_2d.gradient.object.type.html \ - test_2d.gradient.object.return.html \ - test_2d.gradient.interpolate.solid.html \ - test_2d.gradient.interpolate.colour.html \ - test_2d.gradient.interpolate.alpha.html \ - test_2d.gradient.interpolate.colouralpha.html \ - test_2d.gradient.interpolate.outside.html \ - test_2d.gradient.interpolate.zerosize.html \ - test_2d.gradient.interpolate.vertical.html \ - test_2d.gradient.interpolate.multiple.html \ - test_2d.gradient.interpolate.overlap.html \ - test_2d.gradient.interpolate.overlap2.html \ - test_2d.gradient.empty.html \ - test_2d.gradient.object.update.html \ - test_2d.gradient.object.compare.html \ - test_2d.gradient.object.crosscanvas.html \ - test_2d.gradient.object.invalidoffset.html \ - test_2d.gradient.object.invalidcolour.html \ - test_2d.gradient.linear.nonfinite.html \ - test_2d.gradient.linear.transform.1.html \ - test_2d.gradient.linear.transform.2.html \ - test_2d.gradient.linear.transform.3.html \ - test_2d.gradient.radial.negative.html \ - test_2d.gradient.radial.nonfinite.html \ - test_2d.gradient.radial.inside1.html \ - $(NULL) - -_TEST_FILES_3 = \ - test_2d.gradient.radial.cone.bottom.html \ - test_2d.gradient.radial.cone.cylinder.html \ - test_2d.gradient.radial.cone.shape1.html \ - test_2d.gradient.radial.transform.1.html \ - test_2d.gradient.radial.transform.2.html \ - test_2d.gradient.radial.transform.3.html \ - test_2d.pattern.basic.type.html \ - test_2d.pattern.basic.image.html \ - test_2d.pattern.basic.canvas.html \ - test_2d.pattern.basic.zerocanvas.html \ - test_2d.pattern.basic.nocontext.html \ - test_2d.pattern.image.undefined.html \ - test_2d.pattern.image.null.html \ - test_2d.pattern.image.string.html \ - test_2d.pattern.image.incomplete.html \ - test_2d.pattern.image.broken.html \ - test_2d.pattern.repeat.empty.html \ - test_2d.pattern.repeat.null.html \ - test_2d.pattern.repeat.undefined.html \ - test_2d.pattern.repeat.unrecognised.html \ - test_2d.pattern.repeat.case.html \ - test_2d.pattern.repeat.nullsuffix.html \ - test_2d.pattern.modify.image1.html \ - test_2d.pattern.modify.image2.html \ - test_2d.pattern.modify.canvas1.html \ - test_2d.pattern.modify.canvas2.html \ - test_2d.pattern.crosscanvas.html \ - test_2d.pattern.paint.norepeat.basic.html \ - test_2d.pattern.paint.norepeat.outside.html \ - test_2d.pattern.paint.norepeat.coord1.html \ - test_2d.pattern.paint.norepeat.coord2.html \ - test_2d.pattern.paint.norepeat.coord3.html \ - test_2d.pattern.paint.repeat.basic.html \ - test_2d.pattern.paint.repeat.outside.html \ - test_2d.pattern.paint.repeat.coord1.html \ - test_2d.pattern.paint.repeat.coord2.html \ - test_2d.pattern.paint.repeat.coord3.html \ - test_2d.pattern.paint.repeatx.basic.html \ - test_2d.pattern.paint.repeatx.outside.html \ - test_2d.pattern.paint.repeatx.coord1.html \ - test_2d.pattern.paint.repeaty.basic.html \ - test_2d.pattern.paint.repeaty.outside.html \ - test_2d.pattern.paint.repeaty.coord1.html \ - test_2d.pattern.paint.orientation.image.html \ - test_2d.pattern.paint.orientation.canvas.html \ - test_2d.pattern.animated.gif.html \ - test_2d.line.defaults.html \ - test_2d.line.width.basic.html \ - test_2d.line.width.transformed.html \ - test_2d.line.width.invalid.html \ - test_2d.line.cap.butt.html \ - test_2d.line.cap.round.html \ - test_2d.line.cap.square.html \ - test_2d.line.cap.open.html \ - test_2d.line.cap.invalid.html \ - test_2d.line.join.bevel.html \ - test_2d.line.join.round.html \ - test_2d.line.join.miter.html \ - test_2d.line.join.open.html \ - test_2d.line.join.closed.html \ - test_2d.line.join.invalid.html \ - test_2d.line.miter.exceeded.html \ - test_2d.line.miter.acute.html \ - test_2d.line.miter.obtuse.html \ - test_2d.line.miter.rightangle.html \ - test_2d.line.miter.lineedge.html \ - test_2d.line.miter.within.html \ - test_2d.line.miter.invalid.html \ - test_2d.line.cross.html \ - test_2d.line.union.html \ - test_2d.clearRect.basic.html \ - test_2d.clearRect.path.html \ - test_2d.clearRect.zero.html \ - test_2d.clearRect.negative.html \ - test_2d.clearRect.transform.html \ - test_2d.clearRect.globalalpha.html \ - test_2d.clearRect.globalcomposite.html \ - test_2d.clearRect.clip.html \ - test_2d.clearRect.shadow.html \ - test_2d.clearRect.nonfinite.html \ - test_2d.fillRect.basic.html \ - test_2d.fillRect.path.html \ - test_2d.fillRect.zero.html \ - test_2d.fillRect.negative.html \ - test_2d.fillRect.transform.html \ - test_2d.fillRect.clip.html \ - test_2d.fillRect.shadow.html \ - $(NULL) - -_TEST_FILES_4 = \ - test_2d.fillRect.nonfinite.html \ - test_2d.strokeRect.basic.html \ - test_2d.strokeRect.path.html \ - test_2d.strokeRect.zero.1.html \ - test_2d.strokeRect.zero.2.html \ - test_2d.strokeRect.zero.3.html \ - test_2d.strokeRect.zero.4.html \ - test_2d.strokeRect.negative.html \ - test_2d.strokeRect.transform.html \ - test_2d.strokeRect.globalalpha.html \ - test_2d.strokeRect.globalcomposite.html \ - test_2d.strokeRect.clip.html \ - test_2d.strokeRect.shadow.html \ - test_2d.strokeRect.nonfinite.html \ - test_2d.path.initial.html \ - test_2d.path.beginPath.html \ - test_2d.path.moveTo.basic.html \ - test_2d.path.moveTo.newsubpath.html \ - test_2d.path.moveTo.multiple.html \ - test_2d.path.moveTo.nonfinite.html \ - test_2d.path.closePath.empty.html \ - test_2d.path.closePath.newline.html \ - test_2d.path.closePath.nextpoint.html \ - test_2d.path.lineTo.emptysubpath.html \ - test_2d.path.lineTo.basic.html \ - test_2d.path.lineTo.nextpoint.html \ - test_2d.path.lineTo.nonfinite.html \ - test_2d.path.quadraticCurveTo.emptysubpath.html \ - test_2d.path.quadraticCurveTo.basic.html \ - test_2d.path.quadraticCurveTo.shape.html \ - test_2d.path.quadraticCurveTo.scaled.html \ - test_2d.path.quadraticCurveTo.nonfinite.html \ - test_2d.path.bezierCurveTo.emptysubpath.html \ - test_2d.path.bezierCurveTo.basic.html \ - test_2d.path.bezierCurveTo.shape.html \ - test_2d.path.bezierCurveTo.scaled.html \ - test_2d.path.bezierCurveTo.nonfinite.html \ - test_2d.path.arcTo.emptysubpath.html \ - test_2d.path.arcTo.coincide.1.html \ - test_2d.path.arcTo.coincide.2.html \ - test_2d.path.arcTo.collinear.1.html \ - test_2d.path.arcTo.collinear.2.html \ - test_2d.path.arcTo.collinear.3.html \ - test_2d.path.arcTo.shape.curve1.html \ - test_2d.path.arcTo.shape.curve2.html \ - test_2d.path.arcTo.shape.start.html \ - test_2d.path.arcTo.shape.end.html \ - test_2d.path.arcTo.negative.html \ - test_2d.path.arcTo.zero.1.html \ - test_2d.path.arcTo.zero.2.html \ - test_2d.path.arcTo.transformation.html \ - test_2d.path.arcTo.scale.html \ - test_2d.path.arcTo.nonfinite.html \ - test_2d.path.arc.empty.html \ - test_2d.path.arc.nonempty.html \ - test_2d.path.arc.end.html \ - test_2d.path.arc.angle.1.html \ - test_2d.path.arc.angle.2.html \ - test_2d.path.arc.angle.3.html \ - test_2d.path.arc.angle.4.html \ - test_2d.path.arc.angle.5.html \ - test_2d.path.arc.angle.6.html \ - test_2d.path.arc.zero.1.html \ - test_2d.path.arc.zero.2.html \ - test_2d.path.arc.twopie.1.html \ - test_2d.path.arc.twopie.2.html \ - test_2d.path.arc.twopie.3.html \ - test_2d.path.arc.twopie.4.html \ - test_2d.path.arc.shape.1.html \ - test_2d.path.arc.shape.2.html \ - test_2d.path.arc.shape.4.html \ - test_2d.path.arc.shape.5.html \ - test_2d.path.arc.selfintersect.1.html \ - test_2d.path.arc.selfintersect.2.html \ - test_2d.path.arc.negative.html \ - test_2d.path.arc.zeroradius.html \ - test_2d.path.arc.scale.1.html \ - test_2d.path.arc.scale.2.html \ - test_2d.path.arc.nonfinite.html \ - test_2d.path.rect.basic.html \ - test_2d.path.rect.newsubpath.html \ - test_2d.path.rect.closed.html \ - test_2d.path.rect.end.1.html \ - test_2d.path.rect.end.2.html \ - test_2d.path.rect.zero.1.html \ - test_2d.path.rect.zero.2.html \ - test_2d.path.rect.zero.3.html \ - test_2d.path.rect.zero.4.html \ - test_2d.path.rect.zero.5.html \ - test_2d.path.rect.negative.html \ - test_2d.path.rect.winding.html \ - test_2d.path.rect.nonfinite.html \ - test_2d.path.fill.overlap.html \ - test_2d.path.fill.winding.add.html \ - test_2d.path.fill.winding.subtract.1.html \ - test_2d.path.fill.winding.subtract.2.html \ - $(NULL) - -_TEST_FILES_5 = \ - test_2d.path.fill.winding.subtract.3.html \ - test_2d.path.fill.closed.basic.html \ - test_2d.path.fill.closed.unaffected.html \ - test_2d.path.stroke.overlap.html \ - test_2d.path.stroke.union.html \ - test_2d.path.stroke.unaffected.html \ - test_2d.path.stroke.scale1.html \ - test_2d.path.stroke.scale2.html \ - test_2d.path.stroke.skew.html \ - test_2d.path.stroke.empty.html \ - test_2d.path.stroke.prune.line.html \ - test_2d.path.stroke.prune.closed.html \ - test_2d.path.stroke.prune.curve.html \ - test_2d.path.stroke.prune.arc.html \ - test_2d.path.stroke.prune.rect.html \ - test_2d.path.stroke.prune.corner.html \ - test_2d.path.transformation.basic.html \ - test_2d.path.transformation.multiple.html \ - test_2d.path.transformation.changing.html \ - test_2d.path.clip.empty.html \ - test_2d.path.clip.basic.1.html \ - test_2d.path.clip.basic.2.html \ - test_2d.path.clip.intersect.html \ - test_2d.path.clip.winding.1.html \ - test_2d.path.clip.winding.2.html \ - test_2d.path.clip.unaffected.html \ - test_2d.path.isPointInPath.basic.1.html \ - test_2d.path.isPointInPath.basic.2.html \ - test_2d.path.isPointInPath.edge.html \ - test_2d.path.isPointInPath.empty.html \ - test_2d.path.isPointInPath.subpath.html \ - test_2d.path.isPointInPath.outside.html \ - test_2d.path.isPointInPath.unclosed.html \ - test_2d.path.isPointInPath.arc.html \ - test_2d.path.isPointInPath.bigarc.html \ - test_2d.path.isPointInPath.bezier.html \ - test_2d.path.isPointInPath.winding.html \ - test_2d.path.isPointInPath.transform.1.html \ - test_2d.path.isPointInPath.transform.2.html \ - test_2d.path.isPointInPath.transform.3.html \ - test_2d.path.isPointInPath.nonfinite.html \ - test_2d.drawImage.3arg.html \ - test_2d.drawImage.5arg.html \ - test_2d.drawImage.9arg.basic.html \ - test_2d.drawImage.9arg.sourcepos.html \ - test_2d.drawImage.9arg.sourcesize.html \ - test_2d.drawImage.9arg.destpos.html \ - test_2d.drawImage.9arg.destsize.html \ - test_2d.drawImage.canvas.html \ - test_2d.drawImage.self.1.html \ - test_2d.drawImage.self.2.html \ - test_2d.drawImage.null.html \ - test_2d.drawImage.wrongtype.html \ - test_2d.drawImage.floatsource.html \ - test_2d.drawImage.zerosource.html \ - test_2d.drawImage.negativesource.html \ - test_2d.drawImage.negativedest.html \ - test_2d.drawImage.outsidesource.html \ - test_2d.drawImage.incomplete.html \ - test_2d.drawImage.broken.html \ - test_2d.drawImage.animated.gif.html \ - test_2d.drawImage.animated.apng.html \ - test_2d.drawImage.animated.poster.html \ - test_2d.drawImage.path.html \ - test_2d.drawImage.transform.html \ - test_2d.drawImage.alpha.html \ - test_2d.drawImage.clip.html \ - test_2d.drawImage.composite.html \ - test_2d.drawImage.nowrap.html \ - test_2d.drawImage.nonfinite.html \ - test_2d.imageData.create.basic.html \ - test_2d.imageData.create.type.html \ - test_2d.imageData.create.initial.html \ - test_2d.imageData.create.large.html \ - test_2d.imageData.create.tiny.html \ - test_2d.imageData.create.negative.html \ - test_2d.imageData.create.zero.html \ - test_2d.imageData.create.nonfinite.html \ - test_2d.imageData.create.round.html \ - test_2d.imageData.get.basic.html \ - test_2d.imageData.get.type.html \ - test_2d.imageData.get.zero.html \ - test_2d.imageData.get.nonfinite.html \ - test_2d.imageData.get.source.outside.html \ - test_2d.imageData.get.source.negative.html \ - test_2d.imageData.get.source.size.html \ - test_2d.imageData.get.tiny.html \ - test_2d.imageData.get.nonpremul.html \ - test_2d.imageData.get.range.html \ - test_2d.imageData.get.clamp.html \ - test_2d.imageData.get.order.cols.html \ - test_2d.imageData.get.order.rows.html \ - test_2d.imageData.get.order.rgb.html \ - test_2d.imageData.get.order.alpha.html \ - test_2d.imageData.get.unaffected.html \ - test_2d.imageData.object.properties.html \ - test_2d.imageData.object.readonly.html \ - test_2d.imageData.object.ctor.html \ - test_2d.imageData.object.set.html \ - test_2d.imageData.object.undefined.html \ - $(NULL) - -_TEST_FILES_6 = \ - test_2d.imageData.object.nan.html \ - test_2d.imageData.object.string.html \ - test_2d.imageData.object.clamp.html \ - test_2d.imageData.object.round.html \ - test_2d.imageData.put.null.html \ - test_2d.imageData.put.nonfinite.html \ - test_2d.imageData.put.basic.html \ - test_2d.imageData.put.created.html \ - test_2d.imageData.put.wrongtype.html \ - test_2d.imageData.put.cross.html \ - test_2d.imageData.put.alpha.html \ - test_2d.imageData.put.modified.html \ - test_2d.imageData.put.dirty.zero.html \ - test_2d.imageData.put.dirty.rect1.html \ - test_2d.imageData.put.dirty.rect2.html \ - test_2d.imageData.put.dirty.negative.html \ - test_2d.imageData.put.dirty.outside.html \ - test_2d.imageData.put.unchanged.html \ - test_2d.imageData.put.unaffected.html \ - test_2d.imageData.put.clip.html \ - test_2d.imageData.put.path.html \ - test_2d.shadow.attributes.shadowBlur.1.html \ - test_2d.shadow.attributes.shadowBlur.2.html \ - test_2d.shadow.attributes.shadowOffset.1.html \ - test_2d.shadow.attributes.shadowOffset.2.html \ - test_2d.shadow.attributes.shadowColor.1.html \ - test_2d.shadow.attributes.shadowColor.2.html \ - test_2d.shadow.basic.1.html \ - test_2d.shadow.basic.2.html \ - test_2d.shadow.offset.positiveX.html \ - test_2d.shadow.offset.negativeX.html \ - test_2d.shadow.offset.positiveY.html \ - test_2d.shadow.offset.negativeY.html \ - test_2d.shadow.outside.html \ - test_2d.shadow.clip.1.html \ - test_2d.shadow.clip.2.html \ - test_2d.shadow.clip.3.html \ - test_2d.shadow.stroke.basic.html \ - test_2d.shadow.stroke.cap.1.html \ - test_2d.shadow.stroke.cap.2.html \ - test_2d.shadow.stroke.join.1.html \ - test_2d.shadow.stroke.join.2.html \ - test_2d.shadow.stroke.join.3.html \ - test_2d.shadow.image.basic.html \ - test_2d.shadow.image.transparent.1.html \ - test_2d.shadow.image.transparent.2.html \ - test_2d.shadow.image.alpha.html \ - test_2d.shadow.image.section.html \ - test_2d.shadow.image.scale.html \ - test_2d.shadow.canvas.basic.html \ - test_2d.shadow.canvas.transparent.1.html \ - test_2d.shadow.canvas.transparent.2.html \ - test_2d.shadow.canvas.alpha.html \ - test_2d.shadow.pattern.basic.html \ - test_2d.shadow.pattern.transparent.1.html \ - test_2d.shadow.pattern.transparent.2.html \ - test_2d.shadow.pattern.alpha.html \ - test_2d.shadow.gradient.basic.html \ - test_2d.shadow.gradient.transparent.1.html \ - test_2d.shadow.gradient.transparent.2.html \ - test_2d.shadow.gradient.alpha.html \ - test_2d.shadow.transform.1.html \ - test_2d.shadow.transform.2.html \ - test_2d.shadow.blur.low.html \ - test_2d.shadow.blur.high.html \ - test_2d.shadow.alpha.1.html \ - test_2d.shadow.alpha.2.html \ - test_2d.shadow.alpha.3.html \ - test_2d.shadow.alpha.4.html \ - test_2d.shadow.alpha.5.html \ - test_2d.shadow.composite.1.html \ - test_2d.shadow.composite.2.html \ - test_2d.shadow.composite.3.html \ - test_2d.shadow.composite.4.html \ - test_2d.imageSmoothing.html \ - test_bug397524.html \ - test_bug405982.html \ - test_text.font.html \ - test_text.textAlign.html \ - test_text.textBaseline.html \ - test_text.measure.html \ - test_text.space.replace.html \ + test_canvas.html \ image_transparent50.png \ image_redtransparent.png \ image_yellow.png \ @@ -714,7 +65,6 @@ _TEST_FILES_6 = \ image_green.png \ image_green-redirect \ image_green-redirect^headers^ \ - test_2d.imagedata_coercion.html \ $(NULL) # xor and lighter aren't well handled by cairo; they mostly work, but we don't want @@ -746,14 +96,14 @@ _TEST_FILES_6 = \ # Tests that fail on Mac (possibly because spec is underdefined?). Bug 407105 ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa) # XXX vlad don't test these anywhere, cairo behaviour changed -_TEST_FILES_3 += \ +_TEST_FILES_0 += \ test_2d.line.join.parallel.html \ test_2d.strokeRect.zero.5.html \ $(NULL) # This is an issue with Quartz's handling of radial gradients and some numeric # imprecision that results in errors here. -_TEST_FILES_2 += \ +_TEST_FILES_0 += \ test_2d.gradient.radial.inside2.html \ test_2d.gradient.radial.inside3.html \ test_2d.gradient.radial.outside1.html \ @@ -761,7 +111,7 @@ _TEST_FILES_2 += \ test_2d.gradient.radial.cone.top.html \ $(NULL) -_TEST_FILES_3 += \ +_TEST_FILES_0 += \ test_2d.composite.uncovered.image.source-in.html \ test_2d.composite.uncovered.image.destination-in.html \ test_2d.composite.uncovered.image.source-out.html \ @@ -772,7 +122,7 @@ _TEST_FILES_3 += \ # destination bounds seem to have problems with the BEVEL/SQUARE join/cap combo. # The joins are rendered as if with MITER; the correct behaviour is also seen # if BUTT is used instead of SQUARE. -_TEST_FILES_4 += test_2d.line.cap.closed.html +_TEST_FILES_0 += test_2d.line.cap.closed.html endif @@ -780,7 +130,7 @@ endif ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) # still need bug numbers -_TEST_FILES_1 += \ +_TEST_FILES_0 += \ test_2d.composite.uncovered.fill.source-in.html \ test_2d.composite.uncovered.fill.destination-in.html \ test_2d.composite.uncovered.fill.source-out.html \ @@ -792,7 +142,7 @@ _TEST_FILES_1 += \ $(NULL) # still need bug numbers -_TEST_FILES_3 += \ +_TEST_FILES_0 += \ test_2d.gradient.radial.outside2.html \ test_2d.gradient.radial.outside3.html \ test_2d.gradient.radial.cone.shape2.html \ @@ -801,9 +151,9 @@ _TEST_FILES_3 += \ $(NULL) # still need bug numbers -_TEST_FILES_4 += test_2d.path.arc.shape.3.html +_TEST_FILES_0 += test_2d.path.arc.shape.3.html -_TEST_FILES_5 += test_2d.path.rect.selfintersect.html +_TEST_FILES_0 += test_2d.path.rect.selfintersect.html endif # These tests only pass on Mac OS X >= 10.5; see bug 450114 @@ -818,22 +168,3 @@ endif # split up into groups to work around command-line length limits libs:: $(_TEST_FILES_0) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_1) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_2) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_3) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_4) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_5) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - -libs:: $(_TEST_FILES_6) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) - diff --git a/content/canvas/test/test_canvas.html b/content/canvas/test/test_canvas.html new file mode 100644 index 00000000000..29b42967af7 --- /dev/null +++ b/content/canvas/test/test_canvas.html @@ -0,0 +1,24276 @@ + +Canvas Tests + + + + + + + + + +

Canvas test: 2d.canvas.readonly

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.canvas.reference

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.clip

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.globalalpha

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.globalcomposite

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.negative

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.path

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.shadow

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.transform

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.clearRect.zero

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.canvas.copy

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.destination-atop

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.destination-in

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.destination-out

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.destination-over

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.lighter

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.source-atop

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.source-in

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.source-out

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.source-over

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.canvas.xor

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.clip.copy

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.destination-atop

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.destination-in

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.destination-out

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.destination-over

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.lighter

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.source-atop

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.source-in

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.source-out

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.source-over

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.clip.xor

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.canvas

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.canvaspattern - bug 401790

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.default

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.fill

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.image

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.globalAlpha.imagepattern - bug 401790

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.globalAlpha.invalid

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.globalAlpha.range

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.image.copy

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.destination-atop

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.destination-in

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.destination-out

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.destination-over

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.lighter

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.source-atop

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.source-in

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.source-out

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.source-over

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.image.xor

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.operation.casesensitive - bug 401788

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.clear

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.darker

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.default

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.get

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.highlight - bug 401788

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.nullsuffix - bug 401788

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.over

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.operation.unrecognised - bug 401788

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.copy

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.destination-atop

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.destination-in

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.destination-out

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.destination-over

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.lighter

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.source-atop

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.source-in

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.source-out

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.source-over

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.solid.xor

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.copy

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.destination-atop

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.destination-in

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.destination-out

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.destination-over

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.lighter

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.source-atop

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.source-in

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.source-out

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.source-over

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.transparent.xor

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.fill.copy

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.fill.destination-atop

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.fill.destination-in

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.fill.source-in

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.fill.source-out

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.composite.uncovered.image.copy

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.image.destination-atop

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.image.destination-in

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.image.source-in

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.image.source-out

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.pattern.copy

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.pattern.destination-atop

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.pattern.destination-in

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.pattern.source-in

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.composite.uncovered.pattern.source-out

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.3arg

+

FAIL (fallback content)

+ + + + + + +

Canvas test: 2d.drawImage.5arg

+

FAIL (fallback content)

+ + + + + + +

Canvas test: 2d.drawImage.9arg.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.9arg.destpos

+

FAIL (fallback content)

+ + + + + + +

Canvas test: 2d.drawImage.9arg.destsize

+

FAIL (fallback content)

+ + + + + + +

Canvas test: 2d.drawImage.9arg.sourcepos

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.9arg.sourcesize

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.alpha

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.animated.apng

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.animated.gif

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.animated.poster

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.broken

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.canvas

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.clip

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.composite

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.floatsource

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.incomplete

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.negativedest

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.negativesource

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.nonfinite

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.nowrap

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.null

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.outsidesource

+

FAIL (fallback content)

+ + + + + + +

Canvas test: 2d.drawImage.path

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.self.1 - bug 433235

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.self.2 - bug 433235

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.transform

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.drawImage.wrongtype

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.drawImage.zerosource

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.fillRect.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.clip

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.negative

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.path

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.shadow

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.transform

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillRect.zero

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.default

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.get.semitransparent

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.get.solid

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.get.transparent

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.invalidstring

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.invalidtype

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.current.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.current.changed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.current.removed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hex3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hex6

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-clamp-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-clamp-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-clamp-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsl-clamp-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.hsla-clamp-6

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.html4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hex3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hex6

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsl-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsl-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsl-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsl-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsl-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsla-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.hsla-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-6

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgb-7

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgba-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgba-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgba-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgba-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.invalid.rgba-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-clamp-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-clamp-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-clamp-3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-clamp-4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-clamp-5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-num

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgb-percent

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-clamp-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-clamp-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-num-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-num-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-percent

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-solid-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.rgba-solid-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.svg-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.svg-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.system

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.transparent-1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.fillStyle.parse.transparent-2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.getcontext.exists

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.getcontext.shared

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.getcontext.unique

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.empty

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.alpha

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.colour

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.colouralpha

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.multiple

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.outside

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.overlap

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.overlap2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.solid

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.vertical

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.interpolate.zerosize

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.linear.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.linear.transform.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.linear.transform.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.linear.transform.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.compare

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.crosscanvas

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.invalidcolour

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.invalidoffset

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.return

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.type

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.object.update

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.behind

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.beside

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.bottom

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.cylinder

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.front

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.shape1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.shape2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.cone.top

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.equal

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.inside1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.inside2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.inside3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.outside1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.outside2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.outside3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.touch1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.touch2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.touch3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.transform.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.transform.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.gradient.radial.transform.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.basic - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.initial - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.large - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.negative - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.nonfinite - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.round - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.tiny - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.type - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.create.zero - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.clamp

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.nonpremul

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.order.alpha

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.order.cols

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.order.rgb

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.order.rows

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.range

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.source.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.source.outside

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.source.size

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.tiny

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.type

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.unaffected

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.get.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.clamp

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.ctor

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.nan

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.properties

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.readonly

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.round

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.set

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.string

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.object.undefined

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.alpha

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.clip - bug 433397

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.created - bug 433004

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.cross

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.dirty.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.dirty.outside

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.dirty.rect1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.dirty.rect2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.dirty.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.modified

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.null - bug 421715

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.path

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.unaffected

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.unchanged

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageData.put.wrongtype

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.butt

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.closed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.invalid - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.open

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.round

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cap.square

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.cross

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.defaults

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.bevel

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.closed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.invalid - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.miter

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.open

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.parallel

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.join.round

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.acute

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.exceeded

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.invalid

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.lineedge - bug 401791

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.obtuse

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.rightangle - bug 401791

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.miter.within

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.union

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.width.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.width.invalid

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.line.width.transformed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.missingargs

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.4

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.5

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.angle.6

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.empty

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.end

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.nonempty

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.scale.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.scale.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.selfintersect.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.selfintersect.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.shape.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.shape.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.shape.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.shape.4

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.shape.5

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.twopie.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.twopie.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.twopie.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.twopie.4

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.zero.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.zero.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arc.zeroradius

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.coincide.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.coincide.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.collinear.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.collinear.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.collinear.3

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.emptysubpath

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.scale

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.shape.curve1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.shape.curve2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.shape.end

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.shape.start

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.transformation

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.zero.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.arcTo.zero.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.beginPath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.bezierCurveTo.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.bezierCurveTo.emptysubpath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.bezierCurveTo.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.bezierCurveTo.scaled

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.bezierCurveTo.shape

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.basic.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.basic.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.empty

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.intersect

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.unaffected

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.winding.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.clip.winding.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.closePath.empty

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.closePath.newline

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.closePath.nextpoint

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.closed.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.closed.unaffected

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.overlap

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.winding.add

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.winding.subtract.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.winding.subtract.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.fill.winding.subtract.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.initial

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.arc

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.basic.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.basic.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.bezier

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.bigarc

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.edge

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.empty

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.outside

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.subpath

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.transform.1 - bug 405300

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.transform.2 - bug 405300

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.transform.3 - bug 405300

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.unclosed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.isPointInPath.winding

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.lineTo.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.lineTo.emptysubpath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.lineTo.nextpoint

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.lineTo.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.moveTo.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.moveTo.multiple

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.moveTo.newsubpath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.moveTo.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.quadraticCurveTo.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.quadraticCurveTo.emptysubpath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.quadraticCurveTo.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.quadraticCurveTo.scaled

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.quadraticCurveTo.shape

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.closed

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.end.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.end.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.negative

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.newsubpath

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.selfintersect

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.winding

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.rect.zero.6

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.empty

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.overlap

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.arc

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.closed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.corner

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.curve

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.line

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.prune.rect

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.scale1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.scale2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.skew

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.unaffected

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.stroke.union

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.transformation.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.transformation.changing

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.path.transformation.multiple

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.animated.gif

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.basic.canvas

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.basic.image

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.basic.nocontext

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.basic.type

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.basic.zerocanvas

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.crosscanvas

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.image.broken

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.image.incomplete

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.image.null

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.image.string

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.image.undefined

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.modify.canvas1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.modify.canvas2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.modify.image1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.modify.image2

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.norepeat.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.norepeat.coord1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.norepeat.coord2

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.norepeat.coord3

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.norepeat.outside

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.orientation.canvas

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.paint.orientation.image

+ +

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeat.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeat.coord1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeat.coord2

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeat.coord3

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeat.outside

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeatx.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeatx.coord1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeatx.outside

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeaty.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeaty.coord1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.paint.repeaty.outside

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.repeat.case

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.repeat.empty

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.repeat.null

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.pattern.repeat.nullsuffix

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.repeat.undefined

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.pattern.repeat.unrecognised

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.scaled

+ +

FAIL (fallback content)

+ + + +

Canvas test: 2d.shadow.alpha.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.alpha.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.alpha.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.alpha.4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.alpha.5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowBlur.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowBlur.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowColor.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowColor.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowOffset.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.attributes.shadowOffset.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.basic.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.basic.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.blur.high

+

FAIL (fallback content)

+ + + +

Canvas test: 2d.shadow.blur.low

+

FAIL (fallback content)

+ + + +

Canvas test: 2d.shadow.canvas.alpha

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.canvas.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.canvas.transparent.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.canvas.transparent.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.clip.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.clip.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.clip.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.composite.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.composite.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.composite.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.composite.4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.gradient.alpha

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.gradient.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.gradient.transparent.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.gradient.transparent.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.image.alpha

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.image.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.image.scale

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.image.section

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.image.transparent.1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.image.transparent.2

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.offset.negativeX

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.offset.negativeY

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.offset.positiveX

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.offset.positiveY

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.outside

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.pattern.alpha

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.pattern.basic

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.pattern.transparent.1

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.pattern.transparent.2

+

FAIL (fallback content)

+ + + + + +

Canvas test: 2d.shadow.stroke.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.stroke.cap.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.stroke.cap.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.stroke.join.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.stroke.join.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.stroke.join.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.transform.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.shadow.transform.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.bitmap

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.clip

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.fillStyle

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.globalAlpha

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.globalCompositeOperation

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.lineCap

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.lineJoin

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.lineWidth

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.miterLimit

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.path

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.shadowBlur

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.shadowColor

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.shadowOffsetX

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.shadowOffsetY

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.stack

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.stackdepth

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.strokeStyle

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.transformation

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.state.saverestore.underflow - bug 296821

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.basic

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.clip

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.globalalpha

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.globalcomposite

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.negative

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.path

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.shadow

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.transform

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.zero.1

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.zero.2

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.zero.3

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.zero.4

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeRect.zero.5

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.strokeStyle.default

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.order

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.direction

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.radians

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.wrap

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.wrapnegative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.rotate.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.large

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.multiple

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.scale.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.setTransform.multiple

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.setTransform.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.setTransform.skewed

+

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.transform.identity

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.transform.multiply

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.transform.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.transform.skewed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.translate.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.transformation.translate.nonfinite

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.type.delete

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.type.exists

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.type.extend

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.type.prototype

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.type.replace

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.voidreturn

+ +

FAIL (fallback content)

+ + + + + +

Test for Bug 397524

+Mozilla Bug 397524 +

+ + + + + + +

+ +
+
+
+ + + +

Canvas test: toDataURL.png

+

FAIL (fallback content)

+ + + +

Canvas test: context.arguments.extra

+

FAIL (fallback content)

+ + + + +

Canvas test: context.arguments.missing

+

FAIL (fallback content)

+ + + + +

Canvas test: context.casesensitive - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: context.emptystring - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: context.unrecognised.badname - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: context.unrecognised.badsuffix - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: context.unrecognised.nullsuffix - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: context.unrecognised.unicode - bug 401788

+ +

FAIL (fallback content)

+ + + + +

Canvas test: fallback.basic

+ +

FAIL (fallback content)

+ + + + +

Canvas test: fallback.multiple

+ +

FAIL

FAIL

+ + + + +

Canvas test: fallback.nested

+ +

FAIL (fallback content)

FAIL (fallback content)

+ + + + +

Canvas test: initial.colour

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.2dstate

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.clip

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.different

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.gradient

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.path

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.pattern

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.same

+ +

FAIL (fallback content)

+ + + + +

Canvas test: initial.reset.transform

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.default

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.badsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.floatsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.nonnumber

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.percentsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.whitespace

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.parse.zerosuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.reflect.1

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.reflect.2

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.removed

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.badsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.floatsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.negative

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.nonnumber

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.percentsuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.whitespace

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.zero

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.setAttribute.zerosuffix

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.style

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.type.get

+ +

FAIL (fallback content)

+ + + + +

Canvas test: size.attributes.type.set

+ +

FAIL (fallback content)

+ + + + +

Canvas test: text.font

+

FAIL (fallback content)

+ + + + +

Canvas test: text.measure

+

FAIL (fallback content)

+ + + + +

Canvas test: text.space.replace

+

FAIL (fallback content)

+ + + + +

Canvas test: text.textAlign

+

FAIL (fallback content)

+ + + + +

Canvas test: text.textBaseline

+

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.arguments.1 - bug 401795

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.arguments.2 - bug 401795

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.arguments.3 - bug 401795

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.complexcolours

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.default

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.lowercase - bug 401795

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.nocontext

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.png

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.primarycolours

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.unrecognised - bug 401795

+ +

FAIL (fallback content)

+ + + + +

Canvas test: toDataURL.zerosize

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.delete

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.exists

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.extend

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.name

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.prototype

+ +

FAIL (fallback content)

+ + + + +

Canvas test: type.replace

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imagedata_coercion

+ +

FAIL (fallback content)

+ + + + +

Canvas test: 2d.imageRenderingQuality

+
+ + + + From 78f5bb77bee6e446baa130feef7e38d9fbf3b726 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 30 Jul 2009 15:16:44 +1200 Subject: [PATCH 002/175] Bug 486200. Add window.mozInnerScreenX/Y APIs to return usable screen coordinate APIs (as opposed to window.screenX/Y which return window frame coordinates and are unusable). Also adds window.mozScreenPixelsPerCSSPixel which chrome-prileged code can use to convert CSS pixels to actual screen pixels. r=dbaron,sr=jst --HG-- extra : rebase_source : 30f4c953f3e3866da042b935953ba239ac0a93c4 --- dom/base/nsGlobalWindow.cpp | 66 +++++++++++++++ dom/base/nsGlobalWindow.h | 2 + dom/interfaces/base/nsIDOMWindowInternal.idl | 5 +- dom/tests/mochitest/general/Makefile.in | 4 +- .../mochitest/general/test_innerScreen.xul | 83 +++++++++++++++++++ 5 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 dom/tests/mochitest/general/test_innerScreen.xul diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index ac280f57dc6..4123377d56c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -3347,6 +3347,72 @@ nsGlobalWindow::GetScreenX(PRInt32* aScreenX) return NS_OK; } +nsRect +nsGlobalWindow::GetInnerScreenRect() +{ + if (!mDocShell) + return nsRect(); + + nsGlobalWindow* rootWindow = + static_cast(GetPrivateRoot()); + if (rootWindow) { + rootWindow->FlushPendingNotifications(Flush_Layout); + } + + nsCOMPtr presShell; + mDocShell->GetPresShell(getter_AddRefs(presShell)); + if (!presShell) + return nsRect(); + nsIFrame* rootFrame = presShell->GetRootFrame(); + if (!rootFrame) + return nsRect(); + + return rootFrame->GetScreenRectInAppUnits(); +} + +NS_IMETHODIMP +nsGlobalWindow::GetMozInnerScreenX(float* aScreenX) +{ + FORWARD_TO_OUTER(GetMozInnerScreenX, (aScreenX), NS_ERROR_NOT_INITIALIZED); + + nsRect r = GetInnerScreenRect(); + *aScreenX = nsPresContext::AppUnitsToFloatCSSPixels(r.x); + return NS_OK; +} + +NS_IMETHODIMP +nsGlobalWindow::GetMozInnerScreenY(float* aScreenY) +{ + FORWARD_TO_OUTER(GetMozInnerScreenY, (aScreenY), NS_ERROR_NOT_INITIALIZED); + + nsRect r = GetInnerScreenRect(); + *aScreenY = nsPresContext::AppUnitsToFloatCSSPixels(r.y); + return NS_OK; +} + +NS_IMETHODIMP +nsGlobalWindow::GetMozScreenPixelsPerCSSPixel(float* aScreenPixels) +{ + FORWARD_TO_OUTER(GetMozScreenPixelsPerCSSPixel, + (aScreenPixels), NS_ERROR_NOT_INITIALIZED); + + *aScreenPixels = 1; + + if (!nsContentUtils::IsCallerTrustedForRead()) + return NS_ERROR_DOM_SECURITY_ERR; + if (!mDocShell) + return NS_OK; + nsCOMPtr presContext; + mDocShell->GetPresContext(getter_AddRefs(presContext)); + if (!presContext) + return NS_OK; + + *aScreenPixels = float(nsPresContext::AppUnitsPerCSSPixel())/ + presContext->AppUnitsPerDevPixel(); + + return NS_OK; +} + NS_IMETHODIMP nsGlobalWindow::SetScreenX(PRInt32 aScreenX) { diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index e568c7279fc..7cd9814780a 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -90,6 +90,7 @@ #include "nsIXPCScriptable.h" #include "nsPoint.h" #include "nsSize.h" +#include "nsRect.h" #include "mozFlushType.h" #include "prclist.h" #include "nsIDOMStorageObsolete.h" @@ -594,6 +595,7 @@ protected: nsresult GetOuterSize(nsIntSize* aSizeCSSPixels); nsresult SetOuterSize(PRInt32 aLengthCSSPixels, PRBool aIsWidth); + nsRect GetInnerScreenRect(); PRBool IsFrame() { diff --git a/dom/interfaces/base/nsIDOMWindowInternal.idl b/dom/interfaces/base/nsIDOMWindowInternal.idl index ceeedf678e5..9cff9a929a3 100644 --- a/dom/interfaces/base/nsIDOMWindowInternal.idl +++ b/dom/interfaces/base/nsIDOMWindowInternal.idl @@ -44,7 +44,7 @@ interface nsIControllers; interface nsIDOMLocation; interface nsIVariant; -[scriptable, uuid(3414EBC7-731F-4697-9F43-ACA6F5050875)] +[scriptable, uuid(62579239-b619-4bf2-8d39-0b73e8663a85)] interface nsIDOMWindowInternal : nsIDOMWindow2 { readonly attribute nsIDOMWindowInternal window; @@ -104,6 +104,9 @@ interface nsIDOMWindowInternal : nsIDOMWindow2 attribute long outerHeight; attribute long screenX; attribute long screenY; + readonly attribute float mozInnerScreenX; + readonly attribute float mozInnerScreenY; + readonly attribute float mozScreenPixelsPerCSSPixel; /* The offset in pixels by which the window is scrolled */ readonly attribute long pageXOffset; diff --git a/dom/tests/mochitest/general/Makefile.in b/dom/tests/mochitest/general/Makefile.in index 36b8d81c289..120abbd0df6 100644 --- a/dom/tests/mochitest/general/Makefile.in +++ b/dom/tests/mochitest/general/Makefile.in @@ -44,7 +44,9 @@ relativesrcdir = dom/tests/mochitest/general include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk -_TEST_FILES = test_offsets.html \ +_TEST_FILES = \ + test_innerScreen.xul \ + test_offsets.html \ test_offsets.xul \ test_offsets.js \ test_domWindowUtils.html \ diff --git a/dom/tests/mochitest/general/test_innerScreen.xul b/dom/tests/mochitest/general/test_innerScreen.xul new file mode 100644 index 00000000000..46fdabffd82 --- /dev/null +++ b/dom/tests/mochitest/general/test_innerScreen.xul @@ -0,0 +1,83 @@ + + + + + + + + From 5fc2887b3c2f95fa46473a55a7d7c06f38cb42e5 Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 30 Jul 2009 15:26:59 +1200 Subject: [PATCH 003/175] Bug 505988. Don't unwrap when we shouldn't. r=bz --HG-- extra : rebase_source : d251c442ba2fae0f9ffcae99c2c84d780c99ea52 --- js/src/xpconnect/src/xpcprivate.h | 9 +-- js/src/xpconnect/src/xpcvariant.cpp | 69 +++++++++---------- js/src/xpconnect/tests/mochitest/Makefile.in | 1 + .../tests/mochitest/test_bug384632.html | 32 +++++++++ 4 files changed, 70 insertions(+), 41 deletions(-) create mode 100644 js/src/xpconnect/tests/mochitest/test_bug384632.html diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h index 2ce45487d1b..59db7a8afcc 100644 --- a/js/src/xpconnect/src/xpcprivate.h +++ b/js/src/xpconnect/src/xpcprivate.h @@ -4007,7 +4007,7 @@ public: jsval GetJSVal() const {return mJSVal;} - XPCVariant(jsval aJSVal); + XPCVariant(XPCCallContext& ccx, jsval aJSVal); /** * Convert a variant into a jsval. @@ -4032,6 +4032,7 @@ protected: protected: nsDiscriminatedUnion mData; jsval mJSVal; + JSBool mReturnRawObject; }; NS_DEFINE_STATIC_IID_ACCESSOR(XPCVariant, XPCVARIANT_IID) @@ -4040,10 +4041,10 @@ class XPCTraceableVariant: public XPCVariant, public XPCRootSetElem { public: - XPCTraceableVariant(XPCJSRuntime *runtime, jsval aJSVal) - : XPCVariant(aJSVal) + XPCTraceableVariant(XPCCallContext& ccx, jsval aJSVal) + : XPCVariant(ccx, aJSVal) { - runtime->AddVariantRoot(this); + ccx.GetRuntime()->AddVariantRoot(this); } virtual ~XPCTraceableVariant(); diff --git a/js/src/xpconnect/src/xpcvariant.cpp b/js/src/xpconnect/src/xpcvariant.cpp index 9ce012e82e1..6a5f0f6955c 100644 --- a/js/src/xpconnect/src/xpcvariant.cpp +++ b/js/src/xpconnect/src/xpcvariant.cpp @@ -55,10 +55,26 @@ NS_IMPL_CI_INTERFACE_GETTER2(XPCVariant, XPCVariant, nsIVariant) NS_IMPL_CYCLE_COLLECTING_ADDREF(XPCVariant) NS_IMPL_CYCLE_COLLECTING_RELEASE(XPCVariant) -XPCVariant::XPCVariant(jsval aJSVal) +XPCVariant::XPCVariant(XPCCallContext& ccx, jsval aJSVal) : mJSVal(aJSVal) { nsVariant::Initialize(&mData); + if(!JSVAL_IS_PRIMITIVE(mJSVal)) + { + // If the incoming object is an XPCWrappedNative, then it could be a + // double-wrapped object, and we should return the double-wrapped + // object back out to script. + + JSObject* proto; + XPCWrappedNative* wn = + XPCWrappedNative::GetWrappedNativeOfJSObject(ccx, + JSVAL_TO_OBJECT(mJSVal), + nsnull, + &proto); + mReturnRawObject = !wn && !proto; + } + else + mReturnRawObject = JS_FALSE; } XPCTraceableVariant::~XPCTraceableVariant() @@ -119,9 +135,9 @@ XPCVariant* XPCVariant::newVariant(XPCCallContext& ccx, jsval aJSVal) XPCVariant* variant; if(!JSVAL_IS_TRACEABLE(aJSVal)) - variant = new XPCVariant(aJSVal); + variant = new XPCVariant(ccx, aJSVal); else - variant = new XPCTraceableVariant(ccx.GetRuntime(), aJSVal); + variant = new XPCTraceableVariant(ccx, aJSVal); if(!variant) return nsnull; @@ -401,6 +417,15 @@ XPCVariant::VariantDataToJS(XPCCallContext& ccx, return JS_TRUE; } + if(xpcvariant->mReturnRawObject) + { + NS_ASSERTION(type == nsIDataType::VTYPE_INTERFACE || + type == nsIDataType::VTYPE_INTERFACE_IS, + "Weird variant"); + *pJSVal = realVal; + return JS_TRUE; + } + // else, it's an object and we really need to double wrap it if we've // already decided that its 'natural' type is as some sort of interface. @@ -641,40 +666,10 @@ VARIANT_DONE: } else { - // Last ditch check to prevent us from double-wrapping a regular JS - // object. This allows us to unwrap regular JS objects (since we - // normally can't double wrap them). See bug 384632. - *pJSVal = JSVAL_VOID; - if(type == nsIDataType::VTYPE_INTERFACE || - type == nsIDataType::VTYPE_INTERFACE_IS) - { - nsISupports *src = reinterpret_cast(xpctvar.val.p); - if(nsXPCWrappedJSClass::IsWrappedJS(src)) - { - // First QI the wrapper to the right interface. - nsCOMPtr wrapper; - nsresult rv = src->QueryInterface(iid, getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, JS_FALSE); - - // Now, get the actual JS object out of the wrapper. - nsCOMPtr holder = - do_QueryInterface(wrapper); - NS_ENSURE_TRUE(holder, JS_FALSE); - - JSObject *obj; - holder->GetJSObject(&obj); - NS_ASSERTION(obj, "No JS object but the QIs above succeeded?"); - *pJSVal = OBJECT_TO_JSVAL(obj); - success = JS_TRUE; - } - } - if(!JSVAL_IS_OBJECT(*pJSVal)) - { - success = XPCConvert::NativeData2JS(ccx, pJSVal, - (const void*)&xpctvar.val, - xpctvar.type, - &iid, scope, pErr); - } + success = XPCConvert::NativeData2JS(ccx, pJSVal, + (const void*)&xpctvar.val, + xpctvar.type, + &iid, scope, pErr); } if(xpctvar.IsValAllocated()) diff --git a/js/src/xpconnect/tests/mochitest/Makefile.in b/js/src/xpconnect/tests/mochitest/Makefile.in index 3ff1465c03e..90ed8ade38b 100644 --- a/js/src/xpconnect/tests/mochitest/Makefile.in +++ b/js/src/xpconnect/tests/mochitest/Makefile.in @@ -47,6 +47,7 @@ include $(topsrcdir)/config/rules.mk _TEST_FILES = bug500931_helper.html \ inner.html \ test_bug361111.xul \ + test_bug384632.html \ test_bug390488.html \ test_bug393269.html \ test_bug396851.html \ diff --git a/js/src/xpconnect/tests/mochitest/test_bug384632.html b/js/src/xpconnect/tests/mochitest/test_bug384632.html new file mode 100644 index 00000000000..7cdc397adb4 --- /dev/null +++ b/js/src/xpconnect/tests/mochitest/test_bug384632.html @@ -0,0 +1,32 @@ + + + + + Test for Bug 384632 + + + + + +Mozilla Bug 384632 +

+ +
+
+
+ + From 3c037ab317368860deac2da213332a85f6af24cd Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Thu, 30 Jul 2009 15:27:24 +1200 Subject: [PATCH 004/175] Bug 506552. Fix testcase doctype. r=dbaron --HG-- extra : rebase_source : ee371dbb3106880b1609e0d69d1824a3edb7a91e --- layout/reftests/css-mediaqueries/mq_print_orientation.xhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layout/reftests/css-mediaqueries/mq_print_orientation.xhtml b/layout/reftests/css-mediaqueries/mq_print_orientation.xhtml index 418f0787f18..151686e72f3 100644 --- a/layout/reftests/css-mediaqueries/mq_print_orientation.xhtml +++ b/layout/reftests/css-mediaqueries/mq_print_orientation.xhtml @@ -1,5 +1,5 @@ - + Media Query - Print mode test - orientation From a703badcb627f45c0bbf82177e433f6d52ae391a Mon Sep 17 00:00:00 2001 From: Mook Date: Thu, 30 Jul 2009 15:41:03 +1200 Subject: [PATCH 005/175] Bug 502188. Implement xptcall for mingw-Windows64. r=timeless --HG-- extra : rebase_source : 2c03e2ae8d491059b9c3759e7e5082a20c1b715a --- .../reflect/xptcall/src/md/win32/Makefile.in | 5 + .../src/md/win32/xptcinvoke_asm_x86_64_gnu.s | 158 +++++++++ .../src/md/win32/xptcstubs_x86_64_gnu.cpp | 331 ++++++++++++++++++ 3 files changed, 494 insertions(+) create mode 100644 xpcom/reflect/xptcall/src/md/win32/xptcinvoke_asm_x86_64_gnu.s create mode 100644 xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp diff --git a/xpcom/reflect/xptcall/src/md/win32/Makefile.in b/xpcom/reflect/xptcall/src/md/win32/Makefile.in index b08d2e5f7e6..4ab8dbae3e8 100644 --- a/xpcom/reflect/xptcall/src/md/win32/Makefile.in +++ b/xpcom/reflect/xptcall/src/md/win32/Makefile.in @@ -82,8 +82,13 @@ ASFLAGS += /I../../..public endif ifeq ($(TARGET_CPU),x86_64) +ifndef GNU_CXX CPPSRCS := xptcinvoke_x86_64.cpp xptcstubs_x86_64.cpp ASFILES := xptcinvoke_asm_x86_64.asm xptcstubs_asm_x86_64.asm +else #!GNU_CXX +CPPSRCS := xptcinvoke_x86_64.cpp xptcstubs_x86_64_gnu.cpp +ASFILES := xptcinvoke_asm_x86_64_gnu.s +endif #!GNU_CXX endif ifeq ($(OS_ARCH),WINCE) diff --git a/xpcom/reflect/xptcall/src/md/win32/xptcinvoke_asm_x86_64_gnu.s b/xpcom/reflect/xptcall/src/md/win32/xptcinvoke_asm_x86_64_gnu.s new file mode 100644 index 00000000000..dfd9ecc2001 --- /dev/null +++ b/xpcom/reflect/xptcall/src/md/win32/xptcinvoke_asm_x86_64_gnu.s @@ -0,0 +1,158 @@ +# ***** 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 +# Makoto Kato . +# Portions created by the Initial Developer are Copyright (C) 2004 +# the Initial Developer. All Rights Reserved. +# +# 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 ***** + +.extern _invoke_copy_to_stack + + +.text +.intel_syntax + +# +#_XPTC__InvokebyIndex(nsISupports* that, PRUint32 methodIndex, +# PRUint32 paramCount, nsXPTCVariant* params) +# + +.globl _XPTC__InvokebyIndex +.def _XPTC__InvokebyIndex + .scl 3 + .type 46 +.endef +_XPTC__InvokebyIndex: + + # + # store register parameters + # + + mov qword ptr [rsp+32], r9 # params + mov dword ptr [rsp+24], r8d # paramCount + mov dword ptr [rsp+16], edx # methodIndex + mov qword ptr [rsp+8], rcx # that + + # + # store RBX/RBP register for backup + # + + mov qword ptr [rsp-16], rbp + + mov rbp, rsp # store current RSP to RBP + # .SETFRAME rbp, 0 + # .ENDPROLOG + + sub rsp, 32 + + # + # maybe we don't have any parameters to copy + # + + test r8d, r8d + jz noparams + + # + # make space for 1st parameter + + mov eax, r8d + and eax, 1 # AMD64 must be alignment to 16 bytes + add eax, r8d + shl rax, 3 # *= 8 + sub rsp, rax + mov rcx, rsp + + # + # 2nd parameter is parameter count + # + + mov edx, r8d + + # + # 3rd parameter is params + # + + mov r8, r9 + + sub rsp, 32 + + call _invoke_copy_to_stack # rcx = d + # edx = paramCount + # r8 = s + + add rsp, 32 + + # + # Build parameters + # + + mov rdx, qword ptr [rsp] # 1st parameter + movsd xmm1, qword ptr [rsp] # for double + + mov r8, qword ptr [rsp+8] # 2nd parameter + movsd xmm2, qword ptr [rsp+8] # for double + + mov r9, qword ptr [rsp+16] # 3rd parameter + movsd xmm3, qword ptr [rsp+16] # for double + + # + # 1st parameter (this) + # + + mov rcx, qword ptr [rbp+8] # that + +noparams: + + # + # calculate call address + # + + mov r11, qword ptr [rcx] + mov eax, dword ptr [rbp+16] # methodIndex + + # + # Now current stack has parameter list + # But, since callee function backups parameters, make space into stack. + + sub rsp, 8 + + call qword ptr [r11+rax*8] # stdcall, i.e. callee cleans up stack. + + # + # restore registers + # + + mov rsp, rbp + mov rbp, qword ptr [rsp-16] + + ret + + diff --git a/xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp b/xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp new file mode 100644 index 00000000000..c55cba3ffde --- /dev/null +++ b/xpcom/reflect/xptcall/src/md/win32/xptcstubs_x86_64_gnu.cpp @@ -0,0 +1,331 @@ +/* -*- Mode: C; tab-width: 8; 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.org Code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1999 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 "xptcprivate.h" +#include "xptiprivate.h" +#include "../unix/xptc_gcc_x86_unix.h" + +/* + * This is for Windows 64 bit (x86_64) using GCC syntax + * Code was copied from the MSVC version. + */ + +#if !defined(_AMD64_) || !defined(__GNUC__) +# error xptcstubs_x86_64_gnu.cpp being used unexpectedly +#endif + +extern "C" nsresult +PrepareAndDispatch(nsXPTCStubBase * self, PRUint32 methodIndex, + PRUint64 * args, PRUint64 * gprData, double *fprData) +{ +#define PARAM_BUFFER_COUNT 16 +// +// "this" pointer is first parameter, so parameter count is 3. +// +#define PARAM_GPR_COUNT 3 +#define PARAM_FPR_COUNT 3 + + nsXPTCMiniVariant paramBuffer[PARAM_BUFFER_COUNT]; + nsXPTCMiniVariant* dispatchParams = NULL; + const nsXPTMethodInfo* info = NULL; + PRUint8 paramCount; + PRUint8 i; + nsresult result = NS_ERROR_FAILURE; + + NS_ASSERTION(self, "no self"); + + self->mEntry->GetMethodInfo(PRUint16(methodIndex), &info); + NS_ASSERTION(info, "no method info"); + + paramCount = info->GetParamCount(); + + // + // setup variant array pointer + // + + if(paramCount > PARAM_BUFFER_COUNT) + dispatchParams = new nsXPTCMiniVariant[paramCount]; + else + dispatchParams = paramBuffer; + + NS_ASSERTION(dispatchParams,"no place for params"); + + PRUint64* ap = args; + PRUint32 iCount = 0; + + for(i = 0; i < paramCount; i++) + { + const nsXPTParamInfo& param = info->GetParam(i); + const nsXPTType& type = param.GetType(); + nsXPTCMiniVariant* dp = &dispatchParams[i]; + + if(param.IsOut() || !type.IsArithmetic()) + { + if (iCount < PARAM_GPR_COUNT) + dp->val.p = (void*)gprData[iCount++]; + else + dp->val.p = (void*)*ap++; + + continue; + } + // else + switch(type) + { + case nsXPTType::T_I8: + if (iCount < PARAM_GPR_COUNT) + dp->val.i8 = (PRInt8)gprData[iCount++]; + else + dp->val.i8 = *((PRInt8*)ap++); + break; + + case nsXPTType::T_I16: + if (iCount < PARAM_GPR_COUNT) + dp->val.i16 = (PRInt16)gprData[iCount++]; + else + dp->val.i16 = *((PRInt16*)ap++); + break; + + case nsXPTType::T_I32: + if (iCount < PARAM_GPR_COUNT) + dp->val.i32 = (PRInt32)gprData[iCount++]; + else + dp->val.i32 = *((PRInt32*)ap++); + break; + + case nsXPTType::T_I64: + if (iCount < PARAM_GPR_COUNT) + dp->val.i64 = (PRInt64)gprData[iCount++]; + else + dp->val.i64 = *((PRInt64*)ap++); + break; + + case nsXPTType::T_U8: + if (iCount < PARAM_GPR_COUNT) + dp->val.u8 = (PRUint8)gprData[iCount++]; + else + dp->val.u8 = *((PRUint8*)ap++); + break; + + case nsXPTType::T_U16: + if (iCount < PARAM_GPR_COUNT) + dp->val.u16 = (PRUint16)gprData[iCount++]; + else + dp->val.u16 = *((PRUint16*)ap++); + break; + + case nsXPTType::T_U32: + if (iCount < PARAM_GPR_COUNT) + dp->val.u32 = (PRUint32)gprData[iCount++]; + else + dp->val.u32 = *((PRUint32*)ap++); + break; + + case nsXPTType::T_U64: + if (iCount < PARAM_GPR_COUNT) + dp->val.u64 = (PRUint64)gprData[iCount++]; + else + dp->val.u64 = *((PRUint64*)ap++); + break; + + case nsXPTType::T_FLOAT: + if (iCount < PARAM_FPR_COUNT) + dp->val.f = (float)fprData[iCount++]; + else + dp->val.f = *((float*)ap++); + break; + + case nsXPTType::T_DOUBLE: + if (iCount < PARAM_FPR_COUNT) + dp->val.d = (double)fprData[iCount++]; + else + dp->val.d = *((double*)ap++); + break; + + case nsXPTType::T_BOOL: + if (iCount < PARAM_GPR_COUNT) + dp->val.b = (PRBool)gprData[iCount++]; + else + dp->val.b = *((PRBool*)ap++); + break; + + case nsXPTType::T_CHAR: + if (iCount < PARAM_GPR_COUNT) + dp->val.c = (char)gprData[iCount++]; + else + dp->val.c = *((char*)ap++); + break; + + case nsXPTType::T_WCHAR: + if (iCount < PARAM_GPR_COUNT) + dp->val.wc = (wchar_t)gprData[iCount++]; + else + dp->val.wc = *((wchar_t*)ap++); + break; + + default: + NS_ASSERTION(0, "bad type"); + break; + } + } + + result = self->mOuter->CallMethod((PRUint16)methodIndex, info, dispatchParams); + + if(dispatchParams != paramBuffer) + delete [] dispatchParams; + + return result; +} + +__asm__ ( + ".text\n" + ".intel_syntax\n" /* switch to Intel syntax to look like the MSVC assembly */ + ".globl SharedStub\n" + ".def SharedStub ; .scl 3 ; .type 46 ; .endef \n" + "SharedStub:\n" + + /* rcx is this pointer. Need backup for optimized build */ + + "mov qword ptr [rsp+88], rcx\n" + + /* + * fist 4 parameters (1st is "this" pointer) are passed in registers. + */ + + /* for floating value */ + + "movsd qword ptr [rsp+64], xmm1\n" + "movsd qword ptr [rsp+72], xmm2\n" + "movsd qword ptr [rsp+80], xmm3\n" + + /* for integer value */ + + "mov qword ptr [rsp+40], rdx\n" + "mov qword ptr [rsp+48], r8\n" + "mov qword ptr [rsp+56], r9\n" + + /* + * Call PrepareAndDispatch function + */ + + /* 5th parameter (floating parameters) of PrepareAndDispatch */ + + "lea r9, qword ptr [rsp+64]\n" + "mov qword ptr [rsp+32], r9\n" + + /* 4th parameter (normal parameters) of PrepareAndDispatch */ + + "lea r9, qword ptr [rsp+40]\n" + + /* 3rd parameter (pointer to args on stack) */ + + "lea r8, qword ptr [rsp+40+104]\n" + + /* 2nd parameter (vtbl_index) */ + + "mov rdx, r11\n" + + /* 1st parameter (this) (rcx) */ + + "call _PrepareAndDispatch\n" + + /* restore rcx */ + + "mov rcx, qword ptr [rsp+88]\n" + + /* + * clean up register + */ + + "add rsp, 104+8\n" + + /* set return address */ + + "mov rdx, qword ptr [rsp-8]\n" + + /* simulate __stdcall return */ + + "jmp rdx\n" + + /* back to AT&T syntax */ + ".att_syntax\n" + +); + +#define STUB_ENTRY(n) \ +asm(".intel_syntax\n" /* this is in intel syntax */ \ + ".text\n" \ + ".align 2\n" \ + ".if " #n " < 10\n" \ + ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase5Stub" #n "Ev@4\n" \ + ".def " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase5Stub" #n "Ev@4\n" \ + ".scl 3\n" /* private */ \ + ".type 46\n" /* function returning unsigned int */ \ + ".endef\n" \ + SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase5Stub" #n "Ev@4:\n" \ + ".elseif " #n " < 100\n" \ + ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase6Stub" #n "Ev@4\n" \ + ".def " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase6Stub" #n "Ev@4\n" \ + ".scl 3\n" /* private */\ + ".type 46\n" /* function returning unsigned int */ \ + ".endef\n" \ + SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase6Stub" #n "Ev@4:\n" \ + ".elseif " #n " < 1000\n" \ + ".globl " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase7Stub" #n "Ev@4\n" \ + ".def " SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase7Stub" #n "Ev@4\n" \ + ".scl 3\n" /* private */ \ + ".type 46\n" /* function returning unsigned int */ \ + ".endef\n" \ + SYMBOL_UNDERSCORE "_ZN14nsXPTCStubBase7Stub" #n "Ev@4:\n" \ + ".else\n" \ + ".err \"stub number " #n " >= 1000 not yet supported\"\n" \ + ".endif\n" \ + "push rbx\n" \ + "mov rbx, " #n "\n" \ + "jmp SharedStub\n" \ + ".att_syntax\n" /* back to AT&T syntax */ \ + ""); + +#define SENTINEL_ENTRY(n) \ +nsresult nsXPTCStubBase::Sentinel##n() \ +{ \ + NS_ASSERTION(0,"nsXPTCStubBase::Sentinel called"); \ + return NS_ERROR_NOT_IMPLEMENTED; \ +} + +#include "xptcstubsdef.inc" + From d72ab83441d6a7df2c620e2df21219a6b403d303 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Thu, 30 Jul 2009 15:43:22 +1200 Subject: [PATCH 006/175] Bug 486065. Hack nsCSSOffsetState::InitOffsets to zero out computed padding and borders when the scrollbar size is zero. r=dbaron --HG-- extra : rebase_source : 92bcaee46ded00722e3fc86b4ca538e5f90f16ef --- layout/generic/nsHTMLReflowState.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 7bdde4a9c4c..2a207e4da19 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -1925,7 +1925,8 @@ nsCSSOffsetState::InitOffsets(nscoord aContainingBlockWidth, } mComputedBorderPadding += mComputedPadding; - if (frame->GetType() == nsGkAtoms::tableFrame) { + nsIAtom* frameType = frame->GetType(); + if (frameType == nsGkAtoms::tableFrame) { nsTableFrame *tableFrame = static_cast(frame); if (tableFrame->IsBorderCollapse()) { @@ -1936,6 +1937,23 @@ nsCSSOffsetState::InitOffsets(nscoord aContainingBlockWidth, mComputedPadding.SizeTo(0,0,0,0); mComputedBorderPadding = tableFrame->GetIncludedOuterBCBorder(); } + } else if (frameType == nsGkAtoms::scrollbarFrame) { + // scrollbars may have had their width or height smashed to zero + // by the associated scrollframe, in which case we must not report + // any padding or border on that axis. + nsSize size(frame->GetSize()); + if (size.width == 0) { + mComputedPadding.left = 0; + mComputedPadding.right = 0; + mComputedBorderPadding.left = 0; + mComputedBorderPadding.right = 0; + } + if (size.height == 0) { + mComputedPadding.top = 0; + mComputedPadding.bottom = 0; + mComputedBorderPadding.top = 0; + mComputedBorderPadding.bottom = 0; + } } } From d8373a09b7faae19407913f3ded1e719192c6738 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Thu, 30 Jul 2009 15:28:53 +1200 Subject: [PATCH 007/175] Bug 500311 - Count tracks as active during initialization. r=chris.double --HG-- extra : rebase_source : 25e5d3aa9661a95e1a623a9fe15f5c12f31d13f3 --- media/liboggplay/README_MOZILLA | 2 + media/liboggplay/bug500311.patch | 99 +++++++++++++++++++ media/liboggplay/src/liboggplay/oggplay.c | 3 +- .../src/liboggplay/oggplay_callback.c | 4 +- media/liboggplay/update.sh | 1 + 5 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 media/liboggplay/bug500311.patch diff --git a/media/liboggplay/README_MOZILLA b/media/liboggplay/README_MOZILLA index 2171af1dd1a..87b7fccd38c 100644 --- a/media/liboggplay/README_MOZILLA +++ b/media/liboggplay/README_MOZILLA @@ -45,3 +45,5 @@ oggplay_os2.patch: Bug 448918 - add OS/2 support (this patch should be bug498815.patch: Fix for bug 498815. bug498824.patch: Fix for bug 498824. +bug500311.patch: Fix crash during decoder initialization. + diff --git a/media/liboggplay/bug500311.patch b/media/liboggplay/bug500311.patch new file mode 100644 index 00000000000..c0ba0fb492a --- /dev/null +++ b/media/liboggplay/bug500311.patch @@ -0,0 +1,99 @@ +diff --git a/media/liboggplay/src/liboggplay/oggplay.c b/media/liboggplay/src/liboggplay/oggplay.c +--- a/media/liboggplay/src/liboggplay/oggplay.c ++++ b/media/liboggplay/src/liboggplay/oggplay.c +@@ -129,16 +129,17 @@ oggplay_initialise(OggPlay *me, int bloc + } + + /* + * set all the tracks to inactive + */ + for (i = 0; i < me->num_tracks; i++) { + me->decode_data[i]->active = 0; + } ++ me->active_tracks = 0; + + /* + * if the buffer was set up before initialisation, prepare it now + */ + if (me->buffer != NULL) { + oggplay_buffer_prepare(me); + } + +@@ -693,18 +694,18 @@ read_more_data: + /* end-of-file */ + if (r == 0) { + num_records = oggplay_callback_info_prepare(me, &info); + /* + * set all of the tracks to inactive + */ + for (i = 0; i < me->num_tracks; i++) { + me->decode_data[i]->active = 0; +- me->active_tracks = 0; + } ++ me->active_tracks = 0; + + if (info != NULL) { + me->callback (me, num_records, info, me->callback_user_ptr); + oggplay_callback_info_destroy(me, info); + } + + /* + * ensure all tracks have their final data packet set to end_of_stream +diff --git a/media/liboggplay/src/liboggplay/oggplay_callback.c b/media/liboggplay/src/liboggplay/oggplay_callback.c +--- a/media/liboggplay/src/liboggplay/oggplay_callback.c ++++ b/media/liboggplay/src/liboggplay/oggplay_callback.c +@@ -54,16 +54,17 @@ oggplay_init_theora(void *user_data) { + theora_info_init(&(decoder->video_info)); + theora_comment_init(&(decoder->video_comment)); + decoder->remaining_header_packets = 3; + decoder->granulepos_seen = 0; + decoder->frame_delta = 0; + decoder->y_width = 0; + decoder->convert_to_rgb = 0; + decoder->decoder.decoded_type = OGGPLAY_YUV_VIDEO; ++ decoder->decoder.player->active_tracks++; + } + + void + oggplay_shutdown_theora(void *user_data) { + + OggPlayTheoraDecode * decoder = (OggPlayTheoraDecode *)user_data; + + if (decoder->remaining_header_packets == 0) { +@@ -366,16 +367,17 @@ oggplay_init_audio (void * user_data) { + + decoder->sound_info.channels = 0; + fish_sound_set_interleave(decoder->sound_handle, 1); + fish_sound_set_decoded_float_ilv(decoder->sound_handle, + oggplay_fish_sound_callback_floats, + (void *)decoder); + + decoder->decoder.decoded_type = OGGPLAY_FLOATS_AUDIO; ++ decoder->decoder.player->active_tracks++; + } + + void + oggplay_shutdown_audio(void *user_data) { + + OggPlayAudioDecode * decoder = (OggPlayAudioDecode *)user_data; + + fish_sound_delete(decoder->sound_handle); +@@ -566,17 +568,17 @@ oggplay_initialise_decoder(OggPlay *me, + + if (decoder == NULL) + return NULL; + + decoder->serialno = serialno; + decoder->content_type = content_type; + decoder->content_type_name = + oggz_stream_get_content_type (me->oggz, serialno); +- decoder->active = 0; ++ decoder->active = 1; + decoder->final_granulepos = -1; + decoder->player = me; + decoder->decoded_type = OGGPLAY_TYPE_UNKNOWN; + + /* + * set the StreamInfo to unitialised until we get some real data in + */ + decoder->stream_info = OGGPLAY_STREAM_UNINITIALISED; diff --git a/media/liboggplay/src/liboggplay/oggplay.c b/media/liboggplay/src/liboggplay/oggplay.c index ef822f8558d..98dd6a4b8f9 100644 --- a/media/liboggplay/src/liboggplay/oggplay.c +++ b/media/liboggplay/src/liboggplay/oggplay.c @@ -134,6 +134,7 @@ oggplay_initialise(OggPlay *me, int block) { for (i = 0; i < me->num_tracks; i++) { me->decode_data[i]->active = 0; } + me->active_tracks = 0; /* * if the buffer was set up before initialisation, prepare it now @@ -698,8 +699,8 @@ read_more_data: */ for (i = 0; i < me->num_tracks; i++) { me->decode_data[i]->active = 0; - me->active_tracks = 0; } + me->active_tracks = 0; if (info != NULL) { me->callback (me, num_records, info, me->callback_user_ptr); diff --git a/media/liboggplay/src/liboggplay/oggplay_callback.c b/media/liboggplay/src/liboggplay/oggplay_callback.c index a98aef6bc8e..b0b21412c0d 100644 --- a/media/liboggplay/src/liboggplay/oggplay_callback.c +++ b/media/liboggplay/src/liboggplay/oggplay_callback.c @@ -59,6 +59,7 @@ oggplay_init_theora(void *user_data) { decoder->y_width = 0; decoder->convert_to_rgb = 0; decoder->decoder.decoded_type = OGGPLAY_YUV_VIDEO; + decoder->decoder.player->active_tracks++; } void @@ -371,6 +372,7 @@ oggplay_init_audio (void * user_data) { (void *)decoder); decoder->decoder.decoded_type = OGGPLAY_FLOATS_AUDIO; + decoder->decoder.player->active_tracks++; } void @@ -571,7 +573,7 @@ oggplay_initialise_decoder(OggPlay *me, int content_type, int serialno) { decoder->content_type = content_type; decoder->content_type_name = oggz_stream_get_content_type (me->oggz, serialno); - decoder->active = 0; + decoder->active = 1; decoder->final_granulepos = -1; decoder->player = me; decoder->decoded_type = OGGPLAY_TYPE_UNKNOWN; diff --git a/media/liboggplay/update.sh b/media/liboggplay/update.sh index d0c9e239ffb..1d4788edd6b 100644 --- a/media/liboggplay/update.sh +++ b/media/liboggplay/update.sh @@ -64,3 +64,4 @@ patch -p3 < bug498815.patch patch -p3 < bug498824.patch patch -p3 < bug496529.patch patch -p3 < bug499519.patch +patch -p3 < bug500311.patch From 0cf332a1871d48a8857d32f8448f3f8329aa626d Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 29 Jul 2009 21:27:46 -0700 Subject: [PATCH 008/175] Bug 332173 - Followup followup fix for trunk-only necessary-for-templatization changes (not needed for any branches, that is). --HG-- extra : rebase_source : 767c90867afbbe7bf52e29820c987e45c9e60d03 --- xpcom/io/nsWildCard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xpcom/io/nsWildCard.cpp b/xpcom/io/nsWildCard.cpp index 90f3d718178..2f788ba9ae7 100644 --- a/xpcom/io/nsWildCard.cpp +++ b/xpcom/io/nsWildCard.cpp @@ -222,7 +222,7 @@ _handle_union(const T *str, const T *expr, PRBool case_insensitive, if (cp == ABORTED || cp < 4) /* must be at least "(a|b" before ')' */ return ABORTED; ++cp; /* now index of char after closing parenthesis */ - e2 = (T *) NS_Alloc(1 + nsCharTraits::length(expr)); + e2 = (T *) NS_Alloc((1 + nsCharTraits::length(expr)) * sizeof(T)); if (!e2) return ABORTED; for (sx = 1; ; ++sx) { @@ -386,7 +386,7 @@ ns_WildCardMatch(const T *str, const T *xp, PRBool case_insensitive) if (!nsCharTraits::find(xp, nsCharTraits::length(xp), T('~'))) return _shexp_match(str, xp, case_insensitive, 0); - expr = (T *) NS_Alloc(nsCharTraits::length(xp) + 1); + expr = (T *) NS_Alloc((nsCharTraits::length(xp) + 1) * sizeof(T)); if(!expr) return NOMATCH; From cc98ef94548819888b97f71814146e23581ceb60 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Thu, 30 Jul 2009 00:47:06 -0400 Subject: [PATCH 009/175] Cache UTF-8 POSIX path in Mac OS X nsIFile impl. b=505792 r=mstange sr=bsmedberg --- xpcom/io/nsLocalFileOSX.h | 10 +-- xpcom/io/nsLocalFileOSX.mm | 172 +++++++++++-------------------------- 2 files changed, 53 insertions(+), 129 deletions(-) diff --git a/xpcom/io/nsLocalFileOSX.h b/xpcom/io/nsLocalFileOSX.h index f818e742898..957e2d3abfa 100644 --- a/xpcom/io/nsLocalFileOSX.h +++ b/xpcom/io/nsLocalFileOSX.h @@ -95,21 +95,17 @@ private: protected: nsLocalFile(const nsLocalFile& src); - nsresult SetBaseURL(CFURLRef aCFURLRef); // retains aCFURLRef - nsresult GetFSRefInternal(FSRef& aFSRef); - nsresult GetPathInternal(nsACString& path); // Returns path WRT mFollowLinks + nsresult GetPathInternal(nsACString& path); // Returns path respecting mFollowLinks nsresult CopyInternal(nsIFile* newParentDir, const nsAString& newName, PRBool followLinks); nsresult FillStatBufferInternal(struct STAT *statBuffer); - static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr); - protected: - CFURLRef mBaseURL; // The FS object we represent - + CFURLRef mBaseURL; // The FS object we represent + char mPath[PATH_MAX]; // POSIX path, UTF-8, NULL terminated PRPackedBool mFollowLinks; }; diff --git a/xpcom/io/nsLocalFileOSX.mm b/xpcom/io/nsLocalFileOSX.mm index 8f6a4fea256..14e13d89b58 100644 --- a/xpcom/io/nsLocalFileOSX.mm +++ b/xpcom/io/nsLocalFileOSX.mm @@ -76,6 +76,7 @@ static nsresult MacErrorMapper(OSErr inErr); static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult); +static nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr); #pragma mark - #pragma mark [FSRef operator==] @@ -281,30 +282,23 @@ class nsDirEnumerator : public nsISimpleEnumerator, NS_IMPL_ISUPPORTS2(nsDirEnumerator, nsISimpleEnumerator, nsIDirectoryEnumerator) -#define FILENAME_BUFFER_SIZE 512 - const char kPathSepChar = '/'; #pragma mark - #pragma mark [CTORs/DTOR] nsLocalFile::nsLocalFile() : - mBaseURL(nsnull), + mBaseURL(NULL), mFollowLinks(PR_TRUE) { + mPath[0] = '\0'; } nsLocalFile::nsLocalFile(const nsLocalFile& src) : - mBaseURL(src.mBaseURL), + mBaseURL(NULL), mFollowLinks(src.mFollowLinks) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK; - - // CFURL objects are immutable so no need to copy, just retain. - if (mBaseURL) - ::CFRetain(mBaseURL); - - NS_OBJC_END_TRY_ABORT_BLOCK; + SetBaseURL(src.mBaseURL); } nsLocalFile::~nsLocalFile() @@ -388,14 +382,8 @@ NS_IMETHODIMP nsLocalFile::Normalize() CHECK_INIT(); - // CFURL doesn't doesn't seem to resolve paths containing relative - // components, so we'll nick the stdlib code from nsLocalFileUnix - UInt8 path[PATH_MAX] = ""; - if (!::CFURLGetFileSystemRepresentation(mBaseURL, true, path, PATH_MAX)) - return NS_ERROR_FAILURE; - char resolved_path[PATH_MAX] = ""; - if (!realpath((char*)path, resolved_path)) + if (!realpath(mPath, resolved_path)) return NSRESULT_FOR_ERRNO(); // Need to know whether we're a directory to create a new CFURLRef @@ -437,7 +425,7 @@ NS_IMETHODIMP nsLocalFile::Create(PRUint32 type, PRUint32 permissions) CFURLRef pathURLRef = mBaseURL; FSRef pathFSRef; CFStringRef leafStrRef = nsnull; - nsAutoTArray buffer; + nsAutoTArray buffer; Boolean success; // Work backwards through the path to find the last node which @@ -607,12 +595,6 @@ NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& if (NS_FAILED(rv)) return rv; - // Get the source path. - nsCAutoString srcPath; - rv = GetNativePath(srcPath); - if (NS_FAILED(rv)) - return rv; - // Build the destination path. nsCOMPtr parentDir = newParentDir; if (!parentDir) { @@ -650,7 +632,7 @@ NS_IMETHODIMP nsLocalFile::MoveToNative(nsIFile *newParentDir, const nsACString& } // Perform the move. - if (rename(srcPath.get(), destPath.get()) != 0) { + if (rename(mPath, destPath.get()) != 0) { if (errno == EXDEV) { // Can't move across volume (device) boundaries. Copy and remove. rv = CopyToNative(parentDir, newName); @@ -706,17 +688,11 @@ NS_IMETHODIMP nsLocalFile::Remove(PRBool recursive) } } else { - nsCAutoString path; - rv = GetNativePath(path); - if (NS_FAILED(rv)) - return rv; - - const char* pathPtr = path.get(); int status; if (isDirectory) - status = rmdir(pathPtr); + status = rmdir(mPath); else - status = unlink(pathPtr); + status = unlink(mPath); if (status != 0) rv = NSRESULT_FOR_ERRNO(); @@ -750,12 +726,7 @@ NS_IMETHODIMP nsLocalFile::SetPermissions(PRUint32 aPermissions) { CHECK_INIT(); - nsCAutoString path; - nsresult rv = GetPathInternal(path); - if (NS_FAILED(rv)) - return rv; - - if (chmod(path.get(), aPermissions) < 0) + if (chmod(mPath, aPermissions) < 0) return NSRESULT_FOR_ERRNO(); return NS_OK; @@ -847,15 +818,11 @@ NS_IMETHODIMP nsLocalFile::GetFileSize(PRInt64 *aFileSize) NS_ENSURE_ARG_POINTER(aFileSize); *aFileSize = 0; - nsCAutoString path; - nsresult rv = GetPathInternal(path); + struct STAT buf; + nsresult rv = FillStatBufferInternal(&buf); if (NS_FAILED(rv)) return rv; - struct STAT buf; - if (STAT(path.get(), &buf) != 0) - return NSRESULT_FOR_ERRNO(); - if (!S_ISDIR(buf.st_mode)) *aFileSize = (PRInt64)buf.st_size; @@ -866,13 +833,8 @@ NS_IMETHODIMP nsLocalFile::SetFileSize(PRInt64 aFileSize) { CHECK_INIT(); - nsCAutoString path; - nsresult rv = GetPathInternal(path); - if (NS_FAILED(rv)) - return rv; - off_t size = (off_t)aFileSize; - if (truncate(path.get(), size) == -1) + if (truncate(mPath, size) == -1) return NSRESULT_FOR_ERRNO(); return NS_OK; @@ -910,13 +872,8 @@ NS_IMETHODIMP nsLocalFile::GetNativeTarget(nsACString& aNativeTarget) if (!isSymLink) return NS_ERROR_FILE_INVALID_PATH; - nsCAutoString nativeString; - rv = GetNativePath(nativeString); - if (NS_FAILED(rv)) - return rv; - char resolvedPathBuf[PATH_MAX]; - if (!realpath(nativeString.get(), resolvedPathBuf)) + if (!realpath(mPath, resolvedPathBuf)) return NS_ERROR_FILE_INVALID_PATH; unsigned int resolvedPathLength = strlen(resolvedPathBuf); @@ -940,32 +897,19 @@ NS_IMETHODIMP nsLocalFile::GetPath(nsAString& aPath) NS_IMETHODIMP nsLocalFile::GetNativePath(nsACString& aNativePath) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CHECK_INIT(); - nsresult rv = NS_ERROR_FAILURE; - CFStringRef pathStrRef = ::CFURLCopyFileSystemPath(mBaseURL, kCFURLPOSIXPathStyle); - if (pathStrRef) { - rv = CFStringReftoUTF8(pathStrRef, aNativePath); - ::CFRelease(pathStrRef); - } - return rv; + aNativePath.Assign(mPath); - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; + return NS_OK; } NS_IMETHODIMP nsLocalFile::Exists(PRBool *_retval) { NS_ENSURE_ARG_POINTER(_retval); - nsCAutoString path; - nsresult rv = GetPathInternal(path); - if (NS_FAILED(rv)) - return rv; - struct STAT buf; - *_retval = (STAT(path.get(), &buf) == 0); + *_retval = (STAT(mPath, &buf) == 0); return NS_OK; } @@ -1098,26 +1042,17 @@ NS_IMETHODIMP nsLocalFile::IsFile(PRBool *_retval) NS_IMETHODIMP nsLocalFile::IsSymlink(PRBool *_retval) { - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - CHECK_INIT(); NS_ENSURE_ARG(_retval); *_retval = PR_FALSE; - // First see if we are an actual symlink - nsCAutoString path; - nsresult rv = GetNativePath(path); - if (NS_FAILED(rv)) - return rv; struct stat symStat; - if (lstat(path.get(), &symStat) < 0) + if (lstat(mPath, &symStat) < 0) return NSRESULT_FOR_ERRNO(); *_retval = S_ISLNK(symStat.st_mode); return NS_OK; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; } NS_IMETHODIMP nsLocalFile::IsSpecial(PRBool *_retval) @@ -1149,12 +1084,7 @@ NS_IMETHODIMP nsLocalFile::Equals(nsIFile *inFile, PRBool *_retval) if (NS_FAILED(rv)) return rv; - nsCAutoString thisPath; - rv = GetNativePath(thisPath); - if (NS_FAILED(rv)) - return rv; - - *_retval = !strcmp(inPath.get(), thisPath.get()); + *_retval = !strcmp(mPath, inPath.get()); return NS_OK; } @@ -1976,6 +1906,9 @@ nsresult nsLocalFile::SetBaseURL(CFURLRef aCFURLRef) ::CFRelease(mBaseURL); mBaseURL = aCFURLRef; + if (!::CFURLGetFileSystemRepresentation(mBaseURL, NO, (UInt8*)mPath, PATH_MAX)) + return NS_ERROR_FAILURE; + return NS_OK; NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; @@ -2082,43 +2015,14 @@ nsresult nsLocalFile::FillStatBufferInternal(struct STAT *statBuffer) { CHECK_INIT(); - char path[PATH_MAX] = ""; - Boolean gotPath = ::CFURLGetFileSystemRepresentation(mBaseURL, true, (UInt8*)path, PATH_MAX); - if (!gotPath) - return NS_ERROR_FAILURE; - - if (STAT(path, statBuffer) == -1) { - if (LSTAT(path, statBuffer) == -1) + if (STAT(mPath, statBuffer) == -1) { + if (LSTAT(mPath, statBuffer) == -1) return NSRESULT_FOR_ERRNO(); } return NS_OK; } -nsresult nsLocalFile::CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) -{ - NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - - // first see if the conversion would succeed and find the length of the result - CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef); - CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), - kCFStringEncodingUTF8, 0, PR_FALSE, - NULL, 0, &usedBufLen); - if (charsConverted == inStrLen) { - // all characters converted, do the actual conversion - aOutStr.SetLength(usedBufLen); - if (aOutStr.Length() != (unsigned int)usedBufLen) - return NS_ERROR_OUT_OF_MEMORY; - UInt8 *buffer = (UInt8*)aOutStr.BeginWriting(); - ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8, - 0, false, buffer, usedBufLen, &usedBufLen); - return NS_OK; - } - return NS_ERROR_FAILURE; - - NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; -} - // nsIHashable NS_IMETHODIMP @@ -2304,7 +2208,7 @@ static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult) aResult.Assign(chars, length); } else { - nsAutoTArray buffer; + nsAutoTArray buffer; if (!buffer.SetLength(length)) { CopyUTF8toUTF16(aSrc, aResult); } @@ -2317,3 +2221,27 @@ static void CopyUTF8toUTF16NFC(const nsACString& aSrc, nsAString& aResult) NS_OBJC_END_TRY_ABORT_BLOCK; } + +nsresult CFStringReftoUTF8(CFStringRef aInStrRef, nsACString& aOutStr) +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; + + // first see if the conversion would succeed and find the length of the result + CFIndex usedBufLen, inStrLen = ::CFStringGetLength(aInStrRef); + CFIndex charsConverted = ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), + kCFStringEncodingUTF8, 0, PR_FALSE, + NULL, 0, &usedBufLen); + if (charsConverted == inStrLen) { + // all characters converted, do the actual conversion + aOutStr.SetLength(usedBufLen); + if (aOutStr.Length() != (unsigned int)usedBufLen) + return NS_ERROR_OUT_OF_MEMORY; + UInt8 *buffer = (UInt8*)aOutStr.BeginWriting(); + ::CFStringGetBytes(aInStrRef, CFRangeMake(0, inStrLen), kCFStringEncodingUTF8, + 0, false, buffer, usedBufLen, &usedBufLen); + return NS_OK; + } + return NS_ERROR_FAILURE; + + NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; +} From c842f83d5b641507901d6eea54c884813a4065a4 Mon Sep 17 00:00:00 2001 From: Felipe Gomes Date: Wed, 29 Jul 2009 23:01:27 -0700 Subject: [PATCH 010/175] Bug 492152 - Scrollable menus should support panning on Win7. r=jmathies, sr=vlad --- widget/src/windows/nsWinGesture.cpp | 8 ++++---- widget/src/windows/nsWinGesture.h | 4 ++-- widget/src/windows/nsWindow.cpp | 19 ++++++++++++++----- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/widget/src/windows/nsWinGesture.cpp b/widget/src/windows/nsWinGesture.cpp index 887812a1025..24cf2b23fb1 100644 --- a/widget/src/windows/nsWinGesture.cpp +++ b/widget/src/windows/nsWinGesture.cpp @@ -461,11 +461,11 @@ inline PRBool TestTransition(PRInt32 a, PRInt32 b) } void -nsWinGesture::UpdatePanFeedbackX(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback) +nsWinGesture::UpdatePanFeedbackX(HWND hWnd, PRInt32 scrollOverflow, PRBool& endFeedback) { // If scroll overflow was returned indicating we panned past the bounds of // the scrollable view port, start feeback. - if (evt.scrollOverflow != 0) { + if (scrollOverflow != 0) { if (!mFeedbackActive) { BeginPanningFeedback(hWnd); mFeedbackActive = PR_TRUE; @@ -490,11 +490,11 @@ nsWinGesture::UpdatePanFeedbackX(HWND hWnd, nsMouseScrollEvent& evt, PRBool& end } void -nsWinGesture::UpdatePanFeedbackY(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback) +nsWinGesture::UpdatePanFeedbackY(HWND hWnd, PRInt32 scrollOverflow, PRBool& endFeedback) { // If scroll overflow was returned indicating we panned past the bounds of // the scrollable view port, start feeback. - if (evt.scrollOverflow != 0) { + if (scrollOverflow != 0) { if (!mFeedbackActive) { BeginPanningFeedback(hWnd); mFeedbackActive = PR_TRUE; diff --git a/widget/src/windows/nsWinGesture.h b/widget/src/windows/nsWinGesture.h index a21b26d5a72..c8b3c9d4786 100644 --- a/widget/src/windows/nsWinGesture.h +++ b/widget/src/windows/nsWinGesture.h @@ -206,8 +206,8 @@ public: PRBool ProcessPanMessage(HWND hWnd, WPARAM wParam, LPARAM lParam); PRBool PanDeltaToPixelScrollX(nsMouseScrollEvent& evt); PRBool PanDeltaToPixelScrollY(nsMouseScrollEvent& evt); - void UpdatePanFeedbackX(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback); - void UpdatePanFeedbackY(HWND hWnd, nsMouseScrollEvent& evt, PRBool& endFeedback); + void UpdatePanFeedbackX(HWND hWnd, PRInt32 scrollOverflow, PRBool& endFeedback); + void UpdatePanFeedbackY(HWND hWnd, PRInt32 scrollOverflow, PRBool& endFeedback); void PanFeedbackFinalize(HWND hWnd, PRBool endFeedback); public: diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index d6976c8f08d..3bd7c73bd86 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -4620,17 +4620,26 @@ PRBool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) event.time = ::GetMessageTime(); PRBool endFeedback = PR_TRUE; - + + PRInt32 scrollOverflowX = 0; + PRInt32 scrollOverflowY = 0; + if (mGesture.PanDeltaToPixelScrollX(event)) { DispatchEvent(&event, status); + scrollOverflowX = event.scrollOverflow; } - mGesture.UpdatePanFeedbackX(mWnd, event, endFeedback); - + if (mGesture.PanDeltaToPixelScrollY(event)) { DispatchEvent(&event, status); + scrollOverflowY = event.scrollOverflow; } - mGesture.UpdatePanFeedbackY(mWnd, event, endFeedback); - mGesture.PanFeedbackFinalize(mWnd, endFeedback); + + if (mWindowType != eWindowType_popup) { + mGesture.UpdatePanFeedbackX(mWnd, scrollOverflowX, endFeedback); + mGesture.UpdatePanFeedbackY(mWnd, scrollOverflowY, endFeedback); + mGesture.PanFeedbackFinalize(mWnd, endFeedback); + } + mGesture.CloseGestureInfoHandle((HGESTUREINFO)lParam); return PR_TRUE; From 9adf284bf2d8954cbcec5685934c184555a06f8f Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 29 Jul 2009 23:01:36 -0700 Subject: [PATCH 011/175] Bug 506696 - [WinCE] updater.exe doesn't use the working directory when applying the update. r=bsmedberg, r=blassey --- .../update/src/updater/archivereader.cpp | 10 +- .../update/src/updater/archivereader.h | 15 +- .../mozapps/update/src/updater/updater.cpp | 233 ++++++++++++------ .../update/src/updater/updater_wince.cpp | 94 +++---- .../update/src/updater/updater_wince.h | 6 +- 5 files changed, 208 insertions(+), 150 deletions(-) diff --git a/toolkit/mozapps/update/src/updater/archivereader.cpp b/toolkit/mozapps/update/src/updater/archivereader.cpp index a2686a99069..10f81ea2f6e 100644 --- a/toolkit/mozapps/update/src/updater/archivereader.cpp +++ b/toolkit/mozapps/update/src/updater/archivereader.cpp @@ -53,11 +53,7 @@ #endif int -#ifdef XP_WIN -ArchiveReader::Open(const WCHAR *path) -#else -ArchiveReader::Open(const char *path) -#endif +ArchiveReader::Open(const NS_tchar *path) { if (mArchive) Close(); @@ -83,14 +79,14 @@ ArchiveReader::Close() } int -ArchiveReader::ExtractFile(const char *name, const char *dest) +ArchiveReader::ExtractFile(const char *name, const NS_tchar *dest) { const MarItem *item = mar_find_item(mArchive, name); if (!item) return READ_ERROR; #ifdef XP_WIN - FILE* fp = fopen(dest, "wb+"); + FILE* fp = _wfopen(dest, L"wb+"); #else int fd = creat(dest, item->flags); if (fd == -1) diff --git a/toolkit/mozapps/update/src/updater/archivereader.h b/toolkit/mozapps/update/src/updater/archivereader.h index 2421faedee9..ce3fc6a9a39 100644 --- a/toolkit/mozapps/update/src/updater/archivereader.h +++ b/toolkit/mozapps/update/src/updater/archivereader.h @@ -42,6 +42,12 @@ #include #include "mar.h" +#ifdef XP_WIN +# define NS_tchar WCHAR +#else +# define NS_tchar char +#endif + // This class provides an API to extract files from an update archive. class ArchiveReader { @@ -49,15 +55,10 @@ public: ArchiveReader() : mArchive(NULL) {} ~ArchiveReader() { Close(); } -#ifdef XP_WIN - int Open(const WCHAR *path); -#else - int Open(const char *path); -#endif - + int Open(const NS_tchar *path); void Close(); - int ExtractFile(const char *item, const char *destination); + int ExtractFile(const char *item, const NS_tchar *destination); int ExtractFileToStream(const char *item, FILE *fp); private: diff --git a/toolkit/mozapps/update/src/updater/updater.cpp b/toolkit/mozapps/update/src/updater/updater.cpp index b41bff278dc..f41a06dd14f 100644 --- a/toolkit/mozapps/update/src/updater/updater.cpp +++ b/toolkit/mozapps/update/src/updater/updater.cpp @@ -59,15 +59,23 @@ # define putenv _putenv # define snprintf _snprintf # define fchmod(a,b) -# define mkdir(path, perms) _mkdir(path) # define NS_T(str) L ## str # define NS_tsnprintf _snwprintf # define NS_tstrrchr wcsrchr +# define NS_taccess _waccess # define NS_tchdir _wchdir +# define NS_tchmod _wchmod +# define NS_tmkdir(path, perms) _wmkdir(path) # define NS_tremove _wremove # define NS_tfopen _wfopen # define NS_tatoi _wtoi64 +#ifndef WINCE +# define stat _stat +#endif +# define NS_tstat _wstat +# define BACKUP_EXT L".moz-backup" +# define LOG_S "%S" #else # include # include @@ -75,10 +83,16 @@ # define NS_T(str) str # define NS_tsnprintf snprintf # define NS_tstrrchr strrchr +# define NS_taccess access # define NS_tchdir chdir +# define NS_tchmod chmod +# define NS_tmkdir mkdir # define NS_tremove remove # define NS_tfopen fopen # define NS_tatoi atoi +# define NS_tstat stat +# define BACKUP_EXT ".moz-backup" +# define LOG_S "%s" #endif #include "bspatch.h" @@ -297,6 +311,11 @@ private: //----------------------------------------------------------------------------- +#ifdef WINCE +// Since WinCE doesn't have a current working directory store the current +// working directory specified in the command line arguments +static NS_tchar* gDestPath; +#endif static NS_tchar* gSourcePath; static ArchiveReader gArchiveReader; #ifdef XP_WIN @@ -392,10 +411,38 @@ mstrtok(const char *delims, char **str) return ret; } -static void ensure_write_permissions(const char *path) +#ifdef XP_WIN +// Returns a wchar path. The path returned will be absolute on Windows CE / +// Windows Mobile and relative on all other versions of Windows. +static NS_tchar* +get_wide_path(const char *path) +{ + WCHAR *s = (WCHAR*) malloc(MAXPATHLEN * sizeof(WCHAR)); + if (!s) + return NULL; + + WCHAR *c = s; + WCHAR wpath[MAXPATHLEN]; + MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAXPATHLEN); +#ifndef WINCE + wcscpy(c, wpath); + c += wcslen(wpath); +#else + wcscpy(c, gDestPath); + c += wcslen(gDestPath); + wcscat(c, wpath); + c += wcslen(wpath); +#endif + *c = NS_T('\0'); + c++; + return s; +} +#endif + +static void ensure_write_permissions(const NS_tchar *path) { #ifdef XP_WIN - (void) chmod(path, _S_IREAD | _S_IWRITE); + (void) _wchmod(path, _S_IREAD | _S_IWRITE); #else struct stat fs; if (!stat(path, &fs) && !(fs.st_mode & S_IWUSR)) { @@ -404,25 +451,25 @@ static void ensure_write_permissions(const char *path) #endif } -static int ensure_remove(const char *path) +static int ensure_remove(const NS_tchar *path) { ensure_write_permissions(path); - int rv = remove(path); + int rv = NS_tremove(path); if (rv) - LOG(("remove failed: %d,%d (%s)\n", rv, errno, path)); + LOG(("remove failed: %d,%d (" LOG_S ")\n", rv, errno, path)); return rv; } -static FILE* ensure_open(const char *path, const char* flags, unsigned int options) +static FILE* ensure_open(const NS_tchar *path, const NS_tchar* flags, unsigned int options) { ensure_write_permissions(path); - FILE* f = fopen(path, flags); - if (chmod(path, options) != 0) { + FILE* f = NS_tfopen(path, flags); + if (NS_tchmod(path, options) != 0) { fclose(f); return NULL; } struct stat ss; - if (stat(path, &ss) != 0 || ss.st_mode != options) { + if (NS_tstat(path, &ss) != 0 || ss.st_mode != options) { fclose(f); return NULL; } @@ -430,30 +477,30 @@ static FILE* ensure_open(const char *path, const char* flags, unsigned int optio } // Ensure that the directory containing this file exists. -static int ensure_parent_dir(const char *path) +static int ensure_parent_dir(const NS_tchar *path) { int rv = OK; - char *slash = (char *) strrchr(path, '/'); - if (slash) - { - *slash = '\0'; + NS_tchar *slash = (NS_tchar *) NS_tstrrchr(path, NS_T('/')); + if (slash) { + *slash = NS_T('\0'); rv = ensure_parent_dir(path); if (rv == OK) { - rv = mkdir(path, 0755); - // If the directory already exists, then ignore the error. + rv = NS_tmkdir(path, 0755); + // If the directory already exists, then ignore the error. On WinCE rv + // will equal 0 if the directory already exists. if (rv < 0 && errno != EEXIST) { rv = WRITE_ERROR; } else { rv = OK; } } - *slash = '/'; + *slash = NS_T('/'); } return rv; } -static int copy_file(const char *spath, const char *dpath) +static int copy_file(const NS_tchar *spath, const NS_tchar *dpath) { int rv = ensure_parent_dir(dpath); if (rv) @@ -461,15 +508,15 @@ static int copy_file(const char *spath, const char *dpath) struct stat ss; - AutoFile sfile = fopen(spath, "rb"); + AutoFile sfile = NS_tfopen(spath, NS_T("rb")); if (sfile == NULL || fstat(fileno(sfile), &ss)) { - LOG(("copy_file: failed to open or stat: %p,%s,%d\n", sfile.get(), spath, errno)); + LOG(("copy_file: failed to open or stat: %p," LOG_S ",%d\n", sfile, spath, errno)); return READ_ERROR; } - AutoFile dfile = ensure_open(dpath, "wb+", ss.st_mode); + AutoFile dfile = ensure_open(dpath, NS_T("wb+"), ss.st_mode); if (dfile == NULL) { - LOG(("copy_file: failed to open: %s,%d\n", dpath, errno)); + LOG(("copy_file: failed to open: " LOG_S ",%d\n", dpath, errno)); return WRITE_ERROR; } @@ -498,13 +545,11 @@ static int copy_file(const char *spath, const char *dpath) //----------------------------------------------------------------------------- -#define BACKUP_EXT ".moz-backup" - // Create a backup copy of the specified file alongside it. -static int backup_create(const char *path) +static int backup_create(const NS_tchar *path) { - char backup[MAXPATHLEN]; - snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path); + NS_tchar backup[MAXPATHLEN]; + NS_tsnprintf(backup, sizeof(backup), NS_T("%s" BACKUP_EXT), path); return copy_file(path, backup); } @@ -512,10 +557,10 @@ static int backup_create(const char *path) // Copy the backup copy of the specified file back overtop // the specified file. // XXX should be a file move instead -static int backup_restore(const char *path) +static int backup_restore(const NS_tchar *path) { - char backup[MAXPATHLEN]; - snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path); + NS_tchar backup[MAXPATHLEN]; + NS_tsnprintf(backup, sizeof(backup), NS_T("%s" BACKUP_EXT), path); int rv = copy_file(backup, path); if (rv) @@ -529,10 +574,10 @@ static int backup_restore(const char *path) } // Discard the backup copy of the specified file. -static int backup_discard(const char *path) +static int backup_discard(const NS_tchar *path) { - char backup[MAXPATHLEN]; - snprintf(backup, sizeof(backup), "%s" BACKUP_EXT, path); + NS_tchar backup[MAXPATHLEN]; + NS_tsnprintf(backup, sizeof(backup), NS_T("%s" BACKUP_EXT), path); int rv = ensure_remove(backup); if (rv) @@ -542,7 +587,7 @@ static int backup_discard(const char *path) } // Helper function for post-processing a temporary backup. -static void backup_finish(const char *path, int status) +static void backup_finish(const NS_tchar *path, int status) { if (status == OK) backup_discard(path); @@ -586,7 +631,7 @@ private: class RemoveFile : public Action { public: - RemoveFile() : mFile(NULL), mSkip(0) { } + RemoveFile() : mFile(NULL), mDestFile(NULL), mSkip(0) { } int Parse(char *line); int Prepare(); @@ -595,6 +640,7 @@ public: private: const char* mFile; + const NS_tchar* mDestFile; int mSkip; }; @@ -607,35 +653,45 @@ RemoveFile::Parse(char *line) if (!mFile) return PARSE_ERROR; +#ifdef XP_WIN + mDestFile = get_wide_path(mFile); + if (!mDestFile) + return PARSE_ERROR; +#else + mDestFile = mFile; +#endif + return OK; } int RemoveFile::Prepare() { - LOG(("PREPARE REMOVE %s\n", mFile)); + LOG(("PREPARE REMOVE " LOG_S "\n", mDestFile)); // We expect the file to exist if we are to remove it. - int rv = access(mFile, F_OK); + int rv = NS_taccess(mDestFile, F_OK); if (rv) { LOG(("file cannot be removed because it does not exist; skipping\n")); mSkip = 1; return OK; } - char *slash = (char *) strrchr(mFile, '/'); +#ifndef WINCE + NS_tchar *slash = (NS_tchar *) NS_tstrrchr(mDestFile, NS_T('/')); if (slash) { - *slash = '\0'; - rv = access(mFile, W_OK); - *slash = '/'; + *slash = NS_T('\0'); + rv = NS_taccess(mDestFile, W_OK); + *slash = NS_T('/'); } else { - rv = access(".", W_OK); + rv = NS_taccess(NS_T("."), W_OK); } if (rv) { LOG(("access failed: %d\n", errno)); return WRITE_ERROR; } +#endif return OK; } @@ -643,7 +699,7 @@ RemoveFile::Prepare() int RemoveFile::Execute() { - LOG(("EXECUTE REMOVE %s\n", mFile)); + LOG(("EXECUTE REMOVE " LOG_S "\n", mDestFile)); if (mSkip) return OK; @@ -651,7 +707,7 @@ RemoveFile::Execute() // We expect the file to exist if we are to remove it. We check here as well // as in PREPARE since we might have been asked to remove the same file more // than once: bug 311099. - int rv = access(mFile, F_OK); + int rv = NS_taccess(mDestFile, F_OK); if (rv) { LOG(("file cannot be removed because it does not exist; skipping\n")); mSkip = 1; @@ -661,13 +717,13 @@ RemoveFile::Execute() // save a complete copy of the old file, and then remove the // old file. we'll clean up the copy in Finish. - rv = backup_create(mFile); + rv = backup_create(mDestFile); if (rv) { LOG(("backup_create failed: %d\n", rv)); return rv; } - rv = ensure_remove(mFile); + rv = ensure_remove(mDestFile); if (rv) return WRITE_ERROR; @@ -677,18 +733,18 @@ RemoveFile::Execute() void RemoveFile::Finish(int status) { - LOG(("FINISH REMOVE %s\n", mFile)); + LOG(("FINISH REMOVE " LOG_S "\n", mDestFile)); if (mSkip) return; - backup_finish(mFile, status); + backup_finish(mDestFile, status); } class AddFile : public Action { public: - AddFile() : mFile(NULL) { } + AddFile() : mFile(NULL), mDestFile(NULL) { } virtual int Parse(char *line); virtual int Prepare(); // check that the source file exists @@ -697,6 +753,7 @@ public: private: const char *mFile; + const NS_tchar *mDestFile; }; int @@ -708,13 +765,21 @@ AddFile::Parse(char *line) if (!mFile) return PARSE_ERROR; +#ifdef XP_WIN + mDestFile = get_wide_path(mFile); + if (!mDestFile) + return PARSE_ERROR; +#else + mDestFile = mFile; +#endif + return OK; } int AddFile::Prepare() { - LOG(("PREPARE ADD %s\n", mFile)); + LOG(("PREPARE ADD " LOG_S "\n", mDestFile)); return OK; } @@ -722,37 +787,35 @@ AddFile::Prepare() int AddFile::Execute() { - LOG(("EXECUTE ADD %s\n", mFile)); + LOG(("EXECUTE ADD " LOG_S "\n", mDestFile)); int rv; // First make sure that we can actually get rid of any existing file. - if (access(mFile, F_OK) == 0) - { - rv = backup_create(mFile); + rv = NS_taccess(mDestFile, F_OK); + if (rv == 0) { + rv = backup_create(mDestFile); if (rv) return rv; - rv = ensure_remove(mFile); + rv = ensure_remove(mDestFile); if (rv) return WRITE_ERROR; - } - else - { - rv = ensure_parent_dir(mFile); + } else { + rv = ensure_parent_dir(mDestFile); if (rv) return rv; } - - return gArchiveReader.ExtractFile(mFile, mFile); + + return gArchiveReader.ExtractFile(mFile, mDestFile); } void AddFile::Finish(int status) { - LOG(("FINISH ADD %s\n", mFile)); + LOG(("FINISH ADD " LOG_S "\n", mDestFile)); - backup_finish(mFile, status); + backup_finish(mDestFile, status); } class PatchFile : public Action @@ -773,6 +836,7 @@ private: const char *mPatchFile; const char *mFile; + const NS_tchar *mDestFile; int mPatchIndex; MBSPatchHeader header; FILE* pfile; @@ -855,13 +919,21 @@ PatchFile::Parse(char *line) if (!mFile) return PARSE_ERROR; +#ifdef XP_WIN + mDestFile = get_wide_path(mFile); + if (!mDestFile) + return PARSE_ERROR; +#else + mDestFile = mFile; +#endif + return OK; } int PatchFile::Prepare() { - LOG(("PREPARE PATCH %s\n", mFile)); + LOG(("PREPARE PATCH " LOG_S "\n", mDestFile)); // extract the patch to a temporary file mPatchIndex = sPatchIndex++; @@ -893,7 +965,7 @@ PatchFile::Prepare() if (rv) return rv; - AutoFile ofile = fopen(mFile, "rb"); + AutoFile ofile = NS_tfopen(mDestFile, NS_T("rb")); if (ofile == NULL) return READ_ERROR; @@ -906,23 +978,23 @@ PatchFile::Prepare() int PatchFile::Execute() { - LOG(("EXECUTE PATCH %s\n", mFile)); + LOG(("EXECUTE PATCH %s\n", mDestFile)); // Create backup copy of the destination file before proceeding. struct stat ss; - if (stat(mFile, &ss)) + if (NS_tstat(mDestFile, &ss)) return READ_ERROR; - int rv = backup_create(mFile); + int rv = backup_create(mDestFile); if (rv) return rv; - rv = ensure_remove(mFile); + rv = ensure_remove(mDestFile); if (rv) return WRITE_ERROR; - AutoFile ofile = ensure_open(mFile, "wb+", ss.st_mode); + AutoFile ofile = ensure_open(mDestFile, NS_T("wb+"), ss.st_mode); if (ofile == NULL) return WRITE_ERROR; @@ -932,9 +1004,9 @@ PatchFile::Execute() void PatchFile::Finish(int status) { - LOG(("FINISH PATCH %s\n", mFile)); + LOG(("FINISH PATCH %s\n", mDestFile)); - backup_finish(mFile, status); + backup_finish(mDestFile, status); } class AddIfFile : public AddFile @@ -1177,7 +1249,7 @@ LaunchCallbackApp(const NS_tchar *workingDir, int argc, NS_tchar **argv) // Run from the specified working directory (see bug 312360). if(NS_tchdir(workingDir) != 0) - LOG(("Warning: chdir failed")); + LOG(("Warning: chdir failed\n")); #if defined(USE_EXECV) execv(argv[0], argv); @@ -1250,10 +1322,17 @@ int NS_main(int argc, NS_tchar **argv) // necessary for the parent process to exit before its executable image may // be altered. +#ifndef WINCE if (argc < 2) { fprintf(stderr, "Usage: updater [parent-pid [working-dir callback args...]]\n"); return 1; } +#else + if (argc < 4) { + fprintf(stderr, "Usage: updater parent-pid [callback args...]]\n"); + return 1; + } +#endif if (argc > 2 ) { int pid = NS_tatoi(argv[2]); @@ -1366,6 +1445,9 @@ int NS_main(int argc, NS_tchar **argv) #endif gSourcePath = argv[1]; +#ifdef WINCE + gDestPath = argv[3]; +#endif LogInit(); @@ -1477,8 +1559,7 @@ ActionList::Execute() UpdateProgressUI(1.0f + float(i++) / divisor); int rv = a->Execute(); - if (rv) - { + if (rv) { LOG(("### execution failed\n")); return rv; } diff --git a/toolkit/mozapps/update/src/updater/updater_wince.cpp b/toolkit/mozapps/update/src/updater/updater_wince.cpp index 3f6ea35712b..04ca864ca24 100644 --- a/toolkit/mozapps/update/src/updater/updater_wince.cpp +++ b/toolkit/mozapps/update/src/updater/updater_wince.cpp @@ -42,55 +42,39 @@ # define W_OK 02 # define R_OK 04 -int remove(const char* path) +int chmod(const char* path, unsigned int mode) { - if (!_unlink(path)) { - return 0; - } - else if (GetLastError() == ERROR_ACCESS_DENIED) { - WCHAR wpath[MAX_PATH]; - - MultiByteToWideChar(CP_ACP, - 0, - path, - -1, - wpath, - MAX_PATH ); - return RemoveDirectoryW(wpath) ? 0:-1; - } - else { - return -1; - } + return 0; } -int chmod(const char* path, unsigned int mode) +int _wchmod(const WCHAR* path, unsigned int mode) { return 0; } int fstat(FILE* handle, struct stat* buff) { - int position = ftell(handle); - if (position < 0) - return -1; + int position = ftell(handle); + if (position < 0) + return -1; - if (fseek(handle, 0, SEEK_END) < 0) - return -1; + if (fseek(handle, 0, SEEK_END) < 0) + return -1; - buff->st_size = ftell(handle); + buff->st_size = ftell(handle); - if (fseek(handle, position, SEEK_SET) < 0) - return -1; + if (fseek(handle, position, SEEK_SET) < 0) + return -1; - if (buff->st_size < 0) - return -1; + if (buff->st_size < 0) + return -1; - buff->st_mode = _S_IFREG | _S_IREAD | _S_IWRITE | _S_IEXEC; - /* can't get time from a file handle on wince */ - buff->st_ctime = 0; - buff->st_atime = 0; - buff->st_mtime = 0; - return 0; + buff->st_mode = _S_IFREG | _S_IREAD | _S_IWRITE | _S_IEXEC; + /* can't get time from a file handle on wince */ + buff->st_ctime = 0; + buff->st_atime = 0; + buff->st_mtime = 0; + return 0; } int stat(const char* path, struct stat* buf) @@ -101,16 +85,20 @@ int stat(const char* path, struct stat* buf) return rv; } -int _mkdir(const char* path) +int _wstat(const WCHAR* path, struct stat* buf) { - WCHAR wpath[MAX_PATH]; - MultiByteToWideChar(CP_ACP, - 0, - path, - -1, - wpath, - MAX_PATH ); - return CreateDirectoryW(wpath, NULL) ? 0 : -1; + FILE* f = _wfopen(path, L"r"); + int rv = fstat(f, buf); + fclose(f); + return rv; +} + +int _wmkdir(const WCHAR* path) +{ + DWORD dwAttr = GetFileAttributesW(path); + if (dwAttr != INVALID_FILE_ATTRIBUTES) + return (dwAttr & FILE_ATTRIBUTE_DIRECTORY) ? 0 : -1; + return CreateDirectoryW(path, NULL) ? 0 : -1; } FILE* fileno(FILE* f) @@ -127,22 +115,14 @@ int _access(const char* path, int amode) -1, wpath, MAX_PATH ); - switch (amode) { - case R_OK: - return (GetFileAttributesW(wpath) != INVALID_FILE_ATTRIBUTES); - default: - return 0; - } + return _waccess(wpath, amode); } + int _waccess(const WCHAR* path, int amode) { - switch (amode) { - case R_OK: - return (GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES); - default: - return 0; - } - + if (amode == F_OK) + return (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES) ? -1 : 0; + return -1; } int _wremove(const WCHAR* wpath) diff --git a/toolkit/mozapps/update/src/updater/updater_wince.h b/toolkit/mozapps/update/src/updater/updater_wince.h index 11e300f72da..a32d6a69087 100644 --- a/toolkit/mozapps/update/src/updater/updater_wince.h +++ b/toolkit/mozapps/update/src/updater/updater_wince.h @@ -54,11 +54,11 @@ struct stat { time_t st_atime; time_t st_mtime; }; -int remove(const char* path); -int chmod(const char* path, unsigned int mode); +int _wchmod(const WCHAR* path, unsigned int mode); int fstat(FILE* handle, struct stat* buff); int stat(const char* path, struct stat* buf); -int _mkdir(const char* path); +int _wstat(const WCHAR* path, struct stat* buf); +int _wmkdir(const WCHAR* path); int access(const char* path, int amode); int _waccess(const WCHAR* path, int amode); int _wremove(const WCHAR* wpath); From 1627769f99bab3fd25acb0b9b80d20dd8ffa5e7d Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 29 Jul 2009 23:01:43 -0700 Subject: [PATCH 012/175] Updated tests for Bug 506696. r=me --- .../test/unit/data/aus-0110_general.mar | Bin 1444 -> 1577 bytes .../test/unit/data/aus-0111_general.mar | Bin 1255 -> 1392 bytes .../update/test/unit/test_0110_general.js | 46 ++-- .../update/test/unit/test_0111_general.js | 63 +++--- .../update/test/unit/test_0112_general.js | 199 ++++++++++++++++++ 5 files changed, 258 insertions(+), 50 deletions(-) create mode 100644 toolkit/mozapps/update/test/unit/test_0112_general.js diff --git a/toolkit/mozapps/update/test/unit/data/aus-0110_general.mar b/toolkit/mozapps/update/test/unit/data/aus-0110_general.mar index f24dfd4fc9c532b4ba405b17346ff814a625f754..9642ff94e25a0842136a381bbb341f823eff4540 100644 GIT binary patch delta 302 zcmZ3&y^<%>*D=VDfq}KaDJsL#&@oaiI5KVRDH#R^rP~b*0s)L07#J9qEsk+eU`RSpc z{o+Ng76B7LMsAF~$TIl{i)H;*pphIvoXEhyxUIAxC9xz`FE=qSGcC2a1Srgq$-uyp z4wB1FEQ&7y3hEo`8^(iid}eN9da9vbK^{;61Iq;()q6&K8G=Q+WetGu&W_~6UQ-p--%-8+@$Xn1JPPc&xO zn0b+<3}`|T5OV-=C<6oIw$g%>#FA9K+{C=hwAA7fpfE!$0|QGsNG>s0E zvg1GlL)WZc;VWqimwokjRu|jlExbx}%E=z~76qs7qMR2xt|uK@b~<}mE}P{j$f@me z_aUc^c-;=Yh!sX5PriAc`jC?s7p)^2qwhKMiCow6q<0=1%ejQaU#p27*qHa1d9odg zfz589jT}H+&%nUAt+XH|u_RS5H!&|WEw#7=D9q5#z`(o^B$u056kh@q)Hl>Oj0fZR v$qQISax{R7^l>OCNv$XWdY6GE97yQmQDh8K)PkYNSl<|E6%Ye$2AKo^l?i7E delta 228 zcmeys^_;Wb*D=VDfq^C4DJsL#&@oaiIC7G%i3tOP*6jucfdIxG3=9nG9^Z9PVDRwt zIB_|{p~}HEAcM_u;$)S$ZLKfPE;}(Tjn#;GVc(=8RX_g>KL<;umh+xVb|`CEE&brN zbdJ}ux0~w4i(D-NCV+h|?zTWf2$3V_;xj$iTq3 pEjO_!z9hA{M1S%J77=59paOjq;gZyf5}@-LSnPm=E{2pb0{}LsNPYkS diff --git a/toolkit/mozapps/update/test/unit/test_0110_general.js b/toolkit/mozapps/update/test/unit/test_0110_general.js index 372bea97050..fa77b9d1af0 100644 --- a/toolkit/mozapps/update/test/unit/test_0110_general.js +++ b/toolkit/mozapps/update/test/unit/test_0110_general.js @@ -36,15 +36,14 @@ * ***** END LICENSE BLOCK ***** */ -/* General Complete MAR File Patch Apply Tests */ +/* General Complete MAR File Patch Apply Test */ function run_test() { // The directory the updates will be applied to is the current working // directory and not dist/bin. - var testDir = do_get_cwd(); + var testDir = do_get_file("mar_test", true); // The mar files were created with all files in a subdirectory named // mar_test... clear it out of the way if it exists and then create it. - testDir.append("mar_test"); try { if (testDir.exists()) testDir.remove(true); @@ -55,12 +54,12 @@ function run_test() { } dump("Testing: successful removal of the directory used to apply the mar file\n"); do_check_false(testDir.exists()); + testDir = do_get_file("mar_test/1/1_1/", true); testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); // Create an empty test file to test the complete mar's ability to replace an // existing file. - var testFile = testDir.clone(); - testFile.append("text1"); + var testFile = do_get_file("mar_test/1/1_1/1_1_text1", true); testFile.create(AUS_Ci.nsIFile.NORMAL_FILE_TYPE, PERMS_FILE); var binDir = getGREDir(); @@ -81,9 +80,7 @@ function run_test() { } // Use a directory outside of dist/bin to lessen the garbage in dist/bin - var updatesSubDir = do_get_cwd(); - updatesSubDir.append("0110_complete_mar"); - + var updatesSubDir = do_get_file("0110_complete_mar", true); try { // Mac OS X intermittently fails when removing the dir where the updater // binary was launched. @@ -104,14 +101,24 @@ function run_test() { "a complete mar\n"); do_check_eq(exitValue, 0); + dump("Testing: update.status should be set to STATE_SUCCEEDED\n"); + testFile = updatesSubDir.clone(); + testFile.append("update.status"); + do_check_eq(readFile(testFile).split("\n")[0], STATE_SUCCEEDED); + dump("Testing: contents of files added by a complete mar\n"); - do_check_eq(getFileBytes(getTestFile(testDir, "text1")), "ToBeModified\n"); - do_check_eq(getFileBytes(getTestFile(testDir, "text2")), "ToBeDeleted\n"); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text1", true)), + "ToBeModified\n"); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text2", true)), + "ToBeDeleted\n"); var refImage = do_get_file("data/aus-0110_general_ref_image.png"); - var srcImage = getTestFile(testDir, "image1.png"); + var srcImage = do_get_file("mar_test/1/1_1/1_1_image1.png", true); do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); + do_check_eq(getFileBytes(do_get_file("mar_test/2/2_1/2_1_text1", true)), + "ToBeDeleted\n"); + try { // Mac OS X intermittently fails when removing the dir where the updater // binary was launched. @@ -146,25 +153,18 @@ function runUpdate(aUpdatesSubDir, aUpdater) { if (/ /.test(updatesSubDirPath)) updatesSubDirPath = '"' + updatesSubDirPath + '"'; + var cwdPath = do_get_file("/", true).path; + if (/ /.test(cwdPath)) + cwdPath = '"' + cwdPath + '"'; + var process = AUS_Cc["@mozilla.org/process/util;1"]. createInstance(AUS_Ci.nsIProcess); process.init(updateBin); - var args = [updatesSubDirPath]; + var args = [updatesSubDirPath, 0, cwdPath]; process.run(true, args, args.length); return process.exitValue; } -// Gets a file in the mar_test subdirectory of the current working directory -// which is where the mar will be applied. -function getTestFile(aDir, aLeafName) { - var file = aDir.clone(); - file.append(aLeafName); - if (!(file instanceof AUS_Ci.nsILocalFile)) - do_throw("File must be a nsILocalFile for this test! File: " + aLeafName); - - return file; -} - // Returns the binary contents of a file function getFileBytes(aFile) { var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. diff --git a/toolkit/mozapps/update/test/unit/test_0111_general.js b/toolkit/mozapps/update/test/unit/test_0111_general.js index 6639ef5b913..17dcb54dbac 100644 --- a/toolkit/mozapps/update/test/unit/test_0111_general.js +++ b/toolkit/mozapps/update/test/unit/test_0111_general.js @@ -36,15 +36,14 @@ * ***** END LICENSE BLOCK ***** */ -/* General Partial MAR File Patch Apply Tests */ +/* General Partial MAR File Patch Apply Test */ function run_test() { // The directory the updates will be applied to is the current working // directory and not dist/bin. - var testDir = do_get_cwd(); + var testDir = do_get_file("mar_test", true); // The mar files were created with all files in a subdirectory named // mar_test... clear it out of the way if it exists and then create it. - testDir.append("mar_test"); try { if (testDir.exists()) testDir.remove(true); @@ -55,20 +54,22 @@ function run_test() { } dump("Testing: successful removal of the directory used to apply the mar file\n"); do_check_false(testDir.exists()); + testDir = do_get_file("mar_test/1/1_1/", true); testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); // Create the files to test the partial mar's ability to modify and delete // files. - var testFile = testDir.clone(); - testFile.append("text1"); + var testFile = do_get_file("mar_test/1/1_1/1_1_text1", true); writeFile(testFile, "ToBeModified\n"); - testFile = testDir.clone(); - testFile.append("text2"); + testFile = do_get_file("mar_test/1/1_1/1_1_text2", true); writeFile(testFile, "ToBeDeleted\n"); testFile = do_get_file("data/aus-0110_general_ref_image.png"); - testFile.copyTo(testDir, "image1.png"); + testFile.copyTo(testDir, "1_1_image1.png"); + + testFile = do_get_file("mar_test/2/2_1/2_1_text1", true); + writeFile(testFile, "ToBeDeleted\n"); var binDir = getGREDir(); @@ -88,9 +89,7 @@ function run_test() { } // Use a directory outside of dist/bin to lessen the garbage in dist/bin - var updatesSubDir = do_get_cwd(); - updatesSubDir.append("0111_partial_mar"); - + var updatesSubDir = do_get_file("0111_complete_mar", true); try { // Mac OS X intermittently fails when removing the dir where the updater // binary was launched. @@ -111,16 +110,33 @@ function run_test() { "a partial mar\n"); do_check_eq(exitValue, 0); + dump("Testing: update.status should be set to STATE_SUCCEEDED\n"); + testFile = updatesSubDir.clone(); + testFile.append("update.status"); + do_check_eq(readFile(testFile).split("\n")[0], STATE_SUCCEEDED); + dump("Testing: removal of a file and contents of added / modified files by " + "a partial mar\n"); - do_check_eq(getFileBytes(getTestFile(testDir, "text1")), "Modified\n"); - do_check_false(getTestFile(testDir, "text2").exists()); // file removed - do_check_eq(getFileBytes(getTestFile(testDir, "text3")), "Added\n"); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text1", true)), + "Modified\n"); + do_check_false(do_get_file("mar_test/1/1_1/1_1_text2", true).exists()); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text3", true)), + "Added\n"); var refImage = do_get_file("data/aus-0111_general_ref_image.png"); - var srcImage = getTestFile(testDir, "image1.png"); + var srcImage = do_get_file("mar_test/1/1_1/1_1_image1.png", true); do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); + dump("Testing: removal of a file by a partial mar\n"); + do_check_false(do_get_file("mar_test/2/2_1/2_1_text1", true).exists()); + + do_check_eq(getFileBytes(do_get_file("mar_test/3/3_1/3_1_text1", true)), + "Added\n"); + + dump("Testing: directory still exists after removal of the last file in " + + "the directory (bug 386760)\n"); + do_check_true(do_get_file("mar_test/2/2_1/", true).exists()); + try { // Mac OS X intermittently fails when removing the dir where the updater // binary was launched. @@ -155,25 +171,18 @@ function runUpdate(aUpdatesSubDir, aUpdater) { if (/ /.test(updatesSubDirPath)) updatesSubDirPath = '"' + updatesSubDirPath + '"'; + var cwdPath = do_get_file("/", true).path; + if (/ /.test(cwdPath)) + cwdPath = '"' + cwdPath + '"'; + var process = AUS_Cc["@mozilla.org/process/util;1"]. createInstance(AUS_Ci.nsIProcess); process.init(updateBin); - var args = [updatesSubDirPath]; + var args = [updatesSubDirPath, 0, cwdPath]; process.run(true, args, args.length); return process.exitValue; } -// Gets a file in the mar_test subdirectory of the current working directory -// which is where the mar will be applied. -function getTestFile(aDir, aLeafName) { - var file = aDir.clone(); - file.append(aLeafName); - if (!(file instanceof AUS_Ci.nsILocalFile)) - do_throw("File must be a nsILocalFile for this test! File: " + aLeafName); - - return file; -} - // Returns the binary contents of a file function getFileBytes(aFile) { var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. diff --git a/toolkit/mozapps/update/test/unit/test_0112_general.js b/toolkit/mozapps/update/test/unit/test_0112_general.js new file mode 100644 index 00000000000..1672ca99aba --- /dev/null +++ b/toolkit/mozapps/update/test/unit/test_0112_general.js @@ -0,0 +1,199 @@ +/* ***** 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 the Application Update Service. + * + * The Initial Developer of the Original Code is + * Robert Strong . + * + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Mozilla Foundation . All Rights Reserved. + * + * 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 ***** + */ + +/* General Partial MAR File Patch Apply Failure Test */ + +function run_test() { + // The directory the updates will be applied to is the current working + // directory and not dist/bin. + var testDir = do_get_file("mar_test", true); + // The mar files were created with all files in a subdirectory named + // mar_test... clear it out of the way if it exists and then create it. + try { + if (testDir.exists()) + testDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + testDir.path + + "\nException: " + e + "\n"); + } + dump("Testing: successful removal of the directory used to apply the mar file\n"); + do_check_false(testDir.exists()); + testDir = do_get_file("mar_test/1/1_1/", true); + testDir.create(AUS_Ci.nsIFile.DIRECTORY_TYPE, PERMS_DIRECTORY); + + // Create the files to test the partial mar's ability to modify and delete + // files. + var testFile = do_get_file("mar_test/1/1_1/1_1_text1", true); + writeFile(testFile, "ShouldNotBeModified\n"); + + testFile = do_get_file("mar_test/1/1_1/1_1_text2", true); + writeFile(testFile, "ShouldNotBeDeleted\n"); + + testFile = do_get_file("data/aus-0111_general_ref_image.png"); + testFile.copyTo(testDir, "1_1_image1.png"); + + testFile = do_get_file("mar_test/2/2_1/2_1_text1", true); + writeFile(testFile, "ShouldNotBeDeleted\n"); + + var binDir = getGREDir(); + + // The updater binary file + var updater = binDir.clone(); + updater.append("updater.app"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater.exe"); + if (!updater.exists()) { + updater = binDir.clone(); + updater.append("updater"); + if (!updater.exists()) { + do_throw("Unable to find updater binary!"); + } + } + } + + // Use a directory outside of dist/bin to lessen the garbage in dist/bin + var updatesSubDir = do_get_file("0112_complete_mar", true); + try { + // Mac OS X intermittently fails when removing the dir where the updater + // binary was launched. + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + updatesSubDir.path + + "\nException: " + e + "\n"); + } + + var mar = do_get_file("data/aus-0111_general.mar"); + mar.copyTo(updatesSubDir, "update.mar"); + + // apply the partial mar and check the innards of the files + var exitValue = runUpdate(updatesSubDir, updater); + dump("Testing: updater binary process exitValue for success when applying " + + "a partial mar\n"); + do_check_eq(exitValue, 0); + + dump("Testing: update.status should be set to STATE_FAILED\n"); + testFile = updatesSubDir.clone(); + testFile.append("update.status"); + // The update status format for a failure is failed: # where # is the error + // code for the failure. + do_check_eq(readFile(testFile).split(": ")[0], STATE_FAILED); + + dump("Testing: files should not be modified or deleted when an update " + + "fails\n"); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text1", true)), + "ShouldNotBeModified\n"); + do_check_true(do_get_file("mar_test/1/1_1/1_1_text2", true).exists()); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text2", true)), + "ShouldNotBeDeleted\n"); + + var refImage = do_get_file("data/aus-0111_general_ref_image.png"); + var srcImage = do_get_file("mar_test/1/1_1/1_1_image1.png", true); + do_check_eq(getFileBytes(srcImage), getFileBytes(refImage)); + + dump("Testing: removal of a file by a partial mar\n"); + do_check_true(do_get_file("mar_test/2/2_1/2_1_text1", true).exists()); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text2", true)), + "ShouldNotBeDeleted\n"); + + do_check_false(do_get_file("mar_test/3/3_1/3_1_text1", true).exists()); + do_check_eq(getFileBytes(do_get_file("mar_test/1/1_1/1_1_text2", true)), + "ShouldNotBeDeleted\n"); + + try { + // Mac OS X intermittently fails when removing the dir where the updater + // binary was launched. + if (updatesSubDir.exists()) + updatesSubDir.remove(true); + } + catch (e) { + dump("Unable to remove directory\npath: " + updatesSubDir.path + + "\nException: " + e + "\n"); + } + + cleanUp(); +} + +// Launches the updater binary to apply a mar file +function runUpdate(aUpdatesSubDir, aUpdater) { + // Copy the updater binary to the update directory so the updater.ini is not + // in the same directory as it is. This prevents ui from displaying and the + // PostUpdate executable which is defined in the updater.ini from launching. + aUpdater.copyTo(aUpdatesSubDir, aUpdater.leafName); + var updateBin = aUpdatesSubDir.clone(); + updateBin.append(aUpdater.leafName); + if (updateBin.leafName == "updater.app") { + updateBin.append("Contents"); + updateBin.append("MacOS"); + updateBin.append("updater"); + if (!updateBin.exists()) + do_throw("Unable to find the updater executable!"); + } + + var updatesSubDirPath = aUpdatesSubDir.path; + if (/ /.test(updatesSubDirPath)) + updatesSubDirPath = '"' + updatesSubDirPath + '"'; + + var cwdPath = do_get_file("/", true).path; + if (/ /.test(cwdPath)) + cwdPath = '"' + cwdPath + '"'; + + var process = AUS_Cc["@mozilla.org/process/util;1"]. + createInstance(AUS_Ci.nsIProcess); + process.init(updateBin); + var args = [updatesSubDirPath, 0, cwdPath]; + process.run(true, args, args.length); + return process.exitValue; +} + +// Returns the binary contents of a file +function getFileBytes(aFile) { + var fis = AUS_Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(AUS_Ci.nsIFileInputStream); + fis.init(aFile, -1, -1, false); + var bis = AUS_Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(AUS_Ci.nsIBinaryInputStream); + bis.setInputStream(fis); + var data = bis.readBytes(bis.available()); + bis.close(); + fis.close(); + return data; +} From 478624283e827d457d5259895c4b7c076c7f5116 Mon Sep 17 00:00:00 2001 From: Robert Strong Date: Wed, 29 Jul 2009 23:01:50 -0700 Subject: [PATCH 013/175] Bug 504432 - [WinCE] updater.exe not launched when there is an update during startup. r=vlad --- toolkit/xre/nsUpdateDriver.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/toolkit/xre/nsUpdateDriver.cpp b/toolkit/xre/nsUpdateDriver.cpp index ba6f94ff055..6d981e3865d 100644 --- a/toolkit/xre/nsUpdateDriver.cpp +++ b/toolkit/xre/nsUpdateDriver.cpp @@ -125,12 +125,11 @@ GetCurrentWorkingDir(char *buf, size_t size) if (DosQueryPathInfo( ".", FIL_QUERYFULLNAME, buf, size)) return NS_ERROR_FAILURE; #elif defined(XP_WIN) - wchar_t *wpath = _wgetcwd(NULL, size); - if (!wpath) + wchar_t wpath[MAX_PATH]; + if (!_wgetcwd(wpath, size)) return NS_ERROR_FAILURE; NS_ConvertUTF16toUTF8 path(wpath); strncpy(buf, path.get(), size); - free(wpath); #else if(!getcwd(buf, size)) return NS_ERROR_FAILURE; From 500d9d724c630e3e2a456c8d969195fcc04b9011 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 28 May 2009 11:09:05 -0700 Subject: [PATCH 014/175] Bug 189519 - Implement CSS3 (-moz- for now)background-size. r=dbaron, other useful review comments from roc/bz --- dom/interfaces/css/nsIDOMNSCSS2Properties.idl | 5 +- gfx/public/nsCoord.h | 23 +- layout/base/nsCSSRendering.cpp | 216 ++++++++++++++---- layout/base/nsStyleConsts.h | 5 + .../background-size-auto-auto.html | 20 ++ .../background-size-auto-length-ref.html | 23 ++ .../background-size-auto-length.html | 25 ++ .../background-size-auto-percent.html | 25 ++ .../backgrounds/background-size-auto-ref.html | 24 ++ .../backgrounds/background-size-auto.html | 20 ++ ...ackground-size-body-contain-no-repeat.html | 17 ++ ...ackground-size-body-contain-not-fixed.html | 17 ++ .../background-size-body-contain.html | 16 ++ .../background-size-body-cover-no-repeat.html | 17 ++ .../background-size-body-cover-not-fixed.html | 17 ++ .../background-size-body-cover-ref.html | 14 ++ .../background-size-body-cover.html | 16 ++ ...d-size-body-percent-percent-no-repeat.html | 17 ++ ...d-size-body-percent-percent-not-fixed.html | 16 ++ ...ize-body-percent-percent-overflow-ref.html | 14 ++ ...nd-size-body-percent-percent-overflow.html | 31 +++ ...kground-size-body-percent-percent-ref.html | 14 ++ .../background-size-body-percent-percent.html | 16 ++ ...background-size-body-single-not-fixed.html | 16 ++ .../background-size-bounding-box.html | 36 +++ ...ckground-size-contain-clip-border-ref.html | 29 +++ .../background-size-contain-clip-border.html | 27 +++ ...lip-padding-origin-border-padding-ref.html | 40 ++++ ...in-clip-padding-origin-border-padding.html | 30 +++ ...ontain-clip-padding-origin-border-ref.html | 29 +++ ...ze-contain-clip-padding-origin-border.html | 28 +++ ...kground-size-contain-clip-padding-ref.html | 29 +++ .../background-size-contain-clip-padding.html | 27 +++ ...size-contain-position-fifty-fifty-ref.html | 28 +++ ...und-size-contain-position-fifty-fifty.html | 26 +++ .../background-size-contain-ref.html | 23 ++ .../backgrounds/background-size-contain.html | 25 ++ .../background-size-continuous.html | 36 +++ .../background-size-cover-bounding-box.html | 37 +++ .../background-size-cover-continuous.html | 37 +++ .../background-size-cover-each-box.html | 37 +++ .../background-size-cover-ref.html | 18 ++ .../backgrounds/background-size-cover.html | 20 ++ .../backgrounds/background-size-each-box.html | 36 +++ .../background-size-length-auto.html | 25 ++ .../background-size-length-length-ref.html | 23 ++ .../background-size-length-length.html | 25 ++ .../background-size-length-percent-ref.html | 23 ++ .../background-size-length-percent.html | 25 ++ .../backgrounds/background-size-length.html | 25 ++ ...nd-size-no-intrinsic-height-image-ref.html | 23 ++ ...ground-size-no-intrinsic-height-image.html | 25 ++ ...und-size-no-intrinsic-width-image-ref.html | 23 ++ ...kground-size-no-intrinsic-width-image.html | 25 ++ .../background-size-percent-auto.html | 25 ++ .../background-size-percent-length.html | 25 ++ .../background-size-percent-percent-ref.html | 23 ++ ...ound-size-percent-percent-stretch-ref.html | 39 ++++ ...ckground-size-percent-percent-stretch.html | 33 +++ .../background-size-percent-percent.html | 25 ++ .../backgrounds/background-size-percent.html | 25 ++ .../background-size-zoom-no-repeat-ref.html | 34 +++ .../background-size-zoom-no-repeat.html | 35 +++ .../background-size-zoom-repeat-ref.html | 60 +++++ .../background-size-zoom-repeat.html | 61 +++++ .../green-8x20-blue-8x20-vertical.png | Bin 0 -> 107 bytes .../backgrounds/no-intrinsic-size.svg | 8 + layout/reftests/backgrounds/reftest.list | 69 ++++++ layout/style/nsCSSDeclaration.cpp | 22 +- layout/style/nsCSSKeywordList.h | 2 + layout/style/nsCSSParser.cpp | 123 +++++++++- layout/style/nsCSSPropList.h | 10 + layout/style/nsCSSProps.cpp | 7 + layout/style/nsCSSProps.h | 1 + layout/style/nsCSSStruct.cpp | 2 + layout/style/nsCSSStruct.h | 1 + layout/style/nsComputedDOMStyle.cpp | 79 ++++++- layout/style/nsComputedDOMStyle.h | 1 + layout/style/nsRuleNode.cpp | 98 ++++++++ layout/style/nsStyleStruct.cpp | 45 ++++ layout/style/nsStyleStruct.h | 40 +++- layout/style/test/property_database.js | 12 +- 82 files changed, 2302 insertions(+), 67 deletions(-) create mode 100644 layout/reftests/backgrounds/background-size-auto-auto.html create mode 100644 layout/reftests/backgrounds/background-size-auto-length-ref.html create mode 100644 layout/reftests/backgrounds/background-size-auto-length.html create mode 100644 layout/reftests/backgrounds/background-size-auto-percent.html create mode 100644 layout/reftests/backgrounds/background-size-auto-ref.html create mode 100644 layout/reftests/backgrounds/background-size-auto.html create mode 100644 layout/reftests/backgrounds/background-size-body-contain-no-repeat.html create mode 100644 layout/reftests/backgrounds/background-size-body-contain-not-fixed.html create mode 100644 layout/reftests/backgrounds/background-size-body-contain.html create mode 100644 layout/reftests/backgrounds/background-size-body-cover-no-repeat.html create mode 100644 layout/reftests/backgrounds/background-size-body-cover-not-fixed.html create mode 100644 layout/reftests/backgrounds/background-size-body-cover-ref.html create mode 100644 layout/reftests/backgrounds/background-size-body-cover.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent-no-repeat.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent-not-fixed.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent-overflow-ref.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent-overflow.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent-ref.html create mode 100644 layout/reftests/backgrounds/background-size-body-percent-percent.html create mode 100644 layout/reftests/backgrounds/background-size-body-single-not-fixed.html create mode 100644 layout/reftests/backgrounds/background-size-bounding-box.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-border-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-border.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain-clip-padding.html create mode 100644 layout/reftests/backgrounds/background-size-contain-position-fifty-fifty-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain-position-fifty-fifty.html create mode 100644 layout/reftests/backgrounds/background-size-contain-ref.html create mode 100644 layout/reftests/backgrounds/background-size-contain.html create mode 100644 layout/reftests/backgrounds/background-size-continuous.html create mode 100644 layout/reftests/backgrounds/background-size-cover-bounding-box.html create mode 100644 layout/reftests/backgrounds/background-size-cover-continuous.html create mode 100644 layout/reftests/backgrounds/background-size-cover-each-box.html create mode 100644 layout/reftests/backgrounds/background-size-cover-ref.html create mode 100644 layout/reftests/backgrounds/background-size-cover.html create mode 100644 layout/reftests/backgrounds/background-size-each-box.html create mode 100644 layout/reftests/backgrounds/background-size-length-auto.html create mode 100644 layout/reftests/backgrounds/background-size-length-length-ref.html create mode 100644 layout/reftests/backgrounds/background-size-length-length.html create mode 100644 layout/reftests/backgrounds/background-size-length-percent-ref.html create mode 100644 layout/reftests/backgrounds/background-size-length-percent.html create mode 100644 layout/reftests/backgrounds/background-size-length.html create mode 100644 layout/reftests/backgrounds/background-size-no-intrinsic-height-image-ref.html create mode 100644 layout/reftests/backgrounds/background-size-no-intrinsic-height-image.html create mode 100644 layout/reftests/backgrounds/background-size-no-intrinsic-width-image-ref.html create mode 100644 layout/reftests/backgrounds/background-size-no-intrinsic-width-image.html create mode 100644 layout/reftests/backgrounds/background-size-percent-auto.html create mode 100644 layout/reftests/backgrounds/background-size-percent-length.html create mode 100644 layout/reftests/backgrounds/background-size-percent-percent-ref.html create mode 100644 layout/reftests/backgrounds/background-size-percent-percent-stretch-ref.html create mode 100644 layout/reftests/backgrounds/background-size-percent-percent-stretch.html create mode 100644 layout/reftests/backgrounds/background-size-percent-percent.html create mode 100644 layout/reftests/backgrounds/background-size-percent.html create mode 100644 layout/reftests/backgrounds/background-size-zoom-no-repeat-ref.html create mode 100644 layout/reftests/backgrounds/background-size-zoom-no-repeat.html create mode 100644 layout/reftests/backgrounds/background-size-zoom-repeat-ref.html create mode 100644 layout/reftests/backgrounds/background-size-zoom-repeat.html create mode 100644 layout/reftests/backgrounds/green-8x20-blue-8x20-vertical.png create mode 100644 layout/reftests/backgrounds/no-intrinsic-size.svg diff --git a/dom/interfaces/css/nsIDOMNSCSS2Properties.idl b/dom/interfaces/css/nsIDOMNSCSS2Properties.idl index f8c5ebb3a41..c7929428e4e 100644 --- a/dom/interfaces/css/nsIDOMNSCSS2Properties.idl +++ b/dom/interfaces/css/nsIDOMNSCSS2Properties.idl @@ -39,7 +39,7 @@ #include "nsIDOMCSS2Properties.idl" -[scriptable, uuid(e7245a21-3f46-4e67-82bf-a9b326fe74ee)] +[scriptable, uuid(643f64d3-6b95-4d9a-ac75-c01a6f4c88cb)] interface nsIDOMNSCSS2Properties : nsIDOMCSS2Properties { /* Non-DOM 2 extensions */ @@ -256,5 +256,8 @@ interface nsIDOMNSCSS2Properties : nsIDOMCSS2Properties attribute DOMString MozWindowShadow; // raises(DOMException) on setting + + attribute DOMString MozBackgroundSize; + // raises(DOMException) on setting }; diff --git a/gfx/public/nsCoord.h b/gfx/public/nsCoord.h index eb28a240eb6..baee38e4af8 100644 --- a/gfx/public/nsCoord.h +++ b/gfx/public/nsCoord.h @@ -88,12 +88,29 @@ inline void VERIFY_COORD(nscoord aCoord) { #endif } -inline nscoord NSCoordMultiply(nscoord aCoord, float aVal) { +/** + * Returns aCoord * aVal, capping the product to nscoord_MAX. + * + * Note: If/when we start using floats for nscoords, this function won't be + * necessary. Normal float multiplication correctly handles overflowing + * multiplications, automatically saturating to infinity. + */ +inline nscoord NSCoordSaturatingMultiply(nscoord aCoord, float aVal) { VERIFY_COORD(aCoord); + NS_ABORT_IF_FALSE(aVal >= 0.0f, + "negative scaling factors must be handled manually"); #ifdef NS_COORD_IS_FLOAT - return floorf(aCoord*aVal); + return floorf(aCoord * aVal); #else - return (PRInt32)(aCoord*aVal); + // This one's only a warning because it's possible to trigger + NS_WARN_IF_FALSE(aCoord > 0 + ? floorf(aCoord * aVal) < nscoord_MAX + : ceilf(aCoord * aVal) > nscoord_MIN, + "nscoord multiplication capped"); + + if (aCoord > 0) + return PRInt32(PR_MIN(nscoord_MAX, aCoord * aVal)); + return PRInt32(PR_MAX(nscoord_MIN, aCoord * aVal)); #endif } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index e6bbc6cc030..ded69d92211 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -27,6 +27,7 @@ * L. David Baron , Mozilla Corporation * Michael Ventnor * Rob Arnold + * Jeff Walden * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -1395,8 +1396,12 @@ IsSolidBorder(const nsStyleBorder& aBorder) return PR_TRUE; } +/** + * Returns true if the given request is for a background image (that is, it is + * non-null) and that image is fully loaded and its size calculated. + */ static PRBool -UseImageRequestForBackground(imgIRequest *aRequest) +HaveCompleteBackgroundImage(imgIRequest *aRequest) { if (!aRequest) return PR_FALSE; @@ -1496,37 +1501,37 @@ static nscolor DetermineBackgroundColorInternal(nsPresContext* aPresContext, const nsStyleBackground& aBackground, nsIFrame* aFrame, - PRBool* aDrawBackgroundImage, - PRBool* aDrawBackgroundColor, + PRBool& aDrawBackgroundImage, + PRBool& aDrawBackgroundColor, nsCOMPtr& aBottomImage) { - *aDrawBackgroundImage = PR_TRUE; - *aDrawBackgroundColor = PR_TRUE; + aDrawBackgroundImage = PR_TRUE; + aDrawBackgroundColor = PR_TRUE; if (aFrame->HonorPrintBackgroundSettings()) { - *aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw(); - *aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw(); + aDrawBackgroundImage = aPresContext->GetBackgroundImageDraw(); + aDrawBackgroundColor = aPresContext->GetBackgroundColorDraw(); } aBottomImage = aBackground.BottomLayer().mImage; - if (!aDrawBackgroundImage || !UseImageRequestForBackground(aBottomImage)) { + if (!aDrawBackgroundImage || !HaveCompleteBackgroundImage(aBottomImage)) { aBottomImage = nsnull; } nscolor bgColor; - if (*aDrawBackgroundColor) { + if (aDrawBackgroundColor) { bgColor = aBackground.mBackgroundColor; if (NS_GET_A(bgColor) == 0) - *aDrawBackgroundColor = PR_FALSE; + aDrawBackgroundColor = PR_FALSE; } else { // If GetBackgroundColorDraw() is false, we are still expected to // draw color in the background of any frame that's not completely // transparent, but we are expected to use white instead of whatever // color was specified. bgColor = NS_RGB(255, 255, 255); - if (*aDrawBackgroundImage || !aBackground.IsTransparent()) - *aDrawBackgroundColor = PR_TRUE; + if (aDrawBackgroundImage || !aBackground.IsTransparent()) + aDrawBackgroundColor = PR_TRUE; else bgColor = NS_RGBA(0,0,0,0); } @@ -1545,8 +1550,8 @@ nsCSSRendering::DetermineBackgroundColor(nsPresContext* aPresContext, return DetermineBackgroundColorInternal(aPresContext, aBackground, aFrame, - &drawBackgroundImage, - &drawBackgroundColor, + drawBackgroundImage, + drawBackgroundColor, bottomImage); } @@ -1596,8 +1601,8 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, nscolor bgColor = DetermineBackgroundColorInternal(aPresContext, aBackground, aForFrame, - &drawBackgroundImage, - &drawBackgroundColor, + drawBackgroundImage, + drawBackgroundColor, bottomImage); // At this point, drawBackgroundImage and drawBackgroundColor are @@ -1628,7 +1633,7 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, // determined by the value of 'background-clip' in // SetupCurrentBackgroundClip. (Arguably it should be the // intersection, but that breaks the table painter -- in particular, - // taking the intersection breaks reftests/bugs/403429-1[ab].) + // taking the intersection breaks reftests/bugs/403249-1[ab].) nsRect bgClipArea, dirtyRect; gfxRect dirtyRectGfx; PRUint8 currentBackgroundClip; @@ -1722,6 +1727,25 @@ nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext, } } +static inline float +ScaleDimension(nsStyleBackground::Size::Dimension aDimension, + PRUint8 aType, + nscoord aLength, nscoord aAvailLength) +{ + switch (aType) { + case nsStyleBackground::Size::ePercentage: + return double(aDimension.mFloat) * (double(aAvailLength) / double(aLength)); + case nsStyleBackground::Size::eLength: + return double(aDimension.mCoord) / double(aLength); + default: + NS_ABORT_IF_FALSE(PR_FALSE, "bad aDimension.mType"); + return 1.0f; + case nsStyleBackground::Size::eAuto: + NS_ABORT_IF_FALSE(PR_FALSE, "aDimension.mType == eAuto isn't handled"); + return 1.0f; + } +} + static void PaintBackgroundLayer(nsPresContext* aPresContext, nsIRenderingContext& aRenderingContext, @@ -1732,15 +1756,69 @@ PaintBackgroundLayer(nsPresContext* aPresContext, const nsStyleBackground& aBackground, const nsStyleBackground::Layer& aLayer) { + /* + * The background properties we need to keep in mind when drawing background + * layers are: + * + * background-image + * background-repeat + * background-attachment + * background-position + * background-clip (-moz-background-clip) + * background-origin (-moz-background-origin) + * background-size (-moz-background-size) + * background-break (-moz-background-inline-policy) + * + * (background-color applies to the entire element and not to individual + * layers, so it is irrelevant to this method.) + * + * These properties have the following dependencies upon each other when + * determining rendering: + * + * background-image + * no dependencies + * background-repeat + * no dependencies + * background-attachment + * no dependencies + * background-position + * depends upon background-size (for the image's scaled size) and + * background-break (for the background positioning area) + * background-clip + * no dependencies + * background-origin + * depends upon background-attachment (only in the case where that value + * is 'fixed') + * background-size + * depends upon background-break (for the background positioning area for + * resolving percentages), background-image (for the image's intrinsic + * size), background-repeat (if that value is 'round'), and + * background-origin (for the background painting area, when + * background-repeat is 'round') + * background-break + * depends upon background-origin (specifying how the boxes making up the + * background positioning area are determined) + * + * As a result of only-if dependencies we don't strictly do a topological + * sort of the above properties when processing, but it's pretty close to one: + * + * background-clip (by caller) + * background-image + * background-break, background-origin + * background-attachment (postfix for background-{origin,break} if 'fixed') + * background-size + * background-position + * background-repeat + */ + // Lookup the image imgIRequest *req = aLayer.mImage; - if (!UseImageRequestForBackground(req)) { - // There's no image or it's not ready to be painted. + if (!HaveCompleteBackgroundImage(req)) return; - } nsCOMPtr image; req->GetImage(getter_AddRefs(image)); + req = nsnull; nsIntSize imageIntSize; image->GetWidth(&imageIntSize.width); @@ -1750,27 +1828,33 @@ PaintBackgroundLayer(nsPresContext* aPresContext, imageSize.width = nsPresContext::CSSPixelsToAppUnits(imageIntSize.width); imageSize.height = nsPresContext::CSSPixelsToAppUnits(imageIntSize.height); - req = nsnull; + if (imageSize.width == 0 || imageSize.height == 0) + return; // relative to aBorderArea - nsRect bgOriginRect(0, 0, 0, 0); + nsRect bgPositioningArea(0, 0, 0, 0); nsIAtom* frameType = aForFrame->GetType(); nsIFrame* geometryFrame = aForFrame; if (frameType == nsGkAtoms::inlineFrame || frameType == nsGkAtoms::positionedInlineFrame) { + // XXXjwalden Strictly speaking this is not quite faithful to how + // background-break is supposed to interact with background-origin values, + // but it's a non-trivial amount of work to make it fully conformant, and + // until the specification is more finalized (and assuming background-break + // even makes the cut) it doesn't make sense to hammer out exact behavior. switch (aBackground.mBackgroundInlinePolicy) { case NS_STYLE_BG_INLINE_POLICY_EACH_BOX: - bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size()); + bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size()); break; case NS_STYLE_BG_INLINE_POLICY_BOUNDING_BOX: - bgOriginRect = gInlineBGData->GetBoundingRect(aForFrame); + bgPositioningArea = gInlineBGData->GetBoundingRect(aForFrame); break; default: NS_ERROR("Unknown background-inline-policy value! " "Please, teach me what to do."); case NS_STYLE_BG_INLINE_POLICY_CONTINUOUS: - bgOriginRect = gInlineBGData->GetContinuousRect(aForFrame); + bgPositioningArea = gInlineBGData->GetContinuousRect(aForFrame); break; } } else if (frameType == nsGkAtoms::canvasFrame) { @@ -1780,10 +1864,10 @@ PaintBackgroundLayer(nsPresContext* aPresContext, // finished and this page only displays the continuations of // absolutely positioned content). if (geometryFrame) { - bgOriginRect = geometryFrame->GetRect(); + bgPositioningArea = geometryFrame->GetRect(); } } else { - bgOriginRect = nsRect(nsPoint(0,0), aBorderArea.Size()); + bgPositioningArea = nsRect(nsPoint(0,0), aBorderArea.Size()); } // Background images are tiled over the 'background-clip' area @@ -1791,11 +1875,11 @@ PaintBackgroundLayer(nsPresContext* aPresContext, if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_BORDER && geometryFrame) { nsMargin border = geometryFrame->GetUsedBorder(); geometryFrame->ApplySkipSides(border); - bgOriginRect.Deflate(border); + bgPositioningArea.Deflate(border); if (aLayer.mOrigin != NS_STYLE_BG_ORIGIN_PADDING) { nsMargin padding = geometryFrame->GetUsedPadding(); geometryFrame->ApplySkipSides(padding); - bgOriginRect.Deflate(padding); + bgPositioningArea.Deflate(padding); NS_ASSERTION(aLayer.mOrigin == NS_STYLE_BG_ORIGIN_CONTENT, "unknown background-origin value"); } @@ -1805,7 +1889,7 @@ PaintBackgroundLayer(nsPresContext* aPresContext, // // relative to aBorderArea.TopLeft() (which is where the top-left // of aForFrame's border-box will be rendered) - nsPoint imageTopLeft, anchor; + nsPoint imageTopLeft, anchor, offset; if (NS_STYLE_BG_ATTACHMENT_FIXED == aLayer.mAttachment) { // If it's a fixed background attachment, then the image is placed // relative to the viewport, which is the area of the root frame @@ -1827,7 +1911,8 @@ PaintBackgroundLayer(nsPresContext* aPresContext, // else this is an embedded shell and its root frame is what we want } - nsRect viewportArea(nsPoint(0, 0), topFrame->GetSize()); + // Set the background positioning area to the viewport's area. + bgPositioningArea.SetRect(nsPoint(0, 0), topFrame->GetSize()); if (!pageContentFrame) { // Subtract the size of scrollbars. @@ -1835,29 +1920,70 @@ PaintBackgroundLayer(nsPresContext* aPresContext, aPresContext->PresShell()->GetRootScrollFrameAsScrollable(); if (scrollableFrame) { nsMargin scrollbars = scrollableFrame->GetActualScrollbarSizes(); - viewportArea.Deflate(scrollbars); + bgPositioningArea.Deflate(scrollbars); } } - - // Get the anchor point, relative to the viewport. - ComputeBackgroundAnchorPoint(aLayer, viewportArea.Size(), imageSize, - &imageTopLeft, &anchor); - // Convert the anchor point from viewport coordinates to aForFrame - // coordinates. - nsPoint offset = viewportArea.TopLeft() - aForFrame->GetOffsetTo(topFrame); - imageTopLeft += offset; - anchor += offset; + offset = bgPositioningArea.TopLeft() - aForFrame->GetOffsetTo(topFrame); } else { - ComputeBackgroundAnchorPoint(aLayer, bgOriginRect.Size(), imageSize, - &imageTopLeft, &anchor); - imageTopLeft += bgOriginRect.TopLeft(); - anchor += bgOriginRect.TopLeft(); + offset = bgPositioningArea.TopLeft(); } + + // Scale the image as specified for background-size and as required for + // proper background positioning when background-position is defined with + // percentages. + float scaleX, scaleY; + switch (aLayer.mSize.mWidthType) { + case nsStyleBackground::Size::eContain: + case nsStyleBackground::Size::eCover: { + float scaleFitX = double(bgPositioningArea.width) / imageSize.width; + float scaleFitY = double(bgPositioningArea.height) / imageSize.height; + if (aLayer.mSize.mWidthType == nsStyleBackground::Size::eCover) { + scaleX = scaleY = PR_MAX(scaleFitX, scaleFitY); + } else { + scaleX = scaleY = PR_MIN(scaleFitX, scaleFitY); + } + break; + } + default: { + if (aLayer.mSize.mWidthType == nsStyleBackground::Size::eAuto) { + if (aLayer.mSize.mHeightType == nsStyleBackground::Size::eAuto) { + scaleX = scaleY = 1.0f; + } else { + scaleX = scaleY = + ScaleDimension(aLayer.mSize.mHeight, aLayer.mSize.mHeightType, + imageSize.height, bgPositioningArea.height); + } + } else { + if (aLayer.mSize.mHeightType == nsStyleBackground::Size::eAuto) { + scaleX = scaleY = + ScaleDimension(aLayer.mSize.mWidth, aLayer.mSize.mWidthType, + imageSize.width, bgPositioningArea.width); + } else { + scaleX = ScaleDimension(aLayer.mSize.mWidth, aLayer.mSize.mWidthType, + imageSize.width, bgPositioningArea.width); + scaleY = ScaleDimension(aLayer.mSize.mHeight, aLayer.mSize.mHeightType, + imageSize.height, bgPositioningArea.height); + } + } + break; + } + } + imageSize.width = NSCoordSaturatingMultiply(imageSize.width, scaleX); + imageSize.height = NSCoordSaturatingMultiply(imageSize.height, scaleY); + + // Compute the position of the background now that the background's size is + // determined. + ComputeBackgroundAnchorPoint(aLayer, bgPositioningArea.Size(), imageSize, + &imageTopLeft, &anchor); + imageTopLeft += offset; + anchor += offset; nsRect destArea(imageTopLeft + aBorderArea.TopLeft(), imageSize); nsRect fillArea = destArea; PRIntn repeat = aLayer.mRepeat; + PR_STATIC_ASSERT(NS_STYLE_BG_REPEAT_XY == + (NS_STYLE_BG_REPEAT_X | NS_STYLE_BG_REPEAT_Y)); if (repeat & NS_STYLE_BG_REPEAT_X) { fillArea.x = aBGClipRect.x; fillArea.width = aBGClipRect.width; diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h index 6926a24b01a..ee1f3a73e05 100644 --- a/layout/base/nsStyleConsts.h +++ b/layout/base/nsStyleConsts.h @@ -269,11 +269,16 @@ #define NS_STYLE_BG_POSITION_RIGHT (1<<4) // See nsStyleBackground +// Code depends on (BG_REPEAT_X | BG_REPEAT_Y) == BG_REPEAT_XY #define NS_STYLE_BG_REPEAT_OFF 0x00 #define NS_STYLE_BG_REPEAT_X 0x01 #define NS_STYLE_BG_REPEAT_Y 0x02 #define NS_STYLE_BG_REPEAT_XY 0x03 +// See nsStyleBackground +#define NS_STYLE_BG_SIZE_CONTAIN 0 +#define NS_STYLE_BG_SIZE_COVER 1 + // See nsStyleTable #define NS_STYLE_BORDER_COLLAPSE 0 #define NS_STYLE_BORDER_SEPARATE 1 diff --git a/layout/reftests/backgrounds/background-size-auto-auto.html b/layout/reftests/backgrounds/background-size-auto-auto.html new file mode 100644 index 00000000000..22108f013d3 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto-auto.html @@ -0,0 +1,20 @@ + + + + background-size: auto auto; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-auto-length-ref.html b/layout/reftests/backgrounds/background-size-auto-length-ref.html new file mode 100644 index 00000000000..ae7535a84bf --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto-length-ref.html @@ -0,0 +1,23 @@ + + + + background-size: auto 16px; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-auto-length.html b/layout/reftests/backgrounds/background-size-auto-length.html new file mode 100644 index 00000000000..5d54b5c7579 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto-length.html @@ -0,0 +1,25 @@ + + + + background-size: auto 16px; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-auto-percent.html b/layout/reftests/backgrounds/background-size-auto-percent.html new file mode 100644 index 00000000000..b1bed853eec --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto-percent.html @@ -0,0 +1,25 @@ + + + + background-size: auto 12.5%; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-auto-ref.html b/layout/reftests/backgrounds/background-size-auto-ref.html new file mode 100644 index 00000000000..8b2bd45d518 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto-ref.html @@ -0,0 +1,24 @@ + + + + background-size: auto; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-auto.html b/layout/reftests/backgrounds/background-size-auto.html new file mode 100644 index 00000000000..14a4bc9575a --- /dev/null +++ b/layout/reftests/backgrounds/background-size-auto.html @@ -0,0 +1,20 @@ + + + + background-size: auto; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-body-contain-no-repeat.html b/layout/reftests/backgrounds/background-size-body-contain-no-repeat.html new file mode 100644 index 00000000000..1afe70ea039 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-contain-no-repeat.html @@ -0,0 +1,17 @@ + + + + background-size: contain; on body, no-repeat, fixed + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-contain-not-fixed.html b/layout/reftests/backgrounds/background-size-body-contain-not-fixed.html new file mode 100644 index 00000000000..371e08be1df --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-contain-not-fixed.html @@ -0,0 +1,17 @@ + + + + background-size: contain; not fixed, no-repeat on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-contain.html b/layout/reftests/backgrounds/background-size-body-contain.html new file mode 100644 index 00000000000..851a582f981 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-contain.html @@ -0,0 +1,16 @@ + + + + background-size: contain; fixed, repeat on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-cover-no-repeat.html b/layout/reftests/backgrounds/background-size-body-cover-no-repeat.html new file mode 100644 index 00000000000..b758d7a2f4d --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-cover-no-repeat.html @@ -0,0 +1,17 @@ + + + + background-size: cover; on body, no-repeat, fixed + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-cover-not-fixed.html b/layout/reftests/backgrounds/background-size-body-cover-not-fixed.html new file mode 100644 index 00000000000..16e79583f31 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-cover-not-fixed.html @@ -0,0 +1,17 @@ + + + + background-size: cover; no-repeat, not-fixed on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-cover-ref.html b/layout/reftests/backgrounds/background-size-body-cover-ref.html new file mode 100644 index 00000000000..3dfe4ea6b7d --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-cover-ref.html @@ -0,0 +1,14 @@ + + + + background-size: cover; on body reference + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-cover.html b/layout/reftests/backgrounds/background-size-body-cover.html new file mode 100644 index 00000000000..09b4a83376c --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-cover.html @@ -0,0 +1,16 @@ + + + + background-size: cover; on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent-no-repeat.html b/layout/reftests/backgrounds/background-size-body-percent-percent-no-repeat.html new file mode 100644 index 00000000000..aba071be324 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent-no-repeat.html @@ -0,0 +1,17 @@ + + + + background-size: 100% 100%; no-repeat/fixed on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent-not-fixed.html b/layout/reftests/backgrounds/background-size-body-percent-percent-not-fixed.html new file mode 100644 index 00000000000..1d115317738 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent-not-fixed.html @@ -0,0 +1,16 @@ + + + + background-size: 100% 100%; no-repeat/not fixed on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent-overflow-ref.html b/layout/reftests/backgrounds/background-size-body-percent-percent-overflow-ref.html new file mode 100644 index 00000000000..c9bfd99819a --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent-overflow-ref.html @@ -0,0 +1,14 @@ + + + + background-size: 100% 100%; on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent-overflow.html b/layout/reftests/backgrounds/background-size-body-percent-percent-overflow.html new file mode 100644 index 00000000000..46085a90603 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent-overflow.html @@ -0,0 +1,31 @@ + + + + background-size: 100% 100%; on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent-ref.html b/layout/reftests/backgrounds/background-size-body-percent-percent-ref.html new file mode 100644 index 00000000000..3f03f74afaa --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent-ref.html @@ -0,0 +1,14 @@ + + + + background-size: 100% 100%; on body reference + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-percent-percent.html b/layout/reftests/backgrounds/background-size-body-percent-percent.html new file mode 100644 index 00000000000..e5722bd7ba5 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-percent-percent.html @@ -0,0 +1,16 @@ + + + + background-size: 100% 100%; on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-body-single-not-fixed.html b/layout/reftests/backgrounds/background-size-body-single-not-fixed.html new file mode 100644 index 00000000000..e901abad621 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-body-single-not-fixed.html @@ -0,0 +1,16 @@ + + + + background-size: contain; on body + + + + + diff --git a/layout/reftests/backgrounds/background-size-bounding-box.html b/layout/reftests/backgrounds/background-size-bounding-box.html new file mode 100644 index 00000000000..6136a3729cf --- /dev/null +++ b/layout/reftests/backgrounds/background-size-bounding-box.html @@ -0,0 +1,36 @@ + + + + background-break: bounding-box + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-border-ref.html b/layout/reftests/backgrounds/background-size-contain-clip-border-ref.html new file mode 100644 index 00000000000..a061b39dd05 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-border-ref.html @@ -0,0 +1,29 @@ + + + + background-size: contain; -moz-background-clip: border reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-border.html b/layout/reftests/backgrounds/background-size-contain-clip-border.html new file mode 100644 index 00000000000..76b209a1736 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-border.html @@ -0,0 +1,27 @@ + + + + background-size: contain; -moz-background-clip: border + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding-ref.html b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding-ref.html new file mode 100644 index 00000000000..f3bb19eb168 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding-ref.html @@ -0,0 +1,40 @@ + + + + background-size: contain; -moz-background-clip: padding reference, -moz-background-origin: border + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding.html b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding.html new file mode 100644 index 00000000000..12cdbc5a95d --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-padding.html @@ -0,0 +1,30 @@ + + + + background-size: contain; -moz-background-clip: padding, -moz-background-origin: border + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-ref.html b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-ref.html new file mode 100644 index 00000000000..7a3b9f7beb2 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border-ref.html @@ -0,0 +1,29 @@ + + + + background-size: contain; -moz-background-clip: padding reference, -moz-background-origin: border + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border.html b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border.html new file mode 100644 index 00000000000..ab36021fb87 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding-origin-border.html @@ -0,0 +1,28 @@ + + + + background-size: contain; -moz-background-clip: padding, -moz-background-origin: border + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding-ref.html b/layout/reftests/backgrounds/background-size-contain-clip-padding-ref.html new file mode 100644 index 00000000000..3e2af30b903 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding-ref.html @@ -0,0 +1,29 @@ + + + + background-size: contain; -moz-background-clip: padding reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-clip-padding.html b/layout/reftests/backgrounds/background-size-contain-clip-padding.html new file mode 100644 index 00000000000..36408f785c7 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-clip-padding.html @@ -0,0 +1,27 @@ + + + + background-size: contain; -moz-background-clip: padding + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty-ref.html b/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty-ref.html new file mode 100644 index 00000000000..811f4c047cd --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty-ref.html @@ -0,0 +1,28 @@ + + + + background-size: contain; background-position: 50% 50% reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty.html b/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty.html new file mode 100644 index 00000000000..7e3dab555e8 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-position-fifty-fifty.html @@ -0,0 +1,26 @@ + + + + background-size: contain; background-position: 50% 50% + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain-ref.html b/layout/reftests/backgrounds/background-size-contain-ref.html new file mode 100644 index 00000000000..53c346cc74f --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain-ref.html @@ -0,0 +1,23 @@ + + + + background-size: contain; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-contain.html b/layout/reftests/backgrounds/background-size-contain.html new file mode 100644 index 00000000000..654a94c122c --- /dev/null +++ b/layout/reftests/backgrounds/background-size-contain.html @@ -0,0 +1,25 @@ + + + + background-size: contain; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-continuous.html b/layout/reftests/backgrounds/background-size-continuous.html new file mode 100644 index 00000000000..4373cdcd238 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-continuous.html @@ -0,0 +1,36 @@ + + + + background-break: continuous + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-cover-bounding-box.html b/layout/reftests/backgrounds/background-size-cover-bounding-box.html new file mode 100644 index 00000000000..ca12800e0ab --- /dev/null +++ b/layout/reftests/backgrounds/background-size-cover-bounding-box.html @@ -0,0 +1,37 @@ + + + + background-size: cover; background-break: bounding-box + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-cover-continuous.html b/layout/reftests/backgrounds/background-size-cover-continuous.html new file mode 100644 index 00000000000..3f81d372b4b --- /dev/null +++ b/layout/reftests/backgrounds/background-size-cover-continuous.html @@ -0,0 +1,37 @@ + + + + background-size: cover; background-break: continuous + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-cover-each-box.html b/layout/reftests/backgrounds/background-size-cover-each-box.html new file mode 100644 index 00000000000..a53333fc644 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-cover-each-box.html @@ -0,0 +1,37 @@ + + + + background-size: cover; background-break: each-box + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-cover-ref.html b/layout/reftests/backgrounds/background-size-cover-ref.html new file mode 100644 index 00000000000..7d74f7b3c24 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-cover-ref.html @@ -0,0 +1,18 @@ + + + + background-size: cover; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-cover.html b/layout/reftests/backgrounds/background-size-cover.html new file mode 100644 index 00000000000..20e23eb2424 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-cover.html @@ -0,0 +1,20 @@ + + + + background-size: cover; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-each-box.html b/layout/reftests/backgrounds/background-size-each-box.html new file mode 100644 index 00000000000..35c1a205a77 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-each-box.html @@ -0,0 +1,36 @@ + + + + background-break: each-box + + + +
+ + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length-auto.html b/layout/reftests/backgrounds/background-size-length-auto.html new file mode 100644 index 00000000000..9ef400915dd --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length-auto.html @@ -0,0 +1,25 @@ + + + + background-size: 16px auto; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length-length-ref.html b/layout/reftests/backgrounds/background-size-length-length-ref.html new file mode 100644 index 00000000000..05b5c675879 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length-length-ref.html @@ -0,0 +1,23 @@ + + + + background-size: 32px 64px; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length-length.html b/layout/reftests/backgrounds/background-size-length-length.html new file mode 100644 index 00000000000..dca9c8bd834 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length-length.html @@ -0,0 +1,25 @@ + + + + background-size: 32px 64px; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length-percent-ref.html b/layout/reftests/backgrounds/background-size-length-percent-ref.html new file mode 100644 index 00000000000..9c95873190e --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length-percent-ref.html @@ -0,0 +1,23 @@ + + + + background-size: 16px 25%; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length-percent.html b/layout/reftests/backgrounds/background-size-length-percent.html new file mode 100644 index 00000000000..ae630d53f53 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length-percent.html @@ -0,0 +1,25 @@ + + + + background-size: 16px 25%; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-length.html b/layout/reftests/backgrounds/background-size-length.html new file mode 100644 index 00000000000..362c601541d --- /dev/null +++ b/layout/reftests/backgrounds/background-size-length.html @@ -0,0 +1,25 @@ + + + + background-size: 16px; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-no-intrinsic-height-image-ref.html b/layout/reftests/backgrounds/background-size-no-intrinsic-height-image-ref.html new file mode 100644 index 00000000000..c453fed751d --- /dev/null +++ b/layout/reftests/backgrounds/background-size-no-intrinsic-height-image-ref.html @@ -0,0 +1,23 @@ + + + + background-size: 32px auto; for image with no intrinsic height reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-no-intrinsic-height-image.html b/layout/reftests/backgrounds/background-size-no-intrinsic-height-image.html new file mode 100644 index 00000000000..7b01c98465a --- /dev/null +++ b/layout/reftests/backgrounds/background-size-no-intrinsic-height-image.html @@ -0,0 +1,25 @@ + + + + background-size: 32px auto; for image with no intrinsic height + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-no-intrinsic-width-image-ref.html b/layout/reftests/backgrounds/background-size-no-intrinsic-width-image-ref.html new file mode 100644 index 00000000000..a01539de741 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-no-intrinsic-width-image-ref.html @@ -0,0 +1,23 @@ + + + + background-size: 32px auto; for image with no intrinsic size reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-no-intrinsic-width-image.html b/layout/reftests/backgrounds/background-size-no-intrinsic-width-image.html new file mode 100644 index 00000000000..501085f4528 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-no-intrinsic-width-image.html @@ -0,0 +1,25 @@ + + + + background-size: auto 32px; for image with no intrinsic width + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-auto.html b/layout/reftests/backgrounds/background-size-percent-auto.html new file mode 100644 index 00000000000..266619549b1 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-auto.html @@ -0,0 +1,25 @@ + + + + background-size: 25% auto; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-length.html b/layout/reftests/backgrounds/background-size-percent-length.html new file mode 100644 index 00000000000..695a7a506c2 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-length.html @@ -0,0 +1,25 @@ + + + + background-size: 25% 32px; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-percent-ref.html b/layout/reftests/backgrounds/background-size-percent-percent-ref.html new file mode 100644 index 00000000000..a30b75c6b03 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-percent-ref.html @@ -0,0 +1,23 @@ + + + + background-size: 50% 25%; reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-percent-stretch-ref.html b/layout/reftests/backgrounds/background-size-percent-percent-stretch-ref.html new file mode 100644 index 00000000000..65c999d6d99 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-percent-stretch-ref.html @@ -0,0 +1,39 @@ + + + + background-size: 100% 100%; stretch reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-percent-stretch.html b/layout/reftests/backgrounds/background-size-percent-percent-stretch.html new file mode 100644 index 00000000000..baf3e8e6c40 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-percent-stretch.html @@ -0,0 +1,33 @@ + + + + background-size: 100% 100%; stretch + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent-percent.html b/layout/reftests/backgrounds/background-size-percent-percent.html new file mode 100644 index 00000000000..64ce972c531 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent-percent.html @@ -0,0 +1,25 @@ + + + + background-size: 50% 25%; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-percent.html b/layout/reftests/backgrounds/background-size-percent.html new file mode 100644 index 00000000000..76f26c2b839 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-percent.html @@ -0,0 +1,25 @@ + + + + background-size: 25%; + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-zoom-no-repeat-ref.html b/layout/reftests/backgrounds/background-size-zoom-no-repeat-ref.html new file mode 100644 index 00000000000..dd05ee386af --- /dev/null +++ b/layout/reftests/backgrounds/background-size-zoom-no-repeat-ref.html @@ -0,0 +1,34 @@ + + + + background-size + zoom + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-zoom-no-repeat.html b/layout/reftests/backgrounds/background-size-zoom-no-repeat.html new file mode 100644 index 00000000000..f89ff92dda4 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-zoom-no-repeat.html @@ -0,0 +1,35 @@ + + + + background-size + zoom + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-zoom-repeat-ref.html b/layout/reftests/backgrounds/background-size-zoom-repeat-ref.html new file mode 100644 index 00000000000..1a5a1fd0af3 --- /dev/null +++ b/layout/reftests/backgrounds/background-size-zoom-repeat-ref.html @@ -0,0 +1,60 @@ + + + + background-size: 64px 40px; repeat, zoom reference + + + +
+ + diff --git a/layout/reftests/backgrounds/background-size-zoom-repeat.html b/layout/reftests/backgrounds/background-size-zoom-repeat.html new file mode 100644 index 00000000000..26581e57aff --- /dev/null +++ b/layout/reftests/backgrounds/background-size-zoom-repeat.html @@ -0,0 +1,61 @@ + + + + background-size: 64px 40px; repeat, zoom + + + +
+ + diff --git a/layout/reftests/backgrounds/green-8x20-blue-8x20-vertical.png b/layout/reftests/backgrounds/green-8x20-blue-8x20-vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..4236f5b1cebc8c9d6d22545135cf35425bfed836 GIT binary patch literal 107 zcmeAS@N?(olHy`uVBq!ia0vp^B0wy_!3HG7B;uuk6k~CayA#8@b22Z1992&j#}JFt z$yaKXf6j00YUpe{m2l<2!~})K4F`T-EV0OX;2_PxFljSOTti0iRG + + Image with no intrinsic size + + diff --git a/layout/reftests/backgrounds/reftest.list b/layout/reftests/backgrounds/reftest.list index eada98ee65c..c037d0a0803 100644 --- a/layout/reftests/backgrounds/reftest.list +++ b/layout/reftests/backgrounds/reftest.list @@ -24,3 +24,72 @@ == continuous-inline-4b.html continuous-inline-4-ref.html == continuous-inline-5a.html continuous-inline-5-ref.html == continuous-inline-5b.html continuous-inline-5-ref.html + +== background-size-auto-auto.html background-size-auto-ref.html +== background-size-auto.html background-size-auto-ref.html +== background-size-contain.html background-size-contain-ref.html +== background-size-cover.html background-size-cover-ref.html +== background-size-auto-length.html background-size-auto-length-ref.html +== background-size-length-auto.html background-size-auto-length-ref.html +== background-size-length.html background-size-auto-length-ref.html +== background-size-auto-percent.html background-size-auto-length-ref.html +== background-size-percent-auto.html background-size-auto-length-ref.html +== background-size-percent.html background-size-auto-length-ref.html +== background-size-length-percent.html background-size-length-percent-ref.html +== background-size-percent-length.html background-size-length-percent-ref.html +== background-size-percent-percent.html background-size-percent-percent-ref.html +== background-size-length-length.html background-size-length-length-ref.html +== background-size-percent-percent-stretch.html background-size-percent-percent-stretch-ref.html + +== background-size-body-percent-percent.html background-size-body-percent-percent-ref.html +== background-size-body-percent-percent-no-repeat.html background-size-body-percent-percent-ref.html +== background-size-body-percent-percent-not-fixed.html background-size-body-percent-percent-ref.html +== background-size-body-cover.html background-size-body-cover-ref.html +== background-size-body-cover-no-repeat.html background-size-body-cover-ref.html +!= background-size-body-cover-not-fixed.html background-size-body-cover-ref.html +!= background-size-body-cover-not-fixed.html background-size-body-single-not-fixed.html + +# relies on reftest window having greater height than width +== background-size-body-contain.html background-size-body-cover-ref.html +!= background-size-body-contain-no-repeat.html background-size-body-cover-ref.html +!= background-size-body-contain-not-fixed.html background-size-body-cover-ref.html +!= background-size-body-cover-not-fixed.html background-size-body-contain-not-fixed.html + +!= background-size-body-percent-percent-overflow.html background-size-body-percent-percent-overflow-ref.html + +== background-size-zoom-no-repeat.html background-size-zoom-no-repeat-ref.html + +== background-size-contain-clip-padding.html background-size-contain-clip-padding-ref.html +== background-size-contain-clip-border.html background-size-contain-clip-border-ref.html +== background-size-contain-position-fifty-fifty.html background-size-contain-position-fifty-fifty-ref.html +== background-size-contain-clip-padding-origin-border.html background-size-contain-clip-padding-origin-border-ref.html +== background-size-contain-clip-padding-origin-border-padding.html background-size-contain-clip-padding-origin-border-padding-ref.html + +# -moz-background-inline-policy is touchy and hard to test due to stretching +# artifacts and the difficulty of covering exact lines, and its CSS3 analog is +# on the chopping block at the moment, so just make sure background-size results +# in a different rendering when present. +!= background-size-cover-continuous.html background-size-continuous.html +!= background-size-cover-each-box.html background-size-each-box.html +!= background-size-cover-bounding-box.html background-size-bounding-box.html + +# ...and make sure each rendering with background-size is different from the +# others +!= background-size-cover-continuous.html background-size-cover-each-box.html +!= background-size-cover-continuous.html background-size-cover-bounding-box.html +!= background-size-cover-each-box.html background-size-cover-bounding-box.html + + +# There seems to be a pixel-snapping problem here, where the repeated background +# image isn't being snapped at its boundaries. Note that the boundaries within +# the image aren't the issue, because they're being obscured to avoid sampling +# algorithm dependencies (at least assuming the sampling algorithm in use +# doesn't sample too far astray from the boundaries). +fails == background-size-zoom-repeat.html background-size-zoom-repeat-ref.html + +# background-size affects images without intrinsic dimensions specially; we may +# not support such image formats right now, but when we do, we want +# background-size to be changed accordingly, and hopefully these tests should +# start failing when we do. +fails == background-size-no-intrinsic-width-image.html background-size-no-intrinsic-width-image-ref.html +fails == background-size-no-intrinsic-height-image.html background-size-no-intrinsic-height-image-ref.html diff --git a/layout/style/nsCSSDeclaration.cpp b/layout/style/nsCSSDeclaration.cpp index 315fab2307e..4eebd5bf336 100644 --- a/layout/style/nsCSSDeclaration.cpp +++ b/layout/style/nsCSSDeclaration.cpp @@ -174,11 +174,16 @@ PRBool nsCSSDeclaration::AppendValueToString(nsCSSProperty aProperty, nsAString& ((aProperty == eCSSProperty_background_position || aProperty == eCSSProperty__moz_transform_origin) && pair->mXValue.GetUnit() != eCSSUnit_Inherit && - pair->mXValue.GetUnit() != eCSSUnit_Initial)) { - // Only output a Y value if it's different from the X value + pair->mXValue.GetUnit() != eCSSUnit_Initial) || + (aProperty == eCSSProperty__moz_background_size && + pair->mXValue.GetUnit() != eCSSUnit_Inherit && + pair->mXValue.GetUnit() != eCSSUnit_Initial && + pair->mXValue.GetUnit() != eCSSUnit_Enumerated)) { + // Only output a Y value if it's different from the X value, // or if it's a background-position value other than 'initial' - // or 'inherit' or if it's a -moz-transform-origin value other - // than 'initial' or 'inherit'. + // or 'inherit', or if it's a -moz-transform-origin value other + // than 'initial' or 'inherit', or if it's a -moz-background-size + // value other than 'initial' or 'inherit' or 'contain' or 'cover'. aResult.Append(PRUnichar(' ')); AppendCSSValueToString(aProperty, pair->mYValue, aResult); } @@ -772,6 +777,8 @@ nsCSSDeclaration::GetValue(nsCSSProperty aProperty, * data->ValueListStorageFor(eCSSProperty__moz_background_clip); const nsCSSValueList *origin = * data->ValueListStorageFor(eCSSProperty__moz_background_origin); + const nsCSSValuePairList *size = + * data->ValuePairListStorageFor(eCSSProperty__moz_background_size); for (;;) { AppendCSSValueToString(eCSSProperty_background_image, image->mValue, aValue); @@ -800,7 +807,7 @@ nsCSSDeclaration::GetValue(nsCSSProperty aProperty, // support for content-box on background-clip. PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_BORDER == NS_STYLE_BG_ORIGIN_BORDER); - PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_PADDING == + PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_PADDING == NS_STYLE_BG_ORIGIN_PADDING); // PR_STATIC_ASSERT(NS_STYLE_BG_CLIP_CONTENT == /* does not exist */ // NS_STYLE_BG_ORIGIN_CONTENT); @@ -824,16 +831,17 @@ nsCSSDeclaration::GetValue(nsCSSProperty aProperty, position = position->mNext; clip = clip->mNext; origin = origin->mNext; + size = size->mNext; if (!image) { - if (repeat || attachment || position || clip || origin) { + if (repeat || attachment || position || clip || origin || size) { // Uneven length lists, so can't be serialized as shorthand. aValue.Truncate(); return NS_OK; } break; } - if (!repeat || !attachment || !position || !clip || !origin) { + if (!repeat || !attachment || !position || !clip || !origin || !size) { // Uneven length lists, so can't be serialized as shorthand. aValue.Truncate(); return NS_OK; diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 082933ccfc5..85e26b6cef7 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -241,11 +241,13 @@ CSS_KEY(code, code) CSS_KEY(col-resize, col_resize) CSS_KEY(collapse, collapse) CSS_KEY(condensed, condensed) +CSS_KEY(contain, contain) CSS_KEY(content, content) CSS_KEY(content-box, content_box) CSS_KEY(context-menu, context_menu) CSS_KEY(continuous, continuous) CSS_KEY(copy, copy) +CSS_KEY(cover, cover) CSS_KEY(crop, crop) CSS_KEY(cross, cross) CSS_KEY(crosshair, crosshair) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index e30a329af8b..6ac64e3691d 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -26,6 +26,7 @@ * Boris Zbarsky * Mats Palmgren * Christian Biesinger + * Jeff Walden * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), @@ -407,6 +408,7 @@ protected: nsCSSValuePair mPosition; nsCSSValue mClip; nsCSSValue mOrigin; + nsCSSValuePair mSize; // The background-color is set as a side-effect, and if so, mLastItem // is set to true. PRBool mLastItem; @@ -421,6 +423,8 @@ protected: PRBool ParseBackgroundList(nsCSSProperty aPropID); // a single value prop-id PRBool ParseBackgroundPosition(); PRBool ParseBoxPositionValues(nsCSSValuePair& aOut); + PRBool ParseBackgroundSize(); + PRBool ParseBackgroundSizeValues(nsCSSValuePair& aOut); PRBool ParseBorderColor(); PRBool ParseBorderColors(nsCSSValueList** aResult, nsCSSProperty aProperty); @@ -5061,6 +5065,8 @@ CSSParserImpl::ParseProperty(nsCSSProperty aPropID) case eCSSProperty__moz_background_origin: case eCSSProperty_background_repeat: return ParseBackgroundList(aPropID); + case eCSSProperty__moz_background_size: + return ParseBackgroundSize(); case eCSSProperty_border: return ParseBorderSide(kBorderTopIDs, PR_TRUE); case eCSSProperty_border_color: @@ -5341,6 +5347,7 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, case eCSSProperty_border_start: case eCSSProperty_border_top: case eCSSProperty_border_width: + case eCSSProperty__moz_background_size: case eCSSProperty__moz_border_radius: case eCSSProperty__moz_border_radius_topLeft: case eCSSProperty__moz_border_radius_topRight: @@ -5986,6 +5993,7 @@ CSSParserImpl::ParseBackground() BackgroundItem bgitem; nsCSSValuePairList *positionHead = nsnull, **positionTail = &positionHead; + nsCSSValuePairList *sizeHead = nsnull, **sizeTail = &sizeHead; static const BackgroundItemSimpleValueInfo simpleValues[] = { { &BackgroundItem::mImage, eCSSProperty_background_image }, { &BackgroundItem::mRepeat, eCSSProperty_background_repeat }, @@ -6003,6 +6011,7 @@ CSSParserImpl::ParseBackground() if (!ParseBackgroundItem(bgitem, !positionHead)) { break; } + nsCSSValuePairList *positionItem = new nsCSSValuePairList; if (!positionItem) { mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); @@ -6013,6 +6022,16 @@ CSSParserImpl::ParseBackground() *positionTail = positionItem; positionTail = &positionItem->mNext; + nsCSSValuePairList *sizeItem = new nsCSSValuePairList; + if (!sizeItem) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + break; + } + sizeItem->mXValue = bgitem.mSize.mXValue; + sizeItem->mYValue = bgitem.mSize.mYValue; + *sizeTail = sizeItem; + sizeTail = &sizeItem->mNext; + PRBool fail = PR_FALSE; for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { nsCSSValueList *item = new nsCSSValueList; @@ -6037,6 +6056,7 @@ CSSParserImpl::ParseBackground() } mTempData.mColor.mBackPosition = positionHead; + mTempData.mColor.mBackSize = sizeHead; for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { nsCSSValueList **source = static_cast( mTempData.PropertyAt(simpleValues[i].propID)); @@ -6049,9 +6069,11 @@ CSSParserImpl::ParseBackground() mTempData.SetPropertyBit(eCSSProperty_background_position); mTempData.SetPropertyBit(eCSSProperty__moz_background_clip); mTempData.SetPropertyBit(eCSSProperty__moz_background_origin); + mTempData.SetPropertyBit(eCSSProperty__moz_background_size); return PR_TRUE; } delete positionHead; + delete sizeHead; for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(simpleValues); ++i) { delete simpleHeads[i]; } @@ -6073,6 +6095,8 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem, aItem.mPosition.mYValue.SetPercentValue(0.0f); aItem.mClip.SetIntValue(NS_STYLE_BG_CLIP_BORDER, eCSSUnit_Enumerated); aItem.mOrigin.SetIntValue(NS_STYLE_BG_ORIGIN_PADDING, eCSSUnit_Enumerated); + aItem.mSize.mXValue.SetAutoValue(); + aItem.mSize.mYValue.SetAutoValue(); aItem.mLastItem = PR_FALSE; PRBool haveColor = PR_FALSE, @@ -6113,6 +6137,8 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem, aItem.mPosition.SetBothValuesTo(val); aItem.mClip = val; aItem.mOrigin = val; + aItem.mSize.mXValue = val; + aItem.mSize.mYValue = val; aItem.mLastItem = PR_TRUE; haveSomething = PR_TRUE; break; @@ -6160,9 +6186,9 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem, // support for content-box on background-clip. } else if (nsCSSProps::FindKeyword(keyword, nsCSSProps::kBackgroundClipKTable, dummy)) { - // For now, we use the background-clip table, because we don't - // support 'content' on background-clip. But that's dangerous - // if we eventually support no-clip. + // For now, we use the background-clip table rather than have a special + // background-origin table, because we don't support 'content-box' on + // background-origin. NS_ASSERTION( nsCSSProps::kBackgroundClipKTable[0] == eCSSKeyword_border && nsCSSProps::kBackgroundClipKTable[2] == eCSSKeyword_padding && @@ -6230,7 +6256,8 @@ CSSParserImpl::ParseBackgroundItem(CSSParserImpl::BackgroundItem& aItem, return haveSomething; } -// This function is very similar to ParseBackgroundPosition. +// This function is very similar to ParseBackgroundPosition and +// ParseBackgroundSize. PRBool CSSParserImpl::ParseBackgroundList(nsCSSProperty aPropID) { @@ -6271,7 +6298,7 @@ CSSParserImpl::ParseBackgroundList(nsCSSProperty aPropID) return PR_FALSE; } -// This function is very similar to ParseBackgroundList. +// This function is very similar to ParseBackgroundList and ParseBackgroundSize. PRBool CSSParserImpl::ParseBackgroundPosition() { @@ -6316,7 +6343,7 @@ CSSParserImpl::ParseBackgroundPosition() * values corresponding to percentages of the box, raw offsets, or keywords * like "top," "left center," etc. * - * @param aOut The nsCSSValuePair where to place the result. + * @param aOut The nsCSSValuePair in which to place the result. * @return Whether or not the operation succeeded. */ PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut) @@ -6398,6 +6425,90 @@ PRBool CSSParserImpl::ParseBoxPositionValues(nsCSSValuePair &aOut) return PR_TRUE; } +// This function is very similar to ParseBackgroundList and +// ParseBackgroundPosition. +PRBool +CSSParserImpl::ParseBackgroundSize() +{ + nsCSSValuePair valuePair; + nsCSSValuePairList *head = nsnull, **tail = &head; + if (ParseVariant(valuePair.mXValue, VARIANT_INHERIT, nsnull)) { + // 'initial' and 'inherit' stand alone, no second value. + head = new nsCSSValuePairList; + if (!head) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + return PR_FALSE; + } + head->mXValue = valuePair.mXValue; + head->mYValue.Reset(); + mTempData.mColor.mBackSize = head; + mTempData.SetPropertyBit(eCSSProperty__moz_background_size); + return ExpectEndProperty(); + } + + for (;;) { + if (!ParseBackgroundSizeValues(valuePair)) { + break; + } + nsCSSValuePairList *item = new nsCSSValuePairList; + if (!item) { + mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY); + break; + } + item->mXValue = valuePair.mXValue; + item->mYValue = valuePair.mYValue; + *tail = item; + tail = &item->mNext; + if (ExpectSymbol(',', PR_TRUE)) { + continue; + } + if (!ExpectEndProperty()) { + break; + } + mTempData.mColor.mBackSize = head; + mTempData.SetPropertyBit(eCSSProperty__moz_background_size); + return PR_TRUE; + } + delete head; + return PR_FALSE; +} + +/** + * Parses two values that correspond to lengths for the -moz-background-size + * property. These can be one or two lengths (or the 'auto' keyword) or + * percentages corresponding to the element's dimensions or the single keywords + * 'contain' or 'cover'. 'initial' and 'inherit' must be handled by the caller + * if desired. + * + * @param aOut The nsCSSValuePair in which to place the result. + * @return Whether or not the operation succeeded. + */ +PRBool CSSParserImpl::ParseBackgroundSizeValues(nsCSSValuePair &aOut) +{ + // First try a percentage or a length value + nsCSSValue &xValue = aOut.mXValue, + &yValue = aOut.mYValue; + if (ParseNonNegativeVariant(xValue, VARIANT_LP | VARIANT_AUTO, nsnull)) { + // We have one percentage/length/auto. Get the optional second + // percentage/length/keyword. + if (ParseNonNegativeVariant(yValue, VARIANT_LP | VARIANT_AUTO, nsnull)) { + // We have a second percentage/length/auto. + return PR_TRUE; + } + + // If only one percentage or length value is given, it sets the + // horizontal size only, and the vertical size will be as if by 'auto'. + yValue.SetAutoValue(); + return PR_TRUE; + } + + // Now address 'contain' and 'cover'. + if (!ParseEnum(xValue, nsCSSProps::kBackgroundSizeKTable)) + return PR_FALSE; + yValue.Reset(); + return PR_TRUE; +} + PRBool CSSParserImpl::ParseBorderColor() { diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index baccdb9522a..32da6d747e6 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -470,6 +470,16 @@ CSS_PROP_BACKGROUND( mBackRepeat, eCSSType_ValueList, kBackgroundRepeatKTable) +CSS_PROP_BACKGROUND( + -moz-background-size, + _moz_background_size, + MozBackgroundSize, + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_VALUE_LIST_USES_COMMAS, + Color, + mBackSize, + eCSSType_ValuePairList, + kBackgroundSizeKTable) CSS_PROP_DISPLAY( -moz-binding, binding, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 71aca561afd..161aefc6e8d 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -527,6 +527,12 @@ const PRInt32 nsCSSProps::kBackgroundRepeatKTable[] = { eCSSKeyword_UNKNOWN,-1 }; +const PRInt32 nsCSSProps::kBackgroundSizeKTable[] = { + eCSSKeyword_contain, NS_STYLE_BG_SIZE_CONTAIN, + eCSSKeyword_cover, NS_STYLE_BG_SIZE_COVER, + eCSSKeyword_UNKNOWN,-1 +}; + const PRInt32 nsCSSProps::kBorderCollapseKTable[] = { eCSSKeyword_collapse, NS_STYLE_BORDER_COLLAPSE, eCSSKeyword_separate, NS_STYLE_BORDER_SEPARATE, @@ -1502,6 +1508,7 @@ static const nsCSSProperty gBackgroundSubpropTable[] = { eCSSProperty_background_position, eCSSProperty__moz_background_clip, eCSSProperty__moz_background_origin, + eCSSProperty__moz_background_size, eCSSProperty_UNKNOWN }; diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index 4a4a04c96a5..7bd5836cc6f 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -170,6 +170,7 @@ public: static const PRInt32 kBackgroundOriginKTable[]; static const PRInt32 kBackgroundPositionKTable[]; static const PRInt32 kBackgroundRepeatKTable[]; + static const PRInt32 kBackgroundSizeKTable[]; static const PRInt32 kBorderCollapseKTable[]; static const PRInt32 kBorderColorKTable[]; static const PRInt32 kBorderImageKTable[]; diff --git a/layout/style/nsCSSStruct.cpp b/layout/style/nsCSSStruct.cpp index 71805fa2f91..05e413b518b 100644 --- a/layout/style/nsCSSStruct.cpp +++ b/layout/style/nsCSSStruct.cpp @@ -110,6 +110,7 @@ nsCSSColor::nsCSSColor(void) , mBackRepeat(nsnull) , mBackAttachment(nsnull) , mBackPosition(nsnull) + , mBackSize(nsnull) , mBackClip(nsnull) , mBackOrigin(nsnull) { @@ -124,6 +125,7 @@ nsCSSColor::~nsCSSColor(void) delete mBackRepeat; delete mBackAttachment; delete mBackPosition; + delete mBackSize; delete mBackClip; delete mBackOrigin; } diff --git a/layout/style/nsCSSStruct.h b/layout/style/nsCSSStruct.h index a928cdd325c..cb617f35e9c 100644 --- a/layout/style/nsCSSStruct.h +++ b/layout/style/nsCSSStruct.h @@ -312,6 +312,7 @@ struct nsCSSColor : public nsCSSStruct { nsCSSValueList* mBackRepeat; nsCSSValueList* mBackAttachment; nsCSSValuePairList* mBackPosition; + nsCSSValuePairList* mBackSize; nsCSSValueList* mBackClip; nsCSSValueList* mBackOrigin; nsCSSValue mBackInlinePolicy; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index b63091e582f..c299ffa80d5 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1209,7 +1209,7 @@ nsComputedDOMStyle::GetBackgroundList(PRUint8 nsStyleBackground::Layer::* aMembe delete valueList; return NS_ERROR_OUT_OF_MEMORY; } - val->SetIdent(nsCSSProps::ValueToKeywordEnum(bg->mLayers[i].*aMember, + val->SetIdent(nsCSSProps::ValueToKeywordEnum(bg->mLayers[i].*aMember, aTable)); } @@ -1358,6 +1358,82 @@ nsComputedDOMStyle::GetBackgroundRepeat(nsIDOMCSSValue** aValue) aValue); } +nsresult +nsComputedDOMStyle::GetMozBackgroundSize(nsIDOMCSSValue** aValue) +{ + const nsStyleBackground* bg = GetStyleBackground(); + + nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE); + NS_ENSURE_TRUE(valueList, NS_ERROR_OUT_OF_MEMORY); + + for (PRUint32 i = 0, i_end = bg->mSizeCount; i < i_end; ++i) { + const nsStyleBackground::Size &size = bg->mLayers[i].mSize; + + switch (size.mWidthType) { + case nsStyleBackground::Size::eContain: + case nsStyleBackground::Size::eCover: { + NS_ABORT_IF_FALSE(size.mWidthType == size.mHeightType, + "unsynced types"); + nsCSSKeyword keyword = size.mWidthType == nsStyleBackground::Size::eContain + ? eCSSKeyword_contain + : eCSSKeyword_cover; + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + if (!val || !valueList->AppendCSSValue(val)) { + delete valueList; + delete val; + return NS_ERROR_OUT_OF_MEMORY; + } + val->SetIdent(keyword); + break; + } + default: { + nsDOMCSSValueList *itemList = GetROCSSValueList(PR_FALSE); + if (!itemList || !valueList->AppendCSSValue(itemList)) { + delete valueList; + delete itemList; + return NS_ERROR_OUT_OF_MEMORY; + } + + nsROCSSPrimitiveValue* valX = GetROCSSPrimitiveValue(); + nsROCSSPrimitiveValue* valY = GetROCSSPrimitiveValue(); + if (!valX || !itemList->AppendCSSValue(valX)) { + delete valueList; + delete valX; + return NS_ERROR_OUT_OF_MEMORY; + } + if (!valY || !itemList->AppendCSSValue(valY)) { + delete valueList; + delete valY; + return NS_ERROR_OUT_OF_MEMORY; + } + + if (size.mWidthType == nsStyleBackground::Size::eAuto) { + valX->SetIdent(eCSSKeyword_auto); + } else if (size.mWidthType == nsStyleBackground::Size::ePercentage) { + valX->SetPercent(size.mWidth.mFloat); + } else { + NS_ABORT_IF_FALSE(size.mWidthType == nsStyleBackground::Size::eLength, + "bad mWidthType"); + valX->SetAppUnits(size.mWidth.mCoord); + } + + if (size.mHeightType == nsStyleBackground::Size::eAuto) { + valY->SetIdent(eCSSKeyword_auto); + } else if (size.mHeightType == nsStyleBackground::Size::ePercentage) { + valY->SetPercent(size.mHeight.mFloat); + } else { + NS_ABORT_IF_FALSE(size.mHeightType == nsStyleBackground::Size::eLength, + "bad mHeightType"); + valY->SetAppUnits(size.mHeight.mCoord); + } + break; + } + } + } + + return CallQueryInterface(valueList, aValue); +} + nsresult nsComputedDOMStyle::GetPadding(nsIDOMCSSValue** aValue) { @@ -4216,6 +4292,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength) COMPUTED_STYLE_MAP_ENTRY(_moz_background_clip, BackgroundClip), COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy), COMPUTED_STYLE_MAP_ENTRY(_moz_background_origin, BackgroundOrigin), + COMPUTED_STYLE_MAP_ENTRY(_moz_background_size, MozBackgroundSize), COMPUTED_STYLE_MAP_ENTRY(binding, Binding), COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors, BorderBottomColors), COMPUTED_STYLE_MAP_ENTRY(border_image, BorderImage), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 178d7594ae4..7e8bf8fc710 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -175,6 +175,7 @@ private: nsresult GetBackgroundClip(nsIDOMCSSValue** aValue); nsresult GetBackgroundInlinePolicy(nsIDOMCSSValue** aValue); nsresult GetBackgroundOrigin(nsIDOMCSSValue** aValue); + nsresult GetMozBackgroundSize(nsIDOMCSSValue** aValue); /* Padding properties */ nsresult GetPadding(nsIDOMCSSValue** aValue); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index b02f42382a2..47625d13eab 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -1446,6 +1446,7 @@ nsRuleNode::GetBackgroundData(nsStyleContext* aContext) colorData.mBackRepeat = nsnull; colorData.mBackAttachment = nsnull; colorData.mBackPosition = nsnull; + colorData.mBackSize = nsnull; colorData.mBackClip = nsnull; colorData.mBackOrigin = nsnull; @@ -3871,6 +3872,91 @@ struct BackgroundItemComputer }; +struct BackgroundSizeAxis { + nsCSSValue nsCSSValuePairList::* specified; + nsStyleBackground::Size::Dimension nsStyleBackground::Size::* result; + PRUint8 nsStyleBackground::Size::* type; +}; + +static const BackgroundSizeAxis gBGSizeAxes[] = { + { &nsCSSValuePairList::mXValue, + &nsStyleBackground::Size::mWidth, + &nsStyleBackground::Size::mWidthType }, + { &nsCSSValuePairList::mYValue, + &nsStyleBackground::Size::mHeight, + &nsStyleBackground::Size::mHeightType } +}; + +NS_SPECIALIZE_TEMPLATE +struct BackgroundItemComputer +{ + static void ComputeValue(nsStyleContext* aStyleContext, + const nsCSSValuePairList* aSpecifiedValue, + nsStyleBackground::Size& aComputedValue, + PRBool& aCanStoreInRuleTree) + { + nsStyleBackground::Size &size = aComputedValue; + for (const BackgroundSizeAxis *axis = gBGSizeAxes, + *axis_end = gBGSizeAxes + NS_ARRAY_LENGTH(gBGSizeAxes); + axis != axis_end; ++axis) { + const nsCSSValue &specified = aSpecifiedValue->*(axis->specified); + if (eCSSUnit_Auto == specified.GetUnit()) { + size.*(axis->type) = nsStyleBackground::Size::eAuto; + } + else if (eCSSUnit_Enumerated == specified.GetUnit()) { + PR_STATIC_ASSERT(nsStyleBackground::Size::eContain == + NS_STYLE_BG_SIZE_CONTAIN); + PR_STATIC_ASSERT(nsStyleBackground::Size::eCover == + NS_STYLE_BG_SIZE_COVER); + NS_ABORT_IF_FALSE(specified.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || + specified.GetIntValue() == NS_STYLE_BG_SIZE_COVER, + "invalid enumerated value for size coordinate"); + size.*(axis->type) = specified.GetIntValue(); + } + else if (eCSSUnit_Null == specified.GetUnit()) { + NS_ABORT_IF_FALSE(axis == gBGSizeAxes + 1, + "null allowed only as height value, and only " + "for contain/cover/initial/inherit"); +#ifdef DEBUG + { + const nsCSSValue &widthValue = aSpecifiedValue->mXValue; + NS_ABORT_IF_FALSE(widthValue.GetUnit() != eCSSUnit_Inherit && + widthValue.GetUnit() != eCSSUnit_Initial, + "initial/inherit should already have been handled"); + NS_ABORT_IF_FALSE(widthValue.GetUnit() == eCSSUnit_Enumerated && + (widthValue.GetIntValue() == NS_STYLE_BG_SIZE_CONTAIN || + widthValue.GetIntValue() == NS_STYLE_BG_SIZE_COVER), + "null height value not corresponding to allowable " + "non-null width value"); + } +#endif + size.*(axis->type) = size.mWidthType; + } + else if (eCSSUnit_Percent == specified.GetUnit()) { + (size.*(axis->result)).mFloat = specified.GetPercentValue(); + size.*(axis->type) = nsStyleBackground::Size::ePercentage; + } + else { + NS_ABORT_IF_FALSE(specified.IsLengthUnit(), "unexpected unit"); + (size.*(axis->result)).mCoord = + CalcLength(specified, aStyleContext, aStyleContext->PresContext(), + aCanStoreInRuleTree); + size.*(axis->type) = nsStyleBackground::Size::eLength; + } + } + + NS_ABORT_IF_FALSE(size.mWidthType < nsStyleBackground::Size::eDimensionType_COUNT, + "bad width type"); + NS_ABORT_IF_FALSE(size.mHeightType < nsStyleBackground::Size::eDimensionType_COUNT, + "bad height type"); + NS_ABORT_IF_FALSE((size.mWidthType != nsStyleBackground::Size::eContain && + size.mWidthType != nsStyleBackground::Size::eCover) || + size.mWidthType == size.mHeightType, + "contain/cover apply to both dimensions or to neither"); + } +}; + + template static void SetBackgroundList(nsStyleContext* aStyleContext, @@ -4018,6 +4104,15 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, bg->mPositionCount, maxItemCount, rebuild, canStoreInRuleTree); + // background-size: enum, length, auto, inherit, initial [pair list] + nsStyleBackground::Size initialSize; + initialSize.SetInitialValues(); + SetBackgroundList(aContext, colorData.mBackSize, bg->mLayers, + parentBG->mLayers, &nsStyleBackground::Layer::mSize, + initialSize, parentBG->mSizeCount, + bg->mSizeCount, maxItemCount, rebuild, + canStoreInRuleTree); + if (rebuild) { // Delete any extra items. We need to keep layers in which any // property was specified. @@ -4036,6 +4131,8 @@ nsRuleNode::ComputeBackgroundData(void* aStartStruct, bg->mOriginCount, fillCount); FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mPosition, bg->mPositionCount, fillCount); + FillBackgroundList(bg->mLayers, &nsStyleBackground::Layer::mSize, + bg->mSizeCount, fillCount); } COMPUTE_END_RESET(Background, bg) @@ -5750,6 +5847,7 @@ nsRuleNode::HasAuthorSpecifiedRules(nsStyleContext* aStyleContext, colorData.mBackPosition = nsnull; colorData.mBackClip = nsnull; colorData.mBackOrigin = nsnull; + colorData.mBackSize = nsnull; if (ruleData.mLevel == nsStyleSet::eAgentSheet || ruleData.mLevel == nsStyleSet::eUserSheet) { diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 49eb59a0043..20ee637a521 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -1217,6 +1217,7 @@ nsStyleBackground::nsStyleBackground() , mRepeatCount(1) , mPositionCount(1) , mImageCount(1) + , mSizeCount(1) , mBackgroundColor(NS_RGBA(0, 0, 0, 0)) , mBackgroundInlinePolicy(NS_STYLE_BG_INLINE_POLICY_CONTINUOUS) { @@ -1232,6 +1233,7 @@ nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) , mRepeatCount(aSource.mRepeatCount) , mPositionCount(aSource.mPositionCount) , mImageCount(aSource.mImageCount) + , mSizeCount(aSource.mSizeCount) , mLayers(aSource.mLayers) // deep copy , mBackgroundColor(aSource.mBackgroundColor) , mBackgroundInlinePolicy(aSource.mBackgroundInlinePolicy) @@ -1246,6 +1248,7 @@ nsStyleBackground::nsStyleBackground(const nsStyleBackground& aSource) mRepeatCount = PR_MAX(mRepeatCount, count); mPositionCount = PR_MAX(mPositionCount, count); mImageCount = PR_MAX(mImageCount, count); + mSizeCount = PR_MAX(mSizeCount, count); } } @@ -1303,6 +1306,46 @@ nsStyleBackground::Position::SetInitialValues() mYIsPercent = PR_TRUE; } +void +nsStyleBackground::Size::SetInitialValues() +{ + mWidthType = mHeightType = eAuto; +} + +PRBool +nsStyleBackground::Size::operator==(const Size& aOther) const +{ + NS_ABORT_IF_FALSE(mWidthType < eDimensionType_COUNT, + "bad mWidthType for this"); + NS_ABORT_IF_FALSE(mHeightType < eDimensionType_COUNT, + "bad mHeightType for this"); + NS_ABORT_IF_FALSE(aOther.mWidthType < eDimensionType_COUNT, + "bad mWidthType for aOther"); + NS_ABORT_IF_FALSE(aOther.mHeightType < eDimensionType_COUNT, + "bad mHeightType for aOther"); + + if (mWidthType != aOther.mWidthType || mHeightType != aOther.mHeightType) + return PR_FALSE; + + if (mWidthType == ePercentage) { + if (mWidth.mFloat != aOther.mWidth.mFloat) + return PR_FALSE; + } else if (mWidthType == eLength) { + if (mWidth.mCoord != aOther.mWidth.mCoord) + return PR_FALSE; + } + + if (mHeightType == ePercentage) { + if (mHeight.mFloat != aOther.mHeight.mFloat) + return PR_FALSE; + } else if (mHeightType == eLength) { + if (mHeight.mCoord != aOther.mHeight.mCoord) + return PR_FALSE; + } + + return PR_TRUE; +} + nsStyleBackground::Layer::Layer() { } @@ -1319,6 +1362,7 @@ nsStyleBackground::Layer::SetInitialValues() mOrigin = NS_STYLE_BG_ORIGIN_PADDING; mRepeat = NS_STYLE_BG_REPEAT_XY; mPosition.SetInitialValues(); + mSize.SetInitialValues(); mImage = nsnull; } @@ -1329,6 +1373,7 @@ PRBool nsStyleBackground::Layer::operator==(const Layer& aOther) const mOrigin == aOther.mOrigin && mRepeat == aOther.mRepeat && mPosition == aOther.mPosition && + mSize == aOther.mSize && EqualImages(mImage, aOther.mImage); } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index 13a1fd3179b..c8fff5e98ee 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -194,6 +194,40 @@ struct nsStyleBackground { } }; + struct Size; + friend struct Size; + struct Size { + typedef union { + nscoord mCoord; // for lengths + float mFloat; // for percents + } Dimension; + Dimension mWidth, mHeight; + + enum DimensionType { + // If one of mWidth and mHeight is eContain or eCover, then both are. + // Also, these two values must equal the corresponding values in + // kBackgroundSizeKTable. + eContain, eCover, + + eAuto, + ePercentage, + eLength, + eDimensionType_COUNT + }; + PRUint8 mWidthType, mHeightType; + + // Initialize nothing + Size() {} + + // Initialize to initial values + void SetInitialValues(); + + PRBool operator==(const Size& aOther) const; + PRBool operator!=(const Size& aOther) const { + return !(*this == aOther); + } + }; + struct Layer; friend struct Layer; struct Layer { @@ -203,6 +237,7 @@ struct nsStyleBackground { PRUint8 mRepeat; // [reset] See nsStyleConsts.h Position mPosition; // [reset] nsCOMPtr mImage; // [reset] + Size mSize; // [reset] // Initializes only mImage Layer(); @@ -225,7 +260,8 @@ struct nsStyleBackground { mOriginCount, mRepeatCount, mPositionCount, - mImageCount; + mImageCount, + mSizeCount; // Layers are stored in an array, matching the top-to-bottom order in // which they are specified in CSS. The number of layers to be used // should come from the background-image property. We create @@ -234,7 +270,7 @@ struct nsStyleBackground { // callers in layout care about (which is also the one whose // background-clip applies to the background-color) may not be last // layer. In layers below the bottom layer, properties will be - // unitialized unless their count, above, indicates that they are + // uninitialized unless their count, above, indicates that they are // present. nsAutoTArray mLayers; diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 684cf658c36..420b0955676 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -100,6 +100,14 @@ var gCSSProperties = { other_values: [ "border", "content", "border, padding", "padding, padding, padding", "border, border" ], invalid_values: [ "margin", "padding padding" ] }, + "-moz-background-size": { + domProp: "MozBackgroundSize", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto", "auto auto" ], + other_values: [ "contain", "cover", "100px auto", "auto 100px", "100% auto", "auto 100%", "25% 50px", "3em 40%" ], + invalid_values: [ "contain contain", "cover cover", "cover auto", "auto cover", "contain cover", "cover contain", "-5px 3px", "3px -5px", "auto -5px", "-5px auto" ] + }, "-moz-binding": { domProp: "MozBinding", inherited: false, @@ -613,8 +621,8 @@ var gCSSProperties = { domProp: "background", inherited: false, type: CSS_TYPE_TRUE_SHORTHAND, - subproperties: [ "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "-moz-background-clip", "-moz-background-origin" ], - initial_values: [ "transparent", "none", "repeat", "scroll", "0% 0%", "top left", "left top", "transparent none", "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "transparent none repeat scroll top left", "left top repeat none scroll transparent"], + subproperties: [ "background-attachment", "background-color", "background-image", "background-position", "background-repeat", "-moz-background-clip", "-moz-background-origin", "-moz-background-size" ], + initial_values: [ "transparent", "none", "repeat", "scroll", "0% 0%", "top left", "left top", "transparent none", "top left none", "left top none", "none left top", "none top left", "none 0% 0%", "transparent none repeat scroll top left", "left top repeat none scroll transparent" ], other_values: [ /* without multiple backgrounds */ "green", "none green repeat scroll left top", "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==)", "repeat url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAKElEQVR42u3NQQ0AAAgEoNP+nTWFDzcoQE1udQQCgUAgEAgEAsGTYAGjxAE/G/Q2tQAAAABJRU5ErkJggg==') transparent left top scroll", "repeat-x", "repeat-y", "no-repeat", "none repeat-y transparent scroll 0% 0%", "fixed", "0% top transparent fixed repeat none", "top", "left", "50% 50%", "center", "bottom right scroll none transparent repeat", "50% transparent", "transparent 50%", "50%", From 97300665e7329ac2d4390c98d683a67ce97d8926 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Mon, 13 Jul 2009 12:19:03 -0700 Subject: [PATCH 015/175] Bug 455555 - Use asynchronous queries for places AutoComplete. This changes the location bar's AutoComplete provider to use asynchronous queries instead of synchronous ones. No more blocking the main thread FTW! r=mak r=Mardak r=dietrich --- browser/installer/unix/packages-static | 1 + browser/installer/windows/packages-static | 1 + storage/public/storage.h | 2 + toolkit/components/places/public/Makefile.in | 1 + .../places/public/mozIPlacesAutoComplete.idl | 110 ++ toolkit/components/places/src/Makefile.in | 23 +- .../components/places/src/SQLFunctions.cpp | 292 ++++ toolkit/components/places/src/SQLFunctions.h | 208 +++ .../components/places/src/nsNavHistory.cpp | 298 ++-- toolkit/components/places/src/nsNavHistory.h | 132 +- .../places/src/nsNavHistoryAutoComplete.cpp | 1276 ----------------- .../places/src/nsPlacesAutoComplete.js | 1056 ++++++++++++++ .../components/places/src/nsPlacesModule.cpp | 6 - .../tests/autocomplete/head_autocomplete.js | 9 +- .../places/tests/unit/test_000_frecency.js | 9 +- 15 files changed, 1844 insertions(+), 1580 deletions(-) create mode 100644 toolkit/components/places/public/mozIPlacesAutoComplete.idl create mode 100644 toolkit/components/places/src/SQLFunctions.cpp create mode 100644 toolkit/components/places/src/SQLFunctions.h delete mode 100644 toolkit/components/places/src/nsNavHistoryAutoComplete.cpp create mode 100644 toolkit/components/places/src/nsPlacesAutoComplete.js diff --git a/browser/installer/unix/packages-static b/browser/installer/unix/packages-static index 703a17fc21c..53661b5c25b 100644 --- a/browser/installer/unix/packages-static +++ b/browser/installer/unix/packages-static @@ -238,6 +238,7 @@ bin/components/txEXSLTRegExFunctions.js bin/components/nsLivemarkService.js bin/components/nsTaggingService.js bin/components/nsPlacesDBFlush.js +bin/components/nsPlacesAutoComplete.js bin/components/nsDefaultCLH.js bin/components/nsContentPrefService.js bin/components/nsContentDispatchChooser.js diff --git a/browser/installer/windows/packages-static b/browser/installer/windows/packages-static index fdde0d3037b..122c38956c6 100644 --- a/browser/installer/windows/packages-static +++ b/browser/installer/windows/packages-static @@ -245,6 +245,7 @@ bin\components\txEXSLTRegExFunctions.js bin\components\nsLivemarkService.js bin\components\nsTaggingService.js bin\components\nsPlacesDBFlush.js +bin\components\nsPlacesAutoComplete.js bin\components\nsDefaultCLH.js bin\components\nsContentPrefService.js bin\components\nsContentDispatchChooser.js diff --git a/storage/public/storage.h b/storage/public/storage.h index cdf828a1202..ce3877b7d7d 100644 --- a/storage/public/storage.h +++ b/storage/public/storage.h @@ -61,4 +61,6 @@ #include "mozStorageHelper.h" #include "mozStorageCID.h" +#include "mozilla/storage/Variant.h" + #endif // mozilla_storage_h_ diff --git a/toolkit/components/places/public/Makefile.in b/toolkit/components/places/public/Makefile.in index 47d4e07dedb..2c2b53b6611 100644 --- a/toolkit/components/places/public/Makefile.in +++ b/toolkit/components/places/public/Makefile.in @@ -61,6 +61,7 @@ XPIDLSRCS += \ nsITaggingService.idl \ nsPIPlacesDatabase.idl \ nsPIPlacesHistoryListenersNotifier.idl \ + mozIPlacesAutoComplete.idl \ $(NULL) endif diff --git a/toolkit/components/places/public/mozIPlacesAutoComplete.idl b/toolkit/components/places/public/mozIPlacesAutoComplete.idl new file mode 100644 index 00000000000..ef18a1fe961 --- /dev/null +++ b/toolkit/components/places/public/mozIPlacesAutoComplete.idl @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 + * ***** 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 Places code + * + * 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): + * Shawn Wilsher (Original Author) + * + * 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" + +/** + * This interface provides some constants used by the Places AutoComplete + * search provider. + */ +[scriptable, uuid(a5ae8332-333c-412a-bb02-a35df8247714)] +interface mozIPlacesAutoComplete : nsISupports +{ + ////////////////////////////////////////////////////////////////////////////// + //// Matching Constants + + /** + * Match anywhere in each searchable term. + */ + const long MATCH_ANYWHERE = 0; + + /** + * Match first on word boundaries, and if we do not get enough results, then + * match anywhere in each searchable term. + */ + const long MATCH_BOUNDARY_ANYWHERE = 1; + + /** + * Match on word boundaries in each searchable term. + */ + const long MATCH_BOUNDARY = 2; + + /** + * Match only the beginning of each search term. + */ + const long MATCH_BEGINNING = 3; + + ////////////////////////////////////////////////////////////////////////////// + //// Search Behavior Constants + + /** + * Search through history. + */ + const long BEHAVIOR_HISTORY = 1 << 0; + + /** + * Search though bookmarks. + */ + const long BEHAVIOR_BOOKMARK = 1 << 1; + + /** + * Search through tags. + */ + const long BEHAVIOR_TAG = 1 << 2; + + /** + * Search the title of pages. + */ + const long BEHAVIOR_TITLE = 1 << 3; + + /** + * Search the URL of pages. + */ + const long BEHAVIOR_URL = 1 << 4; + + /** + * Search for typed pages. + */ + const long BEHAVIOR_TYPED = 1 << 5; + + /** + * Search javascript: URLs. + */ + const long BEHAVIOR_JAVASCRIPT = 1 << 6; +}; diff --git a/toolkit/components/places/src/Makefile.in b/toolkit/components/places/src/Makefile.in index 4bdc5208f7f..8607ef2c6cd 100644 --- a/toolkit/components/places/src/Makefile.in +++ b/toolkit/components/places/src/Makefile.in @@ -87,14 +87,9 @@ CPPSRCS = \ nsMaybeWeakPtr.cpp \ nsMorkHistoryImporter.cpp \ nsPlacesModule.cpp \ + SQLFunctions.cpp \ $(NULL) -ifdef MOZ_XUL -CPPSRCS += \ - nsNavHistoryAutoComplete.cpp \ - $(NULL) -endif - EXTRA_DSO_LDOPTS += \ $(DEPTH)/db/morkreader/$(LIB_PREFIX)morkreader_s.$(LIB_SUFFIX) \ $(MOZ_UNICHARUTIL_LIBS) \ @@ -103,10 +98,18 @@ EXTRA_DSO_LDOPTS += \ LOCAL_INCLUDES += -I$(srcdir)/../../build -EXTRA_PP_COMPONENTS = nsLivemarkService.js \ - nsTaggingService.js \ - nsPlacesDBFlush.js \ - $(NULL) +EXTRA_PP_COMPONENTS = \ + nsLivemarkService.js \ + nsTaggingService.js \ + $(NULL) + +EXTRA_COMPONENTS = \ + nsPlacesDBFlush.js \ + $(NULL) + +ifdef MOZ_XUL +EXTRA_COMPONENTS += nsPlacesAutoComplete.js +endif EXTRA_JS_MODULES = \ utils.js \ diff --git a/toolkit/components/places/src/SQLFunctions.cpp b/toolkit/components/places/src/SQLFunctions.cpp new file mode 100644 index 00000000000..8ca9b7a8d9c --- /dev/null +++ b/toolkit/components/places/src/SQLFunctions.cpp @@ -0,0 +1,292 @@ +/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 Places code. + * + * 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): + * Shawn Wilsher (Original Author) + * + * 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 "mozilla/storage.h" +#include "nsString.h" +#include "nsUnicharUtils.h" +#include "nsWhitespaceTokenizer.h" +#include "nsEscape.h" +#include "mozIPlacesAutoComplete.h" +#include "SQLFunctions.h" + +using namespace mozilla::storage; + +namespace mozilla { +namespace places { + +//////////////////////////////////////////////////////////////////////////////// +//// AutoComplete Matching Function + + ////////////////////////////////////////////////////////////////////////////// + //// MatchAutoCompleteFunction + + /* static */ + nsresult + MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn) + { + nsRefPtr function(new MatchAutoCompleteFunction); + NS_ENSURE_TRUE(function, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = aDBConn->CreateFunction( + NS_LITERAL_CSTRING("autocomplete_match"), kArgIndexLength, function + ); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + + /* static */ + nsString + MatchAutoCompleteFunction::fixupURISpec(const nsDependentCString &aURISpec) + { + nsCAutoString unescapedSpec; + (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy, + unescapedSpec); + + // If this unescaped string is valid UTF-8, we'll convert it. Otherwise, + // we will simply convert our original string. + nsString fixedSpec; + if (IsUTF8(unescapedSpec)) + CopyUTF8toUTF16(unescapedSpec, fixedSpec); + else + CopyUTF8toUTF16(aURISpec, fixedSpec); + + if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("http://"))) + fixedSpec.Cut(0, 7); + else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("https://"))) + fixedSpec.Cut(0, 8); + else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("ftp://"))) + fixedSpec.Cut(0, 6); + + return fixedSpec; + } + + /* static */ + bool + MatchAutoCompleteFunction::findAnywhere(const nsDependentSubstring &aToken, + const nsAString &aSourceString) + { + return !!CaseInsensitiveFindInReadable(aToken, aSourceString); + } + + /* static */ + bool + MatchAutoCompleteFunction::findBeginning(const nsDependentSubstring &aToken, + const nsAString &aSourceString) + { + return !!StringBeginsWith(aSourceString, aToken, + nsCaseInsensitiveStringComparator()); + } + + /* static */ + bool + MatchAutoCompleteFunction::findOnBoundary(const nsDependentSubstring &aToken, + const nsAString &aSourceString) + { + // We cannot match anything if there is nothing to search. + if (aSourceString.IsEmpty()) + return false; + + // Define a const instance of this class so it is created once. + const nsCaseInsensitiveStringComparator caseInsensitiveCompare; + + const_wchar_iterator tokenStart(aToken.BeginReading()), + tokenEnd(aToken.EndReading()), + sourceStart(aSourceString.BeginReading()), + sourceEnd(aSourceString.EndReading()); + + // The start of aSourceString is considered a word boundary, so start there. + do { + // We are on a word boundary, so start by copying the iterators. + const_wchar_iterator testTokenItr(tokenStart), + testSourceItr(sourceStart); + + // Keep trying to match the token one by one until it doesn't match. + while (!caseInsensitiveCompare(*testTokenItr, *testSourceItr)) { + // We matched something, so move down one. + testTokenItr++; + testSourceItr++; + + // Matched the full token, so we are done! + if (testTokenItr == tokenEnd) + return true; + + // However, if we ran into the end of the source while matching the + // token, we will not find it. + if (testSourceItr == sourceEnd) + return false; + } + + // Always advance our starting iterator, and if we are not currently on a + // word boundary, advance to the next word boundary. + if (!isWordBoundary(ToLowerCase(*sourceStart++))) + sourceStart = nextWordBoundary(sourceStart, sourceEnd); + } while (sourceStart != sourceEnd); + + return false; + } + + /* static */ + MatchAutoCompleteFunction::const_wchar_iterator + MatchAutoCompleteFunction::nextWordBoundary(const_wchar_iterator aStart, + const_wchar_iterator aEnd) + { + while (aStart != aEnd && !isWordBoundary(*aStart)) + aStart++; + return aStart; + } + + /* static */ + bool + MatchAutoCompleteFunction::isWordBoundary(const PRUnichar &aChar) + { + // Only check lowercase alphabetic characters so we can match CamelCase + // words. This means that matches will happen after an upper-case + // character. + return !(PRUnichar('a') <= aChar && aChar <= PRUnichar('z')); + } + + /* static */ + MatchAutoCompleteFunction::searchFunctionPtr + MatchAutoCompleteFunction::getSearchFunction(PRInt32 aBehavior) + { + switch (aBehavior) { + case mozIPlacesAutoComplete::MATCH_ANYWHERE: + return findAnywhere; + case mozIPlacesAutoComplete::MATCH_BEGINNING: + return findBeginning; + case mozIPlacesAutoComplete::MATCH_BOUNDARY: + default: + return findOnBoundary; + }; + } + + NS_IMPL_THREADSAFE_ISUPPORTS1( + MatchAutoCompleteFunction, + mozIStorageFunction + ) + + ////////////////////////////////////////////////////////////////////////////// + //// mozIStorageFunction + + NS_IMETHODIMP + MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments, + nsIVariant **_result) + { + // Macro to make the code a bit cleaner and easier to read. Operates on + // searchBehavior. + PRInt32 searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior); + #define HAS_BEHAVIOR(aBitName) \ + (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName) + + nsDependentString searchString; + (void)aArguments->GetString(kArgSearchString, searchString); + nsDependentCString url; + (void)aArguments->GetUTF8String(kArgIndexURL, url); + + // We only want to filter javascript: URLs if we are not supposed to search + // for them, and the search does not start with "javascript:". + if (!HAS_BEHAVIOR(JAVASCRIPT) && + !StringBeginsWith(searchString, NS_LITERAL_STRING("javascript:")) && + StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:"))) { + NS_IF_ADDREF(*_result = new IntegerVariant(0)); + NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); + return NS_OK; + } + + PRInt32 visitCount = aArguments->AsInt32(kArgIndexVisitCount); + bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false; + bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false; + nsDependentString tags; + (void)aArguments->GetString(kArgIndexTags, tags); + + // Make sure we match all the filter requirements. If a given restriction + // is active, make sure the corresponding condition is not true. + bool matches = !( + (HAS_BEHAVIOR(HISTORY) && visitCount == 0) || + (HAS_BEHAVIOR(TYPED) && !typed) || + (HAS_BEHAVIOR(BOOKMARK) && !bookmark) || + (HAS_BEHAVIOR(TAG) && tags.IsVoid()) + ); + if (!matches) { + NS_IF_ADDREF(*_result = new IntegerVariant(0)); + NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); + return NS_OK; + } + + // Clean up our URI spec and prepare it for searching. + nsString fixedURI = fixupURISpec(url); + + // Obtain our search function. + PRInt32 matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior); + searchFunctionPtr searchFunction = getSearchFunction(matchBehavior); + + nsDependentString title; + (void)aArguments->GetString(kArgIndexTitle, title); + + // Determine if every token matches either the bookmark title, tags, page + // title, or page URL. + nsWhitespaceTokenizer tokenizer(searchString); + while (matches && tokenizer.hasMoreTokens()) { + const nsDependentSubstring &token = tokenizer.nextToken(); + + bool matchTags = searchFunction(token, tags); + bool matchTitle = searchFunction(token, title); + + // Make sure we match something in the title or tags if we have to. + matches = matchTags || matchTitle; + if (HAS_BEHAVIOR(TITLE) && !matches) + break; + + bool matchURL = searchFunction(token, fixedURI); + // If we do not match the URL when we have to, reset matches to false. + // Otherwise, keep track that we did match the current search. + if (HAS_BEHAVIOR(URL) && !matchURL) + matches = false; + else + matches = matches || matchURL; + } + + NS_IF_ADDREF(*_result = new IntegerVariant(matches ? 1 : 0)); + NS_ENSURE_TRUE(*_result, NS_ERROR_OUT_OF_MEMORY); + return NS_OK; + #undef HAS_BEHAVIOR + } + +} // namespace places +} // namespace mozilla diff --git a/toolkit/components/places/src/SQLFunctions.h b/toolkit/components/places/src/SQLFunctions.h new file mode 100644 index 00000000000..dc6e3f5d536 --- /dev/null +++ b/toolkit/components/places/src/SQLFunctions.h @@ -0,0 +1,208 @@ +/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 Places code. + * + * 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): + * Shawn Wilsher (Original Author) + * + * 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_places_SQLFunctions_h_ +#define mozilla_places_SQLFunctions_h_ + +/** + * This file contains functions that Places adds to the database handle that can + * be accessed by SQL queries. + */ + +#include "mozIStorageFunction.h" + +class mozIStorageConnection; + +namespace mozilla { +namespace places { + +//////////////////////////////////////////////////////////////////////////////// +//// AutoComplete Matching Function + +/** + * This function is used to determine if a given set of data should match an + * AutoComplete query. + * + * In SQL, you'd use it in the WHERE clause like so: + * WHERE AUTOCOMPLETE_MATCH(aSearchString, aURL, aTitle, aTags, aVisitCount, + * aTyped) + * + * @param aSearchString + * The string to compare against. + * @param aURL + * The URL to test for an AutoComplete match. + * @param aTitle + * The title to test for an AutoComplete match. + * @param aTags + * The tags to test for an AutoComplete match. + * @param aVisitCount + * The number of visits aURL has. + * @param aTyped + * Indicates if aURL is a typed URL or not. Treated as a boolean. + * @param aBookmark + * Indicates if aURL is a bookmark or not. Treated as a boolean. + * @param aMatchBehavior + * The match behavior to use for this search. + * @param aSearchBehavior + * A bitfield dictating the search behavior. + */ +class MatchAutoCompleteFunction : public mozIStorageFunction +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGEFUNCTION + + /** + * Registers the function with the specified database connection. + * + * @param aDBConn + * The database connection to register with. + */ + static nsresult create(mozIStorageConnection *aDBConn); + +private: + /** + * Argument Indexes + */ + static const PRUint32 kArgSearchString = 0; + static const PRUint32 kArgIndexURL = 1; + static const PRUint32 kArgIndexTitle = 2; + static const PRUint32 kArgIndexTags = 3; + static const PRUint32 kArgIndexVisitCount = 4; + static const PRUint32 kArgIndexTyped = 5; + static const PRUint32 kArgIndexBookmark = 6; + static const PRUint32 kArgIndexMatchBehavior = 7; + static const PRUint32 kArgIndexSearchBehavior = 8; + static const PRUint32 kArgIndexLength = 9; + + /** + * Typedefs + */ + typedef bool (*searchFunctionPtr)(const nsDependentSubstring &aToken, + const nsAString &aSourceString); + + typedef nsAString::const_char_iterator const_wchar_iterator; + + /** + * Obtains the search function to match on. + * + * @param aBehavior + * The matching behavior to use defined by one of the + * mozIPlacesAutoComplete::MATCH_* values. + * @return a pointer to the function that will perform the proper search. + */ + static searchFunctionPtr getSearchFunction(PRInt32 aBehavior); + + /** + * Searches aSourceString for aToken anywhere in the string in a case- + * insensitive way. + * + * @param aToken + * The string to search for. + * @param aSourceString + * The string to search. + * @return true if found, false otherwise. + */ + static bool findAnywhere(const nsDependentSubstring &aToken, + const nsAString &aSourceString); + + /** + * Tests if aSourceString starts with aToken. + * + * @param aToken + * The string to search for. + * @param aSourceString + * The string to search. + * @return true if found, false otherwise. + */ + static bool findBeginning(const nsDependentSubstring &aToken, + const nsAString &aSourceString); + + /** + * Tests if aToken is found on a word boundary in aSourceString. + * + * @param aToken + * The string to search for. + * @param aSourceString + * The string to search. + * @return true if found, false otherwise. + */ + static bool findOnBoundary(const nsDependentSubstring &aToken, + const nsAString &aSourceString); + + /** + * Obtains an iterator to the next word boundary as defined by isWordBoundary. + * + * @param aStart + * An iterator pointing to the start of the string. + * @param aEnd + * An iterator pointing to the end of the string. + * @return an iterator pointing to the next word boundary. + */ + static const_wchar_iterator nextWordBoundary(const_wchar_iterator aStart, + const_wchar_iterator aEnd); + /** + * Determines if aChar is a word boundary. A 'word boundary' is anything that + * is not used to build up a word from a string of characters. We are very + * conservative here because anything that we do not list will be treated as a + * word boundary. This means searching for that not-actually-a-word-boundary + * character can still be matched in the middle of a word. + * + * @param aChar + * The Unicode character to check against. + * @return true if the character is considered a word boundary, false + * otherwise. + */ + static inline bool isWordBoundary(const PRUnichar &aChar); + + /** + * Fixes a URI's spec such that it is ready to be searched. This includes + * unescaping escaped characters and removing certain specs that we do not + * care to search for. + * + * @param aURISpec + * The spec of the URI to prepare for searching. + * @return the new string to use for the URI spec. + */ + static nsString fixupURISpec(const nsDependentCString &aURISpec); +}; + +} // namespace places +} // namespace storage + +#endif // mozilla_places_SQLFunctions_h_ diff --git a/toolkit/components/places/src/nsNavHistory.cpp b/toolkit/components/places/src/nsNavHistory.cpp index 3720f11aa2f..349be7169a3 100644 --- a/toolkit/components/places/src/nsNavHistory.cpp +++ b/toolkit/components/places/src/nsNavHistory.cpp @@ -54,6 +54,7 @@ #include "nsPlacesIndexes.h" #include "nsPlacesTriggers.h" #include "nsPlacesMacros.h" +#include "SQLFunctions.h" #include "nsIArray.h" #include "nsTArray.h" @@ -89,20 +90,18 @@ #include "nsIIDNService.h" #include "nsIClassInfoImpl.h" #include "nsThreadUtils.h" - -#include "mozIStorageConnection.h" -#include "mozIStorageFunction.h" -#include "mozIStoragePendingStatement.h" -#include "mozIStorageService.h" -#include "mozIStorageStatement.h" -#include "mozIStorageValueArray.h" -#include "mozStorageCID.h" -#include "mozStorageHelper.h" -#include "mozIStorageError.h" #include "nsAppDirectoryServiceDefs.h" +#include "mozilla/storage.h" + +#ifdef MOZ_XUL +#include "nsIAutoCompleteInput.h" +#include "nsIAutoCompletePopup.h" +#endif #include "nsMathUtils.h" // for NS_ceilf() +using namespace mozilla::places; + // Microsecond timeout for "recent" events such as typed and bookmark following. // If you typed it more than this time ago, it's not recent. // This is 15 minutes m s/m us/s @@ -124,20 +123,6 @@ #define PREF_BROWSER_HISTORY_EXPIRE_DAYS_MIN "history_expire_days_min" #define PREF_BROWSER_HISTORY_EXPIRE_DAYS_MAX "history_expire_days" #define PREF_BROWSER_HISTORY_EXPIRE_SITES "history_expire_sites" -#define PREF_AUTOCOMPLETE_ENABLED "urlbar.autocomplete.enabled" -#define PREF_AUTOCOMPLETE_MATCH_BEHAVIOR "urlbar.matchBehavior" -#define PREF_AUTOCOMPLETE_FILTER_JAVASCRIPT "urlbar.filter.javascript" -#define PREF_AUTOCOMPLETE_ENABLED "urlbar.autocomplete.enabled" -#define PREF_AUTOCOMPLETE_MAX_RICH_RESULTS "urlbar.maxRichResults" -#define PREF_AUTOCOMPLETE_DEFAULT_BEHAVIOR "urlbar.default.behavior" -#define PREF_AUTOCOMPLETE_RESTRICT_HISTORY "urlbar.restrict.history" -#define PREF_AUTOCOMPLETE_RESTRICT_BOOKMARK "urlbar.restrict.bookmark" -#define PREF_AUTOCOMPLETE_RESTRICT_TAG "urlbar.restrict.tag" -#define PREF_AUTOCOMPLETE_MATCH_TITLE "urlbar.match.title" -#define PREF_AUTOCOMPLETE_MATCH_URL "urlbar.match.url" -#define PREF_AUTOCOMPLETE_RESTRICT_TYPED "urlbar.restrict.typed" -#define PREF_AUTOCOMPLETE_SEARCH_CHUNK_SIZE "urlbar.search.chunkSize" -#define PREF_AUTOCOMPLETE_SEARCH_TIMEOUT "urlbar.search.timeout" #define PREF_DB_CACHE_PERCENTAGE "history_cache_percentage" #define PREF_FRECENCY_NUM_VISITS "places.frecency.numVisits" #define PREF_FRECENCY_FIRST_BUCKET_CUTOFF "places.frecency.firstBucketCutoff" @@ -160,6 +145,8 @@ #define PREF_FRECENCY_UNVISITED_BOOKMARK_BONUS "places.frecency.unvisitedBookmarkBonus" #define PREF_FRECENCY_UNVISITED_TYPED_BONUS "places.frecency.unvisitedTypedBonus" +#define PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC "places-autocomplete-feedback-updated" + // Default (integer) value of PREF_DB_CACHE_PERCENTAGE from 0-100 // This is 6% of machine memory, giving 15MB for a user with 256MB of memory. // The most that will be used is the size of the DB file. Normal history sizes @@ -239,10 +226,6 @@ NS_INTERFACE_MAP_BEGIN(nsNavHistory) NS_INTERFACE_MAP_ENTRY(nsICharsetResolver) NS_INTERFACE_MAP_ENTRY(nsPIPlacesDatabase) NS_INTERFACE_MAP_ENTRY(nsPIPlacesHistoryListenersNotifier) -#ifdef MOZ_XUL - NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSearch) - NS_INTERFACE_MAP_ENTRY(nsIAutoCompleteSimpleResultListener) -#endif NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsINavHistoryService) NS_IMPL_QUERY_CLASSINFO(nsNavHistory) NS_INTERFACE_MAP_END @@ -366,21 +349,6 @@ const PRInt32 nsNavHistory::kGetInfoIndex_ItemId = 8; const PRInt32 nsNavHistory::kGetInfoIndex_ItemDateAdded = 9; const PRInt32 nsNavHistory::kGetInfoIndex_ItemLastModified = 10; -const PRInt32 nsNavHistory::kAutoCompleteIndex_URL = 0; -const PRInt32 nsNavHistory::kAutoCompleteIndex_Title = 1; -const PRInt32 nsNavHistory::kAutoCompleteIndex_FaviconURL = 2; -const PRInt32 nsNavHistory::kAutoCompleteIndex_ParentId = 3; -const PRInt32 nsNavHistory::kAutoCompleteIndex_BookmarkTitle = 4; -const PRInt32 nsNavHistory::kAutoCompleteIndex_Tags = 5; -const PRInt32 nsNavHistory::kAutoCompleteIndex_VisitCount = 6; -const PRInt32 nsNavHistory::kAutoCompleteIndex_Typed = 7; - -const PRInt32 nsNavHistory::kAutoCompleteBehaviorHistory = 1 << 0; -const PRInt32 nsNavHistory::kAutoCompleteBehaviorBookmark = 1 << 1; -const PRInt32 nsNavHistory::kAutoCompleteBehaviorTag = 1 << 2; -const PRInt32 nsNavHistory::kAutoCompleteBehaviorTitle = 1 << 3; -const PRInt32 nsNavHistory::kAutoCompleteBehaviorUrl = 1 << 4; -const PRInt32 nsNavHistory::kAutoCompleteBehaviorTyped = 1 << 5; static const char* gQuitApplicationGrantedMessage = "quit-application-granted"; static const char* gXpcomShutdown = "xpcom-shutdown"; @@ -424,21 +392,6 @@ nsNavHistory::nsNavHistory() : mBatchLevel(0), mNowValid(PR_FALSE), mExpireNowTimer(nsnull), mExpire(this), - mAutoCompleteEnabled(PR_TRUE), - mAutoCompleteMatchBehavior(MATCH_BOUNDARY_ANYWHERE), - mAutoCompleteMaxResults(25), - mAutoCompleteRestrictHistory(NS_LITERAL_STRING("^")), - mAutoCompleteRestrictBookmark(NS_LITERAL_STRING("*")), - mAutoCompleteRestrictTag(NS_LITERAL_STRING("+")), - mAutoCompleteMatchTitle(NS_LITERAL_STRING("#")), - mAutoCompleteMatchUrl(NS_LITERAL_STRING("@")), - mAutoCompleteRestrictTyped(NS_LITERAL_STRING("~")), - mAutoCompleteSearchChunkSize(100), - mAutoCompleteSearchTimeout(100), - mAutoCompleteDefaultBehavior(0), - mAutoCompleteCurrentBehavior(0), - mPreviousChunkOffset(-1), - mAutoCompleteFinishedSearch(PR_FALSE), mExpireDaysMin(0), mExpireDaysMax(0), mExpireSites(0), @@ -519,11 +472,6 @@ nsNavHistory::Init() rv = NS_DispatchToMainThread(completeEvent); NS_ENSURE_SUCCESS(rv, rv); -#ifdef MOZ_XUL - rv = InitAutoComplete(); - NS_ENSURE_SUCCESS(rv, rv); -#endif - // extract the last session ID so we know where to pick up. There is no index // over sessions so the naive statement "SELECT MAX(session) FROM // moz_historyvisits" won't have good performance. @@ -565,19 +513,6 @@ nsNavHistory::Init() nsCOMPtr pbi = do_QueryInterface(mPrefBranch); if (pbi) { - pbi->AddObserver(PREF_AUTOCOMPLETE_ENABLED, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_MATCH_BEHAVIOR, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_FILTER_JAVASCRIPT, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_MAX_RICH_RESULTS, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_DEFAULT_BEHAVIOR, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_RESTRICT_HISTORY, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_RESTRICT_BOOKMARK, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_RESTRICT_TAG, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_MATCH_TITLE, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_MATCH_URL, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_RESTRICT_TYPED, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_SEARCH_CHUNK_SIZE, this, PR_FALSE); - pbi->AddObserver(PREF_AUTOCOMPLETE_SEARCH_TIMEOUT, this, PR_FALSE); pbi->AddObserver(PREF_BROWSER_HISTORY_EXPIRE_DAYS_MAX, this, PR_FALSE); pbi->AddObserver(PREF_BROWSER_HISTORY_EXPIRE_DAYS_MIN, this, PR_FALSE); pbi->AddObserver(PREF_BROWSER_HISTORY_EXPIRE_SITES, this, PR_FALSE); @@ -1151,11 +1086,15 @@ nsNavHistory::InitViews() nsresult nsNavHistory::InitFunctions() { - nsresult rv; + nsCOMPtr func = + new mozStorageFunctionGetUnreversedHost; + NS_ENSURE_TRUE(func, NS_ERROR_OUT_OF_MEMORY); + nsresult rv = mDBConn->CreateFunction( + NS_LITERAL_CSTRING("get_unreversed_host"), 1, func + ); + NS_ENSURE_SUCCESS(rv, rv); - rv = mDBConn->CreateFunction( - NS_LITERAL_CSTRING("get_unreversed_host"), 1, - new mozStorageFunctionGetUnreversedHost); + rv = MatchAutoCompleteFunction::create(mDBConn); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; @@ -2146,62 +2085,6 @@ nsNavHistory::LoadPrefs(PRBool aInitializing) if (NS_FAILED(mPrefBranch->GetIntPref(PREF_BROWSER_HISTORY_EXPIRE_SITES, &mExpireSites))) mExpireSites = EXPIRATION_CAP_SITES; - -#ifdef MOZ_XUL - mPrefBranch->GetBoolPref(PREF_AUTOCOMPLETE_ENABLED, &mAutoCompleteEnabled); - - PRInt32 matchBehavior = 1; - mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_MATCH_BEHAVIOR, - &matchBehavior); - switch (matchBehavior) { - case 0: - mAutoCompleteMatchBehavior = MATCH_ANYWHERE; - break; - case 2: - mAutoCompleteMatchBehavior = MATCH_BOUNDARY; - break; - case 3: - mAutoCompleteMatchBehavior = MATCH_BEGINNING; - break; - case 1: - default: - mAutoCompleteMatchBehavior = MATCH_BOUNDARY_ANYWHERE; - break; - } - - mPrefBranch->GetBoolPref(PREF_AUTOCOMPLETE_FILTER_JAVASCRIPT, - &mAutoCompleteFilterJavascript); - mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_MAX_RICH_RESULTS, - &mAutoCompleteMaxResults); - mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_SEARCH_CHUNK_SIZE, - &mAutoCompleteSearchChunkSize); - mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_SEARCH_TIMEOUT, - &mAutoCompleteSearchTimeout); - mPrefBranch->GetIntPref(PREF_AUTOCOMPLETE_DEFAULT_BEHAVIOR, - &mAutoCompleteDefaultBehavior); - nsXPIDLCString prefStr; - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_RESTRICT_HISTORY, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteRestrictHistory); - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_RESTRICT_BOOKMARK, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteRestrictBookmark); - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_RESTRICT_TAG, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteRestrictTag); - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_MATCH_TITLE, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteMatchTitle); - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_MATCH_URL, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteMatchUrl); - mPrefBranch->GetCharPref(PREF_AUTOCOMPLETE_RESTRICT_TYPED, - getter_Copies(prefStr)); - CopyUTF8toUTF16(prefStr, mAutoCompleteRestrictTyped); - - // Clear out the search on any pref change to invalidate cached search - mCurrentSearchString = EmptyString(); -#endif // get the frecency prefs nsCOMPtr prefs(do_GetService("@mozilla.org/preferences-service;1")); @@ -5657,10 +5540,6 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic, mIdleTimer->Cancel(); mIdleTimer = nsnull; } - if (mAutoCompleteTimer) { - mAutoCompleteTimer->Cancel(); - mAutoCompleteTimer = nsnull; - } nsresult rv; nsCOMPtr prefService = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv); @@ -5682,7 +5561,6 @@ nsNavHistory::Observe(nsISupports *aSubject, const char *aTopic, NS_ENSURE_SUCCESS(rv, rv); observerService->RemoveObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC); observerService->RemoveObserver(this, gIdleDaily); - observerService->RemoveObserver(this, gAutoCompleteFeedback); observerService->RemoveObserver(this, gXpcomShutdown); observerService->RemoveObserver(this, gQuitApplicationGrantedMessage); } @@ -7746,6 +7624,131 @@ nsNavHistory::FixInvalidFrecencies() } +#ifdef MOZ_XUL + +namespace { + +// Used to notify a topic to system observers on async execute completion. +class AutoCompleteStatementCallbackNotifier : public mozIStorageStatementCallback +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_MOZISTORAGESTATEMENTCALLBACK +}; + +NS_IMPL_ISUPPORTS1(AutoCompleteStatementCallbackNotifier, + mozIStorageStatementCallback) + +NS_IMETHODIMP +AutoCompleteStatementCallbackNotifier::HandleCompletion(PRUint16 aReason) +{ + if (aReason != mozIStorageStatementCallback::REASON_FINISHED) + return NS_ERROR_UNEXPECTED; + + nsresult rv; + nsCOMPtr observerService = + do_GetService("@mozilla.org/observer-service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + rv = observerService->NotifyObservers(nsnull, + PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC, + nsnull); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +AutoCompleteStatementCallbackNotifier::HandleError(mozIStorageError *aError) +{ +#ifdef DEBUG + PRInt32 result; + nsresult rv = aError->GetResult(&result); + NS_ENSURE_SUCCESS(rv, rv); + nsCAutoString message; + rv = aError->GetMessage(message); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString warnMsg; + warnMsg.Append("An error occured while executing an async statement: "); + warnMsg.Append(result); + warnMsg.Append(" "); + warnMsg.Append(message); + NS_WARNING(warnMsg.get()); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +AutoCompleteStatementCallbackNotifier::HandleResult(mozIStorageResultSet *aResultSet) +{ + NS_ASSERTION(PR_FALSE, "You cannot use AutoCompleteStatementCallbackNotifier to get async statements resultset"); + return NS_OK; +} + +} // anonymous namespace + +nsresult +nsNavHistory::AutoCompleteFeedback(PRInt32 aIndex, + nsIAutoCompleteController *aController) +{ + // We do not track user choices in the location bar in private browsing mode. + if (InPrivateBrowsingMode()) + return NS_OK; + + mozIStorageStatement *stmt = GetDBFeedbackIncrease(); + mozStorageStatementScoper scope(stmt); + + nsAutoString input; + nsresult rv = aController->GetSearchString(input); + NS_ENSURE_SUCCESS(rv, rv); + rv = stmt->BindStringParameter(0, input); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString url; + rv = aController->GetValueAt(aIndex, url); + NS_ENSURE_SUCCESS(rv, rv); + rv = stmt->BindStringParameter(1, url); + NS_ENSURE_SUCCESS(rv, rv); + + // We do the update asynchronously and we do not care about failures. + nsCOMPtr callback = + new AutoCompleteStatementCallbackNotifier(); + nsCOMPtr canceler; + rv = stmt->ExecuteAsync(callback, getter_AddRefs(canceler)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +mozIStorageStatement* +nsNavHistory::GetDBFeedbackIncrease() +{ + if (mDBFeedbackIncrease) + return mDBFeedbackIncrease; + + nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( + // Leverage the PRIMARY KEY (place_id, input) to insert/update entries. + "INSERT OR REPLACE INTO moz_inputhistory " + // use_count will asymptotically approach the max of 10. + "SELECT h.id, IFNULL(i.input, ?1), IFNULL(i.use_count, 0) * .9 + 1 " + "FROM moz_places_temp h " + "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = ?1 " + "WHERE url = ?2 " + "UNION ALL " + "SELECT h.id, IFNULL(i.input, ?1), IFNULL(i.use_count, 0) * .9 + 1 " + "FROM moz_places h " + "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = ?1 " + "WHERE url = ?2 " + "AND h.id NOT IN (SELECT id FROM moz_places_temp)"), + getter_AddRefs(mDBFeedbackIncrease)); + NS_ENSURE_SUCCESS(rv, nsnull); + + return mDBFeedbackIncrease; +} +#endif + + nsICollation * nsNavHistory::GetCollation() { @@ -7901,6 +7904,9 @@ nsNavHistory::GetDBBookmarkToUrlResult() nsresult nsNavHistory::FinalizeStatements() { mozIStorageStatement* stmts[] = { +#ifdef MOZ_XUL + mDBFeedbackIncrease, +#endif mDBGetURLPageInfo, mDBGetIdPageInfo, mDBRecentVisitOfURL, @@ -7920,16 +7926,6 @@ nsNavHistory::FinalizeStatements() { mDBUpdateFrecencyAndHidden, mDBGetPlaceVisitStats, mDBFullVisitCount, - mDBCurrentQuery, - mDBAutoCompleteQuery, - mDBAutoCompleteTypedQuery, - mDBAutoCompleteHistoryQuery, - mDBAutoCompleteStarQuery, - mDBAutoCompleteTagsQuery, - mDBPreviousQuery, - mDBAdaptiveQuery, - mDBKeywordQuery, - mDBFeedbackIncrease }; for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(stmts); i++) { diff --git a/toolkit/components/places/src/nsNavHistory.h b/toolkit/components/places/src/nsNavHistory.h index 74458d0b0d6..cdec11f0048 100644 --- a/toolkit/components/places/src/nsNavHistory.h +++ b/toolkit/components/places/src/nsNavHistory.h @@ -52,14 +52,6 @@ #include "nsINavHistoryService.h" #include "nsPIPlacesDatabase.h" #include "nsPIPlacesHistoryListenersNotifier.h" -#ifdef MOZ_XUL -#include "nsIAutoCompleteController.h" -#include "nsIAutoCompleteInput.h" -#include "nsIAutoCompletePopup.h" -#include "nsIAutoCompleteSearch.h" -#include "nsIAutoCompleteResult.h" -#include "nsIAutoCompleteSimpleResult.h" -#endif #include "nsIBrowserHistory.h" #include "nsICollation.h" #include "nsIGlobalHistory.h" @@ -109,8 +101,6 @@ #define PLACES_INIT_COMPLETE_EVENT_TOPIC "places-init-complete" #define PLACES_DB_LOCKED_EVENT_TOPIC "places-database-locked" -struct AutoCompleteIntermediateResult; -class AutoCompleteResultComparator; class mozIAnnotationService; class nsNavHistory; class nsNavBookmarks; @@ -118,6 +108,7 @@ class QueryKeyValuePair; class nsIEffectiveTLDService; class nsIIDNService; class PlacesSQLQueryBuilder; +class nsIAutoCompleteController; // nsNavHistory @@ -130,13 +121,7 @@ class nsNavHistory : public nsSupportsWeakReference , public nsICharsetResolver , public nsPIPlacesDatabase , public nsPIPlacesHistoryListenersNotifier -#ifdef MOZ_XUL - , public nsIAutoCompleteSearch - , public nsIAutoCompleteSimpleResultListener -#endif { - friend class AutoCompleteIntermediateResultSet; - friend class AutoCompleteResultComparator; friend class PlacesSQLQueryBuilder; public: @@ -152,10 +137,6 @@ public: NS_DECL_NSIOBSERVER NS_DECL_NSPIPLACESDATABASE NS_DECL_NSPIPLACESHISTORYLISTENERSNOTIFIER -#ifdef MOZ_XUL - NS_DECL_NSIAUTOCOMPLETESEARCH - NS_DECL_NSIAUTOCOMPLETESIMPLERESULTLISTENER -#endif /** @@ -658,124 +639,15 @@ protected: PRInt64 mLastSessionID; PRInt64 GetNewSessionID() { mLastSessionID ++; return mLastSessionID; } - // +#ifdef MOZ_XUL // AutoComplete stuff - // - static const PRInt32 kAutoCompleteIndex_URL; - static const PRInt32 kAutoCompleteIndex_Title; - static const PRInt32 kAutoCompleteIndex_FaviconURL; - static const PRInt32 kAutoCompleteIndex_ParentId; - static const PRInt32 kAutoCompleteIndex_BookmarkTitle; - static const PRInt32 kAutoCompleteIndex_Tags; - static const PRInt32 kAutoCompleteIndex_VisitCount; - static const PRInt32 kAutoCompleteIndex_Typed; - nsCOMPtr mDBCurrentQuery; // kAutoCompleteIndex_* results - nsCOMPtr mDBAutoCompleteQuery; // kAutoCompleteIndex_* results - nsCOMPtr mDBAutoCompleteTypedQuery; // kAutoCompleteIndex_* results - mozIStorageStatement* GetDBAutoCompleteHistoryQuery(); - nsCOMPtr mDBAutoCompleteHistoryQuery; // kAutoCompleteIndex_* results - mozIStorageStatement* GetDBAutoCompleteStarQuery(); - nsCOMPtr mDBAutoCompleteStarQuery; // kAutoCompleteIndex_* results - mozIStorageStatement* GetDBAutoCompleteTagsQuery(); - nsCOMPtr mDBAutoCompleteTagsQuery; // kAutoCompleteIndex_* results - nsCOMPtr mDBPreviousQuery; // kAutoCompleteIndex_* results - nsCOMPtr mDBAdaptiveQuery; // kAutoCompleteIndex_* results - nsCOMPtr mDBKeywordQuery; // kAutoCompleteIndex_* results mozIStorageStatement* GetDBFeedbackIncrease(); nsCOMPtr mDBFeedbackIncrease; - /** - * AutoComplete word matching behavior to determine if words should match on - * word boundaries or not or both. - */ - enum MatchType { - MATCH_ANYWHERE, - MATCH_BOUNDARY_ANYWHERE, - MATCH_BOUNDARY, - MATCH_BEGINNING - }; - - nsresult InitAutoComplete(); - nsresult CreateAutoCompleteQueries(); - PRBool mAutoCompleteEnabled; - MatchType mAutoCompleteMatchBehavior; - PRBool mAutoCompleteFilterJavascript; - PRInt32 mAutoCompleteMaxResults; - nsString mAutoCompleteRestrictHistory; - nsString mAutoCompleteRestrictBookmark; - nsString mAutoCompleteRestrictTag; - nsString mAutoCompleteMatchTitle; - nsString mAutoCompleteMatchUrl; - nsString mAutoCompleteRestrictTyped; - PRInt32 mAutoCompleteSearchChunkSize; - PRInt32 mAutoCompleteSearchTimeout; - nsCOMPtr mAutoCompleteTimer; - - static const PRInt32 kAutoCompleteBehaviorHistory; - static const PRInt32 kAutoCompleteBehaviorBookmark; - static const PRInt32 kAutoCompleteBehaviorTag; - static const PRInt32 kAutoCompleteBehaviorTitle; - static const PRInt32 kAutoCompleteBehaviorUrl; - static const PRInt32 kAutoCompleteBehaviorTyped; - - PRInt32 mAutoCompleteDefaultBehavior; // kAutoCompleteBehavior* bitmap - PRInt32 mAutoCompleteCurrentBehavior; // kAutoCompleteBehavior* bitmap - - // Original search string for case-sensitive usage - nsString mOrigSearchString; - // Search string and tokens for case-insensitive matching - nsString mCurrentSearchString; - nsTArray mCurrentSearchTokens; - void GenerateSearchTokens(); - void AddSearchToken(nsAutoString &aToken); - void ProcessTokensForSpecialSearch(); - -#ifdef MOZ_XUL nsresult AutoCompleteFeedback(PRInt32 aIndex, nsIAutoCompleteController *aController); - - nsCOMPtr mCurrentListener; - nsCOMPtr mCurrentResult; #endif - MatchType mCurrentMatchType; - MatchType mPreviousMatchType; - nsDataHashtable mCurrentResultURLs; - PRInt32 mCurrentChunkOffset; - PRInt32 mPreviousChunkOffset; - - nsDataHashtable mLivemarkFeedItemIds; - nsDataHashtable mLivemarkFeedURIs; - - nsresult AutoCompleteFullHistorySearch(PRBool* aHasMoreResults); - nsresult AutoCompletePreviousSearch(); - nsresult AutoCompleteAdaptiveSearch(); - nsresult AutoCompleteKeywordSearch(); - - /** - * Query type passed to AutoCompleteProcessSearch to determine what style to - * use and if results should be filtered - */ - enum QueryType { - QUERY_KEYWORD, - QUERY_FILTERED - }; - nsresult AutoCompleteProcessSearch(mozIStorageStatement* aQuery, - const QueryType aType, - PRBool *aHasMoreResults = nsnull); - PRBool AutoCompleteHasEnoughResults(); - - nsresult PerformAutoComplete(); - nsresult StartAutoCompleteTimer(PRUint32 aMilliseconds); - static void AutoCompleteTimerCallback(nsITimer* aTimer, void* aClosure); - - PRBool mAutoCompleteFinishedSearch; - void DoneSearching(PRBool aFinished); - - // Used to unescape encoded URI strings for searching - nsCOMPtr mTextURIService; - nsString FixupURIText(const nsAString &aURIText); - PRInt32 mExpireDaysMin; PRInt32 mExpireDaysMax; PRInt32 mExpireSites; diff --git a/toolkit/components/places/src/nsNavHistoryAutoComplete.cpp b/toolkit/components/places/src/nsNavHistoryAutoComplete.cpp deleted file mode 100644 index ea8f6240c22..00000000000 --- a/toolkit/components/places/src/nsNavHistoryAutoComplete.cpp +++ /dev/null @@ -1,1276 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** 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 Places code. - * - * The Initial Developer of the Original Code is - * Google Inc. - * Portions created by the Initial Developer are Copyright (C) 2005 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brett Wilson - * Joe Hewitt - * Blake Ross - * Seth Spitzer - * Dietrich Ayala - * Edward Lee - * Ehsan Akhgari - * Marco Bonardo - * - * 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 ***** */ - - -/** - * Autocomplete algorithm: - * - * Searches moz_places by frecency (in descending order) - * in chunks (mAutoCompleteSearchChunkSize). We currently - * do SQL LIKE searches of the search term in the place title, place url - * and bookmark titles (since a "place" can have multiple bookmarks) - * within in each chunk. The results are ordered by frecency. - * Note, we exclude places with no frecency (0) because - * frecency = 0 means "don't show this in autocomplete". place: queries should - * have that, as should unvisited children of livemark feeds (that aren't - * bookmarked elsewhere). - * - * But places with frecency < 0 are included, as that means that these items - * have not had their frecency calculated yet (will happen on idle). - */ - -#include "nsNavHistory.h" -#include "nsNetUtil.h" -#include "nsEscape.h" -#include "nsThreadUtils.h" - -#include "mozIStorageService.h" -#include "mozIStorageConnection.h" -#include "mozIStorageValueArray.h" -#include "mozIStorageStatement.h" -#include "mozIStorageFunction.h" -#include "mozStorageCID.h" -#include "mozStorageHelper.h" -#include "nsFaviconService.h" -#include "nsUnicharUtils.h" -#include "nsNavBookmarks.h" -#include "nsPrintfCString.h" -#include "nsILivemarkService.h" -#include "mozIStoragePendingStatement.h" -#include "mozIStorageStatementCallback.h" -#include "mozIStorageError.h" -#include "nsPlacesTables.h" - -#define NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID \ - "@mozilla.org/autocomplete/simple-result;1" - -// Helpers to get and set fields in the mAutoCompleteCurrentBehavior bitmap -#define GET_BEHAVIOR(aBitName) \ - (mAutoCompleteCurrentBehavior & kAutoCompleteBehavior##aBitName) -#define SET_BEHAVIOR(aBitName) \ - mAutoCompleteCurrentBehavior |= kAutoCompleteBehavior##aBitName - -// Helper to get a particular column with a desired name from the bookmark and -// tags table based on if we want to include tags or not. Make sure to provide -// enough buffer space for printf. -#define BOOK_TAG_FRAG(name, column, forTag) nsPrintfCString(200, ", (" \ - "SELECT %s " \ - "FROM moz_bookmarks b " \ - "JOIN moz_bookmarks t ON t.id = b.parent AND t.parent %s= ?1 " \ - "WHERE b.fk = h.id AND b.type = %d " \ - "%s) AS %s", \ - column, \ - forTag ? "" : "!", \ - nsINavBookmarksService::TYPE_BOOKMARK, \ - forTag ? "AND LENGTH(t.title) > 0" : "ORDER BY b.lastModified DESC LIMIT 1", \ - name) - -// Get three named columns from the bookmarks and tags table -#define BOOK_TAG_SQL (\ - BOOK_TAG_FRAG("parent", "b.parent", 0) + \ - BOOK_TAG_FRAG("bookmark", "b.title", 0) + \ - BOOK_TAG_FRAG("tags", "GROUP_CONCAT(t.title, ',')", 1)) - -// This separator is used as an RTL-friendly way to split the title and tags. -// It can also be used by an nsIAutoCompleteResult consumer to re-split the -// "comment" back into the title and tag. -// Use a Unichar array to avoid problems with 2-byte char strings: " \u2013 " -const PRUnichar kTitleTagsSeparatorChars[] = { ' ', 0x2013, ' ', 0 }; -#define TITLE_TAGS_SEPARATOR nsAutoString(kTitleTagsSeparatorChars) - -// This fragment is used to get best favicon for a rev_host -#define BEST_FAVICON_FOR_REVHOST( __table_name ) \ - "(SELECT f.url FROM " __table_name " " \ - "JOIN moz_favicons f ON f.id = favicon_id " \ - "WHERE rev_host = IFNULL( " \ - "(SELECT rev_host FROM moz_places_temp WHERE id = b.fk), " \ - "(SELECT rev_host FROM moz_places WHERE id = b.fk)) " \ - "ORDER BY frecency DESC LIMIT 1) " - -#define PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC "places-autocomplete-feedback-updated" - -// Used to notify a topic to system observers on async execute completion. -// Will throw on error. -class AutoCompleteStatementCallbackNotifier : public mozIStorageStatementCallback -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_MOZISTORAGESTATEMENTCALLBACK -}; - -// AutocompleteStatementCallbackNotifier -NS_IMPL_ISUPPORTS1(AutoCompleteStatementCallbackNotifier, - mozIStorageStatementCallback) - -NS_IMETHODIMP -AutoCompleteStatementCallbackNotifier::HandleCompletion(PRUint16 aReason) -{ - if (aReason != mozIStorageStatementCallback::REASON_FINISHED) - return NS_ERROR_UNEXPECTED; - - nsresult rv; - nsCOMPtr observerService = - do_GetService("@mozilla.org/observer-service;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - rv = observerService->NotifyObservers(nsnull, - PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC, - nsnull); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -NS_IMETHODIMP -AutoCompleteStatementCallbackNotifier::HandleError(mozIStorageError *aError) -{ - PRInt32 result; - nsresult rv = aError->GetResult(&result); - NS_ENSURE_SUCCESS(rv, rv); - nsCAutoString message; - rv = aError->GetMessage(message); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString warnMsg; - warnMsg.Append("An error occured while executing an async statement: "); - warnMsg.Append(result); - warnMsg.Append(" "); - warnMsg.Append(message); - NS_WARNING(warnMsg.get()); - - return NS_OK; -} - -NS_IMETHODIMP -AutoCompleteStatementCallbackNotifier::HandleResult(mozIStorageResultSet *aResultSet) -{ - NS_ASSERTION(PR_FALSE, "You cannot use AutoCompleteStatementCallbackNotifier to get async statements resultset"); - return NS_OK; -} - -void GetAutoCompleteBaseQuery(nsACString& aQuery) { -// Define common pieces of various queries -// XXX bug 412736 -// in the case of a frecency tie, break it with h.typed and h.visit_count -// which is better than nothing. but this is slow, so not doing it yet. - -// Try to reduce size of compound table since with partitioning this became -// slower. Limiting moz_places with OFFSET+LIMIT will mostly help speed -// of first chunks, that are usually most wanted. -// Can do this only if there aren't additional conditions on final resultset. - -// Note: h.frecency is selected because we need it for ordering, but will -// not be read later and we don't have an associated kAutoCompleteIndex_ - aQuery = NS_LITERAL_CSTRING( - "SELECT h.url, h.title, f.url") + BOOK_TAG_SQL + NS_LITERAL_CSTRING(", " - "h.visit_count, h.typed " - "FROM (" - "SELECT " MOZ_PLACES_COLUMNS " FROM moz_places_temp " - "UNION ALL " - "SELECT " MOZ_PLACES_COLUMNS " FROM moz_places " - "WHERE +id NOT IN (SELECT id FROM moz_places_temp) " - "ORDER BY frecency DESC LIMIT ?2 OFFSET ?3) h " - "LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id " - "WHERE h.frecency <> 0 " - "{ADDITIONAL_CONDITIONS}"); -} - -//////////////////////////////////////////////////////////////////////////////// -//// nsNavHistoryAutoComplete Helper Functions - -/** - * Returns true if the string starts with javascript: - */ -inline PRBool -StartsWithJS(const nsAString &aString) -{ - return StringBeginsWith(aString, NS_LITERAL_STRING("javascript:")); -} - -/** - * Callback function for putting URLs from a nsDataHashtable into a nsTArray. - * - * @param aKey - * The hashtable entry's key (the url) - * @param aData - * Unused data - * @param aArg - * The nsTArray pointer for collecting URLs - */ -PLDHashOperator -HashedURLsToArray(const nsAString &aKey, PRBool aData, void *aArg) -{ - // Append the current url to the array of urls - static_cast *>(aArg)->AppendElement(aKey); - return PL_DHASH_NEXT; -} - -/** - * Returns true if the unicode character is a word boundary. I.e., anything - * that *isn't* used to build up a word from a string of characters. We are - * conservative here because anything that we don't list will be treated as - * word boundary. This means searching for that not-actually-a-word-boundary - * character can still be matched in the middle of a word. - */ -inline PRBool -IsWordBoundary(const PRUnichar &aChar) -{ - // Lower-case alphabetic, so upper-case matches CamelCase. Because - // upper-case is treated as a word boundary, matches will also happen - // _after_ an upper-case character. - return !(PRUnichar('a') <= aChar && aChar <= PRUnichar('z')); -} - -/** - * Returns true if the token matches the target on a word boundary - * - * @param aToken - * Token to search for that must match on a word boundary - * @param aTarget - * Target string to search against - */ -PRBool -FindOnBoundary(const nsAString &aToken, const nsAString &aTarget) -{ - // Define a const instance of this class so it is created once - const nsCaseInsensitiveStringComparator caseInsensitiveCompare; - - // Can't match anything if there's nothing to match - if (aTarget.IsEmpty()) - return PR_FALSE; - - nsAString::const_iterator tokenStart, tokenEnd; - aToken.BeginReading(tokenStart); - aToken.EndReading(tokenEnd); - - nsAString::const_iterator targetStart, targetEnd; - aTarget.BeginReading(targetStart); - aTarget.EndReading(targetEnd); - - // Go straight into checking the token at the beginning of the target because - // the beginning is considered a word boundary - do { - // We're on a word boundary, so prepare to match by copying the iterators - nsAString::const_iterator testToken(tokenStart); - nsAString::const_iterator testTarget(targetStart); - - // Keep trying to match the token one by one until it doesn't match - while (!caseInsensitiveCompare(*testToken, *testTarget)) { - // We matched something, so move down one - testToken++; - testTarget++; - - // Matched the token! We're done! - if (testToken == tokenEnd) - return PR_TRUE; - - // If we ran into the end while matching the token, we won't find it - if (testTarget == targetEnd) - return PR_FALSE; - } - - // Unconditionally move past the current position in the target, but if - // we're not currently on a word boundary, eat up as many non-word boundary - // characters as possible -- don't kill characters if we're currently on a - // word boundary so that we can match tokens that start on a word boundary. - if (!IsWordBoundary(ToLowerCase(*targetStart++))) - while (targetStart != targetEnd && !IsWordBoundary(*targetStart)) - targetStart++; - - // If we hit the end eating up non-boundaries then boundaries, we're done - } while (targetStart != targetEnd); - - return PR_FALSE; -} - -/** - * A local wrapper to CaseInsensitiveFindInReadable that isn't overloaded - */ -inline PRBool -FindAnywhere(const nsAString &aToken, const nsAString &aTarget) -{ - return CaseInsensitiveFindInReadable(aToken, aTarget); -} - -/** - * A local wrapper to case insensitive StringBeginsWith - */ -inline PRBool -FindBeginning(const nsAString &aToken, const nsAString &aTarget) -{ - return StringBeginsWith(aTarget, aToken, nsCaseInsensitiveStringComparator()); -} - -// nsNavHistory::InitAutoComplete -nsresult -nsNavHistory::InitAutoComplete() -{ - nsresult rv = CreateAutoCompleteQueries(); - NS_ENSURE_SUCCESS(rv, rv); - - if (!mCurrentResultURLs.Init(128)) - return NS_ERROR_OUT_OF_MEMORY; - - if (!mLivemarkFeedItemIds.Init(128)) - return NS_ERROR_OUT_OF_MEMORY; - - if (!mLivemarkFeedURIs.Init(128)) - return NS_ERROR_OUT_OF_MEMORY; - - return NS_OK; -} - -// nsNavHistory::GetDBAutoCompleteHistoryQuery() -// -// Returns the auto complete statement used when autocomplete results are -// restricted to history entries. -mozIStorageStatement* -nsNavHistory::GetDBAutoCompleteHistoryQuery() -{ - if (mDBAutoCompleteHistoryQuery) - return mDBAutoCompleteHistoryQuery; - - nsCString AutoCompleteHistoryQuery; - GetAutoCompleteBaseQuery(AutoCompleteHistoryQuery); - AutoCompleteHistoryQuery.ReplaceSubstring("{ADDITIONAL_CONDITIONS}", - "AND h.visit_count > 0"); - nsresult rv = mDBConn->CreateStatement(AutoCompleteHistoryQuery, - getter_AddRefs(mDBAutoCompleteHistoryQuery)); - NS_ENSURE_SUCCESS(rv, nsnull); - - return mDBAutoCompleteHistoryQuery; -} - -// nsNavHistory::GetDBAutoCompleteStarQuery() -// -// Returns the auto complete statement used when autocomplete results are -// restricted to bookmarked entries. -mozIStorageStatement* -nsNavHistory::GetDBAutoCompleteStarQuery() -{ - if (mDBAutoCompleteStarQuery) - return mDBAutoCompleteStarQuery; - - nsCString AutoCompleteStarQuery; - GetAutoCompleteBaseQuery(AutoCompleteStarQuery); - AutoCompleteStarQuery.ReplaceSubstring("{ADDITIONAL_CONDITIONS}", - "AND bookmark IS NOT NULL"); - nsresult rv = mDBConn->CreateStatement(AutoCompleteStarQuery, - getter_AddRefs(mDBAutoCompleteStarQuery)); - NS_ENSURE_SUCCESS(rv, nsnull); - - return mDBAutoCompleteStarQuery; -} - -// nsNavHistory::GetDBAutoCompleteTagsQuery() -// -// Returns the auto complete statement used when autocomplete results are -// restricted to tagged entries. -mozIStorageStatement* -nsNavHistory::GetDBAutoCompleteTagsQuery() -{ - if (mDBAutoCompleteTagsQuery) - return mDBAutoCompleteTagsQuery; - - nsCString AutoCompleteTagsQuery; - GetAutoCompleteBaseQuery(AutoCompleteTagsQuery); - AutoCompleteTagsQuery.ReplaceSubstring("{ADDITIONAL_CONDITIONS}", - "AND tags IS NOT NULL"); - nsresult rv = mDBConn->CreateStatement(AutoCompleteTagsQuery, - getter_AddRefs(mDBAutoCompleteTagsQuery)); - NS_ENSURE_SUCCESS(rv, nsnull); - - return mDBAutoCompleteTagsQuery; -} - -// nsNavHistory::GetDBFeedbackIncrease() -// -// Returns the statement to update the input history that keeps track of -// selections in the locationbar. Input history is used for adaptive query. -mozIStorageStatement* -nsNavHistory::GetDBFeedbackIncrease() -{ - if (mDBFeedbackIncrease) - return mDBFeedbackIncrease; - - nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING( - // Leverage the PRIMARY KEY (place_id, input) to insert/update entries - "INSERT OR REPLACE INTO moz_inputhistory " - // use_count will asymptotically approach the max of 10 - "SELECT h.id, IFNULL(i.input, ?1), IFNULL(i.use_count, 0) * .9 + 1 " - "FROM moz_places_temp h " - "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = ?1 " - "WHERE url = ?2 " - "UNION ALL " - "SELECT h.id, IFNULL(i.input, ?1), IFNULL(i.use_count, 0) * .9 + 1 " - "FROM moz_places h " - "LEFT JOIN moz_inputhistory i ON i.place_id = h.id AND i.input = ?1 " - "WHERE url = ?2 " - "AND h.id NOT IN (SELECT id FROM moz_places_temp)"), - getter_AddRefs(mDBFeedbackIncrease)); - NS_ENSURE_SUCCESS(rv, nsnull); - - return mDBFeedbackIncrease; -} - -// nsNavHistory::CreateAutoCompleteQueries -// -// The auto complete queries we use depend on options, so we have them in -// a separate function so it can be re-created when the option changes. -// We are not lazy creating these queries because they will be most likely -// used on first search, and we don't want to lag on first autocomplete use. -nsresult -nsNavHistory::CreateAutoCompleteQueries() -{ - nsCString AutoCompleteQuery; - GetAutoCompleteBaseQuery(AutoCompleteQuery); - AutoCompleteQuery.ReplaceSubstring("{ADDITIONAL_CONDITIONS}", ""); - nsresult rv = mDBConn->CreateStatement(AutoCompleteQuery, - getter_AddRefs(mDBAutoCompleteQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString AutoCompleteTypedQuery; - GetAutoCompleteBaseQuery(AutoCompleteTypedQuery); - AutoCompleteTypedQuery.ReplaceSubstring("{ADDITIONAL_CONDITIONS}", - "AND h.typed = 1"); - rv = mDBConn->CreateStatement(AutoCompleteTypedQuery, - getter_AddRefs(mDBAutoCompleteTypedQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - // In this query we are taking BOOK_TAG_SQL only for h.id because it - // uses data from moz_bookmarks table and we sync tables on bookmark insert. - // So, most likely, h.id will always be populated when we have any bookmark. - // We still need to join on moz_places_temp for other data (eg. title). - nsCString sql = NS_LITERAL_CSTRING( - "/* do not warn (bug 487789) */ " - "SELECT IFNULL(h_t.url, h.url), IFNULL(h_t.title, h.title), f.url ") + - BOOK_TAG_SQL + NS_LITERAL_CSTRING(", " - "IFNULL(h_t.visit_count, h.visit_count), IFNULL(h_t.typed, h.typed), rank " - "FROM ( " - "SELECT ROUND(MAX(((i.input = ?2) + (SUBSTR(i.input, 1, LENGTH(?2)) = ?2)) * " - "i.use_count), 1) AS rank, place_id " - "FROM moz_inputhistory i " - "GROUP BY i.place_id HAVING rank > 0 " - ") AS i " - "LEFT JOIN moz_places h ON h.id = i.place_id " - "LEFT JOIN moz_places_temp h_t ON h_t.id = i.place_id " - "LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " - "WHERE IFNULL(h_t.url, h.url) NOTNULL " - "ORDER BY rank DESC, IFNULL(h_t.frecency, h.frecency) DESC"); - rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBAdaptiveQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - sql = NS_LITERAL_CSTRING( - "/* do not warn (bug 487787) */ " - "SELECT IFNULL( " - "(SELECT REPLACE(url, '%s', ?2) FROM moz_places_temp WHERE id = b.fk), " - "(SELECT REPLACE(url, '%s', ?2) FROM moz_places WHERE id = b.fk) " - ") AS search_url, IFNULL(h_t.title, h.title), " - "COALESCE(f.url, " - BEST_FAVICON_FOR_REVHOST("moz_places_temp") ", " - BEST_FAVICON_FOR_REVHOST("moz_places") - "), " - "b.parent, b.title, NULL, IFNULL(h_t.visit_count, h.visit_count), " - "IFNULL(h_t.typed, h.typed) " - "FROM moz_keywords k " - "JOIN moz_bookmarks b ON b.keyword_id = k.id " - "LEFT JOIN moz_places AS h ON h.url = search_url " - "LEFT JOIN moz_places_temp AS h_t ON h_t.url = search_url " - "LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " - "WHERE LOWER(k.keyword) = LOWER(?1) " - "ORDER BY IFNULL(h_t.frecency, h.frecency) DESC"); - rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBKeywordQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -// nsNavHistory::StartAutoCompleteTimer - -nsresult -nsNavHistory::StartAutoCompleteTimer(PRUint32 aMilliseconds) -{ - nsresult rv; - - if (!mAutoCompleteTimer) { - mAutoCompleteTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); - NS_ENSURE_SUCCESS(rv, rv); - } - - rv = mAutoCompleteTimer->InitWithFuncCallback(AutoCompleteTimerCallback, this, - aMilliseconds, - nsITimer::TYPE_ONE_SHOT); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -// nsNavHistory::AutoCompleteTimerCallback - -void // static -nsNavHistory::AutoCompleteTimerCallback(nsITimer* aTimer, void* aClosure) -{ - nsNavHistory* history = static_cast(aClosure); - (void)history->PerformAutoComplete(); -} - -nsresult -nsNavHistory::PerformAutoComplete() -{ - // if there is no listener, our search has been stopped - if (!mCurrentListener) - return NS_OK; - - nsresult rv; - // Only do some extra searches on the first chunk - if (!mCurrentChunkOffset) { - // Only show keywords if there's a search - if (mCurrentSearchTokens.Length()) { - rv = AutoCompleteKeywordSearch(); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Get adaptive results first - rv = AutoCompleteAdaptiveSearch(); - NS_ENSURE_SUCCESS(rv, rv); - } - - PRBool moreChunksToSearch = PR_FALSE; - // If we constructed a previous search query, use it instead of full - if (mDBPreviousQuery) { - rv = AutoCompletePreviousSearch(); - NS_ENSURE_SUCCESS(rv, rv); - - // We want to continue searching if we didn't finish last time, so move to - // one before the previous chunk so that we move up to the previous chunk - if (moreChunksToSearch = mPreviousChunkOffset != -1) - mCurrentChunkOffset = mPreviousChunkOffset - mAutoCompleteSearchChunkSize; - } else { - rv = AutoCompleteFullHistorySearch(&moreChunksToSearch); - NS_ENSURE_SUCCESS(rv, rv); - } - - // If we ran out of pages to search, set offset to -1, so we can tell the - // difference between completing and stopping because we have enough results - PRBool notEnoughResults = !AutoCompleteHasEnoughResults(); - if (!moreChunksToSearch) { - // But check first to see if we don't have enough results, and we're - // matching word boundaries, so try again without the match restriction - if (notEnoughResults && mCurrentMatchType == MATCH_BOUNDARY_ANYWHERE) { - mCurrentMatchType = MATCH_ANYWHERE; - mCurrentChunkOffset = -mAutoCompleteSearchChunkSize; - moreChunksToSearch = PR_TRUE; - } else { - mCurrentChunkOffset = -1; - } - } else { - // We know that we do have more chunks, so make sure we want more results - moreChunksToSearch = notEnoughResults; - } - - // Determine the result of the search - PRUint32 count; - mCurrentResult->GetMatchCount(&count); - - if (count > 0) { - mCurrentResult->SetSearchResult(moreChunksToSearch ? - nsIAutoCompleteResult::RESULT_SUCCESS_ONGOING : - nsIAutoCompleteResult::RESULT_SUCCESS); - mCurrentResult->SetDefaultIndex(0); - } else { - mCurrentResult->SetSearchResult(moreChunksToSearch ? - nsIAutoCompleteResult::RESULT_NOMATCH_ONGOING : - nsIAutoCompleteResult::RESULT_NOMATCH); - mCurrentResult->SetDefaultIndex(-1); - } - - rv = mCurrentResult->SetListener(this); - NS_ENSURE_SUCCESS(rv, rv); - - mCurrentListener->OnSearchResult(this, mCurrentResult); - - // if we're not done searching, adjust our current offset - // and search the next chunk - if (moreChunksToSearch) { - mCurrentChunkOffset += mAutoCompleteSearchChunkSize; - rv = StartAutoCompleteTimer(mAutoCompleteSearchTimeout); - NS_ENSURE_SUCCESS(rv, rv); - } else { - DoneSearching(PR_TRUE); - } - return NS_OK; -} - -void -nsNavHistory::DoneSearching(PRBool aFinished) -{ - mPreviousMatchType = mCurrentMatchType; - mPreviousChunkOffset = mCurrentChunkOffset; - mAutoCompleteFinishedSearch = aFinished; - mCurrentResult = nsnull; - mCurrentListener = nsnull; -} - -// nsNavHistory::StartSearch -// - -NS_IMETHODIMP -nsNavHistory::StartSearch(const nsAString & aSearchString, - const nsAString & aSearchParam, - nsIAutoCompleteResult *aPreviousResult, - nsIAutoCompleteObserver *aListener) -{ - // We don't use aPreviousResult to get some matches from previous results in - // order to make sure ordering of results are consistent between reusing and - // not reusing results, see bug #412730 for details - - NS_ENSURE_ARG_POINTER(aListener); - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - - // Lazily init nsITextToSubURI service - if (!mTextURIService) - mTextURIService = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID); - - // Keep track of the previous search results to try optimizing - PRUint32 prevMatchCount = mCurrentResultURLs.Count(); - nsAutoString prevSearchString(mCurrentSearchString); - - // Keep a copy of the original search for case-sensitive usage - mOrigSearchString = aSearchString; - // Remove whitespace, see bug #392141 for details - mOrigSearchString.Trim(" \r\n\t\b"); - // Copy the input search string for case-insensitive search - ToLowerCase(mOrigSearchString, mCurrentSearchString); - - // Fix up the search the same way we fix up the entry url - mCurrentSearchString = FixupURIText(mCurrentSearchString); - - mCurrentListener = aListener; - - nsresult rv; - mCurrentResult = do_CreateInstance(NS_AUTOCOMPLETESIMPLERESULT_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - mCurrentResult->SetSearchString(aSearchString); - - // Short circuit to give no results if we don't need to search for matches. - // Use the previous in-progress search by looking at which urls it found if - // the new search begins with the old one and both aren't empty. We don't run - // into bug 412730 because we only specify urls and not titles to look at. - // Also, only reuse the search if the previous and new search both start with - // javascript: or both don't. (bug 417798) - if (!mAutoCompleteEnabled || - (!prevSearchString.IsEmpty() && - StringBeginsWith(mCurrentSearchString, prevSearchString) && - (StartsWithJS(prevSearchString) == StartsWithJS(mCurrentSearchString)))) { - - // Got nothing before? We won't get anything new, so stop now - if (!mAutoCompleteEnabled || - (mAutoCompleteFinishedSearch && prevMatchCount == 0)) { - // Set up the result to let the listener know that there's nothing - mCurrentResult->SetSearchResult(nsIAutoCompleteResult::RESULT_NOMATCH); - mCurrentResult->SetDefaultIndex(-1); - - rv = mCurrentResult->SetListener(this); - NS_ENSURE_SUCCESS(rv, rv); - - (void)mCurrentListener->OnSearchResult(this, mCurrentResult); - DoneSearching(PR_TRUE); - - return NS_OK; - } else { - // We either have a previous in-progress search or a finished search that - // has more than 0 results. We can continue from where the previous - // search left off, but first we want to create an optimized query that - // only searches through the urls that were previously found - - // We have to do the bindings for both tables, so we build a temporary - // string - nsCString bindings; - for (PRUint32 i = 0; i < prevMatchCount; i++) { - if (i) - bindings += NS_LITERAL_CSTRING(","); - - // +2 to skip over the ?1 for the tag root parameter - bindings += nsPrintfCString("?%d", i + 2); - } - - nsCString sql = NS_LITERAL_CSTRING( - "SELECT h.url, h.title, f.url") + BOOK_TAG_SQL + NS_LITERAL_CSTRING(", " - "h.visit_count, h.typed " - "FROM ( " - "SELECT " MOZ_PLACES_COLUMNS " FROM moz_places_temp " - "WHERE url IN (") + bindings + NS_LITERAL_CSTRING(") " - "UNION ALL " - "SELECT " MOZ_PLACES_COLUMNS " FROM moz_places " - "WHERE id NOT IN (SELECT id FROM moz_places_temp) " - "AND url IN (") + bindings + NS_LITERAL_CSTRING(") " - ") AS h " - "LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id " - "ORDER BY h.frecency DESC"); - - rv = mDBConn->CreateStatement(sql, getter_AddRefs(mDBPreviousQuery)); - NS_ENSURE_SUCCESS(rv, rv); - - // Collect the previous result's URLs that we want to process - nsTArray urls; - (void)mCurrentResultURLs.EnumerateRead(HashedURLsToArray, &urls); - - // Bind the parameters right away. We can only use the query once. - for (PRUint32 i = 0; i < prevMatchCount; i++) { - rv = mDBPreviousQuery->BindStringParameter(i + 1, urls[i]); - NS_ENSURE_SUCCESS(rv, rv); - } - - // Use the same match behavior as the previous search - mCurrentMatchType = mPreviousMatchType; - } - } else { - // Clear out any previous result queries - mDBPreviousQuery = nsnull; - // Default to matching based on the user's preference - mCurrentMatchType = mAutoCompleteMatchBehavior; - } - - mAutoCompleteFinishedSearch = PR_FALSE; - mCurrentChunkOffset = 0; - mCurrentResultURLs.Clear(); - mCurrentSearchTokens.Clear(); - mLivemarkFeedItemIds.Clear(); - mLivemarkFeedURIs.Clear(); - - // Make the array of search tokens from the search string - GenerateSearchTokens(); - - // Figure out if we need to do special searches - ProcessTokensForSpecialSearch(); - - // find all the items that have the "livemark/feedURI" annotation - // and save off their item ids and URIs. when doing autocomplete, - // if a result's parent item id matches a saved item id, the result - // it is not really a bookmark, but a rss feed item. - // if a results URI matches a saved URI, the result is a bookmark, - // so we should show the star. - mozStorageStatementScoper scope(mFoldersWithAnnotationQuery); - - rv = mFoldersWithAnnotationQuery->BindUTF8StringParameter(0, NS_LITERAL_CSTRING(LMANNO_FEEDURI)); - NS_ENSURE_SUCCESS(rv, rv); - - PRBool hasMore = PR_FALSE; - while (NS_SUCCEEDED(mFoldersWithAnnotationQuery->ExecuteStep(&hasMore)) && hasMore) { - PRInt64 itemId = 0; - rv = mFoldersWithAnnotationQuery->GetInt64(0, &itemId); - NS_ENSURE_SUCCESS(rv, rv); - mLivemarkFeedItemIds.Put(itemId, PR_TRUE); - nsAutoString feedURI; - // no need to worry about duplicates. - rv = mFoldersWithAnnotationQuery->GetString(1, feedURI); - NS_ENSURE_SUCCESS(rv, rv); - mLivemarkFeedURIs.Put(feedURI, PR_TRUE); - } - - // fire right away, we already waited to start searching - rv = StartAutoCompleteTimer(0); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -// nsNavHistory::StopSearch - -NS_IMETHODIMP -nsNavHistory::StopSearch() -{ - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - - if (mAutoCompleteTimer) - mAutoCompleteTimer->Cancel(); - - DoneSearching(PR_FALSE); - - return NS_OK; -} - -void -nsNavHistory::GenerateSearchTokens() -{ - // Split the search string into multiple search tokens - nsString::const_iterator strStart, strEnd; - mCurrentSearchString.BeginReading(strStart); - mCurrentSearchString.EndReading(strEnd); - nsString::const_iterator start = strStart, end = strEnd; - while (FindInReadable(NS_LITERAL_STRING(" "), start, end)) { - // Add in the current match - nsAutoString currentMatch(Substring(strStart, start)); - AddSearchToken(currentMatch); - - // Reposition iterators - strStart = start = end; - end = strEnd; - } - - // Add in the last match - nsAutoString lastMatch(Substring(strStart, strEnd)); - AddSearchToken(lastMatch); -} - -inline void -nsNavHistory::AddSearchToken(nsAutoString &aToken) -{ - aToken.Trim("\r\n\t\b"); - if (!aToken.IsEmpty()) - mCurrentSearchTokens.AppendElement(aToken); -} - -void -nsNavHistory::ProcessTokensForSpecialSearch() -{ - // Start with the default behavior - mAutoCompleteCurrentBehavior = mAutoCompleteDefaultBehavior; - - // Determine which special searches to apply - for (PRInt32 i = PRInt32(mCurrentSearchTokens.Length()); --i >= 0;) { - PRBool needToRemove = PR_TRUE; - const nsString& token = mCurrentSearchTokens[i]; - - if (token.Equals(mAutoCompleteRestrictHistory)) - SET_BEHAVIOR(History); - else if (token.Equals(mAutoCompleteRestrictBookmark)) - SET_BEHAVIOR(Bookmark); - else if (token.Equals(mAutoCompleteRestrictTag)) - SET_BEHAVIOR(Tag); - else if (token.Equals(mAutoCompleteMatchTitle)) - SET_BEHAVIOR(Title); - else if (token.Equals(mAutoCompleteMatchUrl)) - SET_BEHAVIOR(Url); - else if (token.Equals(mAutoCompleteRestrictTyped)) - SET_BEHAVIOR(Typed); - else - needToRemove = PR_FALSE; - - // Remove the token if it's special search token - if (needToRemove) - mCurrentSearchTokens.RemoveElementAt(i); - } - - // Search only typed pages in history for empty searches - if (mOrigSearchString.IsEmpty()) { - SET_BEHAVIOR(History); - SET_BEHAVIOR(Typed); - } - - // We can use optimized queries for restricts, so check for the most - // restrictive query first - mDBCurrentQuery = GET_BEHAVIOR(Tag) ? GetDBAutoCompleteTagsQuery() : - GET_BEHAVIOR(Bookmark) ? GetDBAutoCompleteStarQuery() : - GET_BEHAVIOR(Typed) ? static_cast(mDBAutoCompleteTypedQuery) : - GET_BEHAVIOR(History) ? GetDBAutoCompleteHistoryQuery() : - static_cast(mDBAutoCompleteQuery); -} - -nsresult -nsNavHistory::AutoCompleteKeywordSearch() -{ - mozStorageStatementScoper scope(mDBKeywordQuery); - - // Get the keyword parameters to replace the %s in the keyword search - nsCAutoString params; - PRInt32 paramPos = mOrigSearchString.FindChar(' ') + 1; - // Make sure to escape them as if they were the query in a url, so " " become - // "+"; "+" become "%2B"; non-ascii escaped - NS_Escape(NS_ConvertUTF16toUTF8(Substring(mOrigSearchString, paramPos)), - params, url_XPAlphas); - - // The first search term might be a keyword - const nsAString &keyword = Substring(mOrigSearchString, 0, - paramPos ? paramPos - 1 : mOrigSearchString.Length()); - nsresult rv = mDBKeywordQuery->BindStringParameter(0, keyword); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBKeywordQuery->BindUTF8StringParameter(1, params); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AutoCompleteProcessSearch(mDBKeywordQuery, QUERY_KEYWORD); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsNavHistory::AutoCompleteAdaptiveSearch() -{ - mozStorageStatementScoper scope(mDBAdaptiveQuery); - - nsresult rv = mDBAdaptiveQuery->BindInt64Parameter(0, GetTagsFolder()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBAdaptiveQuery->BindStringParameter(1, mCurrentSearchString); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AutoCompleteProcessSearch(mDBAdaptiveQuery, QUERY_FILTERED); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsNavHistory::AutoCompletePreviousSearch() -{ - nsresult rv = mDBPreviousQuery->BindInt64Parameter(0, GetTagsFolder()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AutoCompleteProcessSearch(mDBPreviousQuery, QUERY_FILTERED); - NS_ENSURE_SUCCESS(rv, rv); - - // Don't use this query more than once - mDBPreviousQuery = nsnull; - - return NS_OK; -} - -// nsNavHistory::AutoCompleteFullHistorySearch -// -// Search for places that have a title, url, -// or bookmark title(s) that contains mCurrentSearchString -// and are within our current chunk of "frecency". -// -// @param aHasMoreResults is false if the query found no matching items -// - -nsresult -nsNavHistory::AutoCompleteFullHistorySearch(PRBool* aHasMoreResults) -{ - mozStorageStatementScoper scope(mDBCurrentQuery); - - nsresult rv = mDBCurrentQuery->BindInt64Parameter(0, GetTagsFolder()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBCurrentQuery->BindInt32Parameter(1, mAutoCompleteSearchChunkSize); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBCurrentQuery->BindInt32Parameter(2, mCurrentChunkOffset); - NS_ENSURE_SUCCESS(rv, rv); - - rv = AutoCompleteProcessSearch(mDBCurrentQuery, QUERY_FILTERED, aHasMoreResults); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsNavHistory::AutoCompleteProcessSearch(mozIStorageStatement* aQuery, - const QueryType aType, - PRBool *aHasMoreResults) -{ - // Unless we're checking if there are any results for the query, don't bother - // processing results if we already have enough results - if (!aHasMoreResults && AutoCompleteHasEnoughResults()) - return NS_OK; - - nsFaviconService* faviconService = nsFaviconService::GetFaviconService(); - NS_ENSURE_TRUE(faviconService, NS_ERROR_OUT_OF_MEMORY); - - // We want to filter javascript: URIs if the search doesn't start with it - PRBool filterJavascript = mAutoCompleteFilterJavascript && - !StartsWithJS(mCurrentSearchString); - - // Determine what type of search to try matching tokens against targets - PRBool (*tokenMatchesTarget)(const nsAString &, const nsAString &); - switch (mCurrentMatchType) { - case MATCH_ANYWHERE: - tokenMatchesTarget = FindAnywhere; - break; - case MATCH_BEGINNING: - tokenMatchesTarget = FindBeginning; - break; - case MATCH_BOUNDARY_ANYWHERE: - case MATCH_BOUNDARY: - default: - tokenMatchesTarget = FindOnBoundary; - break; - } - - PRBool hasMore = PR_FALSE; - // Determine the result of the search - while (NS_SUCCEEDED(aQuery->ExecuteStep(&hasMore)) && hasMore) { - nsAutoString escapedEntryURL; - nsresult rv = aQuery->GetString(kAutoCompleteIndex_URL, escapedEntryURL); - NS_ENSURE_SUCCESS(rv, rv); - - // If we need to filter and have a javascript URI.. skip! - if (filterJavascript && StartsWithJS(escapedEntryURL)) - continue; - - // Prevent duplicates that might appear from previous searches such as tag - // results and chunking. Because we use mCurrentResultURLs to remove - // duplicates, the first url wins, so we might not show it as a "star" if - // the parentId we get first is the one for the livemark and not the - // bookmark or no "star" at all. - // XXX bug 412734 - PRBool dummy; - if (!mCurrentResultURLs.Get(escapedEntryURL, &dummy)) { - PRInt64 parentId = 0; - nsAutoString entryTitle, entryFavicon, entryBookmarkTitle; - rv = aQuery->GetString(kAutoCompleteIndex_Title, entryTitle); - NS_ENSURE_SUCCESS(rv, rv); - rv = aQuery->GetString(kAutoCompleteIndex_FaviconURL, entryFavicon); - NS_ENSURE_SUCCESS(rv, rv); - rv = aQuery->GetInt64(kAutoCompleteIndex_ParentId, &parentId); - NS_ENSURE_SUCCESS(rv, rv); - - // Only fetch the bookmark title if we have a bookmark - if (parentId) { - rv = aQuery->GetString(kAutoCompleteIndex_BookmarkTitle, entryBookmarkTitle); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsAutoString entryTags; - rv = aQuery->GetString(kAutoCompleteIndex_Tags, entryTags); - NS_ENSURE_SUCCESS(rv, rv); - PRInt32 visitCount = 0; - rv = aQuery->GetInt32(kAutoCompleteIndex_VisitCount, &visitCount); - NS_ENSURE_SUCCESS(rv, rv); - PRInt32 typed = 0; - rv = aQuery->GetInt32(kAutoCompleteIndex_Typed, &typed); - - // Always prefer the bookmark title unless it's empty - nsAutoString title = - entryBookmarkTitle.IsEmpty() ? entryTitle : entryBookmarkTitle; - - nsString style; - switch (aType) { - case QUERY_KEYWORD: { - // If we don't have a title, then we must have a keyword, so let the - // UI know it's a keyword; otherwise, we found an exact page match, - // so just show the page like a regular result - if (entryTitle.IsEmpty()) - style = NS_LITERAL_STRING("keyword"); - else - title = entryTitle; - - break; - } - case QUERY_FILTERED: { - // If we get any results, there's potentially another chunk to proces - if (aHasMoreResults) - *aHasMoreResults = PR_TRUE; - - // Keep track of if we've matched all the filter requirements such as - // only history items, only bookmarks, only tags. If a given restrict - // is active, make sure a corresponding condition is *not* true. If - // any are violated, matchAll will be false. - PRBool matchAll = !((GET_BEHAVIOR(History) && visitCount == 0) || - (GET_BEHAVIOR(Typed) && typed == 0) || - (GET_BEHAVIOR(Bookmark) && !parentId) || - (GET_BEHAVIOR(Tag) && entryTags.IsEmpty())); - - // Unescape the url to search for unescaped terms - nsString entryURL = FixupURIText(escapedEntryURL); - - // Determine if every token matches either the bookmark title, tags, - // page title, or page url - for (PRUint32 i = 0; i < mCurrentSearchTokens.Length() && matchAll; i++) { - const nsString& token = mCurrentSearchTokens[i]; - - // Check if the tags match the search term - PRBool matchTags = (*tokenMatchesTarget)(token, entryTags); - // Check if the title matches the search term - PRBool matchTitle = (*tokenMatchesTarget)(token, title); - - // Make sure we match something in the title or tags if we have to - matchAll = matchTags || matchTitle; - if (GET_BEHAVIOR(Title) && !matchAll) - break; - - // Check if the url matches the search term - PRBool matchUrl = (*tokenMatchesTarget)(token, entryURL); - // If we don't match the url when we have to, reset matchAll to - // false; otherwise keep track that we did match the current search - if (GET_BEHAVIOR(Url) && !matchUrl) - matchAll = PR_FALSE; - else - matchAll |= matchUrl; - } - - // Skip if we don't match all terms in the bookmark, tag, title or url - if (!matchAll) - continue; - - break; - } - } - - // Always prefer to show tags if we have them - PRBool showTags = !entryTags.IsEmpty(); - - // Pretend a page isn't bookmarked/tagged if the user only wants history, - // but still show the star and tag if the user explicitly wants them - if (GET_BEHAVIOR(History) && !(GET_BEHAVIOR(Bookmark) || GET_BEHAVIOR(Tag))) { - showTags = PR_FALSE; - style = NS_LITERAL_STRING("favicon"); - } - - // Add the tags to the title if necessary - if (showTags) - title += TITLE_TAGS_SEPARATOR + entryTags; - - // Tags have a special style to show a tag icon; otherwise, style the - // bookmarks that aren't feed items and feed URIs as bookmark - if (style.IsEmpty()) { - if (showTags) - style = NS_LITERAL_STRING("tag"); - else if ((parentId && !mLivemarkFeedItemIds.Get(parentId, &dummy)) || - mLivemarkFeedURIs.Get(escapedEntryURL, &dummy)) - style = NS_LITERAL_STRING("bookmark"); - else - style = NS_LITERAL_STRING("favicon"); - } - - // Get the URI for the favicon - nsCAutoString faviconSpec; - faviconService->GetFaviconSpecForIconString( - NS_ConvertUTF16toUTF8(entryFavicon), faviconSpec); - NS_ConvertUTF8toUTF16 faviconURI(faviconSpec); - - // New item: append to our results and put it in our hash table - rv = mCurrentResult->AppendMatch(escapedEntryURL, title, faviconURI, style); - NS_ENSURE_SUCCESS(rv, rv); - mCurrentResultURLs.Put(escapedEntryURL, PR_TRUE); - - // Stop processing if we have enough results - if (AutoCompleteHasEnoughResults()) - break; - } - } - - return NS_OK; -} - -inline PRBool -nsNavHistory::AutoCompleteHasEnoughResults() -{ - return mCurrentResultURLs.Count() >= (PRUint32)mAutoCompleteMaxResults; -} - -// nsNavHistory::OnValueRemoved (nsIAutoCompleteSimpleResultListener) - -NS_IMETHODIMP -nsNavHistory::OnValueRemoved(nsIAutoCompleteSimpleResult* aResult, - const nsAString& aValue, PRBool aRemoveFromDb) -{ - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - - if (!aRemoveFromDb) - return NS_OK; - - nsresult rv; - nsCOMPtr uri; - rv = NS_NewURI(getter_AddRefs(uri), aValue); - NS_ENSURE_SUCCESS(rv, rv); - - rv = RemovePage(uri); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsNavHistory::AutoCompleteFeedback(PRInt32 aIndex, - nsIAutoCompleteController *aController) -{ - // we don't track user choices in the awesomebar in private browsing mode - if (InPrivateBrowsingMode()) - return NS_OK; - - mozIStorageStatement *stmt = GetDBFeedbackIncrease(); - mozStorageStatementScoper scope(stmt); - - nsAutoString input; - nsresult rv = aController->GetSearchString(input); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindStringParameter(0, input); - NS_ENSURE_SUCCESS(rv, rv); - - nsAutoString url; - rv = aController->GetValueAt(aIndex, url); - NS_ENSURE_SUCCESS(rv, rv); - rv = stmt->BindStringParameter(1, url); - NS_ENSURE_SUCCESS(rv, rv); - - // We do the update async and we don't care about failures - nsCOMPtr callback = - new AutoCompleteStatementCallbackNotifier(); - nsCOMPtr canceler; - rv = stmt->ExecuteAsync(callback, getter_AddRefs(canceler)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsString -nsNavHistory::FixupURIText(const nsAString &aURIText) -{ - // Unescaping utilities expect UTF8 strings - NS_ConvertUTF16toUTF8 escaped(aURIText); - - // Strip off some prefixes so we don't have to match the exact protocol for - // sites. E.g., http://mozilla.org/ can match other mozilla.org pages. - if (StringBeginsWith(escaped, NS_LITERAL_CSTRING("https://"))) - escaped.Cut(0, 8); - else if (StringBeginsWith(escaped, NS_LITERAL_CSTRING("http://"))) - escaped.Cut(0, 7); - else if (StringBeginsWith(escaped, NS_LITERAL_CSTRING("ftp://"))) - escaped.Cut(0, 6); - - nsString fixedUp; - // Use the service if we have it to avoid invalid UTF8 strings - if (mTextURIService) { - mTextURIService->UnEscapeURIForUI(NS_LITERAL_CSTRING("UTF-8"), - escaped, fixedUp); - return fixedUp; - } - - // Fallback on using this if the service is unavailable for some reason - NS_UnescapeURL(escaped); - CopyUTF8toUTF16(escaped, fixedUp); - return fixedUp; -} diff --git a/toolkit/components/places/src/nsPlacesAutoComplete.js b/toolkit/components/places/src/nsPlacesAutoComplete.js new file mode 100644 index 00000000000..52c912cb6ba --- /dev/null +++ b/toolkit/components/places/src/nsPlacesAutoComplete.js @@ -0,0 +1,1056 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * ***** 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 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * 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 ***** */ + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +//////////////////////////////////////////////////////////////////////////////// +//// Constants + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; + +// This is just a helper for the next constant. +function book_tag_sql_fragment(aName, aColumn, aForTag) +{ + return ["(", + "SELECT ", aColumn, " ", + "FROM moz_bookmarks b ", + "JOIN moz_bookmarks t ", + "ON t.id = b.parent ", + "AND t.parent ", (aForTag ? "" : "!"), "= :parent ", + "WHERE b.type = ", Ci.nsINavBookmarksService.TYPE_BOOKMARK, " ", + "AND b.fk = h.id ", + (aForTag ? "AND LENGTH(t.title) > 0" : + "ORDER BY b.lastModified DESC LIMIT 1"), + ") AS ", aName].join(""); +} + +// This SQL query fragment provides the following: +// - the parent folder for bookmarked entries (kQueryIndexParent) +// - the bookmark title, if it is a bookmark (kQueryIndexBookmarkTitle) +// - the tags associated with a bookmarked entry (kQueryIndexTags) +const kBookTagSQLFragment = + book_tag_sql_fragment("parent", "b.parent", false) + ", " + + book_tag_sql_fragment("bookmark", "b.title", false) + ", " + + book_tag_sql_fragment("tags", "GROUP_CONCAT(t.title, ',')", true); + +// observer topics +const kQuitApplication = "quit-application"; +const kPrefChanged = "nsPref:changed"; + +// Match type constants. These indicate what type of search function we should +// be using. +const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE; +const MATCH_BOUNDARY_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY_ANYWHERE; +const MATCH_BOUNDARY = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY; +const MATCH_BEGINNING = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING; + +// AutoComplete index constants. All AutoComplete queries will provide these +// columns in this order. +const kQueryIndexURL = 0; +const kQueryIndexTitle = 1; +const kQueryIndexFaviconURL = 2; +const kQueryIndexParentId = 3; +const kQueryIndexBookmarkTitle = 4; +const kQueryIndexTags = 5; +const kQueryIndexVisitCount = 6; +const kQueryIndexTyped = 7; +const kQueryIndexPlaceId = 8; +const kQueryIndexQueryType = 9; + +// AutoComplete query type constants. Describes the various types of queries +// that we can process. +const kQueryTypeKeyword = 0; +const kQueryTypeFiltered = 1; + +// This separator is used as an RTL-friendly way to split the title and tags. +// It can also be used by an nsIAutoCompleteResult consumer to re-split the +// "comment" back into the title and the tag. +const kTitleTagsSeparator = " \u2013 "; + +// The list of columns in the moz_places table that we use in our queries. +const kMozPlacesColumns = + "id, url, title, rev_host, visit_count, hidden, typed, favicon_id, " + + "frecency, last_visit_date" + +const kBrowserUrlbarBranch = "browser.urlbar."; + +//////////////////////////////////////////////////////////////////////////////// +//// Global Functions + +/** + * Generates the SQL subquery to get the best favicon for a given revhost. This + * is the favicon for the most recent visit. + * + * @param aTableName + * The table to join to the moz_favicons table with. This must have a + * column called favicon_id. + * @return the SQL subquery (in string form) to get the best favicon. + */ +function best_favicon_for_revhost(aTableName) +{ + return "(" + + "SELECT f.url " + + "FROM " + aTableName + " " + + "JOIN moz_favicons f ON f.id = favicon_id " + + "WHERE rev_host = IFNULL( " + + "(SELECT rev_host FROM moz_places_temp WHERE id = b.fk), " + + "(SELECT rev_host FROM moz_places WHERE id = b.fk) " + + ") " + + "ORDER BY frecency DESC " + + "LIMIT 1 " + + ")"; +} + +//////////////////////////////////////////////////////////////////////////////// +//// AutoCompleteStatementCallbackWrapper class + +/** + * Wraps a callback and ensures that handleCompletion is not dispatched if the + * query is no longer tracked. + * + * @param aCallback + * A reference to a nsPlacesAutoComplete. + */ +function AutoCompleteStatementCallbackWrapper(aCallback) +{ + this._callback = aCallback; +} + +AutoCompleteStatementCallbackWrapper.prototype = { + ////////////////////////////////////////////////////////////////////////////// + //// mozIStorageStatementCallback + + handleResult: function ACSCW_handleResult(aResultSet) + { + this._callback.handleResult.apply(this._callback, arguments); + }, + + handleError: function ACSCW_handleError(aError) + { + this._callback.handleError.apply(this._callback, arguments); + }, + + handleCompletion: function ACSCW_handleCompletion(aReason) + { + // Only dispatch handleCompletion if we are not done searching and are a + // pending search. + let callback = this._callback; + if (!callback.isSearchComplete() && callback.isPendingSearch(this._handle)) + callback.handleCompletion.apply(callback, arguments); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// AutoCompleteStatementCallbackWrapper + + /** + * Executes the specified query asynchronously. This object will notify + * this._callback if we should notify (logic explained in handleCompletion). + * + * @param aQuery + * The query to execute asynchronously. + * @return a mozIStoragePendingStatement that can be used to cancel the query. + */ + executeAsync: function ACSCW_executeAsync(aQuery) + { + return this._handle = aQuery.executeAsync(this); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsISupports + + QueryInterface: XPCOMUtils.generateQI([ + Ci.mozIStorageStatementCallback, + ]) +}; + +//////////////////////////////////////////////////////////////////////////////// +//// nsPlacesAutoComplete class + +function nsPlacesAutoComplete() +{ + ////////////////////////////////////////////////////////////////////////////// + //// Shared Constants for Smart Getters + + // Define common pieces of various queries. + // TODO bug 412736 in case of a frecency tie, break it with h.typed and + // h.visit_count which is better than nothing. This is slow, so not doing it + // yet... + // Note: h.frecency is only selected because we need it for ordering. + const SQL_BASE = + "SELECT h.url, h.title, f.url, " + kBookTagSQLFragment + ", h.visit_count, " + + "h.typed, h.id, :query_type " + + "FROM ( " + + "SELECT " + kMozPlacesColumns + " FROM moz_places_temp " + + "UNION ALL " + + "SELECT " + kMozPlacesColumns + " FROM moz_places " + + "WHERE +id NOT IN (SELECT id FROM moz_places_temp) " + + "ORDER BY frecency DESC " + + ") AS h " + + "LEFT OUTER JOIN moz_favicons f " + + "ON f.id = h.favicon_id " + + "WHERE h.frecency <> 0 " + + "AND AUTOCOMPLETE_MATCH(:searchString, h.url, IFNULL(bookmark, h.title), " + + "tags, h.visit_count, h.typed, parent, " + + ":matchBehavior, :searchBehavior) " + + "{ADDITIONAL_CONDITIONS}"; + + ////////////////////////////////////////////////////////////////////////////// + //// Smart Getters + + this.__defineGetter__("_db", function() { + delete this._db; + return this._db = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsPIPlacesDatabase). + DBConnection; + }); + + this.__defineGetter__("_bh", function() { + delete this._bh; + return this._bh = Cc["@mozilla.org/browser/global-history;2"]. + getService(Ci.nsIBrowserHistory); + }); + + this.__defineGetter__("_textURIService", function() { + delete this._textURIService; + return this._textURIService = Cc["@mozilla.org/intl/texttosuburi;1"]. + getService(Ci.nsITextToSubURI); + }); + + this.__defineGetter__("_bs", function() { + delete this._bs; + return this._bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService); + }); + + this.__defineGetter__("_ioService", function() { + delete this._ioService; + return this._ioService = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + }); + + this.__defineGetter__("_faviconService", function() { + delete this._faviconService; + return this._faviconService = Cc["@mozilla.org/browser/favicon-service;1"]. + getService(Ci.nsIFaviconService); + }); + + this.__defineGetter__("_defaultQuery", function() { + delete this._defaultQuery; + let replacementText = ""; + return this._defaultQuery = this._db.createStatement( + SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g") + ); + }); + + this.__defineGetter__("_historyQuery", function() { + delete this._historyQuery; + let replacementText = "AND h.visit_count > 0"; + return this._historyQuery = this._db.createStatement( + SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g") + ); + }); + + this.__defineGetter__("_bookmarkQuery", function() { + delete this._bookmarkQuery; + let replacementText = "AND bookmark IS NOT NULL"; + return this._bookmarkQuery = this._db.createStatement( + SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g") + ); + }); + + this.__defineGetter__("_tagsQuery", function() { + delete this._tagsQuery; + let replacementText = "AND tags IS NOT NULL"; + return this._tagsQuery = this._db.createStatement( + SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g") + ); + }); + + this.__defineGetter__("_typedQuery", function() { + delete this._typedQuery; + let replacementText = "AND h.typed = 1"; + return this._typedQuery = this._db.createStatement( + SQL_BASE.replace("{ADDITIONAL_CONDITIONS}", replacementText, "g") + ); + }); + + this.__defineGetter__("_adaptiveQuery", function() { + delete this._adaptiveQuery; + // In this query, we are taking kBookTagSQLFragment only for h.id because it + // uses data from the moz_bookmarks table and we sync tables on bookmark + // insert. So, most likely, h.id will always be populated when we have any + // bookmark. We still need to join on moz_places_temp for other data (eg. + // title). + return this._adaptiveQuery = this._db.createStatement( + "/* do not warn (bug 487789) */ " + + "SELECT IFNULL(h_t.url, h.url), IFNULL(h_t.title, h.title), f.url, " + + kBookTagSQLFragment + ", IFNULL(h_t.visit_count, h.visit_count), " + + "IFNULL(h_t.typed, h.typed), IFNULL(h_t.id, h.id), " + + ":query_type, rank " + + "FROM ( " + + "SELECT ROUND(MAX(((i.input = :search_string) + " + + "(SUBSTR(i.input, 1, LENGTH(:search_string)) = :search_string)) * " + + "i.use_count), 1) AS rank, place_id " + + "FROM moz_inputhistory i " + + "GROUP BY i.place_id " + + "HAVING rank > 0 " + + ") AS i " + + "LEFT JOIN moz_places h ON h.id = i.place_id " + + "LEFT JOIN moz_places_temp h_t ON h_t.id = i.place_id " + + "LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) "+ + "WHERE IFNULL(h_t.url, h.url) NOTNULL " + + "ORDER BY rank DESC, IFNULL(h_t.frecency, h.frecency) DESC" + ); + }); + + this.__defineGetter__("_keywordQuery", function() { + delete this._keywordQuery; + return this._keywordQuery = this._db.createStatement( + "/* do not warn (bug 487787) */ " + + "SELECT IFNULL( " + + "(SELECT REPLACE(url, '%s', :query_string) FROM moz_places_temp WHERE id = b.fk), " + + "(SELECT REPLACE(url, '%s', :query_string) FROM moz_places WHERE id = b.fk) " + + ") AS search_url, IFNULL(h_t.title, h.title), " + + "COALESCE(f.url, " + best_favicon_for_revhost("moz_places_temp") + "," + + best_favicon_for_revhost("moz_places") + "), b.parent, " + + "b.title, NULL, IFNULL(h_t.visit_count, h.visit_count), " + + "IFNULL(h_t.typed, h.typed), COALESCE(h_t.id, h.id, b.fk), " + + ":query_type " + + "FROM moz_keywords k " + + "JOIN moz_bookmarks b ON b.keyword_id = k.id " + + "LEFT JOIN moz_places AS h ON h.url = search_url " + + "LEFT JOIN moz_places_temp AS h_t ON h_t.url = search_url " + + "LEFT JOIN moz_favicons f ON f.id = IFNULL(h_t.favicon_id, h.favicon_id) " + + "WHERE LOWER(k.keyword) = LOWER(:keyword) " + + "ORDER BY IFNULL(h_t.frecency, h.frecency) DESC" + ); + }); + + ////////////////////////////////////////////////////////////////////////////// + //// Initialization + + // load preferences + this._loadPrefs(true); + + // register observers + this._os = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + this._os.addObserver(this, kQuitApplication, false); + +} + +nsPlacesAutoComplete.prototype = { + ////////////////////////////////////////////////////////////////////////////// + //// nsIAutoCompleteSearch + + startSearch: function PAC_startSearch(aSearchString, aSearchParam, + aPreviousResult, aListener) + { + // Note: We don't use aPreviousResult to make sure ordering of results are + // consistent. See bug 412730 for more details. + + // We want to store the original string with no leading or trailing + // whitespace for case sensitive searches. + this._originalSearchString = aSearchString.trim(); + + this._currentSearchString = + this._fixupSearchText(this._originalSearchString.toLowerCase()); + + this._listener = aListener; + let result = Cc["@mozilla.org/autocomplete/simple-result;1"]. + createInstance(Ci.nsIAutoCompleteSimpleResult); + result.setSearchString(aSearchString); + result.setListener(this); + this._result = result; + this._notifyResults(true); + + // If we are not enabled, we need to return now. + if (!this._enabled) { + this._finishSearch(true); + return; + } + + // Reset our search behavior to the default. + this._behavior = this._defaultBehavior; + + // If we have no search terms, this is a special search that should only + // look for BEHAVIOR_HISTORY and BEHAVIOR_TYPED. + if (!this._currentSearchString) { + this._setBehavior("history"); + this._setBehavior("typed"); + } + + // For any given search, we run up to three queries: + // 1) keywords (this._keywordQuery) + // 2) adaptive learning (this._adaptiveQuery) + // 3) query from this._getSearch + // We always run (2) and (3), but (1) only gets ran if we get any filtered + // tokens from this._getSearch (if there are no tokens, there is nothing to + // match, so there is no reason to run the query). + let {query, tokens} = + this._getSearch(this._getUnfilteredSearchTokens(this._currentSearchString)); + let queries = tokens.length ? + [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery(), query] : + [this._getBoundAdaptiveQuery(), query]; + + // Start executing our queries. + this._executeQueries(queries); + + // Set up our persistent state for the duration of the search. + this._searchTokens = tokens; + this._usedPlaceIds = {}; + }, + + stopSearch: function PAC_stopSearch() + { + // We need to cancel our searches so we do not get any [more] results. + this._stopActiveQueries(); + + this._finishSearch(false); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsIAutoCompleteSimpleResultListener + + onValueRemoved: function PAC_onValueRemoved(aResult, aURISpec, aRemoveFromDB) + { + if (aRemoveFromDB) + this._bh.removePage(this._ioService.newURI(aURISpec, null, null)); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// mozIStorageStatementCallback + + handleResult: function PAC_handleResult(aResultSet) + { + let row, haveMatches = false; + while (row = aResultSet.getNextRow()) { + let match = this._processRow(row); + haveMatches = haveMatches || match; + + if (this._result.matchCount == this._maxRichResults) { + // We have enough results, so stop running our queries. + this._stopActiveQueries(); + + // And finish our search. + this._finishSearch(true); + return; + } + + } + + // Notify about results if we've gotten them. + if (haveMatches) + this._notifyResults(true); + }, + + handleError: function PAC_handleError(aError) + { + Components.utils.reportError("Places AutoComplete: " + aError); + }, + + handleCompletion: function PAC_handleCompletion(aReason) + { + // If we have already finished our search, we should bail out early. + if (this.isSearchComplete()) + return; + + // Remove the first query in our array of pending queries since it is the + // one we are getting notified about. + this._pendingQueries.shift(); + + // If we still have pending queries, we bail out because we aren't done + // getting results. + if (this._pendingQueries.length) + return; + + // If we do not have enough results, and our match type is + // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more + // results. + if (this._matchBehavior == MATCH_BOUNDARY_ANYWHERE && + this._result.matchCount < this._maxRichResults && !this._secondPass) { + this._secondPass = true; + let query = this._getBoundSearchQuery(MATCH_ANYWHERE, this._searchTokens); + this._executeQueries([query]); + return; + } + + this._finishSearch(true); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsIObserver + + observe: function PAC_observe(aSubject, aTopic, aData) + { + if (aTopic == kQuitApplication) { + this._os.removeObserver(this, kQuitApplication); + + // Remove our preference observer. + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(kBrowserUrlbarBranch). + QueryInterface(Ci.nsIPrefBranch2); + prefs.removeObserver("", this); + + // Finalize the statements that we have used. + let stmts = [ + "_defaultQuery", + "_historyQuery", + "_bookmarkQuery", + "_tagsQuery", + "_typedQuery", + "_adaptiveQuery", + "_keywordQuery", + ]; + for (let i = 0; i < stmts.length; i++) { + // We do not want to create any query we haven't already created, so + // see if it is a getter first. __lookupGetter__ returns null if it is + // actually a statement. + if (!this.__lookupGetter__(stmts[i])) + this[stmts[i]].finalize(); + } + } + else if (aTopic == kPrefChanged) { + this._loadPrefs(); + } + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsPlacesAutoComplete + + /** + * Used to unescape encoded URI strings, and drop information that we do not + * care about for searching. + * + * @param aURIString + * The text to unescape and modify. + * @return the modified uri. + */ + _fixupSearchText: function PAC_fixupSearchText(aURIString) + { + let uri = aURIString; + + if (uri.indexOf("http://") == 0) + uri = uri.slice(7); + else if (uri.indexOf("https://") == 0) + uri = uri.slice(8); + else if (uri.indexOf("ftp://") == 0) + uri = uri.slice(6); + + return this._textURIService.unEscapeURIForUI("UTF-8", uri); + }, + + /** + * Generates the tokens used in searching from a given string. + * + * @param aSearchString + * The string to generate tokens from. + * @return an array of tokens. + */ + _getUnfilteredSearchTokens: function PAC_unfilteredSearchTokens(aSearchString) + { + // Calling split on an empty string will return an array containing one + // empty string. We don't want that, as it'll break our logic, so return an + // empty array then. + return aSearchString.length ? aSearchString.split(" ") : []; + }, + + /** + * Properly cleans up when searching is completed. + * + * @param aNotify + * Indicates if we should notify the AutoComplete listener about our + * results or not. + */ + _finishSearch: function PAC_finishSearch(aNotify) + { + // Notify about results if we are supposed to. + if (aNotify) + this._notifyResults(false); + + // Clear our state + delete this._originalSearchString; + delete this._currentSearchString; + delete this._searchTokens; + delete this._listener; + delete this._result; + delete this._usedPlaceIds; + this._secondPass = false; + }, + + /** + * Executes the given queries asynchronously. + * + * @param aQueries + * The queries to execute. + */ + _executeQueries: function PAC_executeQueries(aQueries) + { + // Because we might get a handleCompletion for canceled queries, we want to + // filter out queries we no longer care about (described in the + // handleCompletion implementation of AutoCompleteStatementCallbackWrapper). + let PAC = this; + this._pendingQueries = aQueries.map(function(aQuery) { + // Create our wrapper object and execute the query. + let callbackWrapper = new AutoCompleteStatementCallbackWrapper(PAC); + return callbackWrapper.executeAsync(aQuery); + }); + }, + + /** + * Stops executing all active queries. + */ + _stopActiveQueries: function PAC_stopActiveQueries() + { + this._pendingQueries.forEach(function(aQuery) aQuery.cancel()); + delete this._pendingQueries; + }, + + /** + * Notifies the listener about results. + * + * @param aSearchOngoing + * Indicates if the search is ongoing or not. + */ + _notifyResults: function PAC_notifyResults(aSearchOngoing) + { + let result = this._result; + let resultCode = result.matchCount ? "RESULT_SUCCESS" : "RESULT_NOMATCH"; + if (aSearchOngoing) + resultCode += "_ONGOING"; + result.setSearchResult(Ci.nsIAutoCompleteResult[resultCode]); + result.setDefaultIndex(result.matchCount ? 0 : -1); + this._listener.onSearchResult(this, result); + }, + + /** + * Loads the preferences that we care about. + * + * @param [optional] aRegisterObserver + * Indicates if the preference observer should be added or not. The + * default value is false. + */ + _loadPrefs: function PAC_loadPrefs(aRegisterObserver) + { + let prefs = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(kBrowserUrlbarBranch); + function safeGetter(aName, aDefault) { + let types = { + boolean: "Bool", + number: "Int", + string: "Char" + }; + let type = types[typeof(aDefault)]; + if (!type) + throw "Unknown type!"; + + // If the pref isn't set, we want to use the default. + try { + return prefs["get" + type + "Pref"](aName); + } + catch (e) { + return aDefault; + } + } + + this._enabled = safeGetter("autocomplete.enabled", true); + this._matchBehavior = safeGetter("matchBehavior", MATCH_BOUNDARY_ANYWHERE); + this._filterJavaScript = safeGetter("filter.javascript", true); + this._maxRichResults = safeGetter("maxRichResults", 25); + this._restrictHistoryToken = safeGetter("restrict.history", "^"); + this._restrictBookmarkToken = safeGetter("restrict.bookmark", "*"); + this._restrictTypedToken = safeGetter("restrict.typed", "~"); + this._restrictTagToken = safeGetter("restrct.tag", "+"); + this._matchTitleToken = safeGetter("match.title", "#"); + this._matchURLToken = safeGetter("match.url", "@"); + this._defaultBehavior = safeGetter("default.behavior", 0); + + // Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE. + if (this._matchBehavior != MATCH_ANYWHERE && + this._matchBehavior != MATCH_BOUNDARY && + this._matchBehavior != MATCH_BEGINNING) + this._matchBehavior = MATCH_BOUNDARY_ANYWHERE; + + // register observer + if (aRegisterObserver) { + let pb = prefs.QueryInterface(Ci.nsIPrefBranch2); + pb.addObserver("", this, false); + } + }, + + /** + * Given an array of tokens, this function determines which query should be + * ran. It also removes any special search tokens. + * + * @param aTokens + * An array of search tokens. + * @return an object with two properties: + * query: the correctly optimized, bound query to search the database + * with. + * tokens: the filtered list of tokens to search with. + */ + _getSearch: function PAC_getSearch(aTokens) + { + // Set the proper behavior so our call to _getBoundSearchQuery gives us the + // correct query. + for (let i = aTokens.length - 1; i >= 0; i--) { + switch (aTokens[i]) { + case this._restrictHistoryToken: + this._setBehavior("history"); + break; + case this._restrictBookmarkToken: + this._setBehavior("bookmark"); + break; + case this._restrictTagToken: + this._setBehavior("tag"); + break; + case this._matchTitleToken: + this._setBehavior("title"); + break; + case this._matchURLToken: + this._setBehavior("url"); + break; + case this._restrictTypedToken: + this._setBehavior("typed"); + break; + default: + // We do not want to remove the token if we did not match. + continue; + }; + + aTokens.splice(i, 1); + } + + // Set the right JavaScript behavior based on our preference. Note that the + // preference is whether or not we should filter JavaScript, and the + // behavior is if we should search it or not. + if (!this._filterJavaScript) + this._setBehavior("javascript"); + + return { + query: this._getBoundSearchQuery(this._matchBehavior, aTokens), + tokens: aTokens + }; + }, + + /** + * Obtains the search query to be used based on the previously set search + * behaviors (accessed by this._hasBehavior). The query is bound and ready to + * execute. + * + * @param aMatchBehavior + * How this query should match its tokens to the search string. + * @param aTokens + * An array of search tokens. + * @return the correctly optimized query to search the database with and the + * new list of tokens to search with. The query has all the needed + * parameters bound, so consumers can execute it without doing any + * additional work. + */ + _getBoundSearchQuery: function PAC_getBoundSearchQuery(aMatchBehavior, + aTokens) + { + // We use more optimized queries for restricted searches, so we will always + // return the most restrictive one to the least restrictive one if more than + // one token is found. + let query = this._hasBehavior("tag") ? this._tagsQuery : + this._hasBehavior("bookmark") ? this._bookmarkQuery : + this._hasBehavior("typed") ? this._typedQuery : + this._hasBehavior("history") ? this._historyQuery : + this._defaultQuery; + + // Bind the needed parameters to the query so consumers can use it. + let (params = query.params) { + params.parent = this._bs.tagsFolder; + params.query_type = kQueryTypeFiltered; + params.matchBehavior = aMatchBehavior; + params.searchBehavior = this._behavior; + + // We only want to search the tokens that we are left with - not the + // original search string. + params.searchString = aTokens.join(" "); + } + + return query; + }, + + /** + * Obtains the keyword query with the properly bound parameters. + * + * @param aTokens + * The array of search tokens to check against. + * @return the bound keyword query. + */ + _getBoundKeywordQuery: function PAC_getBoundKeywordQuery(aTokens) + { + // The keyword is the first word in the search string, with the parameters + // following it. + let searchString = this._originalSearchString; + let queryString = searchString.substring(searchString.indexOf(" ") + 1); + + // We need to escape the parameters as if they were the query in a URL + queryString = encodeURIComponent(queryString).replace("%20", "+", "g"); + + // The first word could be a keyword, so that's what we'll search. + let keyword = aTokens[0]; + + let query = this._keywordQuery; + let (params = query.params) { + params.keyword = keyword; + params.query_string = queryString; + params.query_type = kQueryTypeKeyword; + } + + return query; + }, + + /** + * Obtains the adaptive query with the properly bound parameters. + * + * @return the bound adaptive query. + */ + _getBoundAdaptiveQuery: function PAC_getBoundAdaptiveQuery() + { + let query = this._adaptiveQuery; + let (params = query.params) { + params.parent = this._bs.tagsFolder; + params.search_string = this._currentSearchString; + params.query_type = kQueryTypeFiltered; + } + + return query; + }, + + /** + * Processes a mozIStorageRow to generate the proper data for the AutoComplete + * result. This will add an entry to the current result if it matches the + * criteria. + * + * @param aRow + * The row to process. + * @return true if the row is accepted, and false if not. + */ + _processRow: function PAC_processRow(aRow) + { + // Before we do any work, make sure this entry isn't already in our results. + let entryId = aRow.getResultByIndex(kQueryIndexPlaceId); + if (this._inResults(entryId)) + return false; + + let escapedEntryURL = aRow.getResultByIndex(kQueryIndexURL); + let entryTitle = aRow.getResultByIndex(kQueryIndexTitle) || ""; + let entryFavicon = aRow.getResultByIndex(kQueryIndexFaviconURL) || ""; + let entryParentId = aRow.getResultByIndex(kQueryIndexParentId); + let entryBookmarkTitle = entryParentId ? + aRow.getResultByIndex(kQueryIndexBookmarkTitle) : null; + let entryTags = aRow.getResultByIndex(kQueryIndexTags) || ""; + + // Always prefer the bookmark title unless it is empty + let title = entryBookmarkTitle || entryTitle; + + let style; + if (aRow.getResultByIndex(kQueryIndexQueryType) == kQueryTypeKeyword) { + // If we do not have a title, then we must have a keyword, so let the UI + // know it is a keyword. Otherwise, we found an exact page match, so just + // show the page like a regular result. Because the page title is likely + // going to be more specific than the bookmark title (keyword title). + if (!entryTitle) + style = "keyword"; + else + title = entryTitle; + } + + // We will always prefer to show tags if we have them. + let showTags = !!entryTags; + + // However, we'll act as if a page is not bookmarked or tagged if the user + // only wants only history and not bookmarks or tags. + if (this._hasBehavior("history") && + !(this._hasBehavior("bookmark") || this._hasBehavior("tag"))) { + showTags = false; + style = "favicon"; + } + + // If we have tags and should show them, we need to add them to the title. + if (showTags) + title += kTitleTagsSeparator + entryTags; + + // We have to determine the right style to display. Tags show the tag icon, + // bookmarks get the bookmark icon, and keywords get the keyword icon. If + // the result does not fall into any of those, it just gets the favicon. + if (!style) { + // It is possible that we already have a style set (from a keyword + // search), so only set it if we haven't already done so. + if (showTags) + style = "tag"; + else if (entryParentId) + style = "bookmark"; + else + style = "favicon"; + } + + // And finally add this to our results. + this._addToResults(entryId, escapedEntryURL, title, entryFavicon, style); + return true; + }, + + /** + * Checks to see if the given place has already been added to the results. + * + * @param aPlaceId + * The place_id to check for. + * @return true if the place has been added, false otherwise. + */ + _inResults: function PAC_inResults(aPlaceId) + { + return (aPlaceId in this._usedPlaceIds); + }, + + /** + * Adds a result to the AutoComplete results. Also tracks that we've added + * this place_id into the result set. + * + * @param aPlaceId + * The place_id of the item to be added to the result set. This is + * used by _inResults. + * @param aURISpec + * The URI spec for the entry. + * @param aTitle + * The title to give the entry. + * @param aFaviconSpec + * The favicon to give to the entry. + * @param aStyle + * Indicates how the entry should be styled when displayed. + */ + _addToResults: function PAC_addToResults(aPlaceId, aURISpec, aTitle, + aFaviconSpec, aStyle) + { + // Add this to our internal tracker to ensure duplicates do not end up in + // the result. _usedPlaceIds is an Object that is being used as a set. + this._usedPlaceIds[aPlaceId] = true; + + // Obtain the favicon for this URI. + let favicon; + if (aFaviconSpec) { + let uri = this._ioService.newURI(aFaviconSpec, null, null); + favicon = this._faviconService.getFaviconLinkForIcon(uri).spec; + } + favicon = favicon || this._faviconService.defaultFavicon.spec; + + this._result.appendMatch(aURISpec, aTitle, favicon, aStyle); + }, + + /** + * Determines if the specified AutoComplete behavior is set. + * + * @param aType + * The behavior type to test for. + * @return true if the behavior is set, false otherwise. + */ + _hasBehavior: function PAC_hasBehavior(aType) + { + let behavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]; + return (this._behavior & behavior); + }, + + /** + * Enables the desired AutoComplete behavior. + * + * @param aType + * The behavior type to set. + */ + _setBehavior: function PAC_setBehavior(aType) + { + let behavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]; + this._behavior |= behavior; + }, + + /** + * Determines if we are done searching or not. + * + * @return true if we have completed searching, false otherwise. + */ + isSearchComplete: function PAC_isSearchComplete() + { + // If _pendingQueries is null, we should no longer do any work since we have + // already called _finishSearch. This means we completed our search. + return this._pendingQueries == null; + }, + + /** + * Determines if the given handle of a pending statement is a pending search + * or not. + * + * @param aHandle + * A mozIStoragePendingStatement to check and see if we are waiting for + * results from it still. + * @return true if it is a pending query, false otherwise. + */ + isPendingSearch: function PAC_isPendingSearch(aHandle) + { + return this._pendingQueries.indexOf(aHandle) != -1; + }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsISupports + + classDescription: "AutoComplete result generator for Places.", + classID: Components.ID("d0272978-beab-4adc-a3d4-04b76acfa4e7"), + contractID: "@mozilla.org/autocomplete/search;1?name=history", + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIAutoCompleteSearch, + Ci.nsIAutoCompleteSimpleResultListener, + Ci.mozIStorageStatementCallback, + Ci.nsIObserver, + ]) +}; + +//////////////////////////////////////////////////////////////////////////////// +//// Module Registration + +let components = [nsPlacesAutoComplete]; +function NSGetModule(compMgr, fileSpec) +{ + return XPCOMUtils.generateModule(components); +} diff --git a/toolkit/components/places/src/nsPlacesModule.cpp b/toolkit/components/places/src/nsPlacesModule.cpp index b7d4bda1332..0a8b6fe3a7d 100644 --- a/toolkit/components/places/src/nsPlacesModule.cpp +++ b/toolkit/components/places/src/nsPlacesModule.cpp @@ -37,12 +37,6 @@ static const nsModuleComponentInfo components[] = nsNavHistoryConstructor, NS_NAVHISTORY_CLASSINFO }, - { "Browser Navigation History", - NS_NAVHISTORYSERVICE_CID, - "@mozilla.org/autocomplete/search;1?name=history", - nsNavHistoryConstructor, - NS_NAVHISTORY_CLASSINFO }, - { "Download Navigation History", NS_NAVHISTORYSERVICE_CID, NS_DOWNLOADHISTORY_CONTRACTID, diff --git a/toolkit/components/places/tests/autocomplete/head_autocomplete.js b/toolkit/components/places/tests/autocomplete/head_autocomplete.js index e3dd3a425cc..ec318cc40dd 100644 --- a/toolkit/components/places/tests/autocomplete/head_autocomplete.js +++ b/toolkit/components/places/tests/autocomplete/head_autocomplete.js @@ -113,7 +113,7 @@ function ensure_results(aSearch, aExpected) let value = controller.getValueAt(i); let comment = controller.getCommentAt(i); - print("Looking for " + value + ", " + comment + " in expected results..."); + print("Looking for '" + value + "', '" + comment + "' in expected results..."); let j; for (j = 0; j < aExpected.length; j++) { // Skip processed expected results @@ -126,6 +126,7 @@ function ensure_results(aSearch, aExpected) title = kTitles[title]; if (tags && appendTags) title += " \u2013 " + tags.map(function(aTag) kTitles[aTag]); + print("Checking against expected '" + uri + "', '" + title + "'..."); // Got a match on both uri and title? if (uri == value && title == comment) { @@ -138,10 +139,12 @@ function ensure_results(aSearch, aExpected) // We didn't hit the break, so we must have not found it if (j == aExpected.length) - do_throw("Didn't find the current result (" + value + ", " + comment + ") in expected: " + aExpected); + do_throw("Didn't find the current result ('" + value + "', '" + comment + "') in expected: " + aExpected); } // Make sure we have the right number of results + print("Expecting " + aExpected.length + " results; got " + + controller.matchCount + " results"); do_check_eq(controller.matchCount, aExpected.length); // If we expect results, make sure we got matches @@ -156,7 +159,7 @@ function ensure_results(aSearch, aExpected) do_test_finished(); }; - print("Searching for.. " + aSearch); + print("Searching for.. '" + aSearch + "'"); controller.startSearch(aSearch); } diff --git a/toolkit/components/places/tests/unit/test_000_frecency.js b/toolkit/components/places/tests/unit/test_000_frecency.js index 984dcf4fd27..743532a45eb 100644 --- a/toolkit/components/places/tests/unit/test_000_frecency.js +++ b/toolkit/components/places/tests/unit/test_000_frecency.js @@ -241,8 +241,8 @@ AutoCompleteInput.prototype = { function run_test() { var controller = Components.classes["@mozilla.org/autocomplete/controller;1"]. - getService(Components.interfaces.nsIAutoCompleteController); - + getService(Components.interfaces.nsIAutoCompleteController); + // Make an AutoCompleteInput that uses our searches // and confirms results on search complete var input = new AutoCompleteInput(["history"]); @@ -264,7 +264,7 @@ function run_test() { input.onSearchComplete = function() { do_check_eq(numSearchesStarted, 1); - do_check_eq(controller.searchStatus, + do_check_eq(controller.searchStatus, Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH); // test that all records with non-zero frecency were matched @@ -283,7 +283,8 @@ function run_test() { // undefined), so check if frecency matches. This is okay because we // can still ensure the correct number of expected frecencies. let getFrecency = function(aURL) aURL.match(/frecency:(-?\d+)$/)[1]; - print("### searchURL: '"+searchURL+"', expectedURL: '"+expectedURL+"'"); + print("### checking for same frecency between '" + searchURL + + "' and '" + expectURL + "'"); do_check_eq(getFrecency(searchURL), getFrecency(expectURL)); } } From 357681acb4f6edbc5833d2ccb56bc8d4a34bb124 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Mon, 13 Jul 2009 12:19:16 -0700 Subject: [PATCH 016/175] Bug 503360 - Better queries for asynchronous location bar This creates a better query based on what the asynchronous implementation of AutoComplete does. It also limits the amount of work to be done so we don't filter more results than we have to (even if it is happening on a background thread). r=sdwilsh --- .../places/src/nsPlacesAutoComplete.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/toolkit/components/places/src/nsPlacesAutoComplete.js b/toolkit/components/places/src/nsPlacesAutoComplete.js index 52c912cb6ba..f0ffebc86bf 100644 --- a/toolkit/components/places/src/nsPlacesAutoComplete.js +++ b/toolkit/components/places/src/nsPlacesAutoComplete.js @@ -105,11 +105,6 @@ const kQueryTypeFiltered = 1; // "comment" back into the title and the tag. const kTitleTagsSeparator = " \u2013 "; -// The list of columns in the moz_places table that we use in our queries. -const kMozPlacesColumns = - "id, url, title, rev_host, visit_count, hidden, typed, favicon_id, " + - "frecency, last_visit_date" - const kBrowserUrlbarBranch = "browser.urlbar."; //////////////////////////////////////////////////////////////////////////////// @@ -214,23 +209,24 @@ function nsPlacesAutoComplete() // h.visit_count which is better than nothing. This is slow, so not doing it // yet... // Note: h.frecency is only selected because we need it for ordering. - const SQL_BASE = - "SELECT h.url, h.title, f.url, " + kBookTagSQLFragment + ", h.visit_count, " + - "h.typed, h.id, :query_type " + - "FROM ( " + - "SELECT " + kMozPlacesColumns + " FROM moz_places_temp " + - "UNION ALL " + - "SELECT " + kMozPlacesColumns + " FROM moz_places " + - "WHERE +id NOT IN (SELECT id FROM moz_places_temp) " + - "ORDER BY frecency DESC " + - ") AS h " + - "LEFT OUTER JOIN moz_favicons f " + - "ON f.id = h.favicon_id " + - "WHERE h.frecency <> 0 " + - "AND AUTOCOMPLETE_MATCH(:searchString, h.url, IFNULL(bookmark, h.title), " + - "tags, h.visit_count, h.typed, parent, " + - ":matchBehavior, :searchBehavior) " + - "{ADDITIONAL_CONDITIONS}"; + function sql_base_fragment(aTableName) { + return "SELECT h.url, h.title, f.url, " + kBookTagSQLFragment + ", " + + "h.visit_count, h.typed, h.id, :query_type, h.frecency " + + "FROM " + aTableName + " h " + + "LEFT OUTER JOIN moz_favicons f ON f.id = h.favicon_id " + + "WHERE h.frecency <> 0 " + + "AND AUTOCOMPLETE_MATCH(:searchString, h.url, " + + "IFNULL(bookmark, h.title), tags, " + + "h.visit_count, h.typed, parent, " + + ":matchBehavior, :searchBehavior) " + + "{ADDITIONAL_CONDITIONS} "; + } + const SQL_BASE = sql_base_fragment("moz_places_temp") + + "UNION ALL " + + sql_base_fragment("moz_places") + + "AND +h.id NOT IN (SELECT id FROM moz_places_temp) " + + "ORDER BY h.frecency DESC " + + "LIMIT :maxResults"; ////////////////////////////////////////////////////////////////////////////// //// Smart Getters @@ -809,6 +805,10 @@ nsPlacesAutoComplete.prototype = { // We only want to search the tokens that we are left with - not the // original search string. params.searchString = aTokens.join(" "); + + // Limit the query to the the maximum number of desired results. + // This way we can avoid doing more work than needed. + params.maxResults = this._maxRichResults; } return query; From 970f437ddc81a1c3d0705f1367b2514abc4d85c8 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Tue, 14 Jul 2009 09:31:37 -0700 Subject: [PATCH 017/175] Addressing review comment for bug 455555. --- toolkit/components/places/src/nsPlacesAutoComplete.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/toolkit/components/places/src/nsPlacesAutoComplete.js b/toolkit/components/places/src/nsPlacesAutoComplete.js index f0ffebc86bf..568d31afe6a 100644 --- a/toolkit/components/places/src/nsPlacesAutoComplete.js +++ b/toolkit/components/places/src/nsPlacesAutoComplete.js @@ -989,8 +989,8 @@ nsPlacesAutoComplete.prototype = { */ _hasBehavior: function PAC_hasBehavior(aType) { - let behavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]; - return (this._behavior & behavior); + return (this._behavior & + Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]); }, /** @@ -1001,8 +1001,8 @@ nsPlacesAutoComplete.prototype = { */ _setBehavior: function PAC_setBehavior(aType) { - let behavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]; - this._behavior |= behavior; + this._behavior |= + Ci.mozIPlacesAutoComplete["BEHAVIOR_" + aType.toUpperCase()]; }, /** From bb7313647795559249fa025f81bf21b7fe2074f8 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 11:47:29 -0700 Subject: [PATCH 018/175] Bug 504422 - Be smarter with our strings for AUTOCOMPLETE_MATCH SQL function. Avoid copying are argument strings. String copying was showing up as 1% of the time spent in our function, when we should almost never need to copy. r=dwitte r=dietrich --- build/automationutils.py | 46 ---------------- .../components/places/src/SQLFunctions.cpp | 52 +++++++++++-------- toolkit/components/places/src/SQLFunctions.h | 6 ++- 3 files changed, 33 insertions(+), 71 deletions(-) delete mode 100644 build/automationutils.py diff --git a/build/automationutils.py b/build/automationutils.py deleted file mode 100644 index a76a992f641..00000000000 --- a/build/automationutils.py +++ /dev/null @@ -1,46 +0,0 @@ -import sys, glob, os, subprocess, logging - -__all__ = [ - "addCommonOptions", - "checkForCrashes", - ] - -log = logging.getLogger() - -def addCommonOptions(parser, defaults={}): - parser.add_option("--xre-path", - action = "store", type = "string", dest = "xrePath", - # individual scripts will set a sane default - default = None, - help = "absolute path to directory containing XRE (probably xulrunner)") - if 'SYMBOLS_PATH' not in defaults: - defaults['SYMBOLS_PATH'] = None - parser.add_option("--symbols-path", - action = "store", type = "string", dest = "symbolsPath", - default = defaults['SYMBOLS_PATH'], - help = "absolute path to directory containing breakpad symbols") - -def checkForCrashes(dumpDir, symbolsPath, testName=None): - stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) - # try to get the caller's filename if no test name is given - if testName is None: - try: - testName = os.path.basename(sys._getframe(1).f_code.co_filename) - except: - testName = "unknown" - - foundCrash = False - dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) - for d in dumps: - log.info("TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName) - if symbolsPath and stackwalkPath: - nullfd = open(os.devnull, 'w') - # eat minidump_stackwalk errors - subprocess.call([stackwalkPath, d, symbolsPath], stderr=nullfd) - nullfd.close() - os.remove(d) - extra = os.path.splitext(d)[0] + ".extra" - if os.path.exists(extra): - os.remove(extra) - foundCrash = True - return foundCrash diff --git a/toolkit/components/places/src/SQLFunctions.cpp b/toolkit/components/places/src/SQLFunctions.cpp index 8ca9b7a8d9c..5aad78ef03b 100644 --- a/toolkit/components/places/src/SQLFunctions.cpp +++ b/toolkit/components/places/src/SQLFunctions.cpp @@ -71,29 +71,29 @@ namespace places { } /* static */ - nsString - MatchAutoCompleteFunction::fixupURISpec(const nsDependentCString &aURISpec) + void + MatchAutoCompleteFunction::fixupURISpec(const nsDependentCString &aURISpec, + nsString &_fixedSpec) { - nsCAutoString unescapedSpec; + nsCString unescapedSpec; (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy, unescapedSpec); // If this unescaped string is valid UTF-8, we'll convert it. Otherwise, // we will simply convert our original string. - nsString fixedSpec; + NS_ASSERTION(_fixedSpec.IsEmpty(), + "Passing a non-empty string as an out parameter!"); if (IsUTF8(unescapedSpec)) - CopyUTF8toUTF16(unescapedSpec, fixedSpec); + CopyUTF8toUTF16(unescapedSpec, _fixedSpec); else - CopyUTF8toUTF16(aURISpec, fixedSpec); + CopyUTF8toUTF16(aURISpec, _fixedSpec); - if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("http://"))) - fixedSpec.Cut(0, 7); - else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("https://"))) - fixedSpec.Cut(0, 8); - else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("ftp://"))) - fixedSpec.Cut(0, 6); - - return fixedSpec; + if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("http://"))) + _fixedSpec.Cut(0, 7); + else if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("https://"))) + _fixedSpec.Cut(0, 8); + else if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("ftp://"))) + _fixedSpec.Cut(0, 6); } /* static */ @@ -214,10 +214,15 @@ namespace places { #define HAS_BEHAVIOR(aBitName) \ (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName) - nsDependentString searchString; - (void)aArguments->GetString(kArgSearchString, searchString); - nsDependentCString url; - (void)aArguments->GetUTF8String(kArgIndexURL, url); + // Temporaries used to get the strings. + const PRUnichar *wStr; + const char *str; + PRUint32 len; + + (void)aArguments->GetSharedString(kArgSearchString, &len, &wStr); + nsDependentString searchString(wStr, len / sizeof(PRUnichar)); + (void)aArguments->GetSharedUTF8String(kArgIndexURL, &len, &str); + nsDependentCString url(str, len); // We only want to filter javascript: URLs if we are not supposed to search // for them, and the search does not start with "javascript:". @@ -232,8 +237,8 @@ namespace places { PRInt32 visitCount = aArguments->AsInt32(kArgIndexVisitCount); bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false; bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false; - nsDependentString tags; - (void)aArguments->GetString(kArgIndexTags, tags); + (void)aArguments->GetSharedString(kArgIndexTags, &len, &wStr); + nsDependentString tags(wStr, len / sizeof(PRUnichar)); // Make sure we match all the filter requirements. If a given restriction // is active, make sure the corresponding condition is not true. @@ -250,14 +255,15 @@ namespace places { } // Clean up our URI spec and prepare it for searching. - nsString fixedURI = fixupURISpec(url); + nsString fixedURI; + fixupURISpec(url, fixedURI); // Obtain our search function. PRInt32 matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior); searchFunctionPtr searchFunction = getSearchFunction(matchBehavior); - nsDependentString title; - (void)aArguments->GetString(kArgIndexTitle, title); + (void)aArguments->GetSharedString(kArgIndexTitle, &len, &wStr); + nsDependentString title(wStr, len / sizeof(PRUnichar)); // Determine if every token matches either the bookmark title, tags, page // title, or page URL. diff --git a/toolkit/components/places/src/SQLFunctions.h b/toolkit/components/places/src/SQLFunctions.h index dc6e3f5d536..89074db17cb 100644 --- a/toolkit/components/places/src/SQLFunctions.h +++ b/toolkit/components/places/src/SQLFunctions.h @@ -197,9 +197,11 @@ private: * * @param aURISpec * The spec of the URI to prepare for searching. - * @return the new string to use for the URI spec. + * @param _fixedSpec + * An out parameter that is the fixed up string. */ - static nsString fixupURISpec(const nsDependentCString &aURISpec); + static void fixupURISpec(const nsDependentCString &aURISpec, + nsString &_fixedSpec); }; } // namespace places From 735095f74170a076ada7040fd38375527fd060a0 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 12:24:54 -0700 Subject: [PATCH 019/175] Reverting accidental changes to build/automationutils.py from changeset 06433d3dafd9. --- build/automationutils.py | 46 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 build/automationutils.py diff --git a/build/automationutils.py b/build/automationutils.py new file mode 100644 index 00000000000..a76a992f641 --- /dev/null +++ b/build/automationutils.py @@ -0,0 +1,46 @@ +import sys, glob, os, subprocess, logging + +__all__ = [ + "addCommonOptions", + "checkForCrashes", + ] + +log = logging.getLogger() + +def addCommonOptions(parser, defaults={}): + parser.add_option("--xre-path", + action = "store", type = "string", dest = "xrePath", + # individual scripts will set a sane default + default = None, + help = "absolute path to directory containing XRE (probably xulrunner)") + if 'SYMBOLS_PATH' not in defaults: + defaults['SYMBOLS_PATH'] = None + parser.add_option("--symbols-path", + action = "store", type = "string", dest = "symbolsPath", + default = defaults['SYMBOLS_PATH'], + help = "absolute path to directory containing breakpad symbols") + +def checkForCrashes(dumpDir, symbolsPath, testName=None): + stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) + # try to get the caller's filename if no test name is given + if testName is None: + try: + testName = os.path.basename(sys._getframe(1).f_code.co_filename) + except: + testName = "unknown" + + foundCrash = False + dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) + for d in dumps: + log.info("TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName) + if symbolsPath and stackwalkPath: + nullfd = open(os.devnull, 'w') + # eat minidump_stackwalk errors + subprocess.call([stackwalkPath, d, symbolsPath], stderr=nullfd) + nullfd.close() + os.remove(d) + extra = os.path.splitext(d)[0] + ".extra" + if os.path.exists(extra): + os.remove(extra) + foundCrash = True + return foundCrash From 51947037798372fb5630657376e767ab409d0647 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 14:04:12 -0700 Subject: [PATCH 020/175] Backed out changeset 06433d3dafd9 (bug 504422) because the patch is wrong. --- build/automationutils.py | 46 ++++++++++++++++ .../components/places/src/SQLFunctions.cpp | 52 ++++++++----------- toolkit/components/places/src/SQLFunctions.h | 6 +-- 3 files changed, 71 insertions(+), 33 deletions(-) create mode 100644 build/automationutils.py diff --git a/build/automationutils.py b/build/automationutils.py new file mode 100644 index 00000000000..a76a992f641 --- /dev/null +++ b/build/automationutils.py @@ -0,0 +1,46 @@ +import sys, glob, os, subprocess, logging + +__all__ = [ + "addCommonOptions", + "checkForCrashes", + ] + +log = logging.getLogger() + +def addCommonOptions(parser, defaults={}): + parser.add_option("--xre-path", + action = "store", type = "string", dest = "xrePath", + # individual scripts will set a sane default + default = None, + help = "absolute path to directory containing XRE (probably xulrunner)") + if 'SYMBOLS_PATH' not in defaults: + defaults['SYMBOLS_PATH'] = None + parser.add_option("--symbols-path", + action = "store", type = "string", dest = "symbolsPath", + default = defaults['SYMBOLS_PATH'], + help = "absolute path to directory containing breakpad symbols") + +def checkForCrashes(dumpDir, symbolsPath, testName=None): + stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) + # try to get the caller's filename if no test name is given + if testName is None: + try: + testName = os.path.basename(sys._getframe(1).f_code.co_filename) + except: + testName = "unknown" + + foundCrash = False + dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) + for d in dumps: + log.info("TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName) + if symbolsPath and stackwalkPath: + nullfd = open(os.devnull, 'w') + # eat minidump_stackwalk errors + subprocess.call([stackwalkPath, d, symbolsPath], stderr=nullfd) + nullfd.close() + os.remove(d) + extra = os.path.splitext(d)[0] + ".extra" + if os.path.exists(extra): + os.remove(extra) + foundCrash = True + return foundCrash diff --git a/toolkit/components/places/src/SQLFunctions.cpp b/toolkit/components/places/src/SQLFunctions.cpp index 5aad78ef03b..8ca9b7a8d9c 100644 --- a/toolkit/components/places/src/SQLFunctions.cpp +++ b/toolkit/components/places/src/SQLFunctions.cpp @@ -71,29 +71,29 @@ namespace places { } /* static */ - void - MatchAutoCompleteFunction::fixupURISpec(const nsDependentCString &aURISpec, - nsString &_fixedSpec) + nsString + MatchAutoCompleteFunction::fixupURISpec(const nsDependentCString &aURISpec) { - nsCString unescapedSpec; + nsCAutoString unescapedSpec; (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy, unescapedSpec); // If this unescaped string is valid UTF-8, we'll convert it. Otherwise, // we will simply convert our original string. - NS_ASSERTION(_fixedSpec.IsEmpty(), - "Passing a non-empty string as an out parameter!"); + nsString fixedSpec; if (IsUTF8(unescapedSpec)) - CopyUTF8toUTF16(unescapedSpec, _fixedSpec); + CopyUTF8toUTF16(unescapedSpec, fixedSpec); else - CopyUTF8toUTF16(aURISpec, _fixedSpec); + CopyUTF8toUTF16(aURISpec, fixedSpec); - if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("http://"))) - _fixedSpec.Cut(0, 7); - else if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("https://"))) - _fixedSpec.Cut(0, 8); - else if (StringBeginsWith(_fixedSpec, NS_LITERAL_STRING("ftp://"))) - _fixedSpec.Cut(0, 6); + if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("http://"))) + fixedSpec.Cut(0, 7); + else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("https://"))) + fixedSpec.Cut(0, 8); + else if (StringBeginsWith(fixedSpec, NS_LITERAL_STRING("ftp://"))) + fixedSpec.Cut(0, 6); + + return fixedSpec; } /* static */ @@ -214,15 +214,10 @@ namespace places { #define HAS_BEHAVIOR(aBitName) \ (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName) - // Temporaries used to get the strings. - const PRUnichar *wStr; - const char *str; - PRUint32 len; - - (void)aArguments->GetSharedString(kArgSearchString, &len, &wStr); - nsDependentString searchString(wStr, len / sizeof(PRUnichar)); - (void)aArguments->GetSharedUTF8String(kArgIndexURL, &len, &str); - nsDependentCString url(str, len); + nsDependentString searchString; + (void)aArguments->GetString(kArgSearchString, searchString); + nsDependentCString url; + (void)aArguments->GetUTF8String(kArgIndexURL, url); // We only want to filter javascript: URLs if we are not supposed to search // for them, and the search does not start with "javascript:". @@ -237,8 +232,8 @@ namespace places { PRInt32 visitCount = aArguments->AsInt32(kArgIndexVisitCount); bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false; bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false; - (void)aArguments->GetSharedString(kArgIndexTags, &len, &wStr); - nsDependentString tags(wStr, len / sizeof(PRUnichar)); + nsDependentString tags; + (void)aArguments->GetString(kArgIndexTags, tags); // Make sure we match all the filter requirements. If a given restriction // is active, make sure the corresponding condition is not true. @@ -255,15 +250,14 @@ namespace places { } // Clean up our URI spec and prepare it for searching. - nsString fixedURI; - fixupURISpec(url, fixedURI); + nsString fixedURI = fixupURISpec(url); // Obtain our search function. PRInt32 matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior); searchFunctionPtr searchFunction = getSearchFunction(matchBehavior); - (void)aArguments->GetSharedString(kArgIndexTitle, &len, &wStr); - nsDependentString title(wStr, len / sizeof(PRUnichar)); + nsDependentString title; + (void)aArguments->GetString(kArgIndexTitle, title); // Determine if every token matches either the bookmark title, tags, page // title, or page URL. diff --git a/toolkit/components/places/src/SQLFunctions.h b/toolkit/components/places/src/SQLFunctions.h index 89074db17cb..dc6e3f5d536 100644 --- a/toolkit/components/places/src/SQLFunctions.h +++ b/toolkit/components/places/src/SQLFunctions.h @@ -197,11 +197,9 @@ private: * * @param aURISpec * The spec of the URI to prepare for searching. - * @param _fixedSpec - * An out parameter that is the fixed up string. + * @return the new string to use for the URI spec. */ - static void fixupURISpec(const nsDependentCString &aURISpec, - nsString &_fixedSpec); + static nsString fixupURISpec(const nsDependentCString &aURISpec); }; } // namespace places From c2684cc40dc95438b601c4f7a9d11ac897663760 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 14:43:24 -0700 Subject: [PATCH 021/175] Bug 494826 - Compile SQLite with SQLITE_DEBUG defined Compile SQLite in debug mode in our debug builds. We don't hit any assertions in tests currently, and if we ever do, we've just found a bug in SQLite. r=ted --- config/rules.mk | 2 +- db/sqlite3/src/Makefile.in | 29 +++++++++++++++++++++++------ db/sqlite3/src/sqlite.def | 4 ++++ js/src/config/rules.mk | 2 +- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/config/rules.mk b/config/rules.mk index 62e61cca3a0..7c1bcfcf64f 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -337,7 +337,7 @@ endif # SHARED_LIBRARY_NAME endif # MOZ_MAPINFO ifdef DEFFILE -OS_LDFLAGS += -DEF:$(DEFFILE) +OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE)) EXTRA_DEPS += $(DEFFILE) endif diff --git a/db/sqlite3/src/Makefile.in b/db/sqlite3/src/Makefile.in index b40f74bc4ed..6ef8b3d82e6 100644 --- a/db/sqlite3/src/Makefile.in +++ b/db/sqlite3/src/Makefile.in @@ -53,14 +53,25 @@ LIB_IS_C_ONLY = 1 ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) ifndef GNU_CC -DEFFILE = $(win_srcdir)/sqlite.def +DEFFILE = $(CURDIR)/sqlite-processed.def RCFILE = sqlite.rc RESFILE = sqlite.res -GARBAGE += sqlite-version.h +GARBAGE += \ + sqlite-version.h \ + $(DEFFILE) \ + $(NULL) +# We generate the appropriate version header file with our python script. sqlite-version.h: sqlite-version.py sqlite3.h $(PYTHON) $^ > $@ + +# We have to preprocess our def file because we need different symbols in debug +# builds exposed that are not built in non-debug builds. +$(DEFFILE): sqlite.def + @$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ + $(srcdir)/sqlite.def > $(DEFFILE) + export:: sqlite-version.h endif endif @@ -84,8 +95,8 @@ EXPORTS = \ $(NULL) CSRCS = \ - sqlite3.c \ - $(NULL) + sqlite3.c \ + $(NULL) # -DSQLITE_SECURE_DELETE=1 will cause SQLITE to 0-fill delete data so we # don't have to vacuum to make sure the data is not visible in the file. @@ -103,6 +114,11 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) DEFINES += -DSQLITE_ENABLE_LOCKING_STYLE=1 endif +# Turn on SQLite's assertions in debug builds. +ifdef MOZ_DEBUG +DEFINES += -DSQLITE_DEBUG=1 +endif + ifeq ($(OS_ARCH),OS2) ifdef MOZ_OS2_HIGH_MEMORY DEFINES += -DOS2_HIGH_MEMORY @@ -115,6 +131,7 @@ include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES += -I$(srcdir) ifeq ($(OS_ARCH),OS2) -ADD_TO_DEF_FILE = sed -e '1,/^EXPORTS$$/ d' -e 's,sqlite3,_\0,' -e 's,\ DATA.*$$,,' \ - $(srcdir)/sqlite.def >> $(DEF_FILE) +ADD_TO_DEF_FILE = $(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \ + $(srcdir)/sqlite.def | sed -e '1,/^EXPORTS$$/ d' -e 's,sqlite3,_\0,' \ + -e 's,\ DATA.*$$,,' >> $(DEF_FILE) endif diff --git a/db/sqlite3/src/sqlite.def b/db/sqlite3/src/sqlite.def index 2e981e91c08..3aa5775b90d 100644 --- a/db/sqlite3/src/sqlite.def +++ b/db/sqlite3/src/sqlite.def @@ -175,3 +175,7 @@ EXPORTS sqlite3_vfs_unregister sqlite3_vfs_register sqlite3_vmprintf +#ifdef SQLITE_DEBUG + sqlite3_mutex_held + sqlite3_mutex_notheld +#endif diff --git a/js/src/config/rules.mk b/js/src/config/rules.mk index 62e61cca3a0..7c1bcfcf64f 100644 --- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -337,7 +337,7 @@ endif # SHARED_LIBRARY_NAME endif # MOZ_MAPINFO ifdef DEFFILE -OS_LDFLAGS += -DEF:$(DEFFILE) +OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE)) EXTRA_DEPS += $(DEFFILE) endif From 84784904c4234e7891589c905ec80cc95cdb9408 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:18:33 -0700 Subject: [PATCH 022/175] Bug 494828 - Stop using our own mutexes and use SQLite's where possible. Part 1: Create helper objects to make using sqlite3_mutex safer and easier. r=cjones --HG-- rename : xpcom/tests/TestDeadlockDetector.cpp => storage/test/test_deadlock_detector.cpp --- storage/src/SQLiteMutex.h | 207 ++++++++ storage/test/Makefile.in | 18 + storage/test/test_deadlock_detector.cpp | 633 ++++++++++++++++++++++++ storage/test/test_mutex.cpp | 117 +++++ xpcom/tests/TestDeadlockDetector.cpp | 4 +- 5 files changed, 977 insertions(+), 2 deletions(-) create mode 100644 storage/src/SQLiteMutex.h create mode 100644 storage/test/test_deadlock_detector.cpp create mode 100644 storage/test/test_mutex.cpp diff --git a/storage/src/SQLiteMutex.h b/storage/src/SQLiteMutex.h new file mode 100644 index 00000000000..def841079d1 --- /dev/null +++ b/storage/src/SQLiteMutex.h @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Shawn Wilsher (Original Author) + * + * 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_storage_SQLiteMutex_h_ +#define mozilla_storage_SQLiteMutex_h_ + +#include "mozilla/BlockingResourceBase.h" +#include "sqlite3.h" + +namespace mozilla { +namespace storage { + +/** + * Wrapper class for sqlite3_mutexes. To be used whenever we want to use a + * sqlite3_mutex. + * + * @warning Never EVER wrap the same sqlite3_mutex with a different SQLiteMutex. + * If you do this, you void the deadlock detector's warranty! + */ +class SQLiteMutex : private BlockingResourceBase +{ +public: + /** + * Constructs a wrapper for a sqlite3_mutex that has deadlock detecting. + * + * @param aName + * A name which can be used to reference this mutex. + */ + SQLiteMutex(const char *aName) + : BlockingResourceBase(aName, eMutex) + , mMutex(NULL) + { + } + + /** + * Sets the mutex that we are wrapping. We generally do not have access to + * our mutex at class construction, so we have to set it once we get access to + * it. + * + * @param aMutex + * The sqlite3_mutex that we are going to wrap. + */ + void initWithMutex(sqlite3_mutex *aMutex) + { + NS_ASSERTION(aMutex, "You must pass in a valid mutex!"); + NS_ASSERTION(!mMutex, "A mutex has already been set for this!"); + mMutex = aMutex; + } + +#ifndef DEBUG + /** + * Acquires the mutex. + */ + void lock() + { + sqlite3_mutex_enter(mMutex); + } + + /** + * Releases the mutex. + */ + void unlock() + { + sqlite3_mutex_leave(mMutex); + } + + /** + * Asserts that the current thread owns the mutex. + */ + void assertCurrentThreadOwns() + { + } + + /** + * Asserts that the current thread does not own the mutex. + */ + void assertNotCurrentThreadOwns() + { + } + +#else + void lock() + { + NS_ASSERTION(mMutex, "No mutex associated with this wrapper!"); + + // While SQLite Mutexes may be recursive, in our own code we do not want to + // treat them as such. + CallStack callContext = CallStack(); + + CheckAcquire(callContext); + sqlite3_mutex_enter(mMutex); + Acquire(callContext); // Call is protected by us holding the mutex. + } + + void unlock() + { + NS_ASSERTION(mMutex, "No mutex associated with this wrapper!"); + + // While SQLite Mutexes may be recursive, in our own code we do not want to + // treat them as such. + Release(); // Call is protected by us holding the mutex. + sqlite3_mutex_leave(mMutex); + } + + void assertCurrentThreadOwns() + { + NS_ASSERTION(mMutex, "No mutex associated with this wrapper!"); + NS_ASSERTION(sqlite3_mutex_held(mMutex), + "Mutex is not held, but we expect it to be!"); + } + + void assertNotCurrentThreadOwns() + { + NS_ASSERTION(mMutex, "No mutex associated with this wrapper!"); + NS_ASSERTION(sqlite3_mutex_notheld(mMutex), + "Mutex is held, but we expect it to not be!"); + } +#endif // ifndef DEBUG + +private: + sqlite3_mutex *mMutex; +}; + +/** + * Automatically acquires the mutex when it enters scope, and releases it when + * it leaves scope. + */ +class NS_STACK_CLASS SQLiteMutexAutoLock +{ +public: + SQLiteMutexAutoLock(SQLiteMutex &aMutex) + : mMutex(aMutex) + { + mMutex.lock(); + } + + ~SQLiteMutexAutoLock() + { + mMutex.unlock(); + } + +private: + SQLiteMutex &mMutex; +}; + +/** + * Automatically releases the mutex when it enters scope, and acquires it when + * it leaves scope. + */ +class NS_STACK_CLASS SQLiteMutexAutoUnlock +{ +public: + SQLiteMutexAutoUnlock(SQLiteMutex &aMutex) + : mMutex(aMutex) + { + mMutex.unlock(); + } + + ~SQLiteMutexAutoUnlock() + { + mMutex.lock(); + } + +private: + SQLiteMutex &mMutex; +}; + +} // namespace storage +} // namespace mozilla + +#endif // mozilla_storage_SQLiteMutex_h_ diff --git a/storage/test/Makefile.in b/storage/test/Makefile.in index 2b57865ed66..8924532a3a9 100644 --- a/storage/test/Makefile.in +++ b/storage/test/Makefile.in @@ -51,18 +51,36 @@ XPCSHELL_TESTS = unit CPP_UNIT_TESTS = \ test_transaction_helper.cpp \ test_statement_scoper.cpp \ + test_mutex.cpp \ $(NULL) +ifdef MOZ_DEBUG +# Testing assertion failures is fragile, and test_deadlock_detector doesn't like +# windows. +ifneq ($(OS_ARCH), WINNT) +ifneq ($(OS_ARCH), WINCE) +CPP_UNIT_TESTS += \ + test_deadlock_detector.cpp \ + $(NULL) +endif +endif +endif + REQUIRES = \ xpcom \ string \ storage \ $(NULL) +LOCAL_INCLUDES = \ + -I$(srcdir)/../src \ + $(NULL) + LIBS = \ $(LIBS_DIR) \ $(XPCOM_GLUE_LDOPTS) \ $(NSPR_LIBS) \ + $(SQLITE_LIBS) \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/storage/test/test_deadlock_detector.cpp b/storage/test/test_deadlock_detector.cpp new file mode 100644 index 00000000000..c4bab718e76 --- /dev/null +++ b/storage/test/test_deadlock_detector.cpp @@ -0,0 +1,633 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 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.org code. + * + * 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): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either of 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 ***** */ + +/** + * Note: This file is a copy of xpcom/tests/TestDeadlockDetector.cpp, but all + * mutexes were turned into SQLiteMutexes. + */ + +#include "prenv.h" +#include "prerror.h" +#include "prio.h" +#include "prproces.h" + +#include "nsMemory.h" + +#include "mozilla/CondVar.h" +#include "mozilla/Monitor.h" +#include "SQLiteMutex.h" + +#include "TestHarness.h" + +using namespace mozilla; + +/** + * Helper class to allocate a sqlite3_mutex for our SQLiteMutex. Also makes + * keeping the test files in sync easier. + */ +class TestMutex : public mozilla::storage::SQLiteMutex +{ +public: + TestMutex(const char* aName) + : mozilla::storage::SQLiteMutex(aName) + , mInner(sqlite3_mutex_alloc(SQLITE_MUTEX_FAST)) + { + NS_ASSERTION(mInner, "could not allocate a sqlite3_mutex"); + initWithMutex(mInner); + } + + ~TestMutex() + { + sqlite3_mutex_free(mInner); + } + + void Lock() + { + lock(); + } + + void Unlock() + { + unlock(); + } +private: + sqlite3_mutex *mInner; +}; + +static PRThread* +spawn(void (*run)(void*), void* arg) +{ + return PR_CreateThread(PR_SYSTEM_THREAD, + run, + arg, + PR_PRIORITY_NORMAL, + PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, + 0); +} + +#define PASS() \ + do { \ + passed(__FUNCTION__); \ + return NS_OK; \ + } while (0); + + +#define FAIL(why) \ + do { \ + fail(why); \ + return NS_ERROR_FAILURE; \ + } while (0); + +//----------------------------------------------------------------------------- + +static const char* sPathToThisBinary; +static const char* sAssertBehaviorEnv = "XPCOM_DEBUG_BREAK=abort"; + +class Subprocess +{ +public: + // not available until process finishes + PRInt32 mExitCode; + nsCString mStdout; + nsCString mStderr; + + Subprocess(const char* aTestName) { + // set up stdio redirection + PRFileDesc* readStdin; PRFileDesc* writeStdin; + PRFileDesc* readStdout; PRFileDesc* writeStdout; + PRFileDesc* readStderr; PRFileDesc* writeStderr; + PRProcessAttr* pattr = PR_NewProcessAttr(); + + NS_ASSERTION(pattr, "couldn't allocate process attrs"); + + NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdin, &writeStdin), + "couldn't create child stdin pipe"); + NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(readStdin, PR_TRUE), + "couldn't set child stdin inheritable"); + PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardInput, readStdin); + + NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStdout, &writeStdout), + "couldn't create child stdout pipe"); + NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStdout, PR_TRUE), + "couldn't set child stdout inheritable"); + PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardOutput, writeStdout); + + NS_ASSERTION(PR_SUCCESS == PR_CreatePipe(&readStderr, &writeStderr), + "couldn't create child stderr pipe"); + NS_ASSERTION(PR_SUCCESS == PR_SetFDInheritable(writeStderr, PR_TRUE), + "couldn't set child stderr inheritable"); + PR_ProcessAttrSetStdioRedirect(pattr, PR_StandardError, writeStderr); + + // set up argv with test name to run + char* const newArgv[3] = { + strdup(sPathToThisBinary), + strdup(aTestName), + 0 + }; + + // make sure the child will abort if an assertion fails + NS_ASSERTION(PR_SUCCESS == PR_SetEnv(sAssertBehaviorEnv), + "couldn't set XPCOM_DEBUG_BREAK env var"); + + PRProcess* proc; + NS_ASSERTION(proc = PR_CreateProcess(sPathToThisBinary, + newArgv, + 0, // inherit environment + pattr), + "couldn't create process"); + PR_Close(readStdin); + PR_Close(writeStdout); + PR_Close(writeStderr); + + mProc = proc; + mStdinfd = writeStdin; + mStdoutfd = readStdout; + mStderrfd = readStderr; + + free(newArgv[0]); + free(newArgv[1]); + PR_DestroyProcessAttr(pattr); + } + + void RunToCompletion(PRUint32 aWaitMs) + { + PR_Close(mStdinfd); + + PRPollDesc pollfds[2]; + PRInt32 nfds; + PRBool stdoutOpen = PR_TRUE, stderrOpen = PR_TRUE; + char buf[4096]; + PRInt32 len; + + PRIntervalTime now = PR_IntervalNow(); + PRIntervalTime deadline = now + PR_MillisecondsToInterval(aWaitMs); + + while ((stdoutOpen || stderrOpen) && now < deadline) { + nfds = 0; + if (stdoutOpen) { + pollfds[nfds].fd = mStdoutfd; + pollfds[nfds].in_flags = PR_POLL_READ; + pollfds[nfds].out_flags = 0; + ++nfds; + } + if (stderrOpen) { + pollfds[nfds].fd = mStderrfd; + pollfds[nfds].in_flags = PR_POLL_READ; + pollfds[nfds].out_flags = 0; + ++nfds; + } + + PRInt32 rv = PR_Poll(pollfds, nfds, deadline - now); + NS_ASSERTION(0 <= rv, PR_ErrorToName(PR_GetError())); + + if (0 == rv) { // timeout + Finish(PR_FALSE); // abnormal + return; + } + + for (PRInt32 i = 0; i < nfds; ++i) { + if (!pollfds[i].out_flags) + continue; + + PRBool isStdout = mStdoutfd == pollfds[i].fd; + + if (PR_POLL_READ & pollfds[i].out_flags) { + len = PR_Read(pollfds[i].fd, buf, sizeof(buf) - 1); + NS_ASSERTION(0 <= len, PR_ErrorToName(PR_GetError())); + } + else if (PR_POLL_HUP & pollfds[i].out_flags) { + len = 0; + } + else { + NS_ERROR(PR_ErrorToName(PR_GetError())); + } + + if (0 < len) { + buf[len] = '\0'; + if (isStdout) + mStdout += buf; + else + mStderr += buf; + } + else { + if (isStdout) { + stdoutOpen = PR_FALSE; + PR_Close(mStdoutfd); + } + else { + stderrOpen = PR_FALSE; + PR_Close(mStderrfd); + } + } + } + + now = PR_IntervalNow(); + } + + Finish(!stdoutOpen && !stderrOpen && now <= deadline); + } + +private: + void Finish(PRBool normalExit) { + if (!normalExit) { + PR_KillProcess(mProc); + PR_Close(mStdoutfd); + PR_Close(mStderrfd); + mExitCode = -1; + PRInt32 dummy; + PR_WaitProcess(mProc, &dummy); + } + else { + PR_WaitProcess(mProc, &mExitCode); // this had better not block ... + } + } + + PRProcess* mProc; + PRFileDesc* mStdinfd; // writeable + PRFileDesc* mStdoutfd; // readable + PRFileDesc* mStderrfd; // readable +}; + +//----------------------------------------------------------------------------- +// Harness for checking detector errors +bool +CheckForDeadlock(const char* test, const char* const* findTokens) +{ + Subprocess proc(test); + proc.RunToCompletion(1000); + + if (0 == proc.mExitCode) + return false; + + PRInt32 idx = 0; + for (const char* const* tp = findTokens; *tp; ++tp) { + const char* const token = *tp; +#ifdef MOZILLA_INTERNAL_API + idx = proc.mStderr.Find(token, PR_FALSE, idx); +#else + nsCString tokenCString(token); + idx = proc.mStderr.Find(tokenCString, idx); +#endif + if (-1 == idx) { + printf("(missed token '%s' in output)\n", token); + puts("----------------------------------\n"); + puts(proc.mStderr.get()); + puts("----------------------------------\n"); + return false; + } + idx += strlen(token); + } + + return true; +} + +//----------------------------------------------------------------------------- +// Single-threaded sanity tests + +// Stupidest possible deadlock. +nsresult +Sanity_Child() +{ + TestMutex m1("dd.sanity.m1"); + m1.Lock(); + m1.Lock(); + return 0; // not reached +} + +nsresult +Sanity() +{ + const char* const tokens[] = { + "###!!! ERROR: Potential deadlock detected", + "=== Cyclical dependency starts at\n--- Mutex : dd.sanity.m1", + "=== Cycle completed at\n--- Mutex : dd.sanity.m1", + "###!!! Deadlock may happen NOW!", // better catch these easy cases... + "###!!! ASSERTION: Potential deadlock detected", + 0 + }; + if (CheckForDeadlock("Sanity", tokens)) { + PASS(); + } else { + FAIL("deadlock not detected"); + } +} + +// Slightly less stupid deadlock. +nsresult +Sanity2_Child() +{ + TestMutex m1("dd.sanity2.m1"); + TestMutex m2("dd.sanity2.m2"); + m1.Lock(); + m2.Lock(); + m1.Lock(); + return 0; // not reached +} + +nsresult +Sanity2() +{ + const char* const tokens[] = { + "###!!! ERROR: Potential deadlock detected", + "=== Cyclical dependency starts at\n--- Mutex : dd.sanity2.m1", + "--- Next dependency:\n--- Mutex : dd.sanity2.m2", + "=== Cycle completed at\n--- Mutex : dd.sanity2.m1", + "###!!! Deadlock may happen NOW!", // better catch these easy cases... + "###!!! ASSERTION: Potential deadlock detected", + 0 + }; + if (CheckForDeadlock("Sanity2", tokens)) { + PASS(); + } else { + FAIL("deadlock not detected"); + } +} + + +nsresult +Sanity3_Child() +{ + TestMutex m1("dd.sanity3.m1"); + TestMutex m2("dd.sanity3.m2"); + TestMutex m3("dd.sanity3.m3"); + TestMutex m4("dd.sanity3.m4"); + + m1.Lock(); + m2.Lock(); + m3.Lock(); + m4.Lock(); + m4.Unlock(); + m3.Unlock(); + m2.Unlock(); + m1.Unlock(); + + m4.Lock(); + m1.Lock(); + return 0; +} + +nsresult +Sanity3() +{ + const char* const tokens[] = { + "###!!! ERROR: Potential deadlock detected", + "=== Cyclical dependency starts at\n--- Mutex : dd.sanity3.m1", + "--- Next dependency:\n--- Mutex : dd.sanity3.m2", + "--- Next dependency:\n--- Mutex : dd.sanity3.m3", + "--- Next dependency:\n--- Mutex : dd.sanity3.m4", + "=== Cycle completed at\n--- Mutex : dd.sanity3.m1", + "###!!! ASSERTION: Potential deadlock detected", + 0 + }; + if (CheckForDeadlock("Sanity3", tokens)) { + PASS(); + } else { + FAIL("deadlock not detected"); + } +} + + +nsresult +Sanity4_Child() +{ + mozilla::Monitor m1("dd.sanity4.m1"); + TestMutex m2("dd.sanity4.m2"); + m1.Enter(); + m2.Lock(); + m1.Enter(); + return 0; +} + +nsresult +Sanity4() +{ + const char* const tokens[] = { + "Re-entering Monitor after acquiring other resources", + "###!!! ERROR: Potential deadlock detected", + "=== Cyclical dependency starts at\n--- Monitor : dd.sanity4.m1", + "--- Next dependency:\n--- Mutex : dd.sanity4.m2", + "=== Cycle completed at\n--- Monitor : dd.sanity4.m1", + "###!!! ASSERTION: Potential deadlock detected", + 0 + }; + if (CheckForDeadlock("Sanity4", tokens)) { + PASS(); + } else { + FAIL("deadlock not detected"); + } +} + +//----------------------------------------------------------------------------- +// Multithreaded tests + +TestMutex* ttM1; +TestMutex* ttM2; + +static void +TwoThreads_thread(void* arg) +{ + PRInt32 m1First = NS_PTR_TO_INT32(arg); + if (m1First) { + ttM1->Lock(); + ttM2->Lock(); + ttM2->Unlock(); + ttM1->Unlock(); + } + else { + ttM2->Lock(); + ttM1->Lock(); + ttM1->Unlock(); + ttM2->Unlock(); + } +} + +nsresult +TwoThreads_Child() +{ + ttM1 = new TestMutex("dd.twothreads.m1"); + ttM2 = new TestMutex("dd.twothreads.m2"); + if (!ttM1 || !ttM2) + NS_RUNTIMEABORT("couldn't allocate mutexes"); + + PRThread* t1 = spawn(TwoThreads_thread, (void*) 0); + PR_JoinThread(t1); + + PRThread* t2 = spawn(TwoThreads_thread, (void*) 1); + PR_JoinThread(t2); + + return 0; +} + +nsresult +TwoThreads() +{ + const char* const tokens[] = { + "###!!! ERROR: Potential deadlock detected", + "=== Cyclical dependency starts at\n--- Mutex : dd.twothreads.m2", + "--- Next dependency:\n--- Mutex : dd.twothreads.m1", + "=== Cycle completed at\n--- Mutex : dd.twothreads.m2", + "###!!! ASSERTION: Potential deadlock detected", + 0 + }; + + if (CheckForDeadlock("TwoThreads", tokens)) { + PASS(); + } else { + FAIL("deadlock not detected"); + } +} + + +TestMutex* cndMs[4]; +const PRUint32 K = 100000; + +static void +ContentionNoDeadlock_thread(void* arg) +{ + PRInt32 starti = NS_PTR_TO_INT32(arg); + + for (PRUint32 k = 0; k < K; ++k) { + for (PRInt32 i = starti; i < (PRInt32) NS_ARRAY_LENGTH(cndMs); ++i) + cndMs[i]->Lock(); + // comment out the next two lines for deadlocking fun! + for (PRInt32 i = NS_ARRAY_LENGTH(cndMs) - 1; i >= starti; --i) + cndMs[i]->Unlock(); + + starti = (starti + 1) % 3; + } +} + +nsresult +ContentionNoDeadlock_Child() +{ + PRThread* threads[3]; + + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(cndMs); ++i) + cndMs[i] = new TestMutex("dd.cnd.ms"); + + for (PRInt32 i = 0; i < (PRInt32) NS_ARRAY_LENGTH(threads); ++i) + threads[i] = spawn(ContentionNoDeadlock_thread, NS_INT32_TO_PTR(i)); + + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(threads); ++i) + PR_JoinThread(threads[i]); + + for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(cndMs); ++i) + delete cndMs[i]; + + return 0; +} + +nsresult +ContentionNoDeadlock() +{ + const char * func = __func__; + Subprocess proc(func); + proc.RunToCompletion(10000); + if (0 != proc.mExitCode) { + printf("(expected 0 == return code, got %d)\n", proc.mExitCode); + puts("(output)\n----------------------------------\n"); + puts(proc.mStdout.get()); + puts("----------------------------------\n"); + puts("(error output)\n----------------------------------\n"); + puts(proc.mStderr.get()); + puts("----------------------------------\n"); + + FAIL("deadlock"); + } + PASS(); +} + + + +//----------------------------------------------------------------------------- + +int +main(int argc, char** argv) +{ + if (1 < argc) { + // XXX can we run w/o scoped XPCOM? + const char* test = argv[1]; + ScopedXPCOM xpcom(test); + if (xpcom.failed()) + return 1; + + // running in a spawned process. call the specificed child function. + if (!strcmp("Sanity", test)) + return Sanity_Child(); + if (!strcmp("Sanity2", test)) + return Sanity2_Child(); + if (!strcmp("Sanity3", test)) + return Sanity3_Child(); + if (!strcmp("Sanity4", test)) + return Sanity4_Child(); + + if (!strcmp("TwoThreads", test)) + return TwoThreads_Child(); + if (!strcmp("ContentionNoDeadlock", test)) + return ContentionNoDeadlock_Child(); + + FAIL("unknown child test"); + } + + ScopedXPCOM xpcom("Deadlock detector correctness"); + if (xpcom.failed()) + return 1; + + // in the first invocation of this process. we will be the "driver". + int rv = 0; + + sPathToThisBinary = argv[0]; + + if (NS_FAILED(Sanity())) + rv = 1; + if (NS_FAILED(Sanity2())) + rv = 1; + if (NS_FAILED(Sanity3())) + rv = 1; + if (NS_FAILED(Sanity4())) + rv = 1; + + if (NS_FAILED(TwoThreads())) + rv = 1; + if (NS_FAILED(ContentionNoDeadlock())) + rv = 1; + + return rv; +} diff --git a/storage/test/test_mutex.cpp b/storage/test/test_mutex.cpp new file mode 100644 index 00000000000..dfd20f1ea43 --- /dev/null +++ b/storage/test/test_mutex.cpp @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : + * ***** 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 storage test code. + * + * 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): + * Shawn Wilsher (Original Author) + * + * 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 "storage_test_harness.h" + +#include "SQLiteMutex.h" + +using namespace mozilla::storage; + +/** + * This file test our sqlite3_mutex wrapper in SQLiteMutex.h. + */ + +void +test_AutoLock() +{ + int lockTypes[] = { + SQLITE_MUTEX_FAST, + SQLITE_MUTEX_RECURSIVE, + }; + for (size_t i = 0; i < NS_ARRAY_LENGTH(lockTypes); i++) { + // Get our test mutex (we have to allocate a SQLite mutex of the right type + // too!). + SQLiteMutex mutex("TestMutex"); + sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]); + do_check_true(inner); + mutex.initWithMutex(inner); + + // And test that our automatic locking wrapper works as expected. + mutex.assertNotCurrentThreadOwns(); + { + SQLiteMutexAutoLock lockedScope(mutex); + mutex.assertCurrentThreadOwns(); + } + mutex.assertNotCurrentThreadOwns(); + + // Free the wrapped mutex - we don't need it anymore. + sqlite3_mutex_free(inner); + } +} + +void +test_AutoUnlock() +{ + int lockTypes[] = { + SQLITE_MUTEX_FAST, + SQLITE_MUTEX_RECURSIVE, + }; + for (size_t i = 0; i < NS_ARRAY_LENGTH(lockTypes); i++) { + // Get our test mutex (we have to allocate a SQLite mutex of the right type + // too!). + SQLiteMutex mutex("TestMutex"); + sqlite3_mutex *inner = sqlite3_mutex_alloc(lockTypes[i]); + do_check_true(inner); + mutex.initWithMutex(inner); + + // And test that our automatic unlocking wrapper works as expected. + { + SQLiteMutexAutoLock lockedScope(mutex); + + { + SQLiteMutexAutoUnlock unlockedScope(mutex); + mutex.assertNotCurrentThreadOwns(); + } + mutex.assertCurrentThreadOwns(); + } + + // Free the wrapped mutex - we don't need it anymore. + sqlite3_mutex_free(inner); + } +} + +void (*gTests[])(void) = { + test_AutoLock, + test_AutoUnlock, +}; + +const char *file = __FILE__; +#define TEST_NAME "SQLiteMutex" +#define TEST_FILE file +#include "storage_test_harness_tail.h" diff --git a/xpcom/tests/TestDeadlockDetector.cpp b/xpcom/tests/TestDeadlockDetector.cpp index 4d7f0b0d67a..5dca5a0ae82 100644 --- a/xpcom/tests/TestDeadlockDetector.cpp +++ b/xpcom/tests/TestDeadlockDetector.cpp @@ -16,8 +16,8 @@ * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 + * Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): From bc4dacbd7547998ad2e98739645906993178b25b Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:18:33 -0700 Subject: [PATCH 023/175] Bug 494828 - Stop using our own mutexes and use SQLite's where possible. Part 2: Use the helper object in a few places where we had our own mutexes. r=asuth --- db/sqlite3/src/sqlite.def | 1 + storage/src/mozStorageConnection.cpp | 41 ++++++++++------------------ storage/src/mozStorageConnection.h | 21 ++++++++++++-- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/db/sqlite3/src/sqlite.def b/db/sqlite3/src/sqlite.def index 3aa5775b90d..bcaf4ed374c 100644 --- a/db/sqlite3/src/sqlite.def +++ b/db/sqlite3/src/sqlite.def @@ -83,6 +83,7 @@ EXPORTS sqlite3_create_module sqlite3_data_count sqlite3_db_handle + sqlite3_db_mutex sqlite3_declare_vtab sqlite3_enable_load_extension sqlite3_enable_shared_cache diff --git a/storage/src/mozStorageConnection.cpp b/storage/src/mozStorageConnection.cpp index 29c3e23d055..349bcc81085 100644 --- a/storage/src/mozStorageConnection.cpp +++ b/storage/src/mozStorageConnection.cpp @@ -249,10 +249,8 @@ Connection::Connection(Service *aService) , mDBConn(nsnull) , mAsyncExecutionMutex(nsAutoLock::NewLock("AsyncExecutionMutex")) , mAsyncExecutionThreadShuttingDown(PR_FALSE) -, mTransactionMutex(nsAutoLock::NewLock("TransactionMutex")) +, mDBMutex("Connection::mDBMutex") , mTransactionInProgress(PR_FALSE) -, mFunctionsMutex(nsAutoLock::NewLock("FunctionsMutex")) -, mProgressHandlerMutex(nsAutoLock::NewLock("ProgressHandlerMutex")) , mProgressHandler(nsnull) , mStorageService(aService) { @@ -263,9 +261,6 @@ Connection::~Connection() { (void)Close(); nsAutoLock::DestroyLock(mAsyncExecutionMutex); - nsAutoLock::DestroyLock(mTransactionMutex); - nsAutoLock::DestroyLock(mFunctionsMutex); - nsAutoLock::DestroyLock(mProgressHandlerMutex); } NS_IMPL_THREADSAFE_ISUPPORTS1( @@ -301,9 +296,6 @@ Connection::initialize(nsIFile *aDatabaseFile) { NS_ASSERTION (!mDBConn, "Initialize called on already opened database!"); NS_ENSURE_TRUE(mAsyncExecutionMutex, NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mTransactionMutex, NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mFunctionsMutex, NS_ERROR_OUT_OF_MEMORY); - NS_ENSURE_TRUE(mProgressHandlerMutex, NS_ERROR_OUT_OF_MEMORY); int srv; nsresult rv; @@ -326,6 +318,9 @@ Connection::initialize(nsIFile *aDatabaseFile) return convertResultCode(srv); } + // Properly wrap the database handle's mutex. + mDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn)); + #ifdef PR_LOGGING if (!gStorageLog) gStorageLog = ::PR_NewLogModule("mozStorage"); @@ -444,7 +439,7 @@ Connection::databaseElementExists(enum DatabaseElementType aElementType, bool Connection::findFunctionByInstance(nsISupports *aInstance) { - PR_ASSERT_CURRENT_THREAD_OWNS_LOCK(mFunctionsMutex); + mDBMutex.assertCurrentThreadOwns(); FFEArguments args = { aInstance, false }; mFunctions.EnumerateRead(findFunctionEnumerator, &args); return args.found; @@ -460,7 +455,7 @@ Connection::sProgressHelper(void *aArg) int Connection::progressHandler() { - nsAutoLock mutex(mProgressHandlerMutex); + mDBMutex.assertCurrentThreadOwns(); if (mProgressHandler) { PRBool result; nsresult rv = mProgressHandler->OnProgress(this, &result); @@ -512,12 +507,6 @@ Connection::Close() } #endif - { - nsAutoLock mutex(mProgressHandlerMutex); - if (mProgressHandler) - ::sqlite3_progress_handler(mDBConn, 0, NULL, NULL); - } - int srv = ::sqlite3_close(mDBConn); NS_ASSERTION(srv == SQLITE_OK, "sqlite3_close failed. There are probably outstanding statements that are listed above!"); @@ -716,7 +705,7 @@ Connection::IndexExists(const nsACString &aIndexName, NS_IMETHODIMP Connection::GetTransactionInProgress(PRBool *_inProgress) { - nsAutoLock mutex(mTransactionMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); *_inProgress = mTransactionInProgress; return NS_OK; } @@ -730,7 +719,7 @@ Connection::BeginTransaction() NS_IMETHODIMP Connection::BeginTransactionAs(PRInt32 aTransactionType) { - nsAutoLock mutex(mTransactionMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); if (mTransactionInProgress) return NS_ERROR_FAILURE; nsresult rv; @@ -755,7 +744,7 @@ Connection::BeginTransactionAs(PRInt32 aTransactionType) NS_IMETHODIMP Connection::CommitTransaction() { - nsAutoLock mutex(mTransactionMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); if (!mTransactionInProgress) return NS_ERROR_FAILURE; nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION")); @@ -767,7 +756,7 @@ Connection::CommitTransaction() NS_IMETHODIMP Connection::RollbackTransaction() { - nsAutoLock mutex(mTransactionMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); if (!mTransactionInProgress) return NS_ERROR_FAILURE; nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION")); @@ -801,7 +790,7 @@ Connection::CreateFunction(const nsACString &aFunctionName, // Check to see if this function is already defined. We only check the name // because a function can be defined with the same body but different names. - nsAutoLock mutex(mFunctionsMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); int srv = ::sqlite3_create_function(mDBConn, @@ -829,7 +818,7 @@ Connection::CreateAggregateFunction(const nsACString &aFunctionName, if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Check to see if this function name is already defined. - nsAutoLock mutex(mFunctionsMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); // Because aggregate functions depend on state across calls, you cannot have @@ -859,7 +848,7 @@ Connection::RemoveFunction(const nsACString &aFunctionName) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; - nsAutoLock mutex(mFunctionsMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); int srv = ::sqlite3_create_function(mDBConn, @@ -886,7 +875,7 @@ Connection::SetProgressHandler(PRInt32 aGranularity, if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Return previous one - nsAutoLock mutex(mProgressHandlerMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); NS_IF_ADDREF(*_oldHandler = mProgressHandler); if (!aHandler || aGranularity <= 0) { @@ -905,7 +894,7 @@ Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler) if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Return previous one - nsAutoLock mutex(mProgressHandlerMutex); + SQLiteMutexAutoLock lockedScope(mDBMutex); NS_IF_ADDREF(*_oldHandler = mProgressHandler); mProgressHandler = nsnull; diff --git a/storage/src/mozStorageConnection.h b/storage/src/mozStorageConnection.h index c47bd90905b..54a6598d1b9 100644 --- a/storage/src/mozStorageConnection.h +++ b/storage/src/mozStorageConnection.h @@ -48,6 +48,7 @@ #include "nsString.h" #include "nsInterfaceHashtable.h" #include "mozIStorageProgressHandler.h" +#include "SQLiteMutex.h" #include "mozIStorageConnection.h" #include "mozStorageService.h" @@ -158,13 +159,27 @@ private: */ PRBool mAsyncExecutionThreadShuttingDown; - PRLock *mTransactionMutex; + /** + * Wraps the mutex that SQLite gives us from sqlite3_db_mutex. + */ + SQLiteMutex mDBMutex; + + /** + * Tracks if we have a transaction in progress or not. Access protected by + * mDBMutex. + */ PRBool mTransactionInProgress; - PRLock *mFunctionsMutex; + /** + * Stores the mapping of a given function by name to its instance. Access is + * protected by mDBMutex. + */ nsInterfaceHashtable mFunctions; - PRLock *mProgressHandlerMutex; + /** + * Stores the registered progress handler for the database connection. Access + * is protected by mDBMutex. + */ nsCOMPtr mProgressHandler; // This is here for two reasons: 1) It's used to make sure that the From 4d9568aa5e6381fee09f7662ddafa6fb9863998b Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:18:33 -0700 Subject: [PATCH 024/175] Bug 505550 - Acquire the db's mutex when cloning statements in Connection::ExecuteAsync Hold the lock around all our API calls so we don't lose so much to the background thread. r=asuth --- storage/src/mozStorageConnection.cpp | 70 +++++++++++++++------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/storage/src/mozStorageConnection.cpp b/storage/src/mozStorageConnection.cpp index 349bcc81085..deecbc23011 100644 --- a/storage/src/mozStorageConnection.cpp +++ b/storage/src/mozStorageConnection.cpp @@ -630,42 +630,45 @@ Connection::ExecuteAsync(mozIStorageStatement **aStatements, { int rc = SQLITE_OK; nsTArray stmts(aNumStatements); - for (PRUint32 i = 0; i < aNumStatements && rc == SQLITE_OK; i++) { - sqlite3_stmt *old_stmt = - static_cast(aStatements[i])->nativeStatement(); - if (!old_stmt) { - rc = SQLITE_MISUSE; - break; - } - NS_ASSERTION(::sqlite3_db_handle(old_stmt) == mDBConn, - "Statement must be from this database connection!"); + { + SQLiteMutexAutoLock lockedScope(mDBMutex); + for (PRUint32 i = 0; i < aNumStatements && rc == SQLITE_OK; i++) { + sqlite3_stmt *old_stmt = + static_cast(aStatements[i])->nativeStatement(); + if (!old_stmt) { + rc = SQLITE_MISUSE; + break; + } + NS_ASSERTION(::sqlite3_db_handle(old_stmt) == mDBConn, + "Statement must be from this database connection!"); - // Clone this statement. We only need a sqlite3_stmt object, so we can - // avoid all the extra work that making a new Statement would normally - // involve and use the SQLite API. - sqlite3_stmt *new_stmt; - rc = ::sqlite3_prepare_v2(mDBConn, ::sqlite3_sql(old_stmt), -1, &new_stmt, - NULL); - if (rc != SQLITE_OK) - break; + // Clone this statement. We only need a sqlite3_stmt object, so we can + // avoid all the extra work that making a new Statement would normally + // involve and use the SQLite API. + sqlite3_stmt *new_stmt; + rc = ::sqlite3_prepare_v2(mDBConn, ::sqlite3_sql(old_stmt), -1, &new_stmt, + NULL); + if (rc != SQLITE_OK) + break; #ifdef PR_LOGGING - PR_LOG(gStorageLog, PR_LOG_NOTICE, - ("Cloned statement 0x%p to 0x%p", old_stmt, new_stmt)); + PR_LOG(gStorageLog, PR_LOG_NOTICE, + ("Cloned statement 0x%p to 0x%p", old_stmt, new_stmt)); #endif - // Transfer the bindings - rc = sqlite3_transfer_bindings(old_stmt, new_stmt); - if (rc != SQLITE_OK) - break; + // Transfer the bindings + rc = sqlite3_transfer_bindings(old_stmt, new_stmt); + if (rc != SQLITE_OK) + break; - Statement *storageStmt = static_cast(aStatements[i]); - StatementData data(new_stmt, storageStmt->bindingParamsArray()); - if (!stmts.AppendElement(data)) { - rc = SQLITE_NOMEM; - break; - } - } + Statement *storageStmt = static_cast(aStatements[i]); + StatementData data(new_stmt, storageStmt->bindingParamsArray()); + if (!stmts.AppendElement(data)) { + rc = SQLITE_NOMEM; + break; + } + } // for loop + } // locked Scope // Dispatch to the background nsresult rv = NS_OK; @@ -682,8 +685,11 @@ Connection::ExecuteAsync(mozIStorageStatement **aStatements, } // Always reset all the statements - for (PRUint32 i = 0; i < aNumStatements; i++) - (void)aStatements[i]->Reset(); + { + SQLiteMutexAutoLock lockedScope(mDBMutex); + for (PRUint32 i = 0; i < aNumStatements; i++) + (void)aStatements[i]->Reset(); + } return rv; } From 3207e737a20847df0ba419afa7e5c7c45450f031 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:18:34 -0700 Subject: [PATCH 025/175] Bug 505813 - Use the connections executeAsync instead of calling it on each statement. r=dietrich --- .../places/src/nsPlacesAutoComplete.js | 58 +++++++++---------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/toolkit/components/places/src/nsPlacesAutoComplete.js b/toolkit/components/places/src/nsPlacesAutoComplete.js index 568d31afe6a..9d95b0fbe8b 100644 --- a/toolkit/components/places/src/nsPlacesAutoComplete.js +++ b/toolkit/components/places/src/nsPlacesAutoComplete.js @@ -143,10 +143,14 @@ function best_favicon_for_revhost(aTableName) * * @param aCallback * A reference to a nsPlacesAutoComplete. + * @param aDBConnection + * The database connection to execute the queries on. */ -function AutoCompleteStatementCallbackWrapper(aCallback) +function AutoCompleteStatementCallbackWrapper(aCallback, + aDBConnection) { this._callback = aCallback; + this._db = aDBConnection; } AutoCompleteStatementCallbackWrapper.prototype = { @@ -179,13 +183,15 @@ AutoCompleteStatementCallbackWrapper.prototype = { * Executes the specified query asynchronously. This object will notify * this._callback if we should notify (logic explained in handleCompletion). * - * @param aQuery - * The query to execute asynchronously. - * @return a mozIStoragePendingStatement that can be used to cancel the query. + * @param aQueries + * The queries to execute asynchronously. + * @return a mozIStoragePendingStatement that can be used to cancel the + * queries. */ - executeAsync: function ACSCW_executeAsync(aQuery) + executeAsync: function ACSCW_executeAsync(aQueries) { - return this._handle = aQuery.executeAsync(this); + return this._handle = this._db.executeAsync(aQueries, aQueries.length, + this); }, ////////////////////////////////////////////////////////////////////////////// @@ -438,7 +444,7 @@ nsPlacesAutoComplete.prototype = { stopSearch: function PAC_stopSearch() { // We need to cancel our searches so we do not get any [more] results. - this._stopActiveQueries(); + this._stopActiveQuery(); this._finishSearch(false); }, @@ -463,8 +469,8 @@ nsPlacesAutoComplete.prototype = { haveMatches = haveMatches || match; if (this._result.matchCount == this._maxRichResults) { - // We have enough results, so stop running our queries. - this._stopActiveQueries(); + // We have enough results, so stop running our search. + this._stopActiveQuery(); // And finish our search. this._finishSearch(true); @@ -489,15 +495,6 @@ nsPlacesAutoComplete.prototype = { if (this.isSearchComplete()) return; - // Remove the first query in our array of pending queries since it is the - // one we are getting notified about. - this._pendingQueries.shift(); - - // If we still have pending queries, we bail out because we aren't done - // getting results. - if (this._pendingQueries.length) - return; - // If we do not have enough results, and our match type is // MATCH_BOUNDARY_ANYWHERE, search again with MATCH_ANYWHERE to get more // results. @@ -610,6 +607,7 @@ nsPlacesAutoComplete.prototype = { delete this._listener; delete this._result; delete this._usedPlaceIds; + delete this._pendingQuery; this._secondPass = false; }, @@ -624,21 +622,19 @@ nsPlacesAutoComplete.prototype = { // Because we might get a handleCompletion for canceled queries, we want to // filter out queries we no longer care about (described in the // handleCompletion implementation of AutoCompleteStatementCallbackWrapper). - let PAC = this; - this._pendingQueries = aQueries.map(function(aQuery) { - // Create our wrapper object and execute the query. - let callbackWrapper = new AutoCompleteStatementCallbackWrapper(PAC); - return callbackWrapper.executeAsync(aQuery); - }); + + // Create our wrapper object and execute the queries. + let wrapper = new AutoCompleteStatementCallbackWrapper(this, this._db); + this._pendingQuery = wrapper.executeAsync(aQueries); }, /** - * Stops executing all active queries. + * Stops executing our active query. */ - _stopActiveQueries: function PAC_stopActiveQueries() + _stopActiveQuery: function PAC_stopActiveQuery() { - this._pendingQueries.forEach(function(aQuery) aQuery.cancel()); - delete this._pendingQueries; + this._pendingQuery.cancel(); + delete this._pendingQuery; }, /** @@ -1012,9 +1008,9 @@ nsPlacesAutoComplete.prototype = { */ isSearchComplete: function PAC_isSearchComplete() { - // If _pendingQueries is null, we should no longer do any work since we have + // If _pendingQuery is null, we should no longer do any work since we have // already called _finishSearch. This means we completed our search. - return this._pendingQueries == null; + return this._pendingQuery == null; }, /** @@ -1028,7 +1024,7 @@ nsPlacesAutoComplete.prototype = { */ isPendingSearch: function PAC_isPendingSearch(aHandle) { - return this._pendingQueries.indexOf(aHandle) != -1; + return this._pendingQuery == aHandle; }, ////////////////////////////////////////////////////////////////////////////// From 18bc7a3beb69a41091eeef2aa6a7a2ab826a82e1 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:18:34 -0700 Subject: [PATCH 026/175] Bug 505871 - Use a timeout before searching in the location bar Searches are really fast, but we don't want to do too much extra work. So, use a timeout. In most cases, a user should get a result within 100ms still, which should feel instantaneous. r=dietrich --- browser/base/content/browser.xul | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 393b8807545..928c1a6de44 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -384,6 +384,7 @@ showimagecolumn="true" enablehistory="true" maxrows="6" + timeout="75" newlines="stripsurroundingwhitespace" oninput="gBrowser.userTypedValue = this.value;" ontextentered="this.handleCommand(param);" From 247b160761f903fd7abde7378671a62929843e28 Mon Sep 17 00:00:00 2001 From: Shawn Wilsher Date: Wed, 22 Jul 2009 15:29:43 -0700 Subject: [PATCH 027/175] Bug 504384 - Excessive Flickering with the Asynchronous Location Bar This removes the synchronous call to adjustHeight. The adjustHeight call running in the setTimeout will run after _appendCurrentResult has added our results, and after any overflow has happened that we might hit (see _adjustAcItem). r=gavin --- toolkit/content/widgets/autocomplete.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/toolkit/content/widgets/autocomplete.xml b/toolkit/content/widgets/autocomplete.xml index 7774281c8a5..83826ee32f4 100644 --- a/toolkit/content/widgets/autocomplete.xml +++ b/toolkit/content/widgets/autocomplete.xml @@ -975,13 +975,15 @@ Date: Wed, 29 Jul 2009 14:33:53 -0400 Subject: [PATCH 028/175] Bug 478416, replace chromedir attribute with :moz-locale-dir pseudoclass, r=neil,dao,dbaron,sr=dbaron --- browser/base/content/browser-context.inc | 2 - browser/base/content/browser-doctype.inc | 2 - browser/base/content/browser-menubar.inc | 2 - browser/base/content/browser-places.js | 4 +- browser/base/content/browser.css | 2 +- browser/base/content/browser.js | 4 +- browser/base/content/browser.xul | 20 +- browser/base/content/pageinfo/pageInfo.xul | 5 +- browser/base/content/tabbrowser.xml | 23 +- browser/base/content/web-panels.xul | 2 - browser/components/places/content/places.xml | 5 - browser/components/places/content/places.xul | 8 +- browser/components/places/content/toolbar.xml | 3 - browser/components/search/content/search.xml | 8 +- .../themes/gnomestripe/browser/browser.css | 42 ++-- .../gnomestripe/browser/places/organizer.css | 8 +- .../themes/gnomestripe/browser/searchbar.css | 2 +- browser/themes/pinstripe/browser/browser.css | 92 ++++---- .../pinstripe/browser/places/organizer.css | 28 +-- .../themes/pinstripe/browser/searchbar.css | 24 +- .../themes/winstripe/browser/browser-aero.css | 10 +- browser/themes/winstripe/browser/browser.css | 124 +++++----- .../winstripe/browser/places/organizer.css | 18 +- .../themes/winstripe/browser/searchbar.css | 6 +- content/base/public/nsIDocument.h | 13 +- content/base/src/nsGkAtomList.h | 1 + content/xul/content/src/nsXULElement.cpp | 21 +- content/xul/document/public/nsIXULDocument.h | 5 + content/xul/document/src/Makefile.in | 3 + content/xul/document/src/nsXULDocument.cpp | 104 +++++++++ content/xul/document/src/nsXULDocument.h | 18 +- layout/style/nsCSSParser.cpp | 8 + layout/style/nsCSSPseudoClassList.h | 4 + layout/style/nsCSSPseudoClasses.cpp | 3 +- layout/style/nsCSSRuleProcessor.cpp | 29 +++ layout/style/test/test_selectors.html | 10 + modules/libpref/src/init/all.js | 5 + .../components/console/content/console.xul | 5 +- toolkit/components/help/content/help.xul | 10 +- .../help/content/helpContextOverlay.xul | 4 - .../printing/content/printPreviewBindings.xml | 10 +- toolkit/content/tests/chrome/Makefile.in | 4 + .../content/tests/chrome/rtltest/Makefile.in | 49 ++++ .../content/tests/chrome/rtltest/dirtest.xul | 25 ++ .../tests/chrome/rtltest/righttoleft.manifest | 3 + .../content/tests/chrome/test_righttoleft.xul | 217 ++++++++++++++++++ toolkit/content/textbox.css | 4 +- toolkit/content/widgets/datetimepicker.xml | 22 +- toolkit/content/widgets/findbar.xml | 5 - toolkit/content/widgets/menu.xml | 12 +- toolkit/content/widgets/resizer.xml | 15 -- toolkit/content/widgets/scrollbox.xml | 13 +- toolkit/content/widgets/tabbox.xml | 7 +- toolkit/content/widgets/textbox.xml | 14 +- toolkit/content/widgets/toolbarbutton.xml | 9 +- toolkit/content/widgets/tree.xml | 11 +- toolkit/content/widgets/wizard.xml | 14 +- toolkit/content/xul.css | 6 +- .../mozapps/extensions/content/extensions.xml | 2 - .../mozapps/extensions/content/extensions.xul | 4 +- toolkit/themes/faststripe/global/tabbox.css | 18 +- toolkit/themes/faststripe/global/textbox.css | 2 +- toolkit/themes/gnomestripe/global/button.css | 8 +- toolkit/themes/gnomestripe/global/findBar.css | 8 +- .../gnomestripe/global/printPreview.css | 8 +- toolkit/themes/gnomestripe/global/textbox.css | 2 +- toolkit/themes/gnomestripe/global/tree.css | 8 +- toolkit/themes/gnomestripe/help/help.css | 16 +- .../pinstripe/global/datetimepicker.css | 2 +- toolkit/themes/pinstripe/global/findBar.css | 8 +- toolkit/themes/pinstripe/global/resizer.css | 4 +- toolkit/themes/pinstripe/global/tree.css | 8 +- .../themes/pinstripe/global/viewbuttons.css | 16 +- .../winstripe/global/datetimepicker.css | 12 +- .../themes/winstripe/global/printPreview.css | 16 +- toolkit/themes/winstripe/global/resizer.css | 6 +- toolkit/themes/winstripe/global/scrollbox.css | 16 +- toolkit/themes/winstripe/global/tabbox.css | 18 +- toolkit/themes/winstripe/global/textbox.css | 2 +- toolkit/themes/winstripe/global/tree-aero.css | 8 +- toolkit/themes/winstripe/global/tree.css | 8 +- toolkit/themes/winstripe/help/help.css | 2 +- 82 files changed, 857 insertions(+), 472 deletions(-) create mode 100644 toolkit/content/tests/chrome/rtltest/Makefile.in create mode 100644 toolkit/content/tests/chrome/rtltest/dirtest.xul create mode 100644 toolkit/content/tests/chrome/rtltest/righttoleft.manifest create mode 100644 toolkit/content/tests/chrome/test_righttoleft.xul diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index ddd1f60ec43..93979c16369 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -167,13 +167,11 @@ %browserDTD; - -%globalDTD; %globalRegionDTD; diff --git a/browser/base/content/browser-menubar.inc b/browser/base/content/browser-menubar.inc index e186a1af955..485c041c639 100644 --- a/browser/base/content/browser-menubar.inc +++ b/browser/base/content/browser-menubar.inc @@ -474,7 +474,6 @@ #else key="goBackKb" #endif - chromedir="&locale.dir;" command="Browser:BackOrBackDuplicate" onclick="checkForMiddleClick(this, event);"/> toolbaritem > hbox[type="places"] { display: none; } -#feed-menu[chromedir="rtl"] > menuitem { +#feed-menu:-moz-locale-dir(rtl) > menuitem { direction: rtl; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 2cae4f4b2d5..12f78515ddd 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -6628,9 +6628,7 @@ var gIdentityHandler = { // Make sure the identity popup hangs toward the middle of the location bar // in RTL builds - var position = 'after_start'; - if (gURLBar.getAttribute("chromedir") == "rtl") - position = 'after_end'; + var position = (getComputedStyle(gNavToolbox, "").direction == "rtl") ? 'after_end' : 'after_start'; // Add the "open" attribute to the identity box for styling this._identityBox.setAttribute("open", "true"); diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index de65307112f..d8a9a5022e7 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -104,17 +104,16 @@ - - + #ifdef XP_MACOSX + diff --git a/browser/components/places/content/toolbar.xml b/browser/components/places/content/toolbar.xml index 7638d6baa65..ec06ddb933a 100644 --- a/browser/components/places/content/toolbar.xml +++ b/browser/components/places/content/toolbar.xml @@ -41,8 +41,6 @@ -%globalDTD; %browserDTD; ]> @@ -80,7 +78,6 @@ mousethrough="never" collapsed="true" tooltiptext="&bookmarksToolbarChevron.tooltip;" - chromedir="&locale.dir;" onpopupshowing="chevronPopupShowing(event);"> %searchBarDTD; - -%globalDTD; %browserDTD; ]> @@ -80,8 +78,7 @@ xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines"> + anonid="searchbar-engine-button"> - + diff --git a/browser/themes/gnomestripe/browser/browser.css b/browser/themes/gnomestripe/browser/browser.css index d1196ad6f88..5669fb879a3 100644 --- a/browser/themes/gnomestripe/browser/browser.css +++ b/browser/themes/gnomestripe/browser/browser.css @@ -401,13 +401,13 @@ menuitem:not([type]) { list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu&state=disabled"); } -#historyMenuBack[chromedir="rtl"], -#context-back[chromedir="rtl"] { +#historyMenuBack:-moz-locale-dir(rtl), +#context-back:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu"); } -#historyMenuBack[disabled][chromedir="rtl"], -#context-back[disabled][chromedir="rtl"] { +#historyMenuBack[disabled]:-moz-locale-dir(rtl), +#context-back[disabled]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu&state=disabled"); } @@ -421,13 +421,13 @@ menuitem:not([type]) { list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu&state=disabled"); } -#historyMenuForward[chromedir="rtl"], -#context-forward[chromedir="rtl"] { +#historyMenuForward:-moz-locale-dir(rtl), +#context-forward:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu"); } -#historyMenuForward[disabled][chromedir="rtl"], -#context-forward[disabled][chromedir="rtl"] { +#historyMenuForward[disabled]:-moz-locale-dir(rtl), +#context-forward[disabled]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled"); } @@ -522,10 +522,10 @@ toolbar[mode="full"] .toolbarbutton-menubutton-button { list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled"); } -#back-button[chromedir="rtl"] { +#back-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar"); } -#back-button[disabled="true"][chromedir="rtl"] { +#back-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled"); } @@ -536,10 +536,10 @@ toolbar[mode="full"] .toolbarbutton-menubutton-button { list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled"); } -#forward-button[chromedir="rtl"] { +#forward-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar"); } -#forward-button[disabled="true"][chromedir="rtl"] { +#forward-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled"); } @@ -648,13 +648,13 @@ toolbar[iconsize="small"] #back-button[disabled="true"] { list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu&state=disabled"); } -toolbar[iconsize="small"] #back-button[chromedir="rtl"] { +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu"); } -menupopup[chromedir="rtl"] > .unified-nav-back[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-back[_moz-menuactive] { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu") !important; } -toolbar[iconsize="small"] #back-button[disabled="true"][chromedir="rtl"] { +toolbar[iconsize="small"] #back-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu&state=disabled"); } @@ -668,13 +668,13 @@ toolbar[iconsize="small"] #forward-button[disabled="true"] { list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu&state=disabled"); } -toolbar[iconsize="small"] #forward-button[chromedir="rtl"] { +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu"); } -menupopup[chromedir="rtl"] > .unified-nav-forward[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-forward[_moz-menuactive] { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu") !important; } -toolbar[iconsize="small"] #forward-button[disabled="true"][chromedir="rtl"] { +toolbar[iconsize="small"] #forward-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled"); } @@ -833,7 +833,7 @@ toolbar[iconsize="small"] #paste-button[disabled="true"] { direction: ltr !important; } -#PopupAutoComplete[chromedir="rtl"] > tree > treerows { +#PopupAutoComplete:-moz-locale-dir(rtl) > tree > treerows { direction: rtl; } @@ -875,7 +875,7 @@ toolbar[iconsize="small"] #paste-button[disabled="true"] { -moz-border-end: 1px solid ThreeDShadow; } -#identity-box[chromedir="rtl"] { +#identity-box:-moz-locale-dir(rtl) { -moz-border-start: 1px solid ThreeDShadow; } @@ -1338,7 +1338,7 @@ toolbarbutton.chevron { list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important; } -toolbarbutton.chevron[chromedir="rtl"] { +toolbarbutton.chevron:-moz-locale-dir(rtl) { list-style-image: url("chrome://global/skin/toolbar/chevron-rtl.gif") !important; } diff --git a/browser/themes/gnomestripe/browser/places/organizer.css b/browser/themes/gnomestripe/browser/places/organizer.css index ca7b67f3f21..f9a3afba1f7 100644 --- a/browser/themes/gnomestripe/browser/places/organizer.css +++ b/browser/themes/gnomestripe/browser/places/organizer.css @@ -13,10 +13,10 @@ list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled"); } -#back-button[chromedir="rtl"] { +#back-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar"); } -#back-button[disabled="true"][chromedir="rtl"] { +#back-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled"); } @@ -29,10 +29,10 @@ list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled"); } -#forward-button[chromedir="rtl"] { +#forward-button:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar"); } -#forward-button[disabled="true"][chromedir="rtl"] { +#forward-button[disabled="true"]:-moz-locale-dir(rtl) { list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled"); } diff --git a/browser/themes/gnomestripe/browser/searchbar.css b/browser/themes/gnomestripe/browser/searchbar.css index d034e68922a..5c2d6cc1db7 100644 --- a/browser/themes/gnomestripe/browser/searchbar.css +++ b/browser/themes/gnomestripe/browser/searchbar.css @@ -73,7 +73,7 @@ cursor: pointer; } -.search-go-button[chromedir="rtl"] { +.search-go-button:-moz-locale-dir(rtl) { -moz-transform: scaleX(-1); } diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css index 7a9ce2268aa..eb205da1db4 100644 --- a/browser/themes/pinstripe/browser/browser.css +++ b/browser/themes/pinstripe/browser/browser.css @@ -124,12 +124,12 @@ toolbarbutton.bookmark-item[type=menu] > .toolbarbutton-menu-dropmarker { -moz-padding-end: 7px; } -toolbarbutton.bookmark-item[type=menu][chromedir="rtl"] > .toolbarbutton-menu-dropmarker { +toolbarbutton.bookmark-item[type=menu]:-moz-locale-dir(rtl) > .toolbarbutton-menu-dropmarker { -moz-padding-start: 4px; -moz-padding-end: 2px; } -toolbarbutton.bookmark-item[chromedir="rtl"] { +toolbarbutton.bookmark-item:-moz-locale-dir(rtl) { -moz-padding-start: 0px; -moz-padding-end: 7px; } @@ -150,7 +150,7 @@ toolbarbutton.bookmark-item:not([container]) > .toolbarbutton-text { -moz-padding-end: 7px; } -toolbarbutton.bookmark-item[chromedir="rtl"] > .toolbarbutton-text { +toolbarbutton.bookmark-item:-moz-locale-dir(rtl) > .toolbarbutton-text { -moz-padding-end: 0; -moz-padding-start: 7px; } @@ -170,14 +170,14 @@ toolbarbutton.bookmark-item[container]:not([open]):hover > .toolbarbutton-menu-d } toolbarbutton.bookmark-item[container]:hover > .toolbarbutton-text, -toolbarbutton.bookmark-item[container][chromedir="rtl"]:not([open]):hover > .toolbarbutton-menu-dropmarker, +toolbarbutton.bookmark-item[container]:-moz-locale-dir(rtl):not([open]):hover > .toolbarbutton-menu-dropmarker, #home-button.bookmark-item:hover > .toolbarbutton-icon, -#home-button.bookmark-item[chromedir="rtl"]:hover > .toolbarbutton-text { +#home-button.bookmark-item:-moz-locale-dir(rtl):hover > .toolbarbutton-text { background: url("chrome://global/skin/toolbar/toolbarbutton-customhover-mid.png") repeat-x; } -#home-button.bookmark-item:hover[chromedir="rtl"] > .toolbarbutton-icon, -toolbarbutton.bookmark-item[chromedir="rtl"]:hover > .toolbarbutton-text { +#home-button.bookmark-item:hover:-moz-locale-dir(rtl) > .toolbarbutton-icon, +toolbarbutton.bookmark-item:-moz-locale-dir(rtl):hover > .toolbarbutton-text { background: url("chrome://global/skin/toolbar/toolbarbutton-customhover-right.png") no-repeat right top; } @@ -190,15 +190,15 @@ toolbarbutton.bookmark-item[container]:hover:active > .toolbarbutton-menu-dropma toolbarbutton.bookmark-item[container]:hover:active > .toolbarbutton-text, toolbarbutton.bookmark-item[container][open="true"] > .toolbarbutton-text, #home-button.bookmark-item:hover:active > .toolbarbutton-icon, -toolbarbutton.bookmark-item[container][chromedir="rtl"]:hover:active > .toolbarbutton-menu-dropmarker, -toolbarbutton.bookmark-item[container][chromedir="rtl"][open="true"] > .toolbarbutton-menu-dropmarker, -#home-button.bookmark-item:hover:active[chromedir="rtl"] > .toolbarbutton-text { +toolbarbutton.bookmark-item[container]:-moz-locale-dir(rtl):hover:active > .toolbarbutton-menu-dropmarker, +toolbarbutton.bookmark-item[container]:-moz-locale-dir(rtl)[open="true"] > .toolbarbutton-menu-dropmarker, +#home-button.bookmark-item:hover:active:-moz-locale-dir(rtl) > .toolbarbutton-text { background: url("chrome://browser/skin/bookmark-open-mid.png") repeat-x !important; } -toolbarbutton.bookmark-item[chromedir="rtl"][container]:hover:active > .toolbarbutton-text, -toolbarbutton.bookmark-item[chromedir="rtl"][container][open] > .toolbarbutton-text, -#home-button.bookmark-item[chromedir="rtl"]:hover:active > .toolbarbutton-icon { +toolbarbutton.bookmark-item:-moz-locale-dir(rtl)[container]:hover:active > .toolbarbutton-text, +toolbarbutton.bookmark-item:-moz-locale-dir(rtl)[container][open] > .toolbarbutton-text, +#home-button.bookmark-item:-moz-locale-dir(rtl):hover:active > .toolbarbutton-icon { background: url("chrome://browser/skin/bookmark-open-right.png") no-repeat right top !important; } @@ -290,8 +290,8 @@ toolbarbutton.bookmark-item-microsummarized { -moz-box-orient: vertical; } -.toolbarbutton-1[chromedir="rtl"], -#back-forward-dropmarker[chromedir="rtl"] { +.toolbarbutton-1:-moz-locale-dir(rtl), +#back-forward-dropmarker:-moz-locale-dir(rtl) { list-style-image: url("chrome://browser/skin/Toolbar-rtl.png"); } @@ -375,7 +375,7 @@ toolbar[mode="icons"] #unified-back-forward-button > #back-button { border-right: none; } -toolbar[mode="icons"] #unified-back-forward-button > #back-button[chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 571px, 33px, 540px); } @@ -383,7 +383,7 @@ toolbar[mode="icons"] #unified-back-forward-button > #back-button[disabled="true -moz-image-region: rect(33px, 535px, 66px, 504px); } -toolbar[mode="icons"] #unified-back-forward-button > #back-button[disabled="true"][chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #back-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(33px, 571px, 66px, 540px); } @@ -392,8 +392,8 @@ toolbar[mode="icons"] #unified-back-forward-button > #back-button[open="true"] { -moz-image-region: rect(66px, 535px, 99px, 504px); } -toolbar[mode="icons"] #unified-back-forward-button > #back-button:hover:active[chromedir="rtl"]:not([disabled]), -toolbar[mode="icons"] #unified-back-forward-button > #back-button[open="true"][chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #back-button:hover:active:-moz-locale-dir(rtl):not([disabled]), +toolbar[mode="icons"] #unified-back-forward-button > #back-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(66px, 571px, 99px, 540px); } @@ -407,7 +407,7 @@ toolbar[mode="icons"] #unified-back-forward-button > #forward-button { border-right: none; } -toolbar[mode="icons"] #unified-back-forward-button > #forward-button[chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 540px, 33px, 514px); } @@ -415,7 +415,7 @@ toolbar[mode="icons"] #unified-back-forward-button > #forward-button[disabled="t -moz-image-region: rect(33px, 560px, 66px, 535px); } -toolbar[mode="icons"] #unified-back-forward-button > #forward-button[disabled="true"][chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #forward-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(33px, 540px, 66px, 514px); } @@ -425,8 +425,8 @@ toolbar[mode="icons"] #unified-back-forward-button > #forward-button[open="true" -moz-margin-start: -5px; } -toolbar[mode="icons"] #unified-back-forward-button > #forward-button[chromedir="rtl"]:hover:active:not([disabled]), -toolbar[mode="icons"] #unified-back-forward-button > #forward-button[open="true"][chromedir="rtl"] { +toolbar[mode="icons"] #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl):hover:active:not([disabled]), +toolbar[mode="icons"] #unified-back-forward-button > #forward-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(99px, 545px, 132px, 514px); } @@ -440,7 +440,7 @@ toolbar[mode="icons"] #unified-back-forward-button > #forward-button[open="true" border-right: none; } -#back-forward-dropmarker[chromedir="rtl"] { +#back-forward-dropmarker:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 514px, 33px, 504px); } @@ -458,7 +458,7 @@ toolbar[mode="icons"] #back-forward-dropmarker > dropmarker { -moz-image-region: rect(33px, 571px, 66px, 560px); } -#back-forward-dropmarker[disabled="true"][chromedir="rtl"] { +#back-forward-dropmarker[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(33px, 514px, 66px, 504px); } @@ -467,18 +467,18 @@ toolbar[mode="icons"] #back-forward-dropmarker > dropmarker { -moz-image-region: rect(66px, 571px, 99px, 560px); } -#back-forward-dropmarker[chromedir="rtl"]:hover:active:not([disabled]), -#back-forward-dropmarker[open="true"][chromedir="rtl"] { +#back-forward-dropmarker:-moz-locale-dir(rtl):hover:active:not([disabled]), +#back-forward-dropmarker[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(66px, 514px, 99px, 504px); } .unified-nav-back[_moz-menuactive], -menupopup[chromedir="rtl"] > .unified-nav-forward[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-forward[_moz-menuactive] { list-style-image: url("chrome://browser/skin/menu-back.png") !important; } .unified-nav-forward[_moz-menuactive], -menupopup[chromedir="rtl"] > .unified-nav-back[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-back[_moz-menuactive] { list-style-image: url("chrome://browser/skin/menu-forward.png") !important; } @@ -493,7 +493,7 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-but border-right: none; } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 648px, 23px, 614px); } @@ -501,7 +501,7 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-but -moz-image-region: rect(23px, 605px, 46px, 571px); } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[disabled="true"][chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(23px, 648px, 46px, 614px); } @@ -510,8 +510,8 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-but -moz-image-region: rect(46px, 605px, 69px, 571px); } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[chromedir="rtl"]:hover:active:not([disabled]), -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[open="true"][chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button:-moz-locale-dir(rtl):hover:active:not([disabled]), +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #back-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(46px, 648px, 69px, 614px); } @@ -525,7 +525,7 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward- border-right: none; } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 614px, 23px, 580px); } @@ -533,7 +533,7 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward- -moz-image-region: rect(23px, 638px, 46px, 605px); } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[disabled="true"][chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(23px, 614px, 46px, 580px); } @@ -543,8 +543,8 @@ toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward- -moz-margin-start: 0; } -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[chromedir="rtl"]:hover:active:not([disabled]), -toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[open="true"][chromedir="rtl"] { +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl):hover:active:not([disabled]), +toolbar[mode="icons"][iconsize="small"] #unified-back-forward-button > #forward-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(46px, 614px, 69px, 580px); } @@ -554,7 +554,7 @@ toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarke -moz-image-region: rect(0px, 648px, 23px, 638px); } -toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[chromedir="rtl"] { +toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 580px, 23px, 571px); } @@ -562,7 +562,7 @@ toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarke -moz-image-region: rect(23px, 648px, 46px, 638px); } -toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[disabled="true"][chromedir="rtl"] { +toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(23px, 580px, 46px, 571px); } @@ -571,8 +571,8 @@ toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarke -moz-image-region: rect(46px, 648px, 69px, 638px); } -toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[chromedir="rtl"]:hover:active:not([disabled]), -toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[open="true"][chromedir="rtl"] { +toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker:-moz-locale-dir(rtl):hover:active:not([disabled]), +toolbar[iconsize="small"] #unified-back-forward-button > #back-forward-dropmarker[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(46px, 580px, 69px, 571px); } @@ -1461,7 +1461,7 @@ toolbarbutton.chevron { list-style-image: url("chrome://global/skin/icons/chevron.png") !important; } -toolbarbutton.chevron[chromedir="rtl"] { +toolbarbutton.chevron:-moz-locale-dir(rtl) { list-style-image: url("chrome://global/skin/icons/chevron-rtl.png") !important; } @@ -1663,15 +1663,15 @@ tabbrowser > tabbox > tabpanels { border: none; } -.tabbrowser-arrowscrollbox > .scrollbutton-up[chromedir="ltr"], -.tabbrowser-arrowscrollbox > .scrollbutton-down-stack > .scrollbutton-down[chromedir="rtl"] { +.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(ltr), +.tabbrowser-arrowscrollbox > .scrollbutton-down-stack > .scrollbutton-down:-moz-locale-dir(rtl) { border-right: 2px solid; -moz-border-right-colors: rgba(0,0,0,0.25) rgba(255,255,255,0.15); list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png"); } -.tabbrowser-arrowscrollbox > .scrollbutton-down-stack > .scrollbutton-down[chromedir="ltr"], -.tabbrowser-arrowscrollbox > .scrollbutton-up[chromedir="rtl"] { +.tabbrowser-arrowscrollbox > .scrollbutton-down-stack > .scrollbutton-down:-moz-locale-dir(ltr), +.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl) { border-left: 2px solid; -moz-border-left-colors: rgba(0,0,0,0.25) rgba(255,255,255,0.15); list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-right.png"); diff --git a/browser/themes/pinstripe/browser/places/organizer.css b/browser/themes/pinstripe/browser/places/organizer.css index a997a0e6a4a..a5805a92210 100644 --- a/browser/themes/pinstripe/browser/places/organizer.css +++ b/browser/themes/pinstripe/browser/places/organizer.css @@ -96,7 +96,7 @@ border-right: none; } -#back-button[chromedir="rtl"] { +#back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 67px, 23px, 34px); } @@ -104,7 +104,7 @@ -moz-image-region: rect(23px, 34px, 46px, 0px) !important; } -#back-button[disabled="true"][chromedir="rtl"] { +#back-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(23px, 67px, 46px, 34px) !important; } @@ -114,9 +114,9 @@ -moz-image-region: rect(46px, 34px, 69px, 0px); } -#back-button:hover:active[chromedir="rtl"], -#back-button[buttondown="true"][chromedir="rtl"], -#back-button[open="true"][chromedir="rtl"] { +#back-button:hover:active:-moz-locale-dir(rtl), +#back-button[buttondown="true"]:-moz-locale-dir(rtl), +#back-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(46px, 67px, 69px, 34px); } @@ -136,7 +136,7 @@ border-right: none; } -#forward-button[chromedir="rtl"] { +#forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 34px, 23px, 0px); } @@ -144,7 +144,7 @@ -moz-image-region: rect(23px, 67px, 46px, 34px) !important; } -#forward-button[disabled="true"][chromedir="rtl"] { +#forward-button[disabled="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(23px, 34px, 46px, 0px) !important; } @@ -154,9 +154,9 @@ -moz-image-region: rect(46px, 67px, 69px, 34px); } -#forward-button:hover:active[chromedir="rtl"], -#forward-button[buttondown="true"][chromedir="rtl"], -#forward-button[open="true"][chromedir="rtl"] { +#forward-button:hover:active:-moz-locale-dir(rtl), +#forward-button[buttondown="true"]:-moz-locale-dir(rtl), +#forward-button[open="true"]:-moz-locale-dir(rtl) { -moz-image-region: rect(46px, 34px, 69px, 0px); } @@ -169,10 +169,10 @@ background: url("chrome://browser/skin/places/menubutton-end-pressed.png") right center no-repeat; } -#placesToolbar[chromedir="rtl"] > toolbarbutton[type="menu"] { +#placesToolbar:-moz-locale-dir(rtl) > toolbarbutton[type="menu"] { background: url("chrome://browser/skin/places/menubutton-end-rtl.png") left center no-repeat; } -#placesToolbar[chromedir="rtl"] > toolbarbutton[type="menu"][open="true"] { +#placesToolbar:-moz-locale-dir(rtl) > toolbarbutton[type="menu"][open="true"] { background: url("chrome://browser/skin/places/menubutton-end-pressed-rtl.png") left center no-repeat; } @@ -192,10 +192,10 @@ background: url("chrome://browser/skin/places/menubutton-start-pressed.png") left center no-repeat; } -#placesToolbar[chromedir="rtl"] > toolbarbutton[type="menu"] > .toolbarbutton-icon { +#placesToolbar:-moz-locale-dir(rtl) > toolbarbutton[type="menu"] > .toolbarbutton-icon { background: url("chrome://browser/skin/places/menubutton-start-rtl.png") right center no-repeat; } -#placesToolbar[chromedir="rtl"] > toolbarbutton[type="menu"][open="true"] > .toolbarbutton-icon { +#placesToolbar:-moz-locale-dir(rtl) > toolbarbutton[type="menu"][open="true"] > .toolbarbutton-icon { background: url("chrome://browser/skin/places/menubutton-start-pressed-rtl.png") right center no-repeat; } diff --git a/browser/themes/pinstripe/browser/searchbar.css b/browser/themes/pinstripe/browser/searchbar.css index ef80b3b5277..59a7325afb5 100644 --- a/browser/themes/pinstripe/browser/searchbar.css +++ b/browser/themes/pinstripe/browser/searchbar.css @@ -45,7 +45,7 @@ padding: 0; } -.searchbar-engine-button[chromedir="rtl"] { +.searchbar-engine-button:-moz-locale-dir(rtl) { background-image: url("chrome://browser/skin/urlbar/startcap-rtl.png"); } @@ -53,7 +53,7 @@ background-image: url("chrome://browser/skin/urlbar/startcap-focused.png"); } -.searchbar-textbox[focused="true"] > .searchbar-engine-button[chromedir="rtl"] { +.searchbar-textbox[focused="true"] > .searchbar-engine-button:-moz-locale-dir(rtl) { background-image: url("chrome://browser/skin/urlbar/startcap-focused-rtl.png"); } @@ -61,7 +61,7 @@ background-image: url("chrome://browser/skin/urlbar/startcap-focused-graphite.png"); } -.searchbar-textbox[focused="true"] > .searchbar-engine-button[chromedir="rtl"]:-moz-system-metric(mac-graphite-theme) { +.searchbar-textbox[focused="true"] > .searchbar-engine-button:-moz-locale-dir(rtl):-moz-system-metric(mac-graphite-theme) { background-image: url("chrome://browser/skin/urlbar/startcap-focused-graphite-rtl.png"); } @@ -70,8 +70,8 @@ background-image: url("chrome://browser/skin/urlbar/startcap-active.png") !important; } -.searchbar-engine-button:hover:active[chromedir="rtl"], -.searchbar-engine-button[open="true"][chromedir="rtl"] { +.searchbar-engine-button:hover:active:-moz-locale-dir(rtl), +.searchbar-engine-button[open="true"]:-moz-locale-dir(rtl) { background-image: url("chrome://browser/skin/urlbar/startcap-active-rtl.png") !important; } @@ -80,8 +80,8 @@ background-image: url("chrome://browser/skin/urlbar/startcap-active-focused.png") !important; } -.searchbar-textbox[focused="true"] > .searchbar-engine-button[chromedir="rtl"]:active, -.searchbar-textbox[focused="true"] > .searchbar-engine-button[open="true"][chromedir="rtl"] { +.searchbar-textbox[focused="true"] > .searchbar-engine-button:-moz-locale-dir(rtl):active, +.searchbar-textbox[focused="true"] > .searchbar-engine-button[open="true"]:-moz-locale-dir(rtl) { background-image: url("chrome://browser/skin/urlbar/startcap-active-focused-rtl.png") !important; } @@ -90,8 +90,8 @@ background-image: url("chrome://browser/skin/urlbar/startcap-active-focused-graphite.png") !important; } -.searchbar-textbox[focused="true"] > .searchbar-engine-button[chromedir="rtl"]:active:-moz-system-metric(mac-graphite-theme), -.searchbar-textbox[focused="true"] > .searchbar-engine-button[open="true"][chromedir="rtl"]:-moz-system-metric(mac-graphite-theme) { +.searchbar-textbox[focused="true"] > .searchbar-engine-button:-moz-locale-dir(rtl):active:-moz-system-metric(mac-graphite-theme), +.searchbar-textbox[focused="true"] > .searchbar-engine-button[open="true"]:-moz-locale-dir(rtl):-moz-system-metric(mac-graphite-theme) { background-image: url("chrome://browser/skin/urlbar/startcap-active-focused-graphite-rtl.png") !important; } @@ -118,7 +118,7 @@ -moz-padding-end: 5px; } -.search-go-container[chromedir="rtl"] { +.search-go-container:-moz-locale-dir(rtl) { background-image: url("chrome://browser/skin/urlbar/endcap-rtl.png"); } @@ -126,7 +126,7 @@ background-image: url("chrome://browser/skin/urlbar/endcap-focused.png"); } -.searchbar-textbox[focused="true"] > .search-go-container[chromedir="rtl"] { +.searchbar-textbox[focused="true"] > .search-go-container:-moz-locale-dir(rtl) { background: url("chrome://browser/skin/urlbar/endcap-focused-rtl.png") no-repeat left top; } @@ -134,7 +134,7 @@ background-image: url("chrome://browser/skin/urlbar/endcap-focused-graphite.png"); } -.searchbar-textbox[focused="true"] > .search-go-container[chromedir="rtl"]:-moz-system-metric(mac-graphite-theme) { +.searchbar-textbox[focused="true"] > .search-go-container:-moz-locale-dir(rtl):-moz-system-metric(mac-graphite-theme) { background-image: url("chrome://browser/skin/urlbar/endcap-focused-graphite-rtl.png"); } diff --git a/browser/themes/winstripe/browser/browser-aero.css b/browser/themes/winstripe/browser/browser-aero.css index c56d77198b5..8b4176cbfef 100644 --- a/browser/themes/winstripe/browser/browser-aero.css +++ b/browser/themes/winstripe/browser/browser-aero.css @@ -18,20 +18,20 @@ /* ::::: Identity Indicator Styling ::::: */ -#urlbar[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#urlbar:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-margin-start: 9px; } -#identity-box[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#identity-box:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-margin-start: -7px; } -#identity-box[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#identity-box:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-border-radius: 11px 0 2px 11px / 15px 0 2px 15px; } -/* Match #identity-box[chromedir="ltr"]'s -moz-margin-start */ -#identity-popup[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +/* Match #identity-box:-moz-locale-dir(ltr)'s -moz-margin-start */ +#identity-popup:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-margin-start: 7px; } diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index aad9b4beab6..1b0af9a5a71 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -248,16 +248,16 @@ toolbar[iconsize="large"][mode="icons"] #back-button:not([disabled="true"]):hove /* unified back button with keyhole icons, RTL version */ -toolbar[iconsize="large"][mode="icons"] #back-button[chromedir="rtl"] { +toolbar[iconsize="large"][mode="icons"] #back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 516px 34px 480px); } -toolbar[iconsize="large"][mode="icons"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="large"][mode="icons"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(34px 516px 68px 480px); } -toolbar[iconsize="large"][mode="icons"] #back-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="large"][mode="icons"] #back-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(68px 516px 102px 480px); } -toolbar[iconsize="large"][mode="icons"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="large"][mode="icons"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(102px 516px 136px 480px); } @@ -284,16 +284,16 @@ toolbar[iconsize="large"][mode="icons"] #forward-button:not([disabled="true"]):h /* unified forward button with keyhole icons, RTL version */ -toolbar[iconsize="large"][mode="icons"] #forward-button[chromedir="rtl"] { +toolbar[iconsize="large"][mode="icons"] #forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(3px 480px 31px 454px); } -toolbar[iconsize="large"][mode="icons"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="large"][mode="icons"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(37px 480px 65px 454px); } -toolbar[iconsize="large"][mode="icons"] #forward-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="large"][mode="icons"] #forward-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(71px 480px 99px 454px); } -toolbar[iconsize="large"][mode="icons"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="large"][mode="icons"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(105px 480px 133px 454px); } @@ -331,16 +331,16 @@ toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker:not([disabled=" /* unified dropmarker with keyhole icons, RTL version */ -toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"] { +toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl) { -moz-image-region: rect(3px 454px 31px 438px); } -toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(37px 454px 65px 438px); } -toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(71px 454px 99px 438px); } -toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="large"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(105px 454px 133px 438px); } @@ -368,16 +368,16 @@ toolbar[iconsize="small"][mode="icons"] #back-button:not([disabled="true"]):hove /* unified back button with keyhole icons, small icons mode, RTL version */ -toolbar[iconsize="small"][mode="icons"] #back-button[chromedir="rtl"] { +toolbar[iconsize="small"][mode="icons"] #back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 362px 24px 338px); } -toolbar[iconsize="small"][mode="icons"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="small"][mode="icons"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(24px 362px 48px 338px); } -toolbar[iconsize="small"][mode="icons"] #back-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="small"][mode="icons"] #back-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px 362px 72px 338px); } -toolbar[iconsize="small"][mode="icons"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="small"][mode="icons"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(72px 362px 96px 338px); } @@ -398,16 +398,16 @@ toolbar[iconsize="small"][mode="icons"] #forward-button:not([disabled="true"]):h /* unified forward button with keyhole icons, small icons mode, RTL version */ -toolbar[iconsize="small"][mode="icons"] #forward-button[chromedir="rtl"] { +toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 338px 24px 314px); } -toolbar[iconsize="small"][mode="icons"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(24px 338px 48px 314px); } -toolbar[iconsize="small"][mode="icons"] #forward-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px 338px 72px 314px); } -toolbar[iconsize="small"][mode="icons"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="small"][mode="icons"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(72px 338px 96px 314px); } @@ -429,16 +429,16 @@ toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker:not([disabled=" /* unified dropmarker with keyhole icons, small icons mode, RTL version */ -toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"] { +toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 314px 24px 301px); } -toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"]:not([disabled="true"]):hover { +toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl):not([disabled="true"]):hover { -moz-image-region: rect(24px 314px 48px 301px); } -toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px 314px 72px 301px); } -toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(72px 314px 96px 301px); } @@ -460,17 +460,17 @@ toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl" -moz-image-region: rect(96px 24px 120px 0px); } -#back-button[chromedir="rtl"] { +#back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 48px 24px 24px); } -#back-button[chromedir="rtl"]:not([disabled="true"]):hover, -#back-button[chromedir="rtl"][buttonover="true"] { +#back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover, +#back-button:-moz-locale-dir(rtl)[buttonover="true"] { -moz-image-region: rect(24px 48px 48px 24px); } -#back-button[chromedir="rtl"][disabled="true"] { +#back-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px 48px 72px 24px); } -#back-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +#back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(96px 48px 120px 24px); } @@ -490,17 +490,17 @@ toolbar[iconsize="small"][mode="icons"] #back-forward-dropmarker[chromedir="rtl" -moz-image-region: rect(96px 48px 120px 24px); } -#forward-button[chromedir="rtl"] { +#forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 24px 24px 0px); } -#forward-button[chromedir="rtl"]:not([disabled="true"]):hover, -#forward-button[chromedir="rtl"][buttonover="true"] { +#forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover, +#forward-button:-moz-locale-dir(rtl)[buttonover="true"] { -moz-image-region: rect(24px 24px 48px 0px); } -#forward-button[chromedir="rtl"][disabled="true"] { +#forward-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px 24px 72px 0px); } -#forward-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +#forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(96px 24px 120px 0px); } @@ -727,21 +727,21 @@ toolbar[iconsize="small"] #back-button:not([disabled="true"]):hover:active { -moz-image-region: rect(64px 16px 80px 0px); } -toolbar[iconsize="small"] #back-button[chromedir="rtl"] { +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 32px 16px 16px); } -toolbar[iconsize="small"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover, -toolbar[iconsize="small"] #back-button[chromedir="rtl"][buttonover="true"] { +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover, +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl)[buttonover="true"] { -moz-image-region: rect(16px 32px 32px 16px); } -toolbar[iconsize="small"] #back-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(32px 32px 48px 16px); } -toolbar[iconsize="small"] #back-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(64px 32px 80px 16px); } .unified-nav-back[_moz-menuactive], -menupopup[chromedir="rtl"] > .unified-nav-forward[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-forward[_moz-menuactive] { list-style-image: url("chrome://browser/skin/menu-back.png") !important; } @@ -764,21 +764,21 @@ toolbar[iconsize="small"] #forward-button:not([disabled="true"]):hover:active { -moz-image-region: rect(64px 32px 80px 16px); } -toolbar[iconsize="small"] #forward-button[chromedir="rtl"] { +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px 16px 16px 0px); } -toolbar[iconsize="small"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover, -toolbar[iconsize="small"] #forward-button[chromedir="rtl"][buttonover="true"] { +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover, +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl)[buttonover="true"] { -moz-image-region: rect(16px 16px 32px 0px); } -toolbar[iconsize="small"] #forward-button[chromedir="rtl"][disabled="true"] { +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(32px 16px 48px 0px); } -toolbar[iconsize="small"] #forward-button[chromedir="rtl"]:not([disabled="true"]):hover:active { +toolbar[iconsize="small"] #forward-button:-moz-locale-dir(rtl):not([disabled="true"]):hover:active { -moz-image-region: rect(64px 16px 80px 0px); } .unified-nav-forward[_moz-menuactive], -menupopup[chromedir="rtl"] > .unified-nav-back[_moz-menuactive] { +menupopup:-moz-locale-dir(rtl) > .unified-nav-back[_moz-menuactive] { list-style-image: url("chrome://browser/skin/menu-forward.png") !important; } @@ -1101,7 +1101,7 @@ toolbar[iconsize="small"] #paste-button:not([disabled="true"]):hover:active { direction: ltr !important; } -#PopupAutoComplete[chromedir="rtl"] > tree > treerows { +#PopupAutoComplete:-moz-locale-dir(rtl) > tree > treerows { direction: rtl; } @@ -1508,7 +1508,7 @@ tabpanels { -moz-border-radius-topright: 2px; } -.tabbrowser-arrowscrollbox > .scrollbutton-up[chromedir="rtl"] { +.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl) { border-left-style: solid; border-right-style: none; -moz-border-radius-topleft: 2px; @@ -1527,16 +1527,16 @@ tabpanels { list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-right.png"); } -.tabbrowser-arrowscrollbox > .scrollbutton-down[chromedir="rtl"], -.tabs-container > .tabs-newtab-button[chromedir="rtl"], -.tabs-container > stack[chromedir="rtl"] > .tabs-alltabs-button { +.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(rtl), +.tabs-container > .tabs-newtab-button:-moz-locale-dir(rtl), +.tabs-container > stack:-moz-locale-dir(rtl) > .tabs-alltabs-button { border-left-style: none; border-right-style: solid; -moz-border-radius-topleft: 0px; -moz-border-radius-topright: 2px; } -.tabbrowser-arrowscrollbox > .scrollbutton-down[chromedir="rtl"] { +.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(rtl) { list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png"); } @@ -1592,7 +1592,7 @@ tabpanels { opacity: 0.0; } -stack[chromedir="rtl"] > hbox > .tabs-alltabs-box-animate { +stack:-moz-locale-dir(rtl) > hbox > .tabs-alltabs-box-animate { background-image: url("chrome://browser/skin/tabbrowser/alltabs-box-overflow-start-bkgnd-animate.png"); } @@ -1643,7 +1643,7 @@ stack[chromedir="rtl"] > hbox > .tabs-alltabs-box-animate { -moz-border-radius-topleft: 2px; } -.tabs-container > .tabs-closebutton[chromedir="rtl"] { +.tabs-container > .tabs-closebutton:-moz-locale-dir(rtl) { border-left: none; border-right: 1px solid threedshadow; -moz-border-radius-topleft: 0px; @@ -1660,7 +1660,7 @@ toolbarbutton.chevron { list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important; } -toolbarbutton.chevron[chromedir="rtl"] { +toolbarbutton.chevron:-moz-locale-dir(rtl) { list-style-image: url("chrome://global/skin/toolbar/chevron-rtl.gif") !important; } @@ -1712,13 +1712,13 @@ toolbar[mode="text"] > #window-controls > toolbarbutton > .toolbarbutton-text { -moz-image-region: rect(0, 26px, 11px, 13px); } -#urlbar[chromedir="ltr"] > .autocomplete-history-dropmarker { +#urlbar:-moz-locale-dir(ltr) > .autocomplete-history-dropmarker { border-left: 1px solid; -moz-border-left-colors: transparent; } -#urlbar[chromedir="ltr"] > .autocomplete-history-dropmarker:hover , -#urlbar[chromedir="ltr"] > .autocomplete-history-dropmarker[open="true"] { +#urlbar:-moz-locale-dir(ltr) > .autocomplete-history-dropmarker:hover , +#urlbar:-moz-locale-dir(ltr) > .autocomplete-history-dropmarker[open="true"] { -moz-border-left-colors: ButtonShadow; } @@ -1802,7 +1802,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { /* ::::: Identity Indicator Styling ::::: */ /* Location bar visuals*/ -#urlbar[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#urlbar:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-margin-start: 6px; } @@ -1818,10 +1818,10 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { -moz-box-shadow: 1px 1px 0 rgba(255,255,255,.3) inset, 0 -1px 0 rgba(255,255,255,.2) inset; } -#identity-box[chromedir="rtl"] { +#identity-box:-moz-locale-dir(ltr) { -moz-border-start-style: solid; } -#identity-box[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#identity-box:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-border-radius: 6px 0 2px 6px / 15px 0 2px 15px; margin: -1px 0; -moz-margin-start: -4px; @@ -1980,7 +1980,7 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] { } /* Bug 413060, comment 14: Match #identity-box's -moz-margin-start, less 1px */ -#identity-popup[chromedir="ltr"]:-moz-system-metric(windows-default-theme) { +#identity-popup:-moz-locale-dir(ltr):-moz-system-metric(windows-default-theme) { -moz-margin-start: 3px; } diff --git a/browser/themes/winstripe/browser/places/organizer.css b/browser/themes/winstripe/browser/places/organizer.css index f2b5d494c1f..6eb2e9d9c16 100644 --- a/browser/themes/winstripe/browser/places/organizer.css +++ b/browser/themes/winstripe/browser/places/organizer.css @@ -20,36 +20,36 @@ } #back-button, -#forward-button[chromedir="rtl"] { +#forward-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 24px, 24px, 0px); } #back-button:not([disabled="true"]):hover, -#forward-button:not([disabled="true"]):hover[chromedir="rtl"] { +#forward-button:not([disabled="true"]):hover:-moz-locale-dir(rtl) { -moz-image-region: rect(24px, 24px, 48px, 0px); } #back-button[disabled="true"], -#forward-button[chromedir="rtl"][disabled="true"] { +#forward-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px, 24px, 72px, 0px) !important; } #back-button:not([disabled="true"]):hover:active, -#forward-button:not([disabled="true"]):hover:active[chromedir="rtl"] { +#forward-button:not([disabled="true"]):hover:active:-moz-locale-dir(rtl) { -moz-image-region: rect(72px, 24px, 96px, 0px); } #forward-button, -#back-button[chromedir="rtl"] { +#back-button:-moz-locale-dir(rtl) { -moz-image-region: rect(0px, 48px, 24px, 24px); } #forward-button:not([disabled="true"]):hover, -#back-button:not([disabled="true"]):hover[chromedir="rtl"] { +#back-button:not([disabled="true"]):hover:-moz-locale-dir(rtl) { -moz-image-region: rect(24px, 48px, 48px, 24px); } #forward-button[disabled="true"], -#back-button[chromedir="rtl"][disabled="true"] { +#back-button:-moz-locale-dir(rtl)[disabled="true"] { -moz-image-region: rect(48px, 48px, 72px, 24px) !important; } #forward-button:not([disabled="true"]):hover:active, -#back-button:not([disabled="true"]):hover:active[chromedir="rtl"] { +#back-button:not([disabled="true"]):hover:active:-moz-locale-dir(rtl) { -moz-image-region: rect(72px, 48px, 96px, 24px); } @@ -94,7 +94,7 @@ background: url(chrome://global/skin/arrow/arrow-dn.gif) right center no-repeat; } -#placesMenu[chromedir="rtl"] > menu > label { +#placesMenu:-moz-locale-dir(rtl) > menu > label { background-position: left center; } diff --git a/browser/themes/winstripe/browser/searchbar.css b/browser/themes/winstripe/browser/searchbar.css index 7684ba79fa6..6656a4b5044 100644 --- a/browser/themes/winstripe/browser/searchbar.css +++ b/browser/themes/winstripe/browser/searchbar.css @@ -45,7 +45,7 @@ 0 -1px 0 rgba(255,255,255,.4) inset; } -.searchbar-engine-button:-moz-system-metric(windows-default-theme)[chromedir="ltr"] { +.searchbar-engine-button:-moz-system-metric(windows-default-theme):-moz-locale-dir(ltr) { -moz-border-right-colors: rgba(0,0,0,.05); } @@ -68,7 +68,7 @@ background: transparent url(chrome://browser/skin/Search-addengines.png) no-repeat right center; } -.searchbar-engine-button[addengines="true"][chromedir="rtl"] > .button-box { +.searchbar-engine-button[addengines="true"]:-moz-locale-dir(rtl) > .button-box { background-position: left center; } @@ -94,7 +94,7 @@ -moz-image-region: rect(0px 16px 16px 0px); } -.search-go-button[chromedir="rtl"] { +.search-go-button:-moz-locale-dir(rtl) { -moz-transform: scaleX(-1); } diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index 37197b9228e..ea075f5599e 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -105,8 +105,8 @@ class nsIBoxObject; // IID for the nsIDocument interface #define NS_IDOCUMENT_IID \ - { 0x46003091, 0x7f99, 0x420f, \ - { 0x95, 0xbc, 0x28, 0xd7, 0xd5, 0x01, 0x5a, 0x41 } } +{ 0xe0ca6723, 0x1efa, 0x4819, \ + { 0x84, 0xbb, 0xfa, 0x48, 0x39, 0xe8, 0xef, 0x19 } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -1156,6 +1156,15 @@ public: */ virtual void MaybePreLoadImage(nsIURI* uri) = 0; + /** + * Returns true if the locale used for the document specifies a direction of + * right to left. For chrome documents, this comes from the chrome registry. + * This is used to determine the current state for the :-moz-locale-dir pseudoclass + * so once can know whether a document is expected to be rendered left-to-right + * or right-to-left. + */ + virtual PRBool IsDocumentRightToLeft() { return PR_FALSE; } + protected: ~nsIDocument() { diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index d2b6957323c..ac97d1d4043 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -503,6 +503,7 @@ GK_ATOM(listing, "listing") GK_ATOM(listitem, "listitem") GK_ATOM(listrows, "listrows") GK_ATOM(load, "load") +GK_ATOM(localedir, "localedir") GK_ATOM(localName, "local-name") GK_ATOM(longdesc, "longdesc") #ifdef MOZ_MEDIA diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 3bb92cfc1c7..6ed33863613 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -1119,6 +1119,15 @@ nsXULElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName, SetTitlebarColor(color, aName == nsGkAtoms::activetitlebarcolor); } + // if the localedir changed on the root element, reset the document direction + if (aName == nsGkAtoms::localedir && + document && document->GetRootContent() == this) { + nsCOMPtr xuldoc = do_QueryInterface(document); + if (xuldoc) { + xuldoc->ResetDocumentDirection(); + } + } + if (aName == nsGkAtoms::src && document) { LoadSrc(); } @@ -1365,6 +1374,15 @@ nsXULElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, PRBool aNotify) SetTitlebarColor(NS_RGBA(0, 0, 0, 0), aName == nsGkAtoms::activetitlebarcolor); } + // if the localedir changed on the root element, reset the document direction + if (aName == nsGkAtoms::localedir && + doc && doc->GetRootContent() == this) { + nsCOMPtr xuldoc = do_QueryInterface(doc); + if (xuldoc) { + xuldoc->ResetDocumentDirection(); + } + } + // If the accesskey attribute is removed, unregister it here // Also see nsXULLabelFrame, nsBoxFrame and nsTextBoxFrame's AttributeChanged if (aName == nsGkAtoms::accesskey || aName == nsGkAtoms::control) { @@ -1793,8 +1811,7 @@ nsXULElement::GetAttributeChangeHint(const nsIAtom* aAttribute, retval = NS_STYLE_HINT_FRAMECHANGE; } else { // if left or top changes we reflow. This will happen in xul - // containers that manage positioned children such as a - // bulletinboard. + // containers that manage positioned children such as a stack. if (nsGkAtoms::left == aAttribute || nsGkAtoms::top == aAttribute) retval = NS_STYLE_HINT_REFLOW; } diff --git a/content/xul/document/public/nsIXULDocument.h b/content/xul/document/public/nsIXULDocument.h index bcc30d03a72..a6b04e85d3d 100644 --- a/content/xul/document/public/nsIXULDocument.h +++ b/content/xul/document/public/nsIXULDocument.h @@ -122,6 +122,11 @@ public: * Callback notifying when a document could not be parsed properly. */ virtual PRBool OnDocumentParserError() = 0; + + /** + * Reset the document direction so that it is recomputed. + */ + virtual void ResetDocumentDirection() = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULDocument, NS_IXULDOCUMENT_IID) diff --git a/content/xul/document/src/Makefile.in b/content/xul/document/src/Makefile.in index f7db62fb494..f205e1b8772 100644 --- a/content/xul/document/src/Makefile.in +++ b/content/xul/document/src/Makefile.in @@ -95,7 +95,10 @@ include $(topsrcdir)/config/rules.mk LOCAL_INCLUDES = -I$(srcdir)/../../../base/src \ -I$(srcdir)/../../content/src \ -I$(srcdir)/../../templates/src \ + -I$(srcdir)/../../../../layout/base \ + -I$(srcdir)/../../../../layout/generic \ -I$(srcdir)/../../../../layout/style \ + -I$(srcdir)/../../../../layout/xul/base/src \ -I$(srcdir)/../../../xml/document/src \ -I$(srcdir)/../../../events/src \ $(NULL) diff --git a/content/xul/document/src/nsXULDocument.cpp b/content/xul/document/src/nsXULDocument.cpp index 51535642bf0..b3be0dd04bd 100644 --- a/content/xul/document/src/nsXULDocument.cpp +++ b/content/xul/document/src/nsXULDocument.cpp @@ -126,6 +126,7 @@ #include "nsXULPopupManager.h" #include "nsCCUncollectableMarker.h" #include "nsURILoader.h" +#include "nsCSSFrameConstructor.h" //---------------------------------------------------------------------- // @@ -222,6 +223,7 @@ nsRefMapEntry::RemoveContent(nsIContent* aContent) nsXULDocument::nsXULDocument(void) : nsXMLDocument("application/vnd.mozilla.xul+xml"), + mDocDirection(Direction_Uninitialized), mState(eState_Master), mResolutionPhase(nsForwardReference::eStart) { @@ -260,6 +262,10 @@ nsXULDocument::~nsXULDocument() delete mTemplateBuilderTable; + nsContentUtils::UnregisterPrefCallback("intl.uidirection.", + nsXULDocument::DirectionChanged, + this); + if (--gRefCnt == 0) { NS_IF_RELEASE(gRDFService); @@ -1989,6 +1995,10 @@ nsXULDocument::Init() } } + nsContentUtils::RegisterPrefCallback("intl.uidirection.", + nsXULDocument::DirectionChanged, + this); + #ifdef PR_LOGGING if (! gXULLog) gXULLog = PR_NewLogModule("nsXULDocument"); @@ -4627,6 +4637,100 @@ nsXULDocument::GetFocusController(nsIFocusController** aFocusController) *aFocusController = nsnull; } +PRBool +nsXULDocument::IsDocumentRightToLeft() +{ + if (mDocDirection == Direction_Uninitialized) { + mDocDirection = Direction_LeftToRight; // default to ltr on failure + + // setting the localedir attribute on the root element forces a + // specific direction for the document. + nsIContent* content = GetRootContent(); + if (content) { + static nsIContent::AttrValuesArray strings[] = + {&nsGkAtoms::ltr, &nsGkAtoms::rtl, nsnull}; + switch (content->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::localedir, + strings, eCaseMatters)) { + case 0: mDocDirection = Direction_LeftToRight; return PR_FALSE; + case 1: mDocDirection = Direction_RightToLeft; return PR_TRUE; + default: break;// otherwise, not a valid value, so fall through + } + } + + // otherwise, get the locale from the chrome registry and + // look up the intl.uidirection. preference + nsCOMPtr reg = + do_GetService(NS_CHROMEREGISTRY_CONTRACTID); + if (reg) { + nsCAutoString package; + PRBool isChrome; + if (NS_SUCCEEDED(mDocumentURI->SchemeIs("chrome", &isChrome)) && + isChrome) { + mDocumentURI->GetHostPort(package); + } + else { + // use the 'global' package for about and resource uris. + // otherwise, just default to left-to-right. + PRBool isAbout, isResource; + if (NS_SUCCEEDED(mDocumentURI->SchemeIs("about", &isAbout)) && + isAbout) { + package.AssignLiteral("global"); + } + else if (NS_SUCCEEDED(mDocumentURI->SchemeIs("resource", &isResource)) && + isResource) { + package.AssignLiteral("global"); + } + else { + return PR_FALSE; + } + } + + nsCAutoString locale; + reg->GetSelectedLocale(package, locale); + if (locale.Length() >= 2) { + // first check the intl.uidirection. preference, + // and if that is not set, check the same preference but + // with just the first two characters of the locale. If + // that isn't set, default to left-to-right. + nsCAutoString prefString = + NS_LITERAL_CSTRING("intl.uidirection.") + locale; + nsAdoptingCString dir = nsContentUtils::GetCharPref(prefString.get()); + if (dir.IsEmpty()) { + PRInt32 hyphen = prefString.FindChar('-'); + if (hyphen >= 1) { + nsCAutoString shortPref(Substring(prefString, 0, hyphen)); + dir = nsContentUtils::GetCharPref(shortPref.get()); + } + } + + mDocDirection = dir.EqualsLiteral("rtl") ? + Direction_RightToLeft : Direction_LeftToRight; + } + } + } + + return (mDocDirection == Direction_RightToLeft); +} + +int +nsXULDocument::DirectionChanged(const char* aPrefName, void* aData) +{ + // reset the direction and reflow the document. This will happen if + // the direction isn't actually being used, but that doesn't really + // matter too much + nsXULDocument* doc = (nsXULDocument *)aData; + if (doc) + doc->ResetDocumentDirection(); + + nsIPresShell *shell = doc->GetPrimaryShell(); + if (shell) { + shell->FrameConstructor()-> + PostRestyleEvent(doc->GetRootContent(), eReStyle_Self, NS_STYLE_HINT_NONE); + } + + return 0; +} + NS_IMETHODIMP nsXULDocument::GetBoxObjectFor(nsIDOMElement* aElement, nsIBoxObject** aResult) { diff --git a/content/xul/document/src/nsXULDocument.h b/content/xul/document/src/nsXULDocument.h index 447aa843ecc..48c26ccd080 100644 --- a/content/xul/document/src/nsXULDocument.h +++ b/content/xul/document/src/nsXULDocument.h @@ -182,6 +182,10 @@ public: virtual void EndUpdate(nsUpdateType aUpdateType); + virtual PRBool IsDocumentRightToLeft(); + + virtual void ResetDocumentDirection() { mDocDirection = Direction_Uninitialized; } + static PRBool MatchAttribute(nsIContent* aContent, PRInt32 aNameSpaceID, @@ -244,7 +248,8 @@ protected: return kNameSpaceID_XUL; } -protected: + static NS_HIDDEN_(int) DirectionChanged(const char* aPrefName, void* aData); + // pseudo constants static PRInt32 gRefCnt; @@ -326,6 +331,17 @@ protected: nsCOMPtr mTooltipNode; // [OWNER] element triggering the tooltip + /** + * document direction for use with the -moz-locale-dir property + */ + enum DocumentDirection { + Direction_Uninitialized, // not determined yet + Direction_LeftToRight, + Direction_RightToLeft + }; + + DocumentDirection mDocDirection; + /** * Context stack, which maintains the state of the Builder and allows * it to be interrupted. diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 4c340b795fe..e30a329af8b 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -3313,6 +3313,14 @@ CSSParserImpl::ParsePseudoClassWithIdentArg(nsCSSSelector& aSelector, return eSelectorParsingStatus_Error; } + // -moz-locale-dir can only have values of 'ltr' or 'rtl'. + if (aPseudo == nsCSSPseudoClasses::mozLocaleDir) { + if (!mToken.mIdent.EqualsLiteral("ltr") && + !mToken.mIdent.EqualsLiteral("rtl")) { + return eSelectorParsingStatus_Error; + } + } + // Add the pseudo with the language parameter aSelector.AddPseudoClass(aPseudo, mToken.mIdent.get()); diff --git a/layout/style/nsCSSPseudoClassList.h b/layout/style/nsCSSPseudoClassList.h index 113b67fbb45..d61c97bddd3 100644 --- a/layout/style/nsCSSPseudoClassList.h +++ b/layout/style/nsCSSPseudoClassList.h @@ -104,6 +104,10 @@ CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html") // Matches anything when the specified look-and-feel metric is set CSS_PSEUDO_CLASS(mozSystemMetric, ":-moz-system-metric") +// -moz-locale-dir(ltr) and -moz-locale-dir(rtl) may be used +// to match based on the locale's chrome direction +CSS_PSEUDO_CLASS(mozLocaleDir, ":-moz-locale-dir") + #ifdef MOZ_MATHML CSS_PSEUDO_CLASS(mozMathIncrementScriptLevel, ":-moz-math-increment-script-level") #endif diff --git a/layout/style/nsCSSPseudoClasses.cpp b/layout/style/nsCSSPseudoClasses.cpp index bff32293bc6..a37e17f1585 100644 --- a/layout/style/nsCSSPseudoClasses.cpp +++ b/layout/style/nsCSSPseudoClasses.cpp @@ -73,7 +73,8 @@ nsCSSPseudoClasses::HasStringArg(nsIAtom* aAtom) { return aAtom == nsCSSPseudoClasses::lang || aAtom == nsCSSPseudoClasses::mozEmptyExceptChildrenWithLocalname || - aAtom == nsCSSPseudoClasses::mozSystemMetric; + aAtom == nsCSSPseudoClasses::mozSystemMetric || + aAtom == nsCSSPseudoClasses::mozLocaleDir; } PRBool diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index 30323316a7f..a25fcafc9e7 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1558,6 +1558,27 @@ static PRBool SelectorMatches(RuleProcessorData &data, else if (nsCSSPseudoClasses::mozIsHTML == pseudoClass->mAtom) { result = data.mIsHTMLContent && data.mContent->IsInHTMLDocument(); } + else if (nsCSSPseudoClasses::mozLocaleDir == pseudoClass->mAtom) { + nsIDocument* doc = data.mContent ? data.mContent->GetDocument() : + data.mPresContext->Document(); + + if (doc) { + PRBool docIsRTL = doc && doc->IsDocumentRightToLeft(); + + nsDependentString dirString(pseudoClass->u.mString); + NS_ASSERTION(dirString.EqualsLiteral("ltr") || dirString.EqualsLiteral("rtl"), + "invalid value for -moz-locale-dir"); + + if (dirString.EqualsLiteral("rtl")) { + result = docIsRTL; + } else if (dirString.EqualsLiteral("ltr")) { + result = !docIsRTL; + } + } + else { + result = PR_FALSE; + } + } #ifdef MOZ_MATHML else if (nsCSSPseudoClasses::mozMathIncrementScriptLevel == pseudoClass->mAtom) { stateToCheck = NS_EVENT_STATE_INCREMENT_SCRIPT_LEVEL; @@ -2069,6 +2090,14 @@ nsCSSRuleProcessor::HasAttributeDependentStyle(AttributeRuleProcessorData* aData // XXXbz now that :link and :visited are also states, do we need a // similar optimization in HasStateDependentStyle? + // check for the localedir attribute on root XUL elements + if (aData->mAttribute == nsGkAtoms::localedir && + aData->mNameSpaceID == kNameSpaceID_XUL && + aData->mContent == aData->mContent->GetOwnerDoc()->GetRootContent()) + { + data.change = nsReStyleHint(data.change | eReStyle_Self); + } + RuleCascadeData* cascade = GetRuleCascade(aData->mPresContext); // We do the same thing for attributes that we do for state selectors diff --git a/layout/style/test/test_selectors.html b/layout/style/test/test_selectors.html index 63b72a8b261..d7a477066e2 100644 --- a/layout/style/test/test_selectors.html +++ b/layout/style/test/test_selectors.html @@ -632,6 +632,16 @@ function run() { test_selector_in_html("html|a:not(*|a)", single_a, empty_set, set_single, xul_default_ns + html_ns); + // Test -moz-locale-dir + test_parseable(":-moz-locale-dir(ltr)"); + test_parseable(":-moz-locale-dir(rtl)"); + + test_balanced_unparseable(":-moz-locale-dir(other)"); + test_balanced_unparseable(":-moz-locale-dir()"); + test_balanced_unparseable(":-moz-locale-dir('ltr')"); + test_balanced_unparseable(":-moz-locale-dir(ltr, other)"); + test_balanced_unparseable(":-moz-locale-dir"); + run_deferred_tests(); } diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a5bae40372b..4bd02803f00 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -910,6 +910,11 @@ pref("intl.locale.matchOS", false); pref("intl.fallbackCharsetList.ISO-8859-1", "windows-1252"); pref("font.language.group", "chrome://global/locale/intl.properties"); +// these locales have right-to-left UI +pref("intl.uidirection.ar", "rtl"); +pref("intl.uidirection.he", "rtl"); +pref("intl.uidirection.fa", "rtl"); + pref("font.mathfont-family", "STIXNonUnicode, STIXSize1, STIXGeneral, Standard Symbols L, DejaVu Sans, Cambria Math"); // Some CJK fonts have bad underline offset, their CJK character glyphs are overlapped (or adjoined) to its underline. diff --git a/toolkit/components/console/content/console.xul b/toolkit/components/console/content/console.xul index 0492b185587..9a8a78e50ca 100644 --- a/toolkit/components/console/content/console.xul +++ b/toolkit/components/console/content/console.xul @@ -45,7 +45,6 @@ %console; - %global; ]> - + - + diff --git a/toolkit/components/help/content/help.xul b/toolkit/components/help/content/help.xul index fab7524a2eb..cf3a766cfae 100644 --- a/toolkit/components/help/content/help.xul +++ b/toolkit/components/help/content/help.xul @@ -46,8 +46,6 @@ %brandDTD; %helpDTD; - - %globalDTD; ]> - + + tooltiptext="&backButton.tooltip;"> + observes="canGoForward"> %helpDTD; - - %globalDTD; ]> @@ -52,13 +50,11 @@ label="&backButton.label;" accesskey="&backButton.accesskey;" observes="canGoBack" - chromedir="&locale.dir;" oncommand="goBack()"/> %printPreviewDTD; - -%globalDTD; ]> - - - - diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index eb3b95defd4..24415d55f44 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -42,6 +42,9 @@ VPATH = @srcdir@ relativesrcdir = toolkit/content/tests/chrome include $(DEPTH)/config/autoconf.mk + +DIRS = rtltest + include $(topsrcdir)/config/rules.mk _TEST_FILES = findbar_window.xul \ @@ -86,6 +89,7 @@ _TEST_FILES = findbar_window.xul \ test_keys.xul \ window_keys.xul \ test_showcaret.xul \ + test_righttoleft.xul \ $(NULL) # test_panel_focus.xul won't work if the Full Keyboard Access preference is set to diff --git a/toolkit/content/tests/chrome/rtltest/Makefile.in b/toolkit/content/tests/chrome/rtltest/Makefile.in new file mode 100644 index 00000000000..6bfec3fc203 --- /dev/null +++ b/toolkit/content/tests/chrome/rtltest/Makefile.in @@ -0,0 +1,49 @@ +# +# ***** 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 +# Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of 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 ***** + +DEPTH = ../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = toolkit/content/tests/chrome/rtltest + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +libs:: righttoleft.manifest dirtest.xul + $(INSTALL) @srcdir@/righttoleft.manifest $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)/ + $(INSTALL) @srcdir@/dirtest.xul $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)/content/ diff --git a/toolkit/content/tests/chrome/rtltest/dirtest.xul b/toolkit/content/tests/chrome/rtltest/dirtest.xul new file mode 100644 index 00000000000..b75d41eaa3a --- /dev/null +++ b/toolkit/content/tests/chrome/rtltest/dirtest.xul @@ -0,0 +1,25 @@ + + + + + + + +hbox, vbox { background-color: white; } +hbox:-moz-locale-dir(ltr) { background-color: yellow; } +vbox:-moz-locale-dir(rtl) { background-color: green; } + + + + + +
+ + +
+
@@ -74,6 +80,10 @@ fh.addEntry("field3", "aaz");
 fh.addEntry("field3", "aa\xe6"); // 0xae == latin ae pair (0xc6 == AE)
 fh.addEntry("field3", "az");
 fh.addEntry("field3", "z");
+fh.addEntry("field4", "a\xe6");
+fh.addEntry("field4", "aa a\xe6");
+fh.addEntry("field4", "aba\xe6");
+fh.addEntry("field4", "bc d\xe6");
 
 // Restore the form to the default state.
 function restoreForm() {
@@ -436,7 +446,7 @@ function runTest(testNum) {
         break;
 
     case 205:
-        checkMenuEntries(["az"]);
+        ok(getMenuEntries().length > 0, "checking typing in middle of text");
         doKey("left");
         sendChar("a", input);
         break;
@@ -460,6 +470,57 @@ function runTest(testNum) {
         checkMenuEntries([]);
         doKey("escape");
 
+        // Look at form 6, try to trigger autocomplete popup
+        input = $_(6, "field4");
+        restoreForm();
+        testNum = 249;
+        sendChar("a", input);
+        break;
+
+    /* Test substring matches and word boundary bonuses */
+
+    case 250:
+        // alphabetical results for first character
+        checkMenuEntries(["aa a\xe6", "aba\xe6", "a\xe6"]);
+        sendChar("\xc6", input);
+        break;
+
+    case 251:
+        // prefix match comes first, then word boundary match
+        // followed by substring match
+        checkMenuEntries(["a\xe6", "aa a\xe6", "aba\xe6"]);
+
+        restoreForm();
+        sendChar("b", input);
+        break;
+
+    case 252:
+        checkMenuEntries(["bc d\xe6"]);
+        sendChar(" ", input);
+        break;
+
+    case 253:
+        // check that trailing space has no effect after single char.
+        checkMenuEntries(["bc d\xe6"]);
+        sendChar("\xc6", input);
+        break;
+
+    case 254:
+        // check multi-word substring matches
+        checkMenuEntries(["bc d\xe6", "aba\xe6"]);
+        doKey("left");
+        sendChar("d", input);
+        break;
+
+    case 255:
+        // check inserting in multi-word searches
+        checkMenuEntries(["bc d\xe6"]);
+        sendChar("z", input);
+        break;
+
+    case 256:
+        checkMenuEntries([]);
+
         SimpleTest.finish();
         return;
 
diff --git a/toolkit/components/satchel/test/unit/formhistory_1000.sqlite b/toolkit/components/satchel/test/unit/formhistory_1000.sqlite
new file mode 100644
index 0000000000000000000000000000000000000000..5eeab074fd85b693b505047adc189ce8d52c6216
GIT binary patch
literal 164864
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
AOaK4?

literal 0
HcmV?d00001

diff --git a/toolkit/components/satchel/test/unit/perf_autocomplete.js b/toolkit/components/satchel/test/unit/perf_autocomplete.js
new file mode 100644
index 00000000000..278ab88cd35
--- /dev/null
+++ b/toolkit/components/satchel/test/unit/perf_autocomplete.js
@@ -0,0 +1,173 @@
+/* ***** 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 Satchel Test Code.
+ *
+ * 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):
+ *   Matthew Noorenberghe  (Original Author)
+ *
+ * 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 ***** */
+
+var testnum = 0;
+var fh;
+var fac;
+var prefs;
+
+function countAllEntries() {
+    let stmt = fh.DBConnection.createStatement("SELECT COUNT(*) as numEntries FROM moz_formhistory");
+    do_check_true(stmt.step());
+    let numEntries = stmt.row.numEntries;
+    stmt.finalize();
+    return numEntries;
+}
+
+function do_AC_search(searchTerm, previousResult) {
+    var duration = 0;
+    var searchCount = 5;
+    var tempPrevious = null;
+    var startTime;
+    for (var i = 0; i < searchCount; i++) {
+        if (previousResult !== null)
+            tempPrevious = fac.autoCompleteSearch("searchbar-history", previousResult, null, null);
+        startTime = Date.now();
+        results = fac.autoCompleteSearch("searchbar-history", searchTerm, null, tempPrevious);
+        duration += Date.now() - startTime;
+    }
+    dump("[autoCompleteSearch][test " + testnum + "] for '" + searchTerm + "' ");
+    if (previousResult !== null)
+        dump("with '" + previousResult + "' previous result ");
+    else
+        dump("w/o previous result ");
+    dump("took " + duration + " ms with " + results.matchCount + " matches.  ");
+    dump("Average of " + Math.round(duration / searchCount) + " ms per search\n");
+    return results;
+}
+
+function run_test() {
+    try {
+
+        // ===== test init =====
+        var testfile = do_get_file("formhistory_1000.sqlite");
+        var profileDir = dirSvc.get("ProfD", Ci.nsIFile);
+        var results;
+
+        // Cleanup from any previous tests or failures.
+        var destFile = profileDir.clone();
+        destFile.append("formhistory.sqlite");
+        if (destFile.exists())
+          destFile.remove(false);
+
+        testfile.copyTo(profileDir, "formhistory.sqlite");
+
+        fh = Cc["@mozilla.org/satchel/form-history;1"].
+             getService(Ci.nsIFormHistory2);
+        fac = Cc["@mozilla.org/satchel/form-autocomplete;1"].
+              getService(Ci.nsIFormAutoComplete);
+        prefs = Cc["@mozilla.org/preferences-service;1"].
+                getService(Ci.nsIPrefBranch);
+
+        timeGroupingSize = prefs.getIntPref("browser.formfill.timeGroupingSize") * 1000 * 1000;
+        maxTimeGroupings = prefs.getIntPref("browser.formfill.maxTimeGroupings");
+        bucketSize = prefs.getIntPref("browser.formfill.bucketSize");
+
+        // ===== 1 =====
+        // Check initial state is as expected
+        testnum++;
+        do_check_true(fh.hasEntries);
+        do_check_eq(1000, countAllEntries());
+        fac.autoCompleteSearch("searchbar-history", "zzzzzzzzzz", null, null); // warm-up search
+        do_check_true(fh.nameExists("searchbar-history"));
+
+        // ===== 2 =====
+        // Search for '' with no previous result
+        testnum++;
+        results = do_AC_search("", null);
+        do_check_true(results.matchCount > 0);
+
+        // ===== 3 =====
+        // Search for 'r' with no previous result
+        testnum++;
+        results = do_AC_search("r", null);
+        do_check_true(results.matchCount > 0);
+
+        // ===== 4 =====
+        // Search for 'r' with '' previous result
+        testnum++;
+        results = do_AC_search("r", "");
+        do_check_true(results.matchCount > 0);
+
+        // ===== 5 =====
+        // Search for 're' with no previous result
+        testnum++;
+        results = do_AC_search("re", null);
+        do_check_true(results.matchCount > 0);
+
+        // ===== 6 =====
+        // Search for 're' with 'r' previous result
+        testnum++;
+        results = do_AC_search("re", "r");
+        do_check_true(results.matchCount > 0);
+
+        // ===== 7 =====
+        // Search for 'rea' without previous result
+        testnum++;
+        results = do_AC_search("rea", null);
+        let countREA = results.matchCount;
+
+        // ===== 8 =====
+        // Search for 'rea' with 're' previous result
+        testnum++;
+        results = do_AC_search("rea", "re");
+        do_check_eq(countREA, results.matchCount);
+
+        // ===== 9 =====
+        // Search for 'real' with 'rea' previous result
+        testnum++;
+        results = do_AC_search("real", "rea");
+        let countREAL = results.matchCount;
+        do_check_true(results.matchCount <= countREA);
+
+        // ===== 10 =====
+        // Search for 'real' with 're' previous result
+        testnum++;
+        results = do_AC_search("real", "re");
+        do_check_eq(countREAL, results.matchCount);
+
+        // ===== 11 =====
+        // Search for 'real' with no previous result
+        testnum++;
+        results = do_AC_search("real", null);
+        do_check_eq(countREAL, results.matchCount);
+
+
+    } catch (e) {
+      throw "FAILED in test #" + testnum + " -- " + e;
+    }
+}

From 3639559e2759153afd3c5cb79451f1a47417eed8 Mon Sep 17 00:00:00 2001
From: MattN 
Date: Sat, 1 Aug 2009 17:30:27 -0700
Subject: [PATCH 157/175] Bug 444728 - autocomplete disregards maxlength for
 input fields. r=dolske, sr=mconnor

---
 .../satchel/public/nsIFormAutoComplete.idl    |  4 +-
 .../satchel/src/nsFormAutoComplete.js         |  8 +-
 .../satchel/src/nsFormFillController.cpp      |  1 +
 .../satchel/test/test_form_autocomplete.html  | 93 +++++++++++++++++++
 .../satchel/test/unit/test_autocomplete.js    | 14 +--
 5 files changed, 111 insertions(+), 9 deletions(-)

diff --git a/toolkit/components/satchel/public/nsIFormAutoComplete.idl b/toolkit/components/satchel/public/nsIFormAutoComplete.idl
index f0a1db02113..93cf717cb7f 100644
--- a/toolkit/components/satchel/public/nsIFormAutoComplete.idl
+++ b/toolkit/components/satchel/public/nsIFormAutoComplete.idl
@@ -38,8 +38,9 @@
 #include "nsISupports.idl"
 
 interface nsIAutoCompleteResult;
+interface nsIDOMHTMLInputElement;
 
-[scriptable, uuid(2f5bb765-428e-4e33-b2d8-441eb7ddf730)]
+[scriptable, uuid(997c0c05-5d1d-47e5-9cbc-765c0b8ec699)]
 
 interface nsIFormAutoComplete: nsISupports {
     /**
@@ -48,5 +49,6 @@ interface nsIFormAutoComplete: nsISupports {
     nsIAutoCompleteResult autoCompleteSearch(
                                     in AString aInputName,
                                     in AString aSearchString,
+                                    in nsIDOMHTMLInputElement aField,
                                     in nsIAutoCompleteResult aPreviousResult);
 };
diff --git a/toolkit/components/satchel/src/nsFormAutoComplete.js b/toolkit/components/satchel/src/nsFormAutoComplete.js
index fc5e6d3aa3f..b54e33d1aa7 100644
--- a/toolkit/components/satchel/src/nsFormAutoComplete.js
+++ b/toolkit/components/satchel/src/nsFormAutoComplete.js
@@ -173,11 +173,12 @@ FormAutoComplete.prototype = {
      *
      * aInputName    -- |name| attribute from the form input being autocompleted.
      * aUntrimmedSearchString -- current value of the input
+     * aField -- nsIDOMHTMLInputElement being autocompleted (may be null if from chrome)
      * aPreviousResult -- previous search result, if any.
      *
      * Returns: an nsIAutoCompleteResult
      */
-    autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aPreviousResult) {
+    autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) {
         function sortBytotalScore (a, b) {
             let x = a.totalScore;
             let y = b.totalScore;
@@ -222,6 +223,11 @@ FormAutoComplete.prototype = {
             this.log("Creating new autocomplete search result.");
             let entries = this.getAutoCompleteValues(aInputName, searchString);
             result = new FormAutoCompleteResult(this._formHistory, entries, aInputName, aUntrimmedSearchString);
+            if (aField && aField.maxLength > -1) {
+                let original = result.wrappedJSObject.entries;
+                let filtered = original.filter(function (el) el.text.length <= this.maxLength, aField);
+                result.wrappedJSObject.entries = filtered;
+            }
         }
 
         return result;
diff --git a/toolkit/components/satchel/src/nsFormFillController.cpp b/toolkit/components/satchel/src/nsFormFillController.cpp
index 491a3bd0551..a81cfa8eb3c 100644
--- a/toolkit/components/satchel/src/nsFormFillController.cpp
+++ b/toolkit/components/satchel/src/nsFormFillController.cpp
@@ -517,6 +517,7 @@ nsFormFillController::StartSearch(const nsAString &aSearchString, const nsAStrin
 
     rv = formAutoComplete->AutoCompleteSearch(aSearchParam,
                                               aSearchString,
+                                              mFocusedInput,
                                               aPreviousResult,
                                               getter_AddRefs(result));
   }
diff --git a/toolkit/components/satchel/test/test_form_autocomplete.html b/toolkit/components/satchel/test/test_form_autocomplete.html
index b147be11314..c00e0cbf4b4 100644
--- a/toolkit/components/satchel/test/test_form_autocomplete.html
+++ b/toolkit/components/satchel/test/test_form_autocomplete.html
@@ -51,6 +51,12 @@ Form History test: form field autocomplete
     
   
 
+  
+  
+ + +
+
@@ -84,6 +90,10 @@ fh.addEntry("field4", "a\xe6");
 fh.addEntry("field4", "aa a\xe6");
 fh.addEntry("field4", "aba\xe6");
 fh.addEntry("field4", "bc d\xe6");
+fh.addEntry("field5", "1");
+fh.addEntry("field5", "12");
+fh.addEntry("field5", "123");
+fh.addEntry("field5", "1234");
 
 // Restore the form to the default state.
 function restoreForm() {
@@ -521,6 +531,89 @@ function runTest(testNum) {
     case 256:
         checkMenuEntries([]);
 
+        // Look at form 7, try to trigger autocomplete popup
+        input = $_(7, "field5");
+        restoreForm();
+        doKey("down");
+        testNum = 299;
+        break;
+
+    case 300:
+        checkMenuEntries(["1", "12", "123", "1234"]);
+        input.maxLength = 4;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 301:
+        checkMenuEntries(["1", "12", "123", "1234"]);
+        input.maxLength = 3;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 302:
+        checkMenuEntries(["1", "12", "123"]);
+        input.maxLength = 2;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 303:
+        checkMenuEntries(["1", "12"]);
+        input.maxLength = 1;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 304:
+        checkMenuEntries(["1"]);
+        input.maxLength = 0;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 305:
+        checkMenuEntries([]);
+        input.maxLength = 4;
+
+        // now again with a character typed
+        sendChar("1", input);
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 306:
+        checkMenuEntries(["1", "12", "123", "1234"]);
+        input.maxLength = 3;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 307:
+        checkMenuEntries(["1", "12", "123"]);
+        input.maxLength = 2;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 308:
+        checkMenuEntries(["1", "12"]);
+        input.maxLength = 1;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 309:
+        checkMenuEntries(["1"]);
+        input.maxLength = 0;
+        doKey("escape");
+        doKey("down");
+        break;
+
+    case 310:
+        checkMenuEntries([]);
+
         SimpleTest.finish();
         return;
 
diff --git a/toolkit/components/satchel/test/unit/test_autocomplete.js b/toolkit/components/satchel/test/unit/test_autocomplete.js
index b72962e97be..f87308bd716 100644
--- a/toolkit/components/satchel/test/unit/test_autocomplete.js
+++ b/toolkit/components/satchel/test/unit/test_autocomplete.js
@@ -131,13 +131,13 @@ function run_test() {
         // ===== 2 =====
         // Check search contains all entries
         testnum++;
-        results = fac.autoCompleteSearch("field1", "", null);
+        results = fac.autoCompleteSearch("field1", "", null, null);
         do_check_eq(numRecords, results.matchCount);
 
         // ===== 3 =====
         // Check search result ordering with empty search term
         testnum++;
-        results = fac.autoCompleteSearch("field1", "", null);
+        results = fac.autoCompleteSearch("field1", "", null, null);
         let lastFound = numRecords;
         for (let i = 0; i < numRecords; i+=2) {
             do_check_eq(parseInt(results.getValueAt(i + 1).substr(5), 10), --lastFound);
@@ -147,7 +147,7 @@ function run_test() {
         // ===== 4 =====
         // Check search result ordering with "v"
         testnum++;
-        results = fac.autoCompleteSearch("field1", "v", null);
+        results = fac.autoCompleteSearch("field1", "v", null, null);
         lastFound = numRecords;
         for (let i = 0; i < numRecords; i+=2) {
             do_check_eq(parseInt(results.getValueAt(i + 1).substr(5), 10), --lastFound);
@@ -176,7 +176,7 @@ function run_test() {
         // ===== 5 =====
         // Check search result ordering with empty search term
         testnum++;
-        results = fac.autoCompleteSearch("field2", "", null);
+        results = fac.autoCompleteSearch("field2", "", null, null);
         lastFound = timesUsedSamples;
         for (let i = 0; i < timesUsedSamples; i++) {
             do_check_eq(parseInt(results.getValueAt(i).substr(5)), --lastFound);
@@ -185,7 +185,7 @@ function run_test() {
         // ===== 6 =====
         // Check search result ordering with "v"
         testnum++;
-        results = fac.autoCompleteSearch("field2", "v", null);
+        results = fac.autoCompleteSearch("field2", "v", null, null);
         lastFound = timesUsedSamples;
         for (let i = 0; i < timesUsedSamples; i++) {
             do_check_eq(parseInt(results.getValueAt(i).substr(5)), --lastFound);
@@ -217,7 +217,7 @@ function run_test() {
               now +
           ");");
 
-        results = fac.autoCompleteSearch("field3", "", null);
+        results = fac.autoCompleteSearch("field3", "", null, null);
         do_check_eq(results.getValueAt(0), "senior citizen");
         do_check_eq(results.getValueAt(1), "old but not senior");
 
@@ -257,7 +257,7 @@ function run_test() {
               (now * 2) +
           ");");
 
-        results = fac.autoCompleteSearch("field4", "", null);
+        results = fac.autoCompleteSearch("field4", "", null, null);
         do_check_eq(results.matchCount, 3);
 
 

From 876f2f2c1380c113496464fc2b80909b265c70c1 Mon Sep 17 00:00:00 2001
From: Masayuki Nakano 
Date: Sun, 2 Aug 2009 10:51:17 +0900
Subject: [PATCH 158/175] Bug 125282 Webpage-JS can steal focus from URLbar /
 chrome r=enn, sr=smaug

---
 browser/base/content/test/Makefile.in         |  1 +
 .../base/content/test/browser_bug125282.js    | 77 +++++++++++++++++++
 dom/base/nsFocusManager.cpp                   | 15 +++-
 3 files changed, 92 insertions(+), 1 deletion(-)
 create mode 100644 browser/base/content/test/browser_bug125282.js

diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in
index 14b9ae15d64..a2b2b4758dd 100644
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -126,6 +126,7 @@ _BROWSER_FILES = \
                  browser_bug491431.js \
                  browser_bug304198.js \
                  browser_drag.js \
+                 browser_bug125282.js \
     $(NULL)
 
 ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
diff --git a/browser/base/content/test/browser_bug125282.js b/browser/base/content/test/browser_bug125282.js
new file mode 100644
index 00000000000..ee3a18055ea
--- /dev/null
+++ b/browser/base/content/test/browser_bug125282.js
@@ -0,0 +1,77 @@
+function test() {
+  waitForExplicitFinish();
+
+  let fm = Components.classes["@mozilla.org/focus-manager;1"]
+                     .getService(Components.interfaces.nsIFocusManager);
+
+  let tabs = [ gBrowser.mCurrentTab, gBrowser.addTab() ];
+  gBrowser.selectedTab = tabs[0];
+
+  let loadedCount;
+
+  // The first test set is checking the nsIDOMElement.focus can move focus at
+  // onload event.
+  let callback = doTest1;
+  load("data:text/html,");
+
+  function load(aURI) {
+    loadedCount = 0;
+    for (let i = 0; i < tabs.length; i++) {
+      tabs[i].linkedBrowser.addEventListener("load", onLoad, true);
+      tabs[i].linkedBrowser.loadURI(aURI);
+    }
+  }
+
+  function onLoad() {
+    if (++loadedCount == tabs.length) {
+      setTimeout(callback, 10);
+    }
+  }
+
+  function doTest1() {
+    tabs[0].linkedBrowser.removeEventListener("load", onLoad, true);
+    tabs[1].linkedBrowser.removeEventListener("load", onLoad, true);
+
+    let e = tabs[0].linkedBrowser.contentDocument.activeElement;
+    is(e.tagName, "INPUT", "the foreground tab's input element is not active");
+    is(fm.focusedElement, e, "the input element isn't focused");
+    e = tabs[1].linkedBrowser.contentDocument.activeElement;
+    is(e.tagName, "INPUT", "the background tab's input element is not active");
+    isnot(fm.focusedElement, e, "the input element is focused");
+
+    // The second test set is checking the nsIDOMElement.focus can NOT move
+    // if an element of chrome has focus.
+    callback = doTest2;
+    load("data:text/html,");
+
+    BrowserSearch.searchBar.focus();
+  }
+
+  let canRetry = 10;
+
+  function doTest2() {
+    if (canRetry-- > 0 &&
+        (tabs[0].linkedBrowser.contentDocument.activeElement.tagName != "INPUT" ||
+         tabs[1].linkedBrowser.contentDocument.activeElement.tagName != "INPUT")) {
+      setTimeout(doTest2, 10); // retry
+      return;
+    }
+
+    tabs[0].linkedBrowser.removeEventListener("load", onLoad, true);
+    tabs[1].linkedBrowser.removeEventListener("load", onLoad, true);
+
+    let e = tabs[0].linkedBrowser.contentDocument.activeElement;
+    is(e.tagName, "INPUT", "the foreground tab's input element is not active");
+    isnot(fm.focusedElement, e, "the input element is focused");
+    e = tabs[1].linkedBrowser.contentDocument.activeElement;
+    is(e.tagName, "INPUT", "the background tab's input element is not active");
+    isnot(fm.focusedElement, e, "the input element is focused");
+
+    // cleaning-up...
+    tabs[0].linkedBrowser.loadURI("about:blank");
+    gBrowser.selectedTab = tabs[1];
+    gBrowser.removeCurrentTab();
+
+    finish();
+  }
+}
\ No newline at end of file
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index 4d8f59ab9f5..e388f99afa3 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1041,9 +1041,22 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, PRInt32 aFlags,
   PRBool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
                             IsSameOrAncestor(newWindow, mFocusedWindow);
 
+  PRBool canStealFocus = PR_TRUE;
+  // When an element already has focus but this focus changing isn't by the
+  // user input, we should check the permission.
+  if (mFocusedContent && !(aFlags & (FLAG_BYMOUSE | FLAG_BYKEY))) {
+    nsCOMPtr currentFocusedNode =
+                           do_QueryInterface(mFocusedContent);
+    // If the caller cannot access the current focused node, the caller should
+    // not be able to steal focus from it. E.g., When the current focused node
+    // is in chrome, any web contents should not be able to steal the focus.
+    canStealFocus = nsContentUtils::CanCallerAccess(currentFocusedNode);
+  }
+
   // if the element is in the active window, frame switching is allowed and
   // the content is in a visible window, fire blur and focus events.
-  if (isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow)) {
+  if (isElementInActiveWindow && allowFrameSwitch &&
+      IsWindowVisible(newWindow) && canStealFocus) {
     // return if blurring fails or the focus changes during the blur
     if (mFocusedWindow) {
       // if the focus is being moved to another element in the same document,

From 55fc3b142c829553e10ccd237f8748021414488c Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 15:10:37 +0430
Subject: [PATCH 159/175] Backed out changeset 8366e5cc9f57 (bug 125282)
 because of four windows unit test oranges in a row (all timed out when
 running mochitest-plain)

---
 browser/base/content/test/Makefile.in         |  1 -
 .../base/content/test/browser_bug125282.js    | 77 -------------------
 dom/base/nsFocusManager.cpp                   | 15 +---
 3 files changed, 1 insertion(+), 92 deletions(-)
 delete mode 100644 browser/base/content/test/browser_bug125282.js

diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in
index a2b2b4758dd..14b9ae15d64 100644
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -126,7 +126,6 @@ _BROWSER_FILES = \
                  browser_bug491431.js \
                  browser_bug304198.js \
                  browser_drag.js \
-                 browser_bug125282.js \
     $(NULL)
 
 ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT)))
diff --git a/browser/base/content/test/browser_bug125282.js b/browser/base/content/test/browser_bug125282.js
deleted file mode 100644
index ee3a18055ea..00000000000
--- a/browser/base/content/test/browser_bug125282.js
+++ /dev/null
@@ -1,77 +0,0 @@
-function test() {
-  waitForExplicitFinish();
-
-  let fm = Components.classes["@mozilla.org/focus-manager;1"]
-                     .getService(Components.interfaces.nsIFocusManager);
-
-  let tabs = [ gBrowser.mCurrentTab, gBrowser.addTab() ];
-  gBrowser.selectedTab = tabs[0];
-
-  let loadedCount;
-
-  // The first test set is checking the nsIDOMElement.focus can move focus at
-  // onload event.
-  let callback = doTest1;
-  load("data:text/html,");
-
-  function load(aURI) {
-    loadedCount = 0;
-    for (let i = 0; i < tabs.length; i++) {
-      tabs[i].linkedBrowser.addEventListener("load", onLoad, true);
-      tabs[i].linkedBrowser.loadURI(aURI);
-    }
-  }
-
-  function onLoad() {
-    if (++loadedCount == tabs.length) {
-      setTimeout(callback, 10);
-    }
-  }
-
-  function doTest1() {
-    tabs[0].linkedBrowser.removeEventListener("load", onLoad, true);
-    tabs[1].linkedBrowser.removeEventListener("load", onLoad, true);
-
-    let e = tabs[0].linkedBrowser.contentDocument.activeElement;
-    is(e.tagName, "INPUT", "the foreground tab's input element is not active");
-    is(fm.focusedElement, e, "the input element isn't focused");
-    e = tabs[1].linkedBrowser.contentDocument.activeElement;
-    is(e.tagName, "INPUT", "the background tab's input element is not active");
-    isnot(fm.focusedElement, e, "the input element is focused");
-
-    // The second test set is checking the nsIDOMElement.focus can NOT move
-    // if an element of chrome has focus.
-    callback = doTest2;
-    load("data:text/html,");
-
-    BrowserSearch.searchBar.focus();
-  }
-
-  let canRetry = 10;
-
-  function doTest2() {
-    if (canRetry-- > 0 &&
-        (tabs[0].linkedBrowser.contentDocument.activeElement.tagName != "INPUT" ||
-         tabs[1].linkedBrowser.contentDocument.activeElement.tagName != "INPUT")) {
-      setTimeout(doTest2, 10); // retry
-      return;
-    }
-
-    tabs[0].linkedBrowser.removeEventListener("load", onLoad, true);
-    tabs[1].linkedBrowser.removeEventListener("load", onLoad, true);
-
-    let e = tabs[0].linkedBrowser.contentDocument.activeElement;
-    is(e.tagName, "INPUT", "the foreground tab's input element is not active");
-    isnot(fm.focusedElement, e, "the input element is focused");
-    e = tabs[1].linkedBrowser.contentDocument.activeElement;
-    is(e.tagName, "INPUT", "the background tab's input element is not active");
-    isnot(fm.focusedElement, e, "the input element is focused");
-
-    // cleaning-up...
-    tabs[0].linkedBrowser.loadURI("about:blank");
-    gBrowser.selectedTab = tabs[1];
-    gBrowser.removeCurrentTab();
-
-    finish();
-  }
-}
\ No newline at end of file
diff --git a/dom/base/nsFocusManager.cpp b/dom/base/nsFocusManager.cpp
index e388f99afa3..4d8f59ab9f5 100644
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1041,22 +1041,9 @@ nsFocusManager::SetFocusInner(nsIContent* aNewContent, PRInt32 aFlags,
   PRBool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
                             IsSameOrAncestor(newWindow, mFocusedWindow);
 
-  PRBool canStealFocus = PR_TRUE;
-  // When an element already has focus but this focus changing isn't by the
-  // user input, we should check the permission.
-  if (mFocusedContent && !(aFlags & (FLAG_BYMOUSE | FLAG_BYKEY))) {
-    nsCOMPtr currentFocusedNode =
-                           do_QueryInterface(mFocusedContent);
-    // If the caller cannot access the current focused node, the caller should
-    // not be able to steal focus from it. E.g., When the current focused node
-    // is in chrome, any web contents should not be able to steal the focus.
-    canStealFocus = nsContentUtils::CanCallerAccess(currentFocusedNode);
-  }
-
   // if the element is in the active window, frame switching is allowed and
   // the content is in a visible window, fire blur and focus events.
-  if (isElementInActiveWindow && allowFrameSwitch &&
-      IsWindowVisible(newWindow) && canStealFocus) {
+  if (isElementInActiveWindow && allowFrameSwitch && IsWindowVisible(newWindow)) {
     // return if blurring fails or the focus changes during the blur
     if (mFocusedWindow) {
       // if the focus is being moved to another element in the same document,

From effbfd086a086ae79dc5e89cd10b727ed09a0f0c Mon Sep 17 00:00:00 2001
From: Rich Walsh 
Date: Sun, 2 Aug 2009 14:06:02 +0300
Subject: [PATCH 160/175] Bug 506434 - fix ogg video playback regression, add
 OS/2 part to sydneyaudio patch, r=pweilbacher

---
 media/libsydneyaudio/sydney_os2_base.patch | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/media/libsydneyaudio/sydney_os2_base.patch b/media/libsydneyaudio/sydney_os2_base.patch
index 2750a31d72c..a3078274703 100644
--- a/media/libsydneyaudio/sydney_os2_base.patch
+++ b/media/libsydneyaudio/sydney_os2_base.patch
@@ -552,10 +552,12 @@ new file mode 100644
 +  /* keep os2_mixer_event() from reacting to buffer under-runs */
 +  s->state = SAOS2_EXIT;
 +
-+  /* DART won't start playing until 2 buffers have been
-+   * written, so write a dummy 2nd buffer just in case */
-+  memset(buf, 0, sizeof(buf));
-+  sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
++  /* DART won't start playing until 2 buffers have been written,
++   * so write a dummy 2nd buffer if any buffers are in use */
++  if (s->freeCnt < SAOS2_BUF_CNT) {
++    memset(buf, 0, sizeof(buf));
++    sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
++  }
 +
 +  /* write all remaining buffers to the device */
 +  if (s->readyCnt)

From dada0805751ccde995efdc1a5cca22d67aca2710 Mon Sep 17 00:00:00 2001
From: Rich Walsh 
Date: Sun, 2 Aug 2009 14:06:02 +0300
Subject: [PATCH 161/175] Bug 506434 - fix ogg video playback regression,
 cross-platform change, r=chris.double

---
 content/media/ogg/nsOggDecoder.cpp | 10 ++--------
 1 file changed, 2 insertions(+), 8 deletions(-)

diff --git a/content/media/ogg/nsOggDecoder.cpp b/content/media/ogg/nsOggDecoder.cpp
index 3855ffd10ca..20cd6455bcc 100644
--- a/content/media/ogg/nsOggDecoder.cpp
+++ b/content/media/ogg/nsOggDecoder.cpp
@@ -910,22 +910,16 @@ void nsOggDecodeStateMachine::PlayFrame() {
       }
 
       double time;
-      double prevTime = -1.0;
+      PRUint32 hasAudio = frame->mAudioData.Length();
       for (;;) {
         // Even if the frame has had its audio data written we call
         // PlayAudio to ensure that any data we have buffered in the
         // nsAudioStream is written to the hardware.
         PlayAudio(frame);
-        double hwtime = mAudioStream ? mAudioStream->GetPosition() : -1.0;
+        double hwtime = mAudioStream && hasAudio ? mAudioStream->GetPosition() : -1.0;
         time = hwtime < 0.0 ?
           (TimeStamp::Now() - mPlayStartTime - mPauseDuration).ToSeconds() :
           hwtime;
-        // Break out of the loop if we've not played any audio. This can
-        // happen when the frame has no audio, and there's no audio pending
-        // in the nsAudioStream.
-        if (time == prevTime)
-          break;
-        prevTime = time;
         // Is it time for the next frame?  Using an integer here avoids f.p.
         // rounding errors that can cause multiple 0ms waits (Bug 495352)
         PRInt64 wait = PRInt64((frame->mTime - time)*1000);

From 2d6a49e99b5ea7aa48f8827ecc082536fbcee2af Mon Sep 17 00:00:00 2001
From: Rich Walsh 
Date: Sun, 2 Aug 2009 14:06:02 +0300
Subject: [PATCH 162/175] Bug 506434 - fix ogg video playback regression, OS/2
 part, r=pweilbacher

---
 media/libsydneyaudio/src/sydney_audio_os2.c | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/media/libsydneyaudio/src/sydney_audio_os2.c b/media/libsydneyaudio/src/sydney_audio_os2.c
index c3a6f4cb40d..6e768aace7c 100644
--- a/media/libsydneyaudio/src/sydney_audio_os2.c
+++ b/media/libsydneyaudio/src/sydney_audio_os2.c
@@ -555,10 +555,12 @@ int     sa_stream_drain(sa_stream_t *s)
   /* keep os2_mixer_event() from reacting to buffer under-runs */
   s->state = SAOS2_EXIT;
 
-  /* DART won't start playing until 2 buffers have been
-   * written, so write a dummy 2nd buffer just in case */
-  memset(buf, 0, sizeof(buf));
-  sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
+  /* DART won't start playing until 2 buffers have been written,
+   * so write a dummy 2nd buffer if any buffers are in use */
+  if (s->freeCnt < SAOS2_BUF_CNT) {
+    memset(buf, 0, sizeof(buf));
+    sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE);
+  }
 
   /* write all remaining buffers to the device */
   if (s->readyCnt)

From 6538983820aa74a94448d345ade0bbe8211e9cd4 Mon Sep 17 00:00:00 2001
From: Rich Walsh 
Date: Sun, 2 Aug 2009 14:06:02 +0300
Subject: [PATCH 163/175] [OS/2] Bug 501618 - fix plugin display (e.g. MPlayer
 plugin, npmp) after child widget removal, r=pweilbacher

---
 widget/src/os2/nsWindow.cpp | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/widget/src/os2/nsWindow.cpp b/widget/src/os2/nsWindow.cpp
index 39a69ccee60..5cd585b0f49 100644
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -2035,19 +2035,21 @@ nsWindow::ConfigureChildren(const nsTArray& aConfigurations)
       r.UnionRect(r, rects[i]);
 
     // resize the child;  mBounds.x/y contain the child's correct origin;
-    // r.x/y are always <= zero - adding them to r.width/height produces
-    // the actual clipped width/height this window should have
+    // the sum of r.x/y + r.width/height produces the actual clipped
+    // width/height this window should have - it's only smaller than
+    // normal when part or all the window is scrolled off the right
+    // or bottom side of the parent
     w->Resize(configuration.mBounds.x, configuration.mBounds.y,
-              r.width + r.x, r.height + r.y, PR_TRUE);
+              r.width + r.x, r.height + r.y, PR_FALSE);
 
     // some plugins may shrink their window when the Mozilla widget window
     // shrinks, then fail to reinflate when the widget window reinflates;
     // this ensures the plugin's window is always at its full size and is
-    // clipped correctly by the widget's bounds
+    // positioned so the correct part of the child will be clipped
     HWND hwnd = WinQueryWindow( w->mWnd, QW_TOP);
-    ::WinSetWindowPos(hwnd, 0, 0, 0,
-                      configuration.mBounds.width, configuration.mBounds.height,
-                      SWP_MOVE | SWP_SIZE);
+    WinSetWindowPos(hwnd, 0, 0, r.height + r.y - configuration.mBounds.height,
+                    configuration.mBounds.width, configuration.mBounds.height,
+                    SWP_MOVE | SWP_SIZE);
 
     // show or hide the window, then save the array of rects
     // for future reference
@@ -2108,7 +2110,22 @@ void nsWindow::Scroll(const nsIntPoint& aDelta, const nsIntRect& aSource,
   // fetching it unlocks the screen so it can be updated
   HPS hps = 0;
   CheckDragStatus(ACTION_SCROLL, &hps);
-  ::WinScrollWindow(mWnd, aDelta.x, -aDelta.y, &clip, &clip, NULL, NULL, flags);
+
+  // send a WM_VRNDISABLED to the grandchildren of this window;
+  // if they're plugins that blit directly to the screen, this
+  // will halt their output during the scroll - if they're
+  // anything else, this will have no effect
+  HWND hChild;
+  HENUM hEnum = WinBeginEnumWindows(mWnd);
+  while ((hChild = WinGetNextWindow(hEnum)) != 0) {
+    HWND hGrandChild;
+    if ((hGrandChild = WinQueryWindow(hChild, QW_TOP)) != 0)
+      WinSendMsg(hGrandChild, WM_VRNDISABLED, 0, 0);
+  }
+  WinEndEnumWindows(hEnum);
+
+  // do it
+  WinScrollWindow(mWnd, aDelta.x, -aDelta.y, &clip, &clip, NULL, NULL, flags);
 
   // Now make sure all children actually get positioned, sized, and clipped
   // correctly.  If SW_SCROLLCHILDREN was in effect, they may already be.

From 84fece3f2547acc27c1c6a21be1c9d0f70637b70 Mon Sep 17 00:00:00 2001
From: Peter Weilbacher 
Date: Sun, 2 Aug 2009 14:06:02 +0300
Subject: [PATCH 164/175] [OS/2] Bug 506997: optimize repainting after window
 moves, r=dragtext

---
 widget/src/os2/nsWindow.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/widget/src/os2/nsWindow.cpp b/widget/src/os2/nsWindow.cpp
index 5cd585b0f49..c1af77ad9de 100644
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -103,9 +103,6 @@ static const char *sScreenManagerContractID = "@mozilla.org/gfx/screenmanager;1"
 // NS2PM methods for conversion of points & rectangles; position is a bit
 // different in that it's the *parent* window whose height must be used.
 //
-// Deferred window positioning is emulated using WinSetMultWindowPos in
-// the hopes that there was a good reason for adding it to nsIWidget.
-//
 // SetColorSpace() is not implemented on purpose.  So there.
 //
 // John Fairhurst 17-09-98 first version
@@ -1354,9 +1351,12 @@ NS_METHOD nsWindow::Resize(PRInt32 aX,
          ptl.y = WinQuerySysValue(HWND_DESKTOP, SV_CYSCREEN) - h - 1 - aY;
       }
 
-      WinSetWindowPos(GetMainWindow(), 0, ptl.x, ptl.y, w, h, SWP_MOVE | SWP_SIZE);
-      if (aRepaint)
-         Invalidate(PR_FALSE);
+      if (!WinSetWindowPos(GetMainWindow(), 0, ptl.x, ptl.y, w, h,
+                           SWP_MOVE | SWP_SIZE)) {
+         if (aRepaint) {
+            Invalidate(PR_FALSE);
+         }
+      }
 
 #if DEBUG_sobotka
       printf("+++++++++++Resized 0x%lx at %ld, %ld to %d x %d (%d,%d)\n",

From 69fe204dbc2dedc16d7c0f1e3c075ffccdca433e Mon Sep 17 00:00:00 2001
From: Peter Weilbacher 
Date: Sun, 2 Aug 2009 14:08:02 +0300
Subject: [PATCH 165/175] [OS/2] Bug 503744: do not unload MDM to fix MMOS2
 destruction, r=dragtext

---
 widget/src/os2/nsSound.cpp | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/widget/src/os2/nsSound.cpp b/widget/src/os2/nsSound.cpp
index c4473b847fd..304d906ec3d 100644
--- a/widget/src/os2/nsSound.cpp
+++ b/widget/src/os2/nsSound.cpp
@@ -70,7 +70,6 @@ NS_IMPL_ISUPPORTS2(nsSound, nsISound, nsIStreamLoaderObserver)
 static int sInitialized = 0;
 static PRBool sMMPMInstalled = PR_FALSE;
 static HMODULE sHModMMIO = NULLHANDLE;
-static HMODULE sHModMDM = NULLHANDLE;
 
 // function pointer definitions, include underscore (work around redef. warning)
 HMMIO (*APIENTRY _mmioOpen)(PSZ, PMMIOINFO, ULONG);
@@ -100,9 +99,10 @@ static void InitGlobals(void)
 {
   ULONG ulrc = 0;
   char LoadError[CCHMAXPATH];
+  HMODULE hModMDM = NULLHANDLE;
 
   ulrc = DosLoadModule(LoadError, CCHMAXPATH, "MMIO", &sHModMMIO);
-  ulrc += DosLoadModule(LoadError, CCHMAXPATH, "MDM", &sHModMDM);
+  ulrc += DosLoadModule(LoadError, CCHMAXPATH, "MDM", &hModMDM);
   if (ulrc == NO_ERROR) {
 #ifdef DEBUG
     printf("InitGlobals: MMOS2 is installed, both DLLs loaded\n");
@@ -114,12 +114,12 @@ static void InitGlobals(void)
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioClose", (PFN *)&_mmioClose);
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormats", (PFN *)&_mmioGetFormats);
     // mci functions are in MDM.DLL
-    ulrc += DosQueryProcAddr(sHModMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand);
+    ulrc += DosQueryProcAddr(hModMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand);
 #ifdef DEBUG
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetLastError", (PFN *)&_mmioGetLastError);
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioQueryFormatCount", (PFN *)&_mmioQueryFormatCount);
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormatName", (PFN *)&_mmioGetFormatName);
-    ulrc += DosQueryProcAddr(sHModMDM, 0L, "mciGetErrorString", (PFN *)&_mciGetErrorString);
+    ulrc += DosQueryProcAddr(hModMDM, 0L, "mciGetErrorString", (PFN *)&_mciGetErrorString);
 #endif
 
     ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioIniFileHandler", (PFN *)&_mmioIniFileHandler);
@@ -398,7 +398,7 @@ nsSound::~nsSound()
 #endif
     ULONG ulrc;
     ulrc = DosFreeModule(sHModMMIO);
-    ulrc += DosFreeModule(sHModMDM);
+    // do not free MDM.DLL because it doesn't like to be unloaded repeatedly
     if (ulrc != NO_ERROR) {
       NS_WARNING("DosFreeModule did not work");
     }

From ed972bfa6b9bf8a370bda704102efa92efc9e630 Mon Sep 17 00:00:00 2001
From: Peter Weilbacher 
Date: Sun, 2 Aug 2009 14:08:40 +0300
Subject: [PATCH 166/175] [OS/2] Bug 503744: use the few system sounds that we
 have available on OS/2, r=dragtext

---
 widget/src/os2/nsSound.cpp | 42 +++++++++++++++++++++++++-------------
 1 file changed, 28 insertions(+), 14 deletions(-)

diff --git a/widget/src/os2/nsSound.cpp b/widget/src/os2/nsSound.cpp
index 304d906ec3d..ee48d390462 100644
--- a/widget/src/os2/nsSound.cpp
+++ b/widget/src/os2/nsSound.cpp
@@ -457,14 +457,14 @@ NS_IMETHODIMP nsSound::OnStreamComplete(nsIStreamLoader *aLoader,
   return NS_OK;
 }
 
-NS_METHOD nsSound::Beep()
+NS_IMETHODIMP nsSound::Beep()
 {
   WinAlarm(HWND_DESKTOP, WA_WARNING);
 
   return NS_OK;
 }
 
-NS_METHOD nsSound::Play(nsIURL *aURL)
+NS_IMETHODIMP nsSound::Play(nsIURL *aURL)
 {
   nsresult rv;
 
@@ -481,18 +481,22 @@ NS_IMETHODIMP nsSound::Init()
 
 NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
 {
-  // Just beep if MMPM isn't installed.
   if (!sMMPMInstalled) {
-    Beep();
-    return NS_OK;
+    return Beep();
   }
 
   if (NS_IsMozAliasSound(aSoundAlias)) {
     NS_WARNING("nsISound::playSystemSound is called with \"_moz_\" events, they are obsolete, use nsISound::playEventSound instead");
-    // We don't have a default mail sound on OS/2, so just beep.
-    if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
-      Beep();
-    return NS_OK;
+    PRUint32 eventId;
+    if (aSoundAlias.Equals(NS_SYSSOUND_ALERT_DIALOG))
+        eventId = EVENT_ALERT_DIALOG_OPEN;
+    else if (aSoundAlias.Equals(NS_SYSSOUND_CONFIRM_DIALOG))
+        eventId = EVENT_CONFIRM_DIALOG_OPEN;
+    else if (aSoundAlias.Equals(NS_SYSSOUND_MAIL_BEEP))
+        eventId = EVENT_NEW_MAIL_RECEIVED;
+    else
+        return NS_OK;
+    return PlayEventSound(eventId);
   }
   nsCAutoString nativeSoundAlias;
   NS_CopyUnicodeToNative(aSoundAlias, nativeSoundAlias);
@@ -515,11 +519,21 @@ NS_IMETHODIMP nsSound::PlaySystemSound(const nsAString &aSoundAlias)
 
 NS_IMETHODIMP nsSound::PlayEventSound(PRUint32 aEventId)
 {
-  // Just beep if MMPM isn't installed.
-  if (!sMMPMInstalled) {
-    return Beep();
+  // Prompt dialog and select dialog sounds do not correspond to OS/2
+  // system sounds, ignore them. Ignore the menu sounds, too. Try to handle
+  // the rest. Skip the beeps on systems without MMPM, too many of them are
+  // confusing and annoying.
+  switch(aEventId) {
+  case EVENT_NEW_MAIL_RECEIVED:
+    // We don't have a default mail sound on OS/2, so just "beep"
+    return Beep(); // this corresponds to the "Warning" sound
+  case EVENT_ALERT_DIALOG_OPEN:
+    WinAlarm(HWND_DESKTOP, WA_ERROR); // play "Error" sound
+    break;
+  case EVENT_CONFIRM_DIALOG_OPEN:
+    WinAlarm(HWND_DESKTOP, WA_NOTE); // play "Information" sound
+    break;
   }
 
-  // We don't have a default mail sound on OS/2, so just beep.
-  return aEventId == EVENT_NEW_MAIL_RECEIVED ? Beep() : NS_OK;
+  return NS_OK;
 }

From 2ef9c481748a6fe66309eb10e589a21c367f4581 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?D=C3=A3o=20Gottwald?= 
Date: Sun, 2 Aug 2009 18:31:24 +0200
Subject: [PATCH 167/175] Bug 507883 - addCookieToHash() creates a useless
 object that will be clobbered right away. r=zeniko

---
 browser/components/sessionstore/src/nsSessionStore.js | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js
index 958a8838d00..1a9422b5374 100644
--- a/browser/components/sessionstore/src/nsSessionStore.js
+++ b/browser/components/sessionstore/src/nsSessionStore.js
@@ -1594,9 +1594,6 @@ SessionStoreService.prototype = {
         aHash[aHost] = {};
       if (!aHash[aHost][aPath])
         aHash[aHost][aPath] = {};
-      if (!aHash[aHost][aPath][aName])
-        aHash[aHost][aPath][aName] = {};
-
       aHash[aHost][aPath][aName] = aCookie;
     }
 

From e9db04451304acecdd7292edd4622ad420409429 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:01 +0430
Subject: [PATCH 168/175] Bug 487656 - zoomed in by default while entering
 Private Browsing; r=dao,mconnor

---
 browser/base/content/browser-textZoom.js      |  29 ++--
 browser/base/content/browser.js               |   5 +-
 .../base/content/test/browser_bug386835.js    |  79 +++++++++-
 .../privatebrowsing/test/browser/Makefile.in  |   1 +
 .../browser_privatebrowsing_zoomrestore.js    | 137 ++++++++++++++++++
 .../components/printing/content/printUtils.js |  13 +-
 6 files changed, 248 insertions(+), 16 deletions(-)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js

diff --git a/browser/base/content/browser-textZoom.js b/browser/base/content/browser-textZoom.js
index d947ebdd359..9a3246e15e9 100644
--- a/browser/base/content/browser-textZoom.js
+++ b/browser/base/content/browser-textZoom.js
@@ -266,8 +266,19 @@ var FullZoom = {
 
   // location change observer
 
-  onLocationChange: function FullZoom_onLocationChange(aURI, aBrowser) {
-    if (!aURI)
+  /**
+   * Called when the location of a tab changes.
+   * When that happens, we need to update the current zoom level if appropriate.
+   *
+   * @param aURI
+   *        A URI object representing the new location.
+   * @param aIsTabSwitch
+   *        Whether this location change has happened because of a tab switch.
+   * @param aBrowser
+   *        (optional) browser object displaying the document
+   */
+  onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
+    if (!aURI || (aIsTabSwitch && !this.siteSpecific))
       return;
     this._applyPrefToSetting(this._cps.getPref(aURI, this.name), aBrowser);
   },
@@ -302,11 +313,6 @@ var FullZoom = {
     this._removePref();
   },
 
-  setSettingValue: function FullZoom_setSettingValue() {
-    var value = this._cps.getPref(gBrowser.currentURI, this.name);
-    this._applyPrefToSetting(value);
-  },
-
   /**
    * Set the zoom level for the current tab.
    *
@@ -329,12 +335,13 @@ var FullZoom = {
   _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
     var browser = aBrowser || gBrowser.selectedBrowser;
 
-    if (!this.siteSpecific || gInPrintPreviewMode ||
-        browser.contentDocument instanceof Ci.nsIImageDocument)
-      return;
+    var resetZoom = (!this.siteSpecific || gInPrintPreviewMode ||
+                     browser.contentDocument instanceof Ci.nsIImageDocument);
 
     try {
-      if (typeof aValue != "undefined")
+      if (resetZoom)
+        ZoomManager.setZoomForBrowser(browser, 1);
+      else if (typeof aValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
       else if (typeof this.globalValue != "undefined")
         ZoomManager.setZoomForBrowser(browser, this.globalValue);
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index fce6b078c01..b886ea6192d 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2558,7 +2558,6 @@ function onExitPrintPreview()
 {
   // restore chrome to original state
   gInPrintPreviewMode = false;
-  FullZoom.setSettingValue();
   toggleAffectedChrome(false);
 }
 
@@ -4229,7 +4228,7 @@ var XULBrowserWindow = {
   // simulate all change notifications after switching tabs
   onUpdateCurrentBrowser: function (aStateFlags, aStatus, aMessage, aTotalProgress) {
     if (FullZoom.updateBackgroundTabs)
-      FullZoom.onLocationChange(gBrowser.currentURI);
+      FullZoom.onLocationChange(gBrowser.currentURI, true);
     var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
     var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
     // use a pseudo-object instead of a (potentially non-existing) channel for getting
@@ -4296,7 +4295,7 @@ var TabsProgressListener = {
   onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI) {
     // Filter out any sub-frame loads
     if (aBrowser.contentWindow == aWebProgress.DOMWindow)
-      FullZoom.onLocationChange(aLocationURI, aBrowser);
+      FullZoom.onLocationChange(aLocationURI, false, aBrowser);
   },
   
   onStatusChange: function (aBrowser, aWebProgress, aRequest, aStatus, aMessage) {
diff --git a/browser/base/content/test/browser_bug386835.js b/browser/base/content/test/browser_bug386835.js
index 17f12925315..5ce3cc5ebf1 100644
--- a/browser/base/content/test/browser_bug386835.js
+++ b/browser/base/content/test/browser_bug386835.js
@@ -2,6 +2,8 @@ var gTestPage = "http://example.org/browser/browser/base/content/test/dummy_page
 var gTestImage = "http://example.org/browser/browser/base/content/test/moz.png";
 var gTab1, gTab2, gTab3;
 var gLevel;
+const kBack = 0;
+const kForward = 1;
 
 function test() {
   waitForExplicitFinish();
@@ -53,10 +55,74 @@ function imageLoaded() {
   gBrowser.selectedTab = gTab1;
   zoomTest(gTab1, 1, "Zoom should still be 1 when tab with image is selected");
 
-  finishTest();
+  executeSoon(imageZoomSwitch);
+}
+
+function imageZoomSwitch() {
+  navigate(kBack, function() {
+    navigate(kForward, function() {
+      zoomTest(gTab1, 1, "Tab 1 should not be zoomed when an image loads");
+      gBrowser.selectedTab = gTab2;
+      zoomTest(gTab1, 1, "Tab 1 should still not be zoomed when deselected");
+
+      // Mac OS X does not support print preview, so skip those tests
+      let isOSX = ("nsILocalFileMac" in Components.interfaces);
+      if (isOSX)
+        finishTest();
+      else
+        runPrintPreviewTests();
+    });
+  });
+}
+
+function runPrintPreviewTests() {
+  // test print preview on image document
+  testPrintPreview(gTab1, function() {
+    // test print preview on HTML document
+    testPrintPreview(gTab2, function() {
+      // test print preview on image document with siteSpecific set to false
+      gPrefService.setBoolPref("browser.zoom.siteSpecific", false);
+      testPrintPreview(gTab1, function() {
+        // test print preview of HTML document with siteSpecific set to false
+        testPrintPreview(gTab2, function() {
+          gPrefService.clearUserPref("browser.zoom.siteSpecific");
+          finishTest();
+        });
+      });
+    });
+  });
+}
+
+function testPrintPreview(aTab, aCallback) {
+  gBrowser.selectedTab = aTab;
+  FullZoom.enlarge();
+  let level = ZoomManager.zoom;
+
+  function onEnterPP() {
+    toggleAffectedChromeOrig.apply(null, arguments);
+
+    function onExitPP() {
+      toggleAffectedChromeOrig.apply(null, arguments);
+      toggleAffectedChrome = toggleAffectedChromeOrig;
+
+      zoomTest(aTab, level, "Toggling print preview mode should not affect zoom level");
+
+      FullZoom.reset();
+      aCallback();
+    }
+    toggleAffectedChrome = onExitPP;
+    PrintUtils.exitPrintPreview();
+  }
+  let toggleAffectedChromeOrig = toggleAffectedChrome;
+  toggleAffectedChrome = onEnterPP;
+
+  let printPreview = new Function(document.getElementById("cmd_printPreview")
+                                          .getAttribute("oncommand"));
+  executeSoon(printPreview);
 }
 
 function finishTest() {
+  gBrowser.selectedTab = gTab1;
   FullZoom.reset();
   gBrowser.removeTab(gTab1);
   FullZoom.reset();
@@ -77,3 +143,14 @@ function load(tab, url, cb) {
   }, true);
   tab.linkedBrowser.loadURI(url);
 }
+
+function navigate(direction, cb) {
+  gBrowser.addEventListener("pageshow", function(event) {
+    gBrowser.removeEventListener("pageshow", arguments.callee, true);
+    setTimeout(cb, 0);
+  }, true);
+  if (direction == kBack)
+    gBrowser.goBack();
+  else if (direction == kForward)
+    gBrowser.goForward();
+}
diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index 09488ff47c7..ce41daa48d0 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -51,6 +51,7 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_searchbar.js \
 		browser_privatebrowsing_findbar.js \
 		browser_privatebrowsing_zoom.js \
+		browser_privatebrowsing_zoomrestore.js \
 		browser_privatebrowsing_transition.js \
 		browser_privatebrowsing_import.js \
 		browser_privatebrowsing_crh.js \
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
new file mode 100644
index 00000000000..dd1987dade2
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_zoomrestore.js
@@ -0,0 +1,137 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that about:privatebrowsing does not appear zoomed in
+// if there is already a zoom site pref for about:blank (bug 487656).
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  let cps = Cc["@mozilla.org/content-pref/service;1"].
+            getService(Ci.nsIContentPrefService);
+  waitForExplicitFinish();
+
+  let tabBlank = gBrowser.selectedTab;
+  gBrowser.removeAllTabsBut(tabBlank);
+
+  let blankBrowser = gBrowser.getBrowserForTab(tabBlank);
+  blankBrowser.addEventListener("load", function() {
+    blankBrowser.removeEventListener("load", arguments.callee, true);
+
+    // change the zoom on the blank page
+    FullZoom.enlarge();
+    isnot(ZoomManager.zoom, 1, "Zoom level for about:blank should be changed");
+
+    // enter private browsing mode
+    pb.privateBrowsingEnabled = true;
+    let tabAboutPB = gBrowser.selectedTab;
+    let browserAboutPB = gBrowser.getBrowserForTab(tabAboutPB);
+    browserAboutPB.addEventListener("load", function() {
+      browserAboutPB.removeEventListener("load", arguments.callee, true);
+      setTimeout(function() {
+        // make sure the zoom level is set to 1
+        is(ZoomManager.zoom, 1, "Zoom level for about:privatebrowsing should be reset");
+
+        // Mac OS X does not support print preview, so skip those tests
+        let isOSX = ("nsILocalFileMac" in Components.interfaces);
+        if (isOSX) {
+          finishTest();
+          return;
+        }
+
+        // test print preview on HTML document
+        testPrintPreview(browserAboutPB, function() {
+          browserAboutPB.addEventListener("load", function() {
+            browserAboutPB.removeEventListener("load", arguments.callee, true);
+
+            // test print preview on image document
+            testPrintPreview(browserAboutPB, finishTest);
+          }, true);
+          browserAboutPB.loadURI("about:logo");
+        });
+      }, 0);
+    }, true);
+  }, true);
+  blankBrowser.loadURI("about:blank");
+}
+
+function finishTest() {
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  // leave private browsing mode
+  pb.privateBrowsingEnabled = false;
+  let tabBlank = gBrowser.selectedTab;
+  let blankBrowser = gBrowser.getBrowserForTab(tabBlank);
+  blankBrowser.addEventListener("load", function() {
+    blankBrowser.removeEventListener("load", arguments.callee, true);
+
+    executeSoon(function() {
+      // cleanup
+      FullZoom.reset();
+      finish();
+    });
+  }, true);
+}
+
+function testPrintPreview(aBrowser, aCallback) {
+  FullZoom.enlarge();
+  let level = ZoomManager.getZoomForBrowser(aBrowser);
+
+  function onEnterPP(aHide) {
+    toggleAffectedChromeOrig(aHide);
+
+    function onExitPP(aHide) {
+      toggleAffectedChromeOrig(aHide);
+      toggleAffectedChrome = toggleAffectedChromeOrig;
+
+      is(ZoomManager.getZoomForBrowser(aBrowser), level,
+         "Toggling print preview mode should not affect zoom level");
+
+      FullZoom.reset();
+      aCallback();
+    }
+    toggleAffectedChrome = onExitPP;
+    PrintUtils.exitPrintPreview();
+  }
+  let toggleAffectedChromeOrig = toggleAffectedChrome;
+  toggleAffectedChrome = onEnterPP;
+
+  let printPreview = new Function(document.getElementById("cmd_printPreview")
+                                          .getAttribute("oncommand"));
+  executeSoon(printPreview);
+}
diff --git a/toolkit/components/printing/content/printUtils.js b/toolkit/components/printing/content/printUtils.js
index 352587f2a5d..ba79da78533 100644
--- a/toolkit/components/printing/content/printUtils.js
+++ b/toolkit/components/printing/content/printUtils.js
@@ -190,6 +190,7 @@ var PrintUtils = {
     return printSettings;
   },
 
+  _originalZoomValue: null,
   _closeHandlerPP: null,
   _webProgressPP: null,
   _onEnterPP: null,
@@ -218,11 +219,19 @@ var PrintUtils = {
   {
     gFocusedElement = document.commandDispatcher.focusedElement;
 
+    // Reset the zoom value and save it to be restored later.
+    if (typeof ZoomManager == "object") {
+      this._originalZoomValue = ZoomManager.zoom;
+      ZoomManager.reset();
+    }
+
     var webBrowserPrint = this.getWebBrowserPrint(aWindow);
     var printSettings   = this.getPrintSettings();
     try {
       webBrowserPrint.printPreview(printSettings, null, this._webProgressPP.value);
     } catch (e) {
+      if (typeof ZoomManager == "object")
+        ZoomManager.zoom = this._originalZoomValue;
       // Pressing cancel is expressed as an NS_ERROR_ABORT return value,
       // causing an exception to be thrown which we catch here.
       // Unfortunately this will also consume helpful failures, so add a
@@ -280,7 +289,9 @@ var PrintUtils = {
     this._closeHandlerPP = null;
 
     var webBrowserPrint = this.getWebBrowserPrint(aWindow);
-    webBrowserPrint.exitPrintPreview(); 
+    webBrowserPrint.exitPrintPreview();
+    if (typeof ZoomManager == "object")
+      ZoomManager.zoom = this._originalZoomValue;
 
     // remove the print preview toolbar
     var printPreviewTB = document.getElementById("print-preview-toolbar");

From 4b8b52791df2f3c9955175f3e66ff6afc069810a Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:04 +0430
Subject: [PATCH 169/175] Bug 496595 - Privacy leak in "remember for this site"
 permission of geolocation - persists outside of private browsing; r=mconnor

---
 browser/components/nsBrowserGlue.js           | 24 +++--
 .../privatebrowsing/test/browser/Makefile.in  |  2 +
 .../browser_privatebrowsing_geoprompt.js      | 96 +++++++++++++++++++
 ...rowser_privatebrowsing_geoprompt_page.html | 13 +++
 4 files changed, 127 insertions(+), 8 deletions(-)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html

diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
index 6d49a79adc4..77edfebd1b9 100644
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1117,8 +1117,9 @@ GeolocationPrompt.prototype = {
       var buttons = [{
               label: browserBundle.GetStringFromName("geolocation.shareLocation"),
               accessKey: browserBundle.GetStringFromName("geolocation.shareLocation.accesskey"),
-              callback: function(notification) {                  
-                  if (notification.getElementsByClassName("rememberChoice")[0].checked)
+              callback: function(notification) {
+                  var elements = notification.getElementsByClassName("rememberChoice");
+                  if (elements.length && elements[0].checked)
                       setPagePermission(request.requestingURI, true);
                   request.allow(); 
               },
@@ -1127,7 +1128,8 @@ GeolocationPrompt.prototype = {
               label: browserBundle.GetStringFromName("geolocation.dontShareLocation"),
               accessKey: browserBundle.GetStringFromName("geolocation.dontShareLocation.accesskey"),
               callback: function(notification) {
-                  if (notification.getElementsByClassName("rememberChoice")[0].checked)
+                  var elements = notification.getElementsByClassName("rememberChoice");
+                  if (elements.length && elements[0].checked)
                       setPagePermission(request.requestingURI, false);
                   request.cancel();
               },
@@ -1148,11 +1150,17 @@ GeolocationPrompt.prototype = {
       // bar.
       function geolocation_hacks_to_notification () {
 
-        var checkbox = newBar.ownerDocument.createElementNS(XULNS, "checkbox");
-        checkbox.className = "rememberChoice";
-        checkbox.setAttribute("label", browserBundle.GetStringFromName("geolocation.remember"));
-        checkbox.setAttribute("accesskey", browserBundle.GetStringFromName("geolocation.remember.accesskey"));
-        newBar.appendChild(checkbox);
+        // Never show a remember checkbox inside the private browsing mode
+        var inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
+                                getService(Ci.nsIPrivateBrowsingService).
+                                privateBrowsingEnabled;
+        if (!inPrivateBrowsing) {
+          var checkbox = newBar.ownerDocument.createElementNS(XULNS, "checkbox");
+          checkbox.className = "rememberChoice";
+          checkbox.setAttribute("label", browserBundle.GetStringFromName("geolocation.remember"));
+          checkbox.setAttribute("accesskey", browserBundle.GetStringFromName("geolocation.remember.accesskey"));
+          newBar.appendChild(checkbox);
+        }
 
         var link = newBar.ownerDocument.createElementNS(XULNS, "label");
         link.className = "text-link";
diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index ce41daa48d0..11f6f26e48d 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -62,6 +62,8 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_pageinfo.js \
 		browser_privatebrowsing_sslsite_transition.js \
 		browser_privatebrowsing_popupmode.js \
+		browser_privatebrowsing_geoprompt.js \
+		browser_privatebrowsing_geoprompt_page.html \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
new file mode 100644
index 00000000000..82abe577ae6
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt.js
@@ -0,0 +1,96 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that the geolocation prompt does not show a remember
+// control inside the private browsing mode.
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+
+  const testPageURL = "http://localhost:8888/browser/" +
+    "browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html";
+  waitForExplicitFinish();
+
+  let pageTab = gBrowser.addTab();
+  gBrowser.selectedTab = pageTab;
+  let pageBrowser = gBrowser.getBrowserForTab(pageTab);
+  pageBrowser.addEventListener("load", function () {
+    pageBrowser.removeEventListener("load", arguments.callee, true);
+
+    setTimeout(function() {
+      // Make sure the notification is correctly displayed with a remember control
+      let notificationBox = gBrowser.getNotificationBox(pageBrowser);
+      let notification = notificationBox.getNotificationWithValue("geolocation");
+      ok(notification, "Notification box should be displaying outside of private browsing mode");
+      is(notification.getElementsByClassName("rememberChoice").length, 1,
+         "The remember control must be displayed outside of private browsing mode");
+      notificationBox.currentNotification.close();
+
+      gBrowser.removeTab(pageTab);
+
+      // enter the private browsing mode
+      pb.privateBrowsingEnabled = true;
+
+      pageTab = gBrowser.addTab();
+      gBrowser.selectedTab = pageTab;
+      pageBrowser = gBrowser.getBrowserForTab(pageTab);
+      pageBrowser.addEventListener("load", function () {
+        pageBrowser.removeEventListener("load", arguments.callee, true);
+
+        setTimeout(function() {
+          // Make sure the notification is correctly displayed without a remember control
+          let notificationBox = gBrowser.getNotificationBox(pageBrowser);
+          let notification = notificationBox.getNotificationWithValue("geolocation");
+          ok(notification, "Notification box should be displaying outside of private browsing mode");
+          is(notification.getElementsByClassName("rememberChoice").length, 0,
+             "The remember control must not be displayed inside of private browsing mode");
+          notificationBox.currentNotification.close();
+
+          gBrowser.removeTab(pageTab);
+
+          // cleanup
+          pb.privateBrowsingEnabled = false;
+          finish();
+        }, 100); // remember control is added in a setTimeout(0) call
+      }, true);
+      pageBrowser.contentWindow.location = testPageURL;
+    }, 100); // remember control is added in a setTimeout(0) call
+  }, true);
+  pageBrowser.contentWindow.location = testPageURL;
+}
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
new file mode 100644
index 00000000000..36d5e3ceccf
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_geoprompt_page.html
@@ -0,0 +1,13 @@
+
+
+  
+    Geolocation invoker
+  
+  
+    
+  
+

From 69c72dca295b3310929cf91f5662690c72efffd6 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:08 +0430
Subject: [PATCH 170/175] Bug 496123 - the last download directory from private
 browsing persists as the initial directory for the filepicker after stopping
 private browsing; r=mconnor

---
 browser/base/content/browser.js               |  37 ++++-
 .../privatebrowsing/test/browser/Makefile.in  |   1 +
 .../browser_privatebrowsing_opendir.js        | 142 ++++++++++++++++++
 3 files changed, 179 insertions(+), 1 deletion(-)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index b886ea6192d..1466b42dc4f 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1840,6 +1840,35 @@ function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFi
   gBrowser.loadOneTab(aUrl, aReferrer, aCharset, aPostData, false, aAllowThirdPartyFixup);
 }
 
+var gLastOpenDirectory = {
+  _lastDir: null,
+  get path() {
+    if (!this._lastDir || !this._lastDir.exists()) {
+      try {
+        this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
+                                                     Ci.nsILocalFile);
+        if (!this._lastDir.exists())
+          this._lastDir = null;
+      }
+      catch(e) {}
+    }
+    return this._lastDir;
+  },
+  set path(val) {
+    if (!val || !val.exists() || !val.isDirectory())
+      return;
+    this._lastDir = val.clone();
+
+    // Don't save the last open directory pref inside the Private Browsing mode
+    if (!gPrivateBrowsingUI.privateBrowsingEnabled)
+      gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
+                                   this._lastDir);
+  },
+  reset: function() {
+    this._lastDir = null;
+  }
+};
+
 function BrowserOpenFileWindow()
 {
   // Get filepicker component.
@@ -1849,9 +1878,13 @@ function BrowserOpenFileWindow()
     fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
     fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
                      nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
+    fp.displayDirectory = gLastOpenDirectory.path;
 
-    if (fp.show() == nsIFilePicker.returnOK)
+    if (fp.show() == nsIFilePicker.returnOK) {
+      if (fp.file && fp.file.exists())
+        gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
       openTopWin(fp.fileURL.spec);
+    }
   } catch (ex) {
   }
 }
@@ -6966,6 +6999,8 @@ let gPrivateBrowsingUI = {
             .removeAttribute("disabled");
 
     this._privateBrowsingAutoStarted = false;
+
+    gLastOpenDirectory.reset();
   },
 
   _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index 11f6f26e48d..f65cfec2f97 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -64,6 +64,7 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_popupmode.js \
 		browser_privatebrowsing_geoprompt.js \
 		browser_privatebrowsing_geoprompt_page.html \
+		browser_privatebrowsing_opendir.js \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
new file mode 100644
index 00000000000..cb83edf610f
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_opendir.js
@@ -0,0 +1,142 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that the last open directory used inside the private
+// browsing mode is not remembered after leaving that mode.
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  let ds = Cc["@mozilla.org/file/directory_service;1"].
+           getService(Ci.nsIProperties);
+  let dir1 = ds.get("ProfD", Ci.nsIFile);
+  let dir2 = ds.get("TmpD", Ci.nsIFile);
+  let file = dir2.clone();
+  file.append("pbtest.file");
+  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
+
+  const kPrefName = "browser.open.lastDir";
+
+  function setupCleanSlate() {
+    gLastOpenDirectory.reset();
+    gPrefService.clearUserPref(kPrefName);
+  }
+
+  setupCleanSlate();
+
+  // Test 1: general workflow test
+
+  // initial checks
+  ok(!gLastOpenDirectory.path,
+     "Last open directory path should be initially empty");
+  gLastOpenDirectory.path = dir2;
+  is(gLastOpenDirectory.path.path, dir2.path,
+     "The path should be successfully set");
+  gLastOpenDirectory.path = null;
+  is(gLastOpenDirectory.path.path, dir2.path,
+     "The path should be not change when assigning it to null");
+  gLastOpenDirectory.path = dir1;
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The path should be successfully outside of the private browsing mode");
+
+  // enter private browsing mode
+  pb.privateBrowsingEnabled = true;
+
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The path should not change when entering the private browsing mode");
+  gLastOpenDirectory.path = dir2;
+  is(gLastOpenDirectory.path.path, dir2.path,
+     "The path should successfully change inside the private browsing mode");
+
+  // leave private browsing mode
+  pb.privateBrowsingEnabled = false;
+
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The path should be reset to the same path as before entering the private browsing mode");
+
+  setupCleanSlate();
+
+  // Test 2: the user first tries to open a file inside the private browsing mode
+
+  pb.privateBrowsingEnabled = true;
+  ok(!gLastOpenDirectory.path,
+     "No original path should exist inside the private browsing mode");
+  gLastOpenDirectory.path = dir1;
+  is(gLastOpenDirectory.path.path, dir1.path, 
+     "The path should be successfully set inside the private browsing mode");
+  pb.privateBrowsingEnabled = false;
+  ok(!gLastOpenDirectory.path,
+     "The path set inside the private browsing mode should not leak when leaving that mode");
+
+  setupCleanSlate();
+
+  // Test 3: the last open directory is set from a previous session, it should be used
+  // in normal mode
+
+  gPrefService.setComplexValue(kPrefName, Ci.nsILocalFile, dir1);
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The pref set from last session should take effect outside the private browsing mode");
+
+  setupCleanSlate();
+
+  // Test 4: the last open directory is set from a previous session, it should be used
+  // in private browsing mode mode
+
+  gPrefService.setComplexValue(kPrefName, Ci.nsILocalFile, dir1);
+  pb.privateBrowsingEnabled = true;
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The pref set from last session should take effect inside the private browsing mode");
+  pb.privateBrowsingEnabled = false;
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "The pref set from last session should remain in effect after leaving the private browsing mode");
+
+  setupCleanSlate();
+
+  // Test 5: setting the path to a file shouldn't work
+
+  gLastOpenDirectory.path = file;
+  ok(!gLastOpenDirectory.path,
+     "Setting the path to a file shouldn't work when it's originally null");
+  gLastOpenDirectory.path = dir1;
+  gLastOpenDirectory.path = file;
+  is(gLastOpenDirectory.path.path, dir1.path,
+     "Setting the path to a file shouldn't work when it's not originally null");
+
+  // cleanup
+  file.remove(false);
+}

From 00d5e982a9e84295d3a2aff5ac273fa1ac415ca7 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:10 +0430
Subject: [PATCH 171/175] Bug 495999 - Paused download label shown in status
 bar when leaving PB with active downloads; r=mconnor

---
 browser/base/content/browser.js               |  15 ++
 .../privatebrowsing/test/browser/Makefile.in  |   2 +
 ...browser_privatebrowsing_downloadmonitor.js | 168 ++++++++++++++++++
 .../privatebrowsing/test/browser/staller.sjs  |  55 ++++++
 4 files changed, 240 insertions(+)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadmonitor.js
 create mode 100644 browser/components/privatebrowsing/test/browser/staller.sjs

diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 1466b42dc4f..e03e58eaf10 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6754,10 +6754,17 @@ let DownloadMonitorPanel = {
       gDownloadMgr.removeListener(this);
   },
 
+  inited: function DMP_inited() {
+    return this._panel != null;
+  },
+
   /**
    * Update status based on the number of active and paused downloads
    */
   updateStatus: function DMP_updateStatus() {
+    if (!this.inited())
+      return;
+
     let numActive = gDownloadMgr.activeDownloadCount;
 
     // Hide the panel and reset the "last time" if there's no downloads
@@ -6965,6 +6972,10 @@ let gPrivateBrowsingUI = {
         docElement.getAttribute("titlemodifier_privatebrowsing"));
       docElement.setAttribute("browsingmode", "private");
     }
+
+    setTimeout(function () {
+      DownloadMonitorPanel.updateStatus();
+    }, 0);
   },
 
   onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
@@ -7001,6 +7012,10 @@ let gPrivateBrowsingUI = {
     this._privateBrowsingAutoStarted = false;
 
     gLastOpenDirectory.reset();
+
+    setTimeout(function () {
+      DownloadMonitorPanel.updateStatus();
+    }, 0);
   },
 
   _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index f65cfec2f97..fa9f3e5d5d8 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -65,6 +65,8 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_geoprompt.js \
 		browser_privatebrowsing_geoprompt_page.html \
 		browser_privatebrowsing_opendir.js \
+		browser_privatebrowsing_downloadmonitor.js \
+		staller.sjs \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadmonitor.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadmonitor.js
new file mode 100644
index 00000000000..74f79197b5f
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_downloadmonitor.js
@@ -0,0 +1,168 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that the download monitor status bar panel is correctly
+// cleared when switching the private browsing mode on or off.
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  let dm = Cc["@mozilla.org/download-manager;1"].
+           getService(Ci.nsIDownloadManager);
+  if (!gDownloadMgr)
+    gDownloadMgr = dm;
+  let iosvc = Cc["@mozilla.org/network/io-service;1"].
+              getService(Ci.nsIIOService);
+  let panel = document.getElementById("download-monitor");
+  waitForExplicitFinish();
+
+  // Add a new download
+  addDownload(dm, {
+    resultFileName: "pbtest-1",
+    downloadName: "PB Test 1"
+  });
+
+  // Make sure that the download is being displayed in the monitor panel
+  if (!DownloadMonitorPanel.inited())
+    DownloadMonitorPanel.init();
+  else
+    DownloadMonitorPanel.updateStatus();
+  ok(!panel.hidden, "The download panel should be successfully added initially");
+
+  // Enter the private browsing mode
+  pb.privateBrowsingEnabled = true;
+
+  setTimeout(function () {
+    ok(panel.hidden, "The download panel should be hidden when entering the private browsing mode");
+
+    // Add a new download
+    let file = addDownload(dm, {
+      resultFileName: "pbtest-2",
+      downloadName: "PB Test 2"
+    }).targetFile;
+
+    // Update the panel
+    DownloadMonitorPanel.updateStatus();
+
+    // Make sure that the panel is visible
+    ok(!panel.hidden, "The download panel should show up when a new download is added");
+
+    // Exit the private browsing mode
+    pb.privateBrowsingEnabled = false;
+
+    setTimeout(function () {
+      ok(panel.hidden, "The download panel should be hidden when leaving the private browsing mode");
+
+      // cleanup
+      let dls = dm.activeDownloads;
+      while (dls.hasMoreElements()) {
+        let dl = dls.getNext().QueryInterface(Ci.nsIDownload);
+        dm.removeDownload(dl.id);
+        let file = dl.targetFile;
+        if (file.exists())
+          file.remove(false);
+      }
+      if (file.exists())
+        file.remove(false);
+
+      finish();
+    }, 0);
+  }, 0);
+}
+
+/**
+ * Adds a download to the DM, and starts it.
+ * (Copied from toolkit/componentns/downloads/test/unit/head_download_manager.js)
+ * @param aParams (optional): an optional object which contains the function
+ *                            parameters:
+ *                              resultFileName: leaf node for the target file
+ *                              targetFile: nsIFile for the target (overrides resultFileName)
+ *                              sourceURI: the download source URI
+ *                              downloadName: the display name of the download
+ *                              runBeforeStart: a function to run before starting the download
+ */
+function addDownload(dm, aParams)
+{
+  if (!aParams)
+    aParams = {};
+  if (!("resultFileName" in aParams))
+    aParams.resultFileName = "download.result";
+  if (!("targetFile" in aParams)) {
+    let dirSvc = Cc["@mozilla.org/file/directory_service;1"].
+                 getService(Ci.nsIProperties);
+    aParams.targetFile = dirSvc.get("ProfD", Ci.nsIFile);
+    aParams.targetFile.append(aParams.resultFileName);
+  }
+  if (!("sourceURI" in aParams))
+    aParams.sourceURI = "http://localhost:8888/browser/browser/components/privatebrowsing/test/browser/staller.sjs";
+  if (!("downloadName" in aParams))
+    aParams.downloadName = null;
+  if (!("runBeforeStart" in aParams))
+    aParams.runBeforeStart = function () {};
+
+  const nsIWBP = Ci.nsIWebBrowserPersist;
+  let persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
+                .createInstance(Ci.nsIWebBrowserPersist);
+  persist.persistFlags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
+                         nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
+                         nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
+
+  let dl = dm.addDownload(Ci.nsIDownloadManager.DOWNLOAD_TYPE_DOWNLOAD,
+                          createURI(aParams.sourceURI),
+                          createURI(aParams.targetFile), aParams.downloadName, null,
+                          Math.round(Date.now() * 1000), null, persist);
+
+  // This will throw if it isn't found, and that would mean test failure, so no
+  // try catch block
+  let test = dm.getDownload(dl.id);
+
+  aParams.runBeforeStart.call(undefined, dl);
+
+  persist.progressListener = dl.QueryInterface(Ci.nsIWebProgressListener);
+  persist.saveURI(dl.source, null, null, null, null, dl.targetFile);
+
+  return dl;
+}
+
+function createURI(aObj)
+{
+  let ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return (aObj instanceof Ci.nsIFile) ? ios.newFileURI(aObj) :
+                                        ios.newURI(aObj, null, null);
+}
diff --git a/browser/components/privatebrowsing/test/browser/staller.sjs b/browser/components/privatebrowsing/test/browser/staller.sjs
new file mode 100644
index 00000000000..32dae372eaf
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/staller.sjs
@@ -0,0 +1,55 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This provides the tests with a download URL which never finishes.
+
+function handleRequest(request, response) {
+  response.setStatusLine(request.httpVersion, 200, "OK");
+  response.processAsync();
+
+  function stall() {
+    response.write("stalling...\n");
+  }
+
+  response.setHeader("Content-Type", "text/plain", false);
+  stall();
+
+  const nsITimer = Components.interfaces.nsITimer;
+  var timer = Components.classes["@mozilla.org/timer;1"]
+                        .createInstance(nsITimer);
+  timer.initWithCallback(stall, 500, nsITimer.TYPE_REPEATING_SLACK);
+}

From 67ba72ddf5195c35d3bd5a5934d175e43738a697 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:14 +0430
Subject: [PATCH 172/175] Bug 461627 - Hide the UI for saving certificate
 exceptions permanently in Private Browsing mode (test); r=mconnor

---
 .../privatebrowsing/test/browser/Makefile.in  |   1 +
 ...rowser_privatebrowsing_certexceptionsui.js | 137 ++++++++++++++++++
 .../pki/resources/content/exceptionDialog.js  |   5 +
 3 files changed, 143 insertions(+)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js

diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index fa9f3e5d5d8..aed03d8195d 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -67,6 +67,7 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_opendir.js \
 		browser_privatebrowsing_downloadmonitor.js \
 		staller.sjs \
+		browser_privatebrowsing_certexceptionsui.js \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
new file mode 100644
index 00000000000..1bfcc757835
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_certexceptionsui.js
@@ -0,0 +1,137 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that certificate exceptions UI behaves correctly
+// inside the private browsing mode, based on whether it's opened from the prefs
+// window or from the SSL error page (see bug 461627).
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+  let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+           getService(Ci.nsIWindowWatcher);
+
+  const EXCEPTIONS_DLG_URL = 'chrome://pippki/content/exceptionDialog.xul';
+  const EXCEPTIONS_DLG_FEATURES = 'chrome,centerscreen,modal';
+  const INVALID_CERT_LOCATION = 'https://nocert.example.com/';
+  waitForExplicitFinish();
+
+  // enter private browsing mode
+  pb.privateBrowsingEnabled = true;
+
+  let testCheckbox;
+  let obs = {
+      observe: function(aSubject, aTopic, aData) {
+          // unregister ourself
+          ww.unregisterNotification(this);
+
+          let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+          win.addEventListener("load", function() {
+              win.removeEventListener("load", arguments.callee, false);
+              testCheckbox(win.document.defaultView);
+          }, false);
+      }
+  };
+
+  step1();
+
+  // Test the certificate exceptions dialog as it is invoked from about:certerror
+  function step1() {
+    ww.registerNotification(obs);
+    let params = {
+      exceptionAdded : false,
+      location: INVALID_CERT_LOCATION,
+      handlePrivateBrowsing : true,
+      prefetchCert: true,
+    };
+    testCheckbox = function(win) {
+      let obsSvc = Cc["@mozilla.org/observer-service;1"].
+                   getService(Ci.nsIObserverService);
+      obsSvc.addObserver({
+        observe: function(aSubject, aTopic, aData) {
+          obsSvc.removeObserver(this, "cert-exception-ui-ready", false);
+          ok(win.gCert, "The certificate information should be available now");
+
+          let checkbox = win.document.getElementById("permanent");
+          ok(checkbox.hasAttribute("disabled"),
+            "the permanent checkbox should be disabled when handling the private browsing mode");
+          ok(!checkbox.hasAttribute("checked"),
+            "the permanent checkbox should not be checked when handling the private browsing mode");
+          win.close();
+          step2();
+        }
+      }, "cert-exception-ui-ready", false);
+    };
+    window.openDialog(EXCEPTIONS_DLG_URL, '', EXCEPTIONS_DLG_FEATURES, params);
+  }
+
+  // Test the certificate excetions dialog as it is invoked from the Preferences dialog
+  function step2() {
+    ww.registerNotification(obs);
+    let params = {
+      exceptionAdded : false,
+      location: INVALID_CERT_LOCATION,
+      prefetchCert: true,
+    };
+    testCheckbox = function(win) {
+      let obsSvc = Cc["@mozilla.org/observer-service;1"].
+                   getService(Ci.nsIObserverService);
+      obsSvc.addObserver({
+        observe: function(aSubject, aTopic, aData) {
+          obsSvc.removeObserver(this, "cert-exception-ui-ready", false);
+          ok(win.gCert, "The certificate information should be available now");
+
+          let checkbox = win.document.getElementById("permanent");
+          ok(!checkbox.hasAttribute("disabled"),
+            "the permanent checkbox should not be disabled when not handling the private browsing mode");
+          ok(checkbox.hasAttribute("checked"),
+            "the permanent checkbox should be checked when not handling the private browsing mode");
+          win.close();
+          cleanup();
+        }
+      }, "cert-exception-ui-ready", false);
+    };
+    window.openDialog(EXCEPTIONS_DLG_URL, '', EXCEPTIONS_DLG_FEATURES, params);
+  }
+
+  function cleanup() {
+    // leave the private browsing mode
+    pb.privateBrowsingEnabled = false;
+    finish();
+  }
+}
diff --git a/security/manager/pki/resources/content/exceptionDialog.js b/security/manager/pki/resources/content/exceptionDialog.js
index ae36f0ad4ef..4e5ebfef75d 100644
--- a/security/manager/pki/resources/content/exceptionDialog.js
+++ b/security/manager/pki/resources/content/exceptionDialog.js
@@ -306,6 +306,11 @@ function updateCertStatus() {
     }
     
     document.getElementById("viewCertButton").disabled = false;
+
+    // Notify observers about the availability of the certificate
+    Components.classes["@mozilla.org/observer-service;1"]
+              .getService(Components.interfaces.nsIObserverService)
+              .notifyObservers(null, "cert-exception-ui-ready", null);
   }
   else if (gChecking) {
     shortDesc = "addExceptionCheckingShort";

From ac44d89a8f24b2c119323b556e91df5ddeffabd9 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:17 +0430
Subject: [PATCH 173/175] Bug 462639 - Handle view-source windows in Private
 Browsing mode; r=mconnor

---
 .../src/nsPrivateBrowsingService.js           |  41 ++++
 .../privatebrowsing/test/browser/Makefile.in  |   1 +
 .../browser_privatebrowsing_viewsource.js     | 197 ++++++++++++++++++
 3 files changed, 239 insertions(+)
 create mode 100644 browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js

diff --git a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
index e10e032feb0..acd68ab7b9e 100644
--- a/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
+++ b/browser/components/privatebrowsing/src/nsPrivateBrowsingService.js
@@ -123,6 +123,9 @@ PrivateBrowsingService.prototype = {
   // Whether the private browsing mode has been started automatically
   _autoStarted: false,
 
+  // List of view source window URIs for restoring later
+  _viewSrcURLs: [],
+
   // XPCOM registration
   classDescription: "PrivateBrowsing Service",
   contractID: "@mozilla.org/privatebrowsing;1",
@@ -177,6 +180,22 @@ PrivateBrowsingService.prototype = {
 
       this._closePageInfoWindows();
 
+      // save view-source windows URIs and close them
+      let viewSrcWindowsEnum = Cc["@mozilla.org/appshell/window-mediator;1"].
+                               getService(Ci.nsIWindowMediator).
+                               getEnumerator("navigator:view-source");
+      while (viewSrcWindowsEnum.hasMoreElements()) {
+        let win = viewSrcWindowsEnum.getNext();
+        if (this._inPrivateBrowsing) {
+          let plainURL = win.getBrowser().currentURI.spec;
+          if (plainURL.indexOf("view-source:") == 0) {
+            plainURL = plainURL.substr(12);
+            this._viewSrcURLs.push(plainURL);
+          }
+        }
+        win.close();
+      }
+
       if (!this._quitting && this._saveSession) {
         let browserWindow = this._getBrowserWindow();
 
@@ -215,6 +234,28 @@ PrivateBrowsingService.prototype = {
         this._savedBrowserState = null;
 
         this._closePageInfoWindows();
+
+        // re-open all view-source windows
+        let windowWatcher = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+                            getService(Ci.nsIWindowWatcher);
+        this._viewSrcURLs.forEach(function(uri) {
+          let args = Cc["@mozilla.org/supports-array;1"].
+                     createInstance(Ci.nsISupportsArray);
+          let str = Cc["@mozilla.org/supports-string;1"].
+                    createInstance(Ci.nsISupportsString);
+          str.data = uri;
+          args.AppendElement(str);
+          args.AppendElement(null); // charset
+          args.AppendElement(null); // page descriptor
+          args.AppendElement(null); // line number
+          let forcedCharset = Cc["@mozilla.org/supports-PRBool;1"].
+                              createInstance(Ci.nsISupportsPRBool);
+          forcedCharset.data = false;
+          args.AppendElement(forcedCharset);
+          windowWatcher.openWindow(null, "chrome://global/content/viewSource.xul",
+            "_blank", "all,dialog=no", args);
+        });
+        this._viewSrcURLs = [];
       }
       else {
         // otherwise, if we have transitioned into private browsing mode, load
diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index aed03d8195d..a944fc68449 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -68,6 +68,7 @@ _BROWSER_TEST_FILES =  \
 		browser_privatebrowsing_downloadmonitor.js \
 		staller.sjs \
 		browser_privatebrowsing_certexceptionsui.js \
+		browser_privatebrowsing_viewsource.js \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
diff --git a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js
new file mode 100644
index 00000000000..c61efc95df8
--- /dev/null
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_viewsource.js
@@ -0,0 +1,197 @@
+/* ***** 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 Private Browsing Tests.
+ *
+ * The Initial Developer of the Original Code is
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari  (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 ***** */
+
+// This test makes sure that entering the private browsing mode closes
+// all view source windows, and leaving it restores them
+
+function test() {
+  // initialization
+  let pb = Cc["@mozilla.org/privatebrowsing;1"].
+           getService(Ci.nsIPrivateBrowsingService);
+
+  waitForExplicitFinish();
+
+  let tabAbout = gBrowser.addTab();
+  gBrowser.selectedTab = tabAbout;
+  let aboutBrowser = gBrowser.getBrowserForTab(tabAbout);
+  aboutBrowser.addEventListener("load", function () {
+    aboutBrowser.removeEventListener("load", arguments.callee, true);
+
+    let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].
+             getService(Ci.nsIWindowWatcher);
+    let observer = {
+      observe: function(aSubject, aTopic, aData) {
+        if (aTopic == "domwindowopened") {
+          ww.unregisterNotification(this);
+
+          let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+          win.addEventListener("load", function() {
+            win.removeEventListener("load", arguments.callee, false);
+
+            let browser = win.document.defaultView.getBrowser();
+            browser.addEventListener("load", function() {
+              browser.removeEventListener("load", arguments.callee, true);
+              
+              // view source window is loaded, proceed with the rest of the test
+              step1();
+            }, true);
+          }, false);
+        }
+      }
+    };
+    ww.registerNotification(observer);
+
+    openViewSource();
+
+    function openViewSource() {
+      // invoke the View Source command
+      let event = document.createEvent("Events");
+      event.initEvent("command", true, true);
+      document.getElementById("View:PageSource").dispatchEvent(event);
+    }
+
+    function step1() {
+      observer = {
+        observe: function(aSubject, aTopic, aData) {
+          if (aTopic == "domwindowclosed") {
+            ok(true, "Entering the private browsing mode should close the view source window");
+            ww.unregisterNotification(observer);
+
+            step2();
+          }
+          else if (aTopic == "domwindowopened")
+            ok(false, "Entering the private browsing mode should not open any view source window");
+        }
+      };
+      ww.registerNotification(observer);
+
+      gBrowser.addTabsProgressListener({
+        onLocationChange: function() {},
+        onProgressChange: function() {},
+        onSecurityChange: function() {},
+        onStatusChange: function() {},
+        onRefreshAttempted: function() {},
+        onLinkIconAvailable: function() {},
+        onStateChange: function(aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+          if (aStateFlags & (Ci.nsIWebProgressListener.STATE_STOP |
+                             Ci.nsIWebProgressListener.STATE_IS_WINDOW)) {
+            gBrowser.removeTabsProgressListener(this);
+
+            step3();
+          }
+        }
+      });
+
+      // enter private browsing mode
+      pb.privateBrowsingEnabled = true;
+    }
+
+    let events = 0, step2, step3;
+    step2 = step3 = function() {
+      if (++events == 2)
+        step4();
+    }
+
+    function step4() {
+      observer = {
+        observe: function(aSubject, aTopic, aData) {
+          if (aTopic == "domwindowopened") {
+            ww.unregisterNotification(this);
+
+            let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+            win.addEventListener("load", function() {
+              win.removeEventListener("load", arguments.callee, false);
+
+              let browser = win.document.defaultView.getBrowser();
+              browser.addEventListener("load", function() {
+                browser.removeEventListener("load", arguments.callee, true);
+                
+                // view source window inside private browsing mode opened
+                step5();
+              }, true);
+            }, false);
+          }
+        }
+      };
+      ww.registerNotification(observer);
+
+      openViewSource();
+    }
+
+    function step5() {
+      let events = 0;
+
+      observer = {
+        observe: function(aSubject, aTopic, aData) {
+          if (aTopic == "domwindowclosed") {
+            ok(true, "Leaving the private browsing mode should close the existing view source window");
+            if (++events == 2)
+              ww.unregisterNotification(observer);
+          }
+          else if (aTopic == "domwindowopened") {
+            ok(true, "Leaving the private browsing mode should restore the previous view source window");
+            if (++events == 2)
+              ww.unregisterNotification(observer);
+
+            let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
+            win.addEventListener("load", function() {
+              win.removeEventListener("load", arguments.callee, false);
+
+              let browser = win.document.defaultView.getBrowser();
+              browser.addEventListener("load", function() {
+                browser.removeEventListener("load", arguments.callee, true);
+                
+                is(browser.currentURI.spec, "view-source:about:",
+                  "The correct view source window should be restored");
+
+                // cleanup
+                win.close();
+                gBrowser.removeTab(gBrowser.selectedTab);
+                finish();
+              }, true);
+            }, false);
+          }
+        }
+      };
+      ww.registerNotification(observer);
+
+      // exit private browsing mode
+      pb.privateBrowsingEnabled = false;
+    }
+  }, true);
+  aboutBrowser.loadURI("about:");
+}

From eb62c143c1b2401ffcd118e088a99c5271d9dbc1 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:23:19 +0430
Subject: [PATCH 174/175] Bug 506126 - globalOverlay.js is full of caller-less
 functions; r=enndeakin

---
 toolkit/content/globalOverlay.js           | 22 ----------------------
 toolkit/obsolete/content/globalOverlay.xul | 20 ++++++++++++++++++++
 2 files changed, 20 insertions(+), 22 deletions(-)

diff --git a/toolkit/content/globalOverlay.js b/toolkit/content/globalOverlay.js
index 677b548b37b..1703b3a007e 100644
--- a/toolkit/content/globalOverlay.js
+++ b/toolkit/content/globalOverlay.js
@@ -178,11 +178,6 @@ function visitLink(aEvent) {
     protocolSvc.loadUrl(uri);
 }
 
-function isValidLeftClick(aEvent, aName)
-{
-  return (aEvent.button == 0 && aEvent.originalTarget.localName == aName);
-}
-
 function setTooltipText(aID, aTooltipText)
 {
   var element = document.getElementById(aID);
@@ -190,23 +185,6 @@ function setTooltipText(aID, aTooltipText)
     element.setAttribute("tooltiptext", aTooltipText);
 }
 
-function FillInTooltip ( tipElement )
-{
-  var retVal = false;
-  var textNode = document.getElementById("TOOLTIP-tooltipText");
-  if (textNode) {
-    while (textNode.hasChildNodes())
-      textNode.removeChild(textNode.firstChild);
-    var tipText = tipElement.getAttribute("tooltiptext");
-    if (tipText) {
-      var node = document.createTextNode(tipText);
-      textNode.appendChild(node);
-      retVal = true;
-    }
-  }
-  return retVal;
-}
-
 __defineGetter__("NS_ASSERT", function() {
   delete this.NS_ASSERT;
   var tmpScope = {};
diff --git a/toolkit/obsolete/content/globalOverlay.xul b/toolkit/obsolete/content/globalOverlay.xul
index a44f79f7c86..6b37b808f3a 100644
--- a/toolkit/obsolete/content/globalOverlay.xul
+++ b/toolkit/obsolete/content/globalOverlay.xul
@@ -4,6 +4,26 @@
 		 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   
 
   
      

From ce0a154a0d4d64bd7a28cbd8d57665c819358350 Mon Sep 17 00:00:00 2001
From: Ehsan Akhgari 
Date: Sun, 2 Aug 2009 22:25:44 +0430
Subject: [PATCH 175/175] Sort the private browsing browser-chrome test files
 alphabetically

---
 .../privatebrowsing/test/browser/Makefile.in  | 34 +++++++++----------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/browser/components/privatebrowsing/test/browser/Makefile.in b/browser/components/privatebrowsing/test/browser/Makefile.in
index a944fc68449..b0090ef21e1 100644
--- a/browser/components/privatebrowsing/test/browser/Makefile.in
+++ b/browser/components/privatebrowsing/test/browser/Makefile.in
@@ -45,30 +45,30 @@ include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES =  \
-		browser_privatebrowsing_ui.js \
 		browser_console_clear.js \
-		browser_privatebrowsing_theming.js \
-		browser_privatebrowsing_searchbar.js \
-		browser_privatebrowsing_findbar.js \
-		browser_privatebrowsing_zoom.js \
-		browser_privatebrowsing_zoomrestore.js \
-		browser_privatebrowsing_transition.js \
-		browser_privatebrowsing_import.js \
+		browser_privatebrowsing_certexceptionsui.js \
 		browser_privatebrowsing_crh.js \
-		browser_privatebrowsing_windowtitle.js \
-		browser_privatebrowsing_windowtitle_page.html \
-		browser_privatebrowsing_urlbarfocus.js \
+		browser_privatebrowsing_downloadmonitor.js \
+		browser_privatebrowsing_findbar.js \
 		browser_privatebrowsing_forgetthissite.js \
-		browser_privatebrowsing_pageinfo.js \
-		browser_privatebrowsing_sslsite_transition.js \
-		browser_privatebrowsing_popupmode.js \
 		browser_privatebrowsing_geoprompt.js \
 		browser_privatebrowsing_geoprompt_page.html \
+		browser_privatebrowsing_import.js \
 		browser_privatebrowsing_opendir.js \
-		browser_privatebrowsing_downloadmonitor.js \
-		staller.sjs \
-		browser_privatebrowsing_certexceptionsui.js \
+		browser_privatebrowsing_pageinfo.js \
+		browser_privatebrowsing_popupmode.js \
+		browser_privatebrowsing_searchbar.js \
+		browser_privatebrowsing_sslsite_transition.js \
+		browser_privatebrowsing_theming.js \
+		browser_privatebrowsing_transition.js \
+		browser_privatebrowsing_ui.js \
+		browser_privatebrowsing_urlbarfocus.js \
 		browser_privatebrowsing_viewsource.js \
+		browser_privatebrowsing_windowtitle.js \
+		browser_privatebrowsing_windowtitle_page.html \
+		browser_privatebrowsing_zoom.js \
+		browser_privatebrowsing_zoomrestore.js \
+		staller.sjs \
 		$(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)