From d05c5c7fd582f0419845377c490db04f29ef8700 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Fri, 12 Aug 2011 11:49:36 +0100 Subject: [PATCH 01/51] bug 667166 - apply 'locl' feature early along with 'ccmp', before Arabic-specific shaping features. r=jdaggett --- gfx/harfbuzz/src/hb-ot-shape.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/harfbuzz/src/hb-ot-shape.cc b/gfx/harfbuzz/src/hb-ot-shape.cc index 06991e99eb82..125c75bc6cc3 100644 --- a/gfx/harfbuzz/src/hb-ot-shape.cc +++ b/gfx/harfbuzz/src/hb-ot-shape.cc @@ -44,7 +44,7 @@ struct { { HB_TAG('c','u','r','s'), DEFAULT_PRIORITY }, { HB_TAG('k','e','r','n'), DEFAULT_PRIORITY }, { HB_TAG('l','i','g','a'), DEFAULT_PRIORITY }, - { HB_TAG('l','o','c','l'), DEFAULT_PRIORITY }, + { HB_TAG('l','o','c','l'), FIRST_PRIORITY }, { HB_TAG('m','a','r','k'), DEFAULT_PRIORITY }, { HB_TAG('m','k','m','k'), DEFAULT_PRIORITY }, { HB_TAG('r','l','i','g'), DEFAULT_PRIORITY } From d0b2e1b18a0d3cca49349a09c0024f2eec40adc8 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 12 Aug 2011 06:35:37 -0500 Subject: [PATCH 02/51] Bug 678211 - Assertion failure: isScriptFrame(), at ../vm/Stack.h:538 with dummy frame. r=dvander. --- js/src/jit-test/tests/basic/bug678211.js | 3 +++ js/src/jstracer.cpp | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/basic/bug678211.js diff --git a/js/src/jit-test/tests/basic/bug678211.js b/js/src/jit-test/tests/basic/bug678211.js new file mode 100644 index 000000000000..61b5f5d197c0 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug678211.js @@ -0,0 +1,3 @@ +var g = newGlobal('new-compartment'); +g.eval("function f(n) { for (var i = 0; i < n; i++) f(0); }"); +g.f(RUNLOOP + 1); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 071baa17ca81..49d3a2e69730 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10467,7 +10467,10 @@ TraceRecorder::record_EnterFrame() /* Try inlining one level in case this recursion doesn't go too deep. */ if (fp->script() == fp->prev()->script() && - fp->prev()->prev() && fp->prev()->prev()->script() == fp->script()) { + fp->prev()->prev() && + fp->prev()->prev()->isScriptFrame() && + fp->prev()->prev()->script() == fp->script()) + { RETURN_STOP_A("recursion started inlining"); } From 1f4cd3316968d2b278e7c85febde8b51ede34c55 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Mon, 30 May 2011 15:08:55 -0400 Subject: [PATCH 03/51] Bug 660662. Eliminate NS_(ceil|floor). r=roc These just called the system versions. --- gfx/src/nsCoord.h | 20 +++++++++--------- gfx/src/nsFontMetrics.cpp | 8 ++++---- gfx/thebes/gfxCachedTempSurface.cpp | 2 +- gfx/thebes/gfxFT2Utils.cpp | 10 ++++----- gfx/thebes/gfxFont.cpp | 14 ++++++------- gfx/thebes/gfxHarfBuzzShaper.cpp | 10 ++++----- gfx/thebes/gfxMatrix.h | 8 ++++---- gfx/thebes/gfxOS2Fonts.cpp | 10 ++++----- gfx/thebes/gfxPoint.h | 4 ++-- gfx/thebes/gfxRect.cpp | 24 +++++++++++----------- gfx/thebes/gfxUtils.cpp | 2 +- gfx/thebes/gfxWindowsNativeDrawing.cpp | 8 ++++---- layout/base/nsCSSRendering.cpp | 16 +++++++-------- layout/generic/nsTextFrameThebes.cpp | 4 ++-- layout/style/nsRuleNode.cpp | 4 ++-- layout/style/nsStyleAnimation.cpp | 8 ++++---- toolkit/components/places/SQLFunctions.cpp | 8 ++++---- toolkit/components/places/nsNavHistory.cpp | 2 +- widget/src/windows/nsWindow.cpp | 2 +- xpcom/ds/nsMathUtils.h | 24 ---------------------- 20 files changed, 82 insertions(+), 106 deletions(-) diff --git a/gfx/src/nsCoord.h b/gfx/src/nsCoord.h index b5f5bce3e28e..076aa242cbf5 100644 --- a/gfx/src/nsCoord.h +++ b/gfx/src/nsCoord.h @@ -94,7 +94,7 @@ inline nscoord NSToCoordRound(float aValue) #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) return NS_lroundup30(aValue); #else - return nscoord(NS_floorf(aValue + 0.5f)); + return nscoord(floorf(aValue + 0.5f)); #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */ } @@ -103,7 +103,7 @@ inline nscoord NSToCoordRound(double aValue) #if defined(XP_WIN32) && defined(_M_IX86) && !defined(__GNUC__) return NS_lroundup30((float)aValue); #else - return nscoord(NS_floor(aValue + 0.5f)); + return nscoord(floor(aValue + 0.5f)); #endif /* XP_WIN32 && _M_IX86 && !__GNUC__ */ } @@ -360,12 +360,12 @@ inline float NSCoordToFloat(nscoord aCoord) { */ inline nscoord NSToCoordFloor(float aValue) { - return nscoord(NS_floorf(aValue)); + return nscoord(floorf(aValue)); } inline nscoord NSToCoordFloor(double aValue) { - return nscoord(NS_floor(aValue)); + return nscoord(floor(aValue)); } inline nscoord NSToCoordFloorClamped(float aValue) @@ -388,12 +388,12 @@ inline nscoord NSToCoordFloorClamped(float aValue) inline nscoord NSToCoordCeil(float aValue) { - return nscoord(NS_ceilf(aValue)); + return nscoord(ceilf(aValue)); } inline nscoord NSToCoordCeil(double aValue) { - return nscoord(NS_ceil(aValue)); + return nscoord(ceil(aValue)); } inline nscoord NSToCoordCeilClamped(float aValue) @@ -437,12 +437,12 @@ inline nscoord NSToCoordCeilClamped(double aValue) */ inline PRInt32 NSToIntFloor(float aValue) { - return PRInt32(NS_floorf(aValue)); + return PRInt32(floorf(aValue)); } inline PRInt32 NSToIntCeil(float aValue) { - return PRInt32(NS_ceilf(aValue)); + return PRInt32(ceilf(aValue)); } inline PRInt32 NSToIntRound(float aValue) @@ -457,12 +457,12 @@ inline PRInt32 NSToIntRound(double aValue) inline PRInt32 NSToIntRoundUp(float aValue) { - return PRInt32(NS_floorf(aValue + 0.5f)); + return PRInt32(floorf(aValue + 0.5f)); } inline PRInt32 NSToIntRoundUp(double aValue) { - return PRInt32(NS_floor(aValue + 0.5)); + return PRInt32(floor(aValue + 0.5)); } /* diff --git a/gfx/src/nsFontMetrics.cpp b/gfx/src/nsFontMetrics.cpp index 144448af6a14..968e0cd0088c 100644 --- a/gfx/src/nsFontMetrics.cpp +++ b/gfx/src/nsFontMetrics.cpp @@ -147,7 +147,7 @@ nsFontMetrics::Destroy() // XXXTODO get rid of this macro #define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5) -#define CEIL_TO_TWIPS(x) (nscoord)NS_ceil((x) * mP2A) +#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A) const gfxFont::Metrics& nsFontMetrics::GetMetrics() const { @@ -194,15 +194,15 @@ nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics, gfxFontGroup* aFontGroup) { - gfxFloat offset = NS_floor(-aFontGroup->GetUnderlineOffset() + 0.5); + gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5); gfxFloat size = NS_round(aMetrics.underlineSize); - gfxFloat minDescent = NS_floor(offset + size + 0.5); + gfxFloat minDescent = floor(offset + size + 0.5); return NS_MAX(minDescent, aMetrics.maxDescent); } static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics) { - return NS_floor(aMetrics.maxAscent + 0.5); + return floor(aMetrics.maxAscent + 0.5); } nscoord diff --git a/gfx/thebes/gfxCachedTempSurface.cpp b/gfx/thebes/gfxCachedTempSurface.cpp index b480174315ae..e2fd934cb412 100644 --- a/gfx/thebes/gfxCachedTempSurface.cpp +++ b/gfx/thebes/gfxCachedTempSurface.cpp @@ -114,7 +114,7 @@ gfxCachedTempSurface::Get(gfxASurface::gfxContentType aContentType, PRBool cleared = PR_FALSE; if (!mSurface) { - mSize = gfxIntSize(PRInt32(NS_ceil(aRect.width)), PRInt32(NS_ceil(aRect.height))); + mSize = gfxIntSize(PRInt32(ceil(aRect.width)), PRInt32(ceil(aRect.height))); mSurface = aSimilarTo->CreateSimilarSurface(aContentType, mSize); if (!mSurface) return nsnull; diff --git a/gfx/thebes/gfxFT2Utils.cpp b/gfx/thebes/gfxFT2Utils.cpp index 4bffbd705ca2..cd1340f196f9 100644 --- a/gfx/thebes/gfxFT2Utils.cpp +++ b/gfx/thebes/gfxFT2Utils.cpp @@ -70,11 +70,11 @@ ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale) static void SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize) { - gfxFloat snappedSize = NS_MAX(NS_floor(aSize + 0.5), 1.0); + gfxFloat snappedSize = NS_MAX(floor(aSize + 0.5), 1.0); // Correct offset for change in size gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize); // Snap offset - aOffset = NS_floor(offset + 0.5); + aOffset = floor(offset + 0.5); aSize = snappedSize; } @@ -281,16 +281,16 @@ gfxFT2LockedFace::GetMetrics(gfxFont::Metrics* aMetrics, // internalLeading + externalLeading, but first each of these is rounded // to layout units. To ensure that the result is an integer number of // pixels, round each of the components to pixels. - aMetrics->emHeight = NS_floor(emHeight + 0.5); + aMetrics->emHeight = floor(emHeight + 0.5); // maxHeight will normally be an integer, but round anyway in case // FreeType is configured differently. aMetrics->internalLeading = - NS_floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5); + floor(aMetrics->maxHeight - aMetrics->emHeight + 0.5); // Text input boxes currently don't work well with lineHeight // significantly less than maxHeight (with Verdana, for example). - lineHeight = NS_floor(NS_MAX(lineHeight, aMetrics->maxHeight) + 0.5); + lineHeight = floor(NS_MAX(lineHeight, aMetrics->maxHeight) + 0.5); aMetrics->externalLeading = lineHeight - aMetrics->internalLeading - aMetrics->emHeight; diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index ac28b38f8b0d..8a3d2b109d8a 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1594,7 +1594,7 @@ gfxFont::SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID, PRBool aNeed extents.y_bearing >= -fontMetrics.maxAscent && extents.height + extents.y_bearing <= fontMetrics.maxDescent) { PRUint32 appUnitsWidth = - PRUint32(NS_ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit)); + PRUint32(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit)); if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) { aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, PRUint16(appUnitsWidth)); return; @@ -1713,9 +1713,9 @@ RoundToNearestMultiple(double aValue, double aFraction) void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics) { aMetrics.maxAscent = - NS_ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0)); + ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0)); aMetrics.maxDescent = - NS_ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0)); + ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0)); if (aMetrics.xHeight <= 0) { // only happens if we couldn't find either font metrics @@ -1828,13 +1828,13 @@ gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont) // If strikeout line is overflowed from the ascent, the line should be resized and moved for // that being in the ascent space. // Note that the strikeoutOffset is *middle* of the strikeout line position. - gfxFloat halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5); + gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5); if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) { if (aMetrics->strikeoutSize > aMetrics->maxAscent) { aMetrics->strikeoutSize = NS_MAX(aMetrics->maxAscent, 1.0); - halfOfStrikeoutSize = NS_floor(aMetrics->strikeoutSize / 2.0 + 0.5); + halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5); } - gfxFloat ascent = NS_floor(aMetrics->maxAscent + 0.5); + gfxFloat ascent = floor(aMetrics->maxAscent + 0.5); aMetrics->strikeoutOffset = NS_MAX(halfOfStrikeoutSize, ascent / 2.0); } @@ -2544,7 +2544,7 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext, gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch); if (wid >= 0.0) { nscoord advance = - aTextRun->GetAppUnitsPerDevUnit() * NS_floor(wid + 0.5); + aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5); gfxTextRun::CompressedGlyph g; if (gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance)) { aTextRun->SetSimpleGlyph(index, diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index b0698a29e9e2..5cd82b6e495d 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -1128,7 +1128,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, hb_position_t x_advance = posInfo[glyphStart].x_advance; nscoord advance = roundX ? dev2appUnits * FixedToIntRound(x_advance) - : NS_floor(hb2appUnits * x_advance + 0.5); + : floor(hb2appUnits * x_advance + 0.5); if (glyphsInClump == 1 && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) && @@ -1158,18 +1158,18 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, hb_position_t x_offset = posInfo[glyphStart].x_offset; details->mXOffset = roundX ? dev2appUnits * FixedToIntRound(x_offset) - : NS_floor(hb2appUnits * x_offset + 0.5); + : floor(hb2appUnits * x_offset + 0.5); hb_position_t y_offset = posInfo[glyphStart].y_offset; details->mYOffset = yPos - (roundY ? dev2appUnits * FixedToIntRound(y_offset) - : NS_floor(hb2appUnits * y_offset + 0.5)); + : floor(hb2appUnits * y_offset + 0.5)); details->mAdvance = advance; hb_position_t y_advance = posInfo[glyphStart].y_advance; if (y_advance != 0) { yPos -= roundY ? dev2appUnits * FixedToIntRound(y_advance) - : NS_floor(hb2appUnits * y_advance + 0.5); + : floor(hb2appUnits * y_advance + 0.5); } if (++glyphStart >= glyphEnd) { break; @@ -1177,7 +1177,7 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext, x_advance = posInfo[glyphStart].x_advance; advance = roundX ? dev2appUnits * FixedToIntRound(x_advance) - : NS_floor(hb2appUnits * x_advance + 0.5); + : floor(hb2appUnits * x_advance + 0.5); } gfxTextRun::CompressedGlyph g; diff --git a/gfx/thebes/gfxMatrix.h b/gfx/thebes/gfxMatrix.h index a110fdd7817a..c0a1fa23b7f6 100644 --- a/gfx/thebes/gfxMatrix.h +++ b/gfx/thebes/gfxMatrix.h @@ -194,8 +194,8 @@ public: */ PRBool HasNonIntegerTranslation() const { return HasNonTranslation() || - !FuzzyEqual(x0, NS_floor(x0 + 0.5)) || - !FuzzyEqual(y0, NS_floor(y0 + 0.5)); + !FuzzyEqual(x0, floor(x0 + 0.5)) || + !FuzzyEqual(y0, floor(y0 + 0.5)); } /** @@ -291,8 +291,8 @@ public: * Returns true if the matrix has non-integer scale */ PRBool HasNonIntegerScale() const { - return !FuzzyEqual(xx, NS_floor(xx + 0.5)) || - !FuzzyEqual(yy, NS_floor(yy + 0.5)); + return !FuzzyEqual(xx, floor(xx + 0.5)) || + !FuzzyEqual(yy, floor(yy + 0.5)); } private: diff --git a/gfx/thebes/gfxOS2Fonts.cpp b/gfx/thebes/gfxOS2Fonts.cpp index 899d74517620..1906e6d05ff4 100644 --- a/gfx/thebes/gfxOS2Fonts.cpp +++ b/gfx/thebes/gfxOS2Fonts.cpp @@ -127,11 +127,11 @@ static void FillMetricsDefaults(gfxFont::Metrics *aMetrics) // line as close to the original position as possible. static void SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize) { - gfxFloat snappedSize = NS_MAX(NS_floor(aSize + 0.5), 1.0); + gfxFloat snappedSize = NS_MAX(floor(aSize + 0.5), 1.0); // Correct offset for change in size gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize); // Snap offset - aOffset = NS_floor(offset + 0.5); + aOffset = floor(offset + 0.5); aSize = snappedSize; } @@ -155,7 +155,7 @@ const gfxFont::Metrics& gfxOS2Font::GetMetrics() // round size to integer pixels, this is to get full pixels for layout // together with internal/external leading (see below) - mMetrics->emHeight = NS_floor(GetStyle()->size + 0.5); + mMetrics->emHeight = floor(GetStyle()->size + 0.5); cairo_scaled_font_t* scaledFont = CairoScaledFont(); if (!scaledFont) { @@ -274,9 +274,9 @@ const gfxFont::Metrics& gfxOS2Font::GetMetrics() // leadings are not available directly (only for WinFNTs); // better compute them on our own, to get integer values and make // layout happy (see // LockedFTFace::GetMetrics in gfxPangoFonts.cpp) - mMetrics->internalLeading = NS_floor(mMetrics->maxHeight + mMetrics->internalLeading = floor(mMetrics->maxHeight - mMetrics->emHeight + 0.5); - gfxFloat lineHeight = NS_floor(mMetrics->maxHeight + 0.5); + gfxFloat lineHeight = floor(mMetrics->maxHeight + 0.5); mMetrics->externalLeading = lineHeight - mMetrics->internalLeading - mMetrics->emHeight; diff --git a/gfx/thebes/gfxPoint.h b/gfx/thebes/gfxPoint.h index dfbf3594b940..8ff98dfa248d 100644 --- a/gfx/thebes/gfxPoint.h +++ b/gfx/thebes/gfxPoint.h @@ -69,8 +69,8 @@ struct THEBES_API gfxPoint : public mozilla::gfx::BasePoint // And if you need similar method which is using NS_round(), you should // create new |RoundAwayFromZero()| method. gfxPoint& Round() { - x = NS_floor(x + 0.5); - y = NS_floor(y + 0.5); + x = floor(x + 0.5); + y = floor(y + 0.5); return *this; } }; diff --git a/gfx/thebes/gfxRect.cpp b/gfx/thebes/gfxRect.cpp index 210c1e67dda1..80b3c99328e5 100644 --- a/gfx/thebes/gfxRect.cpp +++ b/gfx/thebes/gfxRect.cpp @@ -59,10 +59,10 @@ void gfxRect::Round() { // Note that don't use NS_round here. See the comment for this method in gfxRect.h - gfxFloat x0 = NS_floor(X() + 0.5); - gfxFloat y0 = NS_floor(Y() + 0.5); - gfxFloat x1 = NS_floor(XMost() + 0.5); - gfxFloat y1 = NS_floor(YMost() + 0.5); + gfxFloat x0 = floor(X() + 0.5); + gfxFloat y0 = floor(Y() + 0.5); + gfxFloat x1 = floor(XMost() + 0.5); + gfxFloat y1 = floor(YMost() + 0.5); x = x0; y = y0; @@ -74,10 +74,10 @@ gfxRect::Round() void gfxRect::RoundIn() { - gfxFloat x0 = NS_ceil(X()); - gfxFloat y0 = NS_ceil(Y()); - gfxFloat x1 = NS_floor(XMost()); - gfxFloat y1 = NS_floor(YMost()); + gfxFloat x0 = ceil(X()); + gfxFloat y0 = ceil(Y()); + gfxFloat x1 = floor(XMost()); + gfxFloat y1 = floor(YMost()); x = x0; y = y0; @@ -89,10 +89,10 @@ gfxRect::RoundIn() void gfxRect::RoundOut() { - gfxFloat x0 = NS_floor(X()); - gfxFloat y0 = NS_floor(Y()); - gfxFloat x1 = NS_ceil(XMost()); - gfxFloat y1 = NS_ceil(YMost()); + gfxFloat x0 = floor(X()); + gfxFloat y0 = floor(Y()); + gfxFloat x1 = ceil(XMost()); + gfxFloat y1 = ceil(YMost()); x = x0; y = y0; diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index e115e2c36a21..f90af754f801 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -489,7 +489,7 @@ gfxUtils::ClampToScaleFactor(gfxFloat aVal) if (fabs(power - NS_round(power)) < 1e-6) { power = NS_round(power); } else { - power = NS_ceil(power); + power = ceil(power); } return pow(kScaleResolution, power); diff --git a/gfx/thebes/gfxWindowsNativeDrawing.cpp b/gfx/thebes/gfxWindowsNativeDrawing.cpp index 14b19f586c80..fd774a40778b 100644 --- a/gfx/thebes/gfxWindowsNativeDrawing.cpp +++ b/gfx/thebes/gfxWindowsNativeDrawing.cpp @@ -138,8 +138,8 @@ gfxWindowsNativeDrawing::BeginNativeDrawing() // There's probably a better fix, but I haven't figured out // the root cause of the problem. mTempSurfaceSize = - gfxIntSize((PRInt32) NS_ceil(mNativeRect.Width() + 1), - (PRInt32) NS_ceil(mNativeRect.Height() + 1)); + gfxIntSize((PRInt32) ceil(mNativeRect.Width() + 1), + (PRInt32) ceil(mNativeRect.Height() + 1)); } else { // figure out the scale factors mScale = m.ScaleFactors(PR_TRUE); @@ -153,8 +153,8 @@ gfxWindowsNativeDrawing::BeginNativeDrawing() // See comment above about "+1" mTempSurfaceSize = - gfxIntSize((PRInt32) NS_ceil(mNativeRect.Width() * mScale.width + 1), - (PRInt32) NS_ceil(mNativeRect.Height() * mScale.height + 1)); + gfxIntSize((PRInt32) ceil(mNativeRect.Width() * mScale.width + 1), + (PRInt32) ceil(mNativeRect.Height() * mScale.height + 1)); } } } diff --git a/layout/base/nsCSSRendering.cpp b/layout/base/nsCSSRendering.cpp index 7c100ca0d7b5..29e851b3fa4c 100644 --- a/layout/base/nsCSSRendering.cpp +++ b/layout/base/nsCSSRendering.cpp @@ -1943,7 +1943,7 @@ static nscoord FindTileStart(nscoord aDirtyCoord, nscoord aTilePos, nscoord aTileDim) { NS_ASSERTION(aTileDim > 0, "Non-positive tile dimension"); - double multiples = NS_floor(double(aDirtyCoord - aTilePos)/aTileDim); + double multiples = floor(double(aDirtyCoord - aTilePos)/aTileDim); return NSToCoordRound(multiples*aTileDim + aTilePos); } @@ -2050,7 +2050,7 @@ nsCSSRendering::PaintGradient(nsPresContext* aPresContext, // try to do anything in that case. We certainly need to avoid // dividing by zero. if (stopDelta >= 1e-6) { - double instanceCount = NS_ceil(-firstStop/stopDelta); + double instanceCount = ceil(-firstStop/stopDelta); // Advance stops by instanceCount multiples of the period of the // repeating gradient. double offset = instanceCount*stopDelta; @@ -3607,15 +3607,15 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, PRBool canLiftUnderline = aDescentLimit >= 0.0; - const gfxFloat left = NS_floor(aPt.x + 0.5), - right = NS_floor(aPt.x + aLineSize.width + 0.5); + const gfxFloat left = floor(aPt.x + 0.5), + right = floor(aPt.x + aLineSize.width + 0.5); gfxRect r(left, 0, right - left, 0); gfxFloat lineHeight = NS_round(aLineSize.height); lineHeight = NS_MAX(lineHeight, 1.0); gfxFloat ascent = NS_round(aAscent); - gfxFloat descentLimit = NS_floor(aDescentLimit); + gfxFloat descentLimit = floor(aDescentLimit); gfxFloat suggestedMaxRectHeight = NS_MAX(NS_MIN(ascent, descentLimit), 1.0); r.height = lineHeight; @@ -3671,7 +3671,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, } } - gfxFloat baseline = NS_floor(aPt.y + aAscent + 0.5); + gfxFloat baseline = floor(aPt.y + aAscent + 0.5); gfxFloat offset = 0.0; switch (aDecoration) { case NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE: @@ -3692,7 +3692,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, offset = aOffset - lineHeight + r.Height(); break; case NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH: { - gfxFloat extra = NS_floor(r.Height() / 2.0 + 0.5); + gfxFloat extra = floor(r.Height() / 2.0 + 0.5); extra = NS_MAX(extra, lineHeight); offset = aOffset - lineHeight + extra; break; @@ -3700,7 +3700,7 @@ nsCSSRendering::GetTextDecorationRectInternal(const gfxPoint& aPt, default: NS_ERROR("Invalid decoration value!"); } - r.y = baseline - NS_floor(offset + 0.5); + r.y = baseline - floor(offset + 0.5); return r; } diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 2255b53f02e6..b31472c83c75 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -2767,7 +2767,7 @@ AdvanceToNextTab(gfxFloat aX, nsIFrame* aFrame, // Advance aX to the next multiple of *aCachedTabWidth. We must advance // by at least 1 appunit. // XXX should we make this 1 CSS pixel? - return NS_ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth); + return ceil((aX + 1)/(*aCachedTabWidth))*(*aCachedTabWidth); } void @@ -4518,7 +4518,7 @@ ComputeSelectionUnderlineHeight(nsPresContext* aPresContext, gfxFloat fontSize = NS_MIN(gfxFloat(defaultFontSize), aFontMetrics.emHeight); fontSize = NS_MAX(fontSize, 1.0); - return NS_ceil(fontSize / 20); + return ceil(fontSize / 20); } default: NS_WARNING("Requested underline style is not valid"); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 7614e585cdd4..b70da4cde5d6 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -319,8 +319,8 @@ static nscoord CalcLengthWith(const nsCSSValue& aValue, gfxFloat zeroWidth = (fm->GetThebesFontGroup()->GetFontAt(0) ->GetMetrics().zeroOrAveCharWidth); - return ScaleCoord(aValue, NS_ceil(aPresContext->AppUnitsPerDevPixel() * - zeroWidth)); + return ScaleCoord(aValue, ceil(aPresContext->AppUnitsPerDevPixel() * + zeroWidth)); } // For properties for which lengths are the *only* units accepted in // calc(), we can handle calc() here and just compute a final diff --git a/layout/style/nsStyleAnimation.cpp b/layout/style/nsStyleAnimation.cpp index 28e52049ac90..a49740ee4404 100644 --- a/layout/style/nsStyleAnimation.cpp +++ b/layout/style/nsStyleAnimation.cpp @@ -1384,8 +1384,8 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty, switch (aProperty) { case eCSSProperty_font_stretch: { // Animate just like eUnit_Integer. - PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) + - aCoeff2 * double(aValue2.GetIntValue())); + PRInt32 result = floor(aCoeff1 * double(aValue1.GetIntValue()) + + aCoeff2 * double(aValue2.GetIntValue())); if (result < NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED) { result = NS_STYLE_FONT_STRETCH_ULTRA_CONDENSED; } else if (result > NS_STYLE_FONT_STRETCH_ULTRA_EXPANDED) { @@ -1409,8 +1409,8 @@ nsStyleAnimation::AddWeighted(nsCSSProperty aProperty, case eUnit_Integer: { // http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types- // says we should use floor - PRInt32 result = NS_floor(aCoeff1 * double(aValue1.GetIntValue()) + - aCoeff2 * double(aValue2.GetIntValue())); + PRInt32 result = floor(aCoeff1 * double(aValue1.GetIntValue()) + + aCoeff2 * double(aValue2.GetIntValue())); if (aProperty == eCSSProperty_font_weight) { if (result < 100) { result = 100; diff --git a/toolkit/components/places/SQLFunctions.cpp b/toolkit/components/places/SQLFunctions.cpp index aabe839457dc..b5a95927c876 100644 --- a/toolkit/components/places/SQLFunctions.cpp +++ b/toolkit/components/places/SQLFunctions.cpp @@ -590,9 +590,9 @@ namespace places { } else { // Estimate frecency using the last few visits. - // Use NS_ceilf() so that we don't round down to 0, which + // Use ceilf() so that we don't round down to 0, which // would cause us to completely ignore the place during autocomplete. - NS_ADDREF(*_result = new IntegerVariant((PRInt32) NS_ceilf(fullVisitCount * NS_ceilf(pointsForSampledVisits) / numSampledVisits))); + NS_ADDREF(*_result = new IntegerVariant((PRInt32) ceilf(fullVisitCount * ceilf(pointsForSampledVisits) / numSampledVisits))); } return NS_OK; @@ -625,9 +625,9 @@ namespace places { // Assume "now" as our ageInDays, so use the first bucket. pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0); - // use NS_ceilf() so that we don't round down to 0, which + // use ceilf() so that we don't round down to 0, which // would cause us to completely ignore the place during autocomplete - NS_ADDREF(*_result = new IntegerVariant((PRInt32) NS_ceilf(fullVisitCount * NS_ceilf(pointsForSampledVisits)))); + NS_ADDREF(*_result = new IntegerVariant((PRInt32) ceilf(fullVisitCount * ceilf(pointsForSampledVisits)))); return NS_OK; } diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index cb8256c5626b..e7b1fe7ef28d 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -177,7 +177,7 @@ static const PRInt64 USECS_PER_DAY = LL_INIT(20, 500654080); // long, but we split only the last 6 months. #define HISTORY_DATE_CONT_NUM(_daysFromOldestVisit) \ (HISTORY_ADDITIONAL_DATE_CONT_NUM + \ - NS_MIN(6, (PRInt32)NS_ceilf((float)_daysFromOldestVisit/30))) + NS_MIN(6, (PRInt32)ceilf((float)_daysFromOldestVisit/30))) // Max number of containers, used to initialize the params hash. #define HISTORY_DATE_CONT_MAX 10 diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index ecce0b67be39..44da25661206 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -6359,7 +6359,7 @@ nsWindow::ResetRemainingWheelDelta() static PRInt32 RoundDelta(double aDelta) { - return aDelta >= 0 ? (PRInt32)NS_floor(aDelta) : (PRInt32)NS_ceil(aDelta); + return aDelta >= 0 ? (PRInt32)floor(aDelta) : (PRInt32)ceil(aDelta); } /** diff --git a/xpcom/ds/nsMathUtils.h b/xpcom/ds/nsMathUtils.h index 1fe3d6fccaeb..21ffbeca952f 100644 --- a/xpcom/ds/nsMathUtils.h +++ b/xpcom/ds/nsMathUtils.h @@ -105,30 +105,6 @@ inline NS_HIDDEN_(PRInt32) NS_lroundf(float x) return x >= 0.0f ? PRInt32(x + 0.5f) : PRInt32(x - 0.5f); } -/* - * ceil - */ -inline NS_HIDDEN_(double) NS_ceil(double x) -{ - return ceil(x); -} -inline NS_HIDDEN_(float) NS_ceilf(float x) -{ - return ceilf(x); -} - -/* - * floor - */ -inline NS_HIDDEN_(double) NS_floor(double x) -{ - return floor(x); -} -inline NS_HIDDEN_(float) NS_floorf(float x) -{ - return floorf(x); -} - /* * hypot. We don't need a super accurate version of this, if a platform * turns up with none of the possibilities below it would be okay to fall From dc46cbb1b1863878bea2849d1970d3acdfe90c41 Mon Sep 17 00:00:00 2001 From: Mounir Lamouri Date: Fri, 12 Aug 2011 15:40:00 +0200 Subject: [PATCH 04/51] Bug 674771 - Add a "Paste & Go" entry in the URL bar context menu. r=mfinkle --- mobile/chrome/content/ContextCommands.js | 7 +++++++ mobile/chrome/content/bindings.xml | 6 +++++- mobile/chrome/content/browser.xul | 3 +++ mobile/locales/en-US/chrome/browser.dtd | 1 + 4 files changed, 16 insertions(+), 1 deletion(-) diff --git a/mobile/chrome/content/ContextCommands.js b/mobile/chrome/content/ContextCommands.js index 3edbb6a13bf3..667be0206368 100644 --- a/mobile/chrome/content/ContextCommands.js +++ b/mobile/chrome/content/ContextCommands.js @@ -29,6 +29,13 @@ var ContextCommands = { } }, + pasteAndGo: function cc_pasteAndGo() { + let target = ContextHelper.popupState.target; + target.editor.selectAll(); + target.editor.paste(Ci.nsIClipboard.kGlobalClipboard); + BrowserUI.goToURI(); + }, + selectAll: function cc_selectAll() { let target = ContextHelper.popupState.target; if (target.localName == "browser") { diff --git a/mobile/chrome/content/bindings.xml b/mobile/chrome/content/bindings.xml index 07dd7e31eec1..8724f9a09872 100644 --- a/mobile/chrome/content/bindings.xml +++ b/mobile/chrome/content/bindings.xml @@ -1820,8 +1820,12 @@ let flavors = ["text/unicode"]; let hasData = clipboard.hasDataMatchingFlavors(flavors, flavors.length, Ci.nsIClipboard.kGlobalClipboard); - if (hasData && (!aTextbox.readOnly || aIgnoreReadOnly)) + if (hasData && (!aTextbox.readOnly || aIgnoreReadOnly)) { json.types.push("paste"); + if (aTextbox.type == "url") { + json.types.push("paste-url"); + } + } ContextHelper.showPopup({ target: aTextbox, json: json }); ]]> diff --git a/mobile/chrome/content/browser.xul b/mobile/chrome/content/browser.xul index ddd0f52459fd..473f2d760e75 100644 --- a/mobile/chrome/content/browser.xul +++ b/mobile/chrome/content/browser.xul @@ -626,6 +626,9 @@ + + diff --git a/mobile/locales/en-US/chrome/browser.dtd b/mobile/locales/en-US/chrome/browser.dtd index 191061ee01e6..34eadcc70059 100644 --- a/mobile/locales/en-US/chrome/browser.dtd +++ b/mobile/locales/en-US/chrome/browser.dtd @@ -16,6 +16,7 @@ + From 16091a1e43f9e9ac4a8f3a8d8d7355939bdaf728 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 12 Aug 2011 06:57:45 -0700 Subject: [PATCH 05/51] Bug 677773 - Allow JS crash diagnostics to be disabled; simplify them (r=dmandelin,ted) --- configure.in | 12 ++++++++ js/src/configure.in | 12 ++++++++ js/src/jscompartment.cpp | 2 -- js/src/jscrashreport.cpp | 44 ++++++++++++++--------------- js/src/jscrashreport.h | 44 +++++++++++++++++++++++------ js/src/jsgc.cpp | 9 ++---- js/src/jsgcmark.cpp | 19 ++----------- js/src/jsobj.cpp | 25 ----------------- js/src/jsproxy.cpp | 5 ---- js/src/jsproxy.h | 4 --- js/src/jsscript.cpp | 60 ++++++++++------------------------------ js/src/jsscript.h | 19 ++++--------- js/src/jsutil.h | 25 ++++++++++------- js/src/jswrapper.h | 4 --- 14 files changed, 124 insertions(+), 160 deletions(-) diff --git a/configure.in b/configure.in index c4a3716d526e..3556352f7ec7 100644 --- a/configure.in +++ b/configure.in @@ -7593,6 +7593,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/configure.in b/js/src/configure.in index 42a31ae154a6..74dcc1b14cbd 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -5022,6 +5022,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b9077db2984d..b1f71676206c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -562,8 +562,6 @@ JSCompartment::purge(JSContext *cx) #endif #ifdef JS_METHODJIT - js::CheckCompartmentScripts(this); - for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { diff --git a/js/src/jscrashreport.cpp b/js/src/jscrashreport.cpp index 9a840b0cd1cd..ecc7c3f1185c 100644 --- a/js/src/jscrashreport.cpp +++ b/js/src/jscrashreport.cpp @@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC); static Stack gErrorStack(JS_CRASH_STACK_ERROR); static Ring gRingBuffer(JS_CRASH_RING); +void +SnapshotGCStack() +{ + if (gInitialized) + gGCStack.snapshot(); +} + +void +SnapshotErrorStack() +{ + if (gInitialized) + gErrorStack.snapshot(); +} + +void +SaveCrashData(uint64 tag, void *ptr, size_t size) +{ + if (gInitialized) + gRingBuffer.push(tag, ptr, size); +} + } /* namespace crash */ } /* namespace js */ using namespace js; using namespace js::crash; -JS_FRIEND_API(void) -js_SnapshotGCStack() -{ - if (gInitialized) - gGCStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SnapshotErrorStack() -{ - if (gInitialized) - gErrorStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size) -{ - if (gInitialized) - gRingBuffer.push(tag, ptr, size); -} - JS_PUBLIC_API(void) JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) { -#if 1 +#ifdef JS_CRASH_DIAGNOSTICS if (!gInitialized) { gInitialized = true; (*callback)(&gGCStack, sizeof(gGCStack)); diff --git a/js/src/jscrashreport.h b/js/src/jscrashreport.h index 293cd4396f02..378cbc94104f 100644 --- a/js/src/jscrashreport.h +++ b/js/src/jscrashreport.h @@ -42,18 +42,46 @@ #define jscrashreport_h___ #include "jstypes.h" +#include "jsutil.h" -JS_BEGIN_EXTERN_C +namespace js { +namespace crash { -JS_FRIEND_API(void) -js_SnapshotGCStack(); +void +SnapshotGCStack(); -JS_FRIEND_API(void) -js_SnapshotErrorStack(); +void +SnapshotErrorStack(); -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size); +void +SaveCrashData(uint64 tag, void *ptr, size_t size); -JS_END_EXTERN_C +template +class StackBuffer { + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + volatile char buffer[size + 4]; + + public: + StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + + buffer[0] = marker; + buffer[1] = '['; + + for (size_t i = 0; i < size; i++) { + if (data) + buffer[i + 2] = ((char *)data)[i]; + else + buffer[i + 2] = 0; + } + + buffer[size - 2] = ']'; + buffer[size - 1] = marker; + } +}; + +} /* namespace crash */ +} /* namespace js */ #endif /* jscrashreport_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 76d5939b4200..5e2e6776f877 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -241,7 +241,7 @@ Arena::finalize(JSContext *cx) if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - memset(t, JS_FREE_PATTERN, sizeof(T)); + JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); } } } @@ -2399,9 +2399,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); } #endif - - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - js::CheckCompartmentScripts(*c); } #ifdef JS_THREADSAFE @@ -2693,7 +2690,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) GCCrashData crashData; crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT; crashData.isCompartment = !!comp; - js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); + crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); GCTIMER_BEGIN(rt, comp); @@ -2744,7 +2741,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) rt->gcChunkAllocationSinceLastGC = false; GCTIMER_END(gckind == GC_LAST_CONTEXT); - js_SnapshotGCStack(); + crash::SnapshotGCStack(); } namespace js { diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 52ff83b6381e..93766e5cf26f 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -100,13 +100,6 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); -static void -volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - template void Mark(JSTracer *trc, T *thing) @@ -122,15 +115,9 @@ Mark(JSTracer *trc, T *thing) JS_ASSERT(thing->compartment()); JS_ASSERT(thing->compartment()->rt == rt); - if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment && - thing->compartment() != rt->atomsCompartment) - { - volatile unsigned char dbg[sizeof(T) + 2]; - dbg[0] = 0xab; - dbg[1] = 0xcd; - volatile_memcpy(dbg + 2, thing, sizeof(T)); - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); - } + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, + thing->compartment() == rt->gcCheckCompartment || + thing->compartment() == rt->atomsCompartment); /* * Don't mark things outside a compartment if we are in a per-compartment diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 25d22af52dd0..010299ede142 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3417,24 +3417,6 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to) return true; } -static void -CheckProxy(JSObject *obj) -{ - if (!obj->isProxy()) - return; - - JSProxyHandler *handler = obj->getProxyHandler(); - if (handler->isCrossCompartment()) - return; - - Value priv = obj->getProxyPrivate(); - if (!priv.isObject()) - return; - - if (priv.toObject().compartment() != obj->compartment()) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); -} - JSObject * JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) { @@ -3472,8 +3454,6 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } - CheckProxy(clone); - return clone; } @@ -3586,11 +3566,6 @@ JSObject::swap(JSContext *cx, JSObject *other) TradeGuts(this, otherClone); TradeGuts(other, thisClone); - CheckProxy(this); - CheckProxy(other); - CheckProxy(thisClone); - CheckProxy(otherClone); - return true; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 076e7b242c85..2d6673289955 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1184,11 +1184,6 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje else clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass; - if (!handler->isCrossCompartment() && priv.isObject()) { - if (priv.toObject().compartment() != cx->compartment) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); - } - JSObject *obj = NewNonFunction(cx, clasp, proto, parent); if (!obj || !obj->ensureInstanceReservedSlots(cx, 0)) return NULL; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 8cf1b6a52432..5f7b2769cfdd 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -92,10 +92,6 @@ class JS_FRIEND_API(JSProxyHandler) { return false; } - virtual bool isCrossCompartment() { - return false; - } - inline void *family() { return mFamily; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2a54d36aebbc..7e0ca1327a50 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -46,6 +46,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" +#include "jscrashreport.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" @@ -287,44 +288,26 @@ Bindings::trace(JSTracer *trc) } /* namespace js */ -static void -volatile_memcpy(volatile char *dst, void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - static void CheckScript(JSScript *script, JSScript *prev) { - volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)]; +#ifdef JS_CRASH_DIAGNOSTICS if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) { - volatile_memcpy(dbg1, script, sizeof(JSScript)); - if (prev) - volatile_memcpy(dbg2, prev, sizeof(JSScript)); + crash::StackBuffer buf1(script); + crash::StackBuffer buf2(prev); + JS_OPT_ASSERT(false); } - JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE); +#endif } static void CheckScriptOwner(JSScript *script, JSObject *owner) { - if (script->ownerObject != owner) { - volatile char scriptData[sizeof(JSScript)]; - volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)]; - volatile char savedOwner[sizeof(JSObject *)]; - - volatile_memcpy(scriptData, script, sizeof(JSScript)); - volatile_memcpy(savedOwner, &owner, sizeof(JSObject *)); - if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT) - volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject)); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) - volatile_memcpy(owner2Data, owner, sizeof(JSObject)); - } +#ifdef JS_CRASH_DIAGNOSTICS JS_OPT_ASSERT(script->ownerObject == owner); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) JS_OPT_ASSERT(script->compartment == owner->compartment()); +#endif } #if JS_HAS_XDR @@ -981,8 +964,10 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom return NULL; PodZero(script); +#ifdef JS_CRASH_DIAGNOSTICS script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE; script->ownerObject = JS_NEW_SCRIPT; +#endif script->length = length; script->version = version; new (&script->bindings) Bindings(cx, emptyCallShape); @@ -1286,8 +1271,10 @@ JSScript::totalSize() void JSScript::setOwnerObject(JSObject *owner) { +#ifdef JS_CRASH_DIAGNOSTICS CheckScriptOwner(this, JS_NEW_SCRIPT); ownerObject = owner; +#endif } /* @@ -1328,22 +1315,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) JS_ClearScriptTraps(cx, script); } -namespace js { - -void -CheckCompartmentScripts(JSCompartment *comp) -{ - JSScript *prev = NULL; - for (JSScript *script = (JSScript *)comp->scripts.next; - &script->links != &comp->scripts; - prev = script, script = (JSScript *)script->links.next) - { - CheckScript(script, prev); - } -} - -} /* namespace js */ - static void DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) { @@ -1408,7 +1379,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) if (script->sourceMap) cx->free_(script->sourceMap); - memset(script, 0xdb, script->totalSize()); + JS_POISON(script, 0xdb, sizeof(JSScript)); *(uint32 *)script = caller; cx->free_(script); } @@ -1443,9 +1414,8 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) if (owner) CheckScriptOwner(script, owner); - JSRuntime *rt = trc->context->runtime; - if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment) - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); + DebugOnly rt = trc->context->runtime; + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment); JSAtomMap *map = &script->atomMap; MarkAtomRange(trc, map->length, map->vector, "atomMap"); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index f0cad899a9c7..8464dbd8652e 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -449,7 +449,9 @@ struct JSScript { jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie1; +#endif private: uint16 version; /* JS version under which script was compiled */ @@ -507,7 +509,9 @@ struct JSScript { JSPrincipals *principals;/* principals for this script */ jschar *sourceMap; /* source map file or null */ +#ifdef JS_CRASH_DIAGNOSTICS JSObject *ownerObject; +#endif void setOwnerObject(JSObject *owner); @@ -541,7 +545,9 @@ struct JSScript { /* array of execution counters for every JSOp in the script, by runmode */ JSPCCounters pcCounters; +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie2; +#endif public: #ifdef JS_METHODJIT @@ -747,19 +753,6 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner); extern void js_DestroyCachedScript(JSContext *cx, JSScript *script); -namespace js { - -/* - * This diagnostic function checks that a compartment's list of scripts - * contains only valid scripts. It also searches for the target script - * in the list. If expected is true, it asserts that the target script - * is found. If expected is false, it asserts that it's not found. - */ -void -CheckCompartmentScripts(JSCompartment *comp); - -} /* namespace js */ - extern void js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner); diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 4b1682be3f80..28bb73a77970 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -45,7 +45,6 @@ #define jsutil_h___ #include "jstypes.h" -#include "jscrashreport.h" #include "mozilla/Util.h" #include #include @@ -62,12 +61,24 @@ JS_BEGIN_EXTERN_C #define JS_FREE_PATTERN 0xDA +#ifdef JS_CRASH_DIAGNOSTICS + +#define JS_POISON(p, val, size) memset((p), (val), (size)) + #define JS_OPT_ASSERT(expr) \ ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) #define JS_OPT_ASSERT_IF(cond, expr) \ ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) +#else + +#define JS_POISON(p, val, size) ((void) 0) +#define JS_OPT_ASSERT(expr) ((void) 0) +#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0) + +#endif /* JS_CRASH_DIAGNOSTICS */ + #ifdef DEBUG #define JS_ASSERT(expr) \ @@ -227,12 +238,6 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ #define JS_OOM_POSSIBLY_FAIL() do {} while(0) #endif -static JS_INLINE void *js_record_oom(void *p) { - if (!p) - js_SnapshotErrorStack(); - return p; -} - /* * SpiderMonkey code should not be calling these allocation functions directly. * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. @@ -240,17 +245,17 @@ static JS_INLINE void *js_record_oom(void *p) { */ static JS_INLINE void* js_malloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(malloc(bytes)); + return malloc(bytes); } static JS_INLINE void* js_calloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(calloc(bytes, 1)); + return calloc(bytes, 1); } static JS_INLINE void* js_realloc(void* p, size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(realloc(p, bytes)); + return realloc(p, bytes); } static JS_INLINE void js_free(void* p) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e7e2fcfb9b59..369518afa410 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -155,10 +155,6 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual void trace(JSTracer *trc, JSObject *wrapper); - virtual bool isCrossCompartment() { - return true; - } - static JSCrossCompartmentWrapper singleton; }; From cf08f0e0ea81da4090486ba102d2ccbb99fa7ccb Mon Sep 17 00:00:00 2001 From: Ali Juma Date: Fri, 12 Aug 2011 10:12:03 -0400 Subject: [PATCH 06/51] Bug 678207 - Fix interface of nsWindow::GetLayerManager on Android. r=cjones --HG-- extra : rebase_source : fe546360a7a33042109492009ed20b54633d220d --- widget/src/android/nsWindow.cpp | 3 ++- widget/src/android/nsWindow.h | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 73fe9f66be21..0e0cb5658388 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -662,7 +662,8 @@ nsWindow::SetWindowClass(const nsAString& xulWinType) } mozilla::layers::LayerManager* -nsWindow::GetLayerManager(LayerManagerPersistence, bool* aAllowRetaining) +nsWindow::GetLayerManager(PLayersChild*, LayersBackend, LayerManagerPersistence, + bool* aAllowRetaining) { if (aAllowRetaining) { *aAllowRetaining = true; diff --git a/widget/src/android/nsWindow.h b/widget/src/android/nsWindow.h index d392e8d54339..c856f340fe2d 100644 --- a/widget/src/android/nsWindow.h +++ b/widget/src/android/nsWindow.h @@ -163,8 +163,10 @@ public: NS_IMETHOD OnIMESelectionChange(void); virtual nsIMEUpdatePreference GetIMEUpdatePreference(); - LayerManager* GetLayerManager(LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, - bool* aAllowRetaining = nsnull); + LayerManager* GetLayerManager (PLayersChild* aShadowManager = nsnull, + LayersBackend aBackendHint = LayerManager::LAYERS_NONE, + LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT, + bool* aAllowRetaining = nsnull); gfxASurface* GetThebesSurface(); NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent); From 5653550a7ff45038340765295913d8c8d8b2dc8e Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Fri, 12 Aug 2011 07:35:31 -0700 Subject: [PATCH 07/51] Back out cd7f694c9f2f (bug 677773) because of build failures --- configure.in | 12 -------- js/src/configure.in | 12 -------- js/src/jscompartment.cpp | 2 ++ js/src/jscrashreport.cpp | 44 ++++++++++++++--------------- js/src/jscrashreport.h | 44 ++++++----------------------- js/src/jsgc.cpp | 9 ++++-- js/src/jsgcmark.cpp | 19 +++++++++++-- js/src/jsobj.cpp | 25 +++++++++++++++++ js/src/jsproxy.cpp | 5 ++++ js/src/jsproxy.h | 4 +++ js/src/jsscript.cpp | 60 ++++++++++++++++++++++++++++++---------- js/src/jsscript.h | 19 +++++++++---- js/src/jsutil.h | 25 +++++++---------- js/src/jswrapper.h | 4 +++ 14 files changed, 160 insertions(+), 124 deletions(-) diff --git a/configure.in b/configure.in index 3556352f7ec7..c4a3716d526e 100644 --- a/configure.in +++ b/configure.in @@ -7593,18 +7593,6 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi -dnl ======================================================== -dnl JS opt-mode assertions and minidump instrumentation -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(js-diagnostics, -[ --enable-js-diagnostics - Enable JS diagnostic assertions and breakpad data], - JS_CRASH_DIAGNOSTICS=1, - JS_CRASH_DIAGNOSTICS= ) -if test -n "$JS_CRASH_DIAGNOSTICS"; then - AC_DEFINE(JS_CRASH_DIAGNOSTICS) -fi - dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/configure.in b/js/src/configure.in index 74dcc1b14cbd..42a31ae154a6 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -5022,18 +5022,6 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi -dnl ======================================================== -dnl JS opt-mode assertions and minidump instrumentation -dnl ======================================================== -MOZ_ARG_ENABLE_BOOL(js-diagnostics, -[ --enable-js-diagnostics - Enable JS diagnostic assertions and breakpad data], - JS_CRASH_DIAGNOSTICS=1, - JS_CRASH_DIAGNOSTICS= ) -if test -n "$JS_CRASH_DIAGNOSTICS"; then - AC_DEFINE(JS_CRASH_DIAGNOSTICS) -fi - dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b1f71676206c..b9077db2984d 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -562,6 +562,8 @@ JSCompartment::purge(JSContext *cx) #endif #ifdef JS_METHODJIT + js::CheckCompartmentScripts(this); + for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { diff --git a/js/src/jscrashreport.cpp b/js/src/jscrashreport.cpp index ecc7c3f1185c..9a840b0cd1cd 100644 --- a/js/src/jscrashreport.cpp +++ b/js/src/jscrashreport.cpp @@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC); static Stack gErrorStack(JS_CRASH_STACK_ERROR); static Ring gRingBuffer(JS_CRASH_RING); -void -SnapshotGCStack() -{ - if (gInitialized) - gGCStack.snapshot(); -} - -void -SnapshotErrorStack() -{ - if (gInitialized) - gErrorStack.snapshot(); -} - -void -SaveCrashData(uint64 tag, void *ptr, size_t size) -{ - if (gInitialized) - gRingBuffer.push(tag, ptr, size); -} - } /* namespace crash */ } /* namespace js */ using namespace js; using namespace js::crash; +JS_FRIEND_API(void) +js_SnapshotGCStack() +{ + if (gInitialized) + gGCStack.snapshot(); +} + +JS_FRIEND_API(void) +js_SnapshotErrorStack() +{ + if (gInitialized) + gErrorStack.snapshot(); +} + +JS_FRIEND_API(void) +js_SaveCrashData(uint64 tag, void *ptr, size_t size) +{ + if (gInitialized) + gRingBuffer.push(tag, ptr, size); +} + JS_PUBLIC_API(void) JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) { -#ifdef JS_CRASH_DIAGNOSTICS +#if 1 if (!gInitialized) { gInitialized = true; (*callback)(&gGCStack, sizeof(gGCStack)); diff --git a/js/src/jscrashreport.h b/js/src/jscrashreport.h index 378cbc94104f..293cd4396f02 100644 --- a/js/src/jscrashreport.h +++ b/js/src/jscrashreport.h @@ -42,46 +42,18 @@ #define jscrashreport_h___ #include "jstypes.h" -#include "jsutil.h" -namespace js { -namespace crash { +JS_BEGIN_EXTERN_C -void -SnapshotGCStack(); +JS_FRIEND_API(void) +js_SnapshotGCStack(); -void -SnapshotErrorStack(); +JS_FRIEND_API(void) +js_SnapshotErrorStack(); -void -SaveCrashData(uint64 tag, void *ptr, size_t size); +JS_FRIEND_API(void) +js_SaveCrashData(uint64 tag, void *ptr, size_t size); -template -class StackBuffer { - private: - JS_DECL_USE_GUARD_OBJECT_NOTIFIER - volatile char buffer[size + 4]; - - public: - StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { - JS_GUARD_OBJECT_NOTIFIER_INIT; - - buffer[0] = marker; - buffer[1] = '['; - - for (size_t i = 0; i < size; i++) { - if (data) - buffer[i + 2] = ((char *)data)[i]; - else - buffer[i + 2] = 0; - } - - buffer[size - 2] = ']'; - buffer[size - 1] = marker; - } -}; - -} /* namespace crash */ -} /* namespace js */ +JS_END_EXTERN_C #endif /* jscrashreport_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5e2e6776f877..76d5939b4200 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -241,7 +241,7 @@ Arena::finalize(JSContext *cx) if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); + memset(t, JS_FREE_PATTERN, sizeof(T)); } } } @@ -2399,6 +2399,9 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); } #endif + + for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) + js::CheckCompartmentScripts(*c); } #ifdef JS_THREADSAFE @@ -2690,7 +2693,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) GCCrashData crashData; crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT; crashData.isCompartment = !!comp; - crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); + js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); GCTIMER_BEGIN(rt, comp); @@ -2741,7 +2744,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) rt->gcChunkAllocationSinceLastGC = false; GCTIMER_END(gckind == GC_LAST_CONTEXT); - crash::SnapshotGCStack(); + js_SnapshotGCStack(); } namespace js { diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 93766e5cf26f..52ff83b6381e 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -100,6 +100,13 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); +static void +volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n) +{ + for (size_t i = 0; i < n; i++) + dst[i] = ((char *)src)[i]; +} + template void Mark(JSTracer *trc, T *thing) @@ -115,9 +122,15 @@ Mark(JSTracer *trc, T *thing) JS_ASSERT(thing->compartment()); JS_ASSERT(thing->compartment()->rt == rt); - JS_OPT_ASSERT_IF(rt->gcCheckCompartment, - thing->compartment() == rt->gcCheckCompartment || - thing->compartment() == rt->atomsCompartment); + if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment && + thing->compartment() != rt->atomsCompartment) + { + volatile unsigned char dbg[sizeof(T) + 2]; + dbg[0] = 0xab; + dbg[1] = 0xcd; + volatile_memcpy(dbg + 2, thing, sizeof(T)); + JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); + } /* * Don't mark things outside a compartment if we are in a per-compartment diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 010299ede142..25d22af52dd0 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3417,6 +3417,24 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to) return true; } +static void +CheckProxy(JSObject *obj) +{ + if (!obj->isProxy()) + return; + + JSProxyHandler *handler = obj->getProxyHandler(); + if (handler->isCrossCompartment()) + return; + + Value priv = obj->getProxyPrivate(); + if (!priv.isObject()) + return; + + if (priv.toObject().compartment() != obj->compartment()) + JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); +} + JSObject * JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) { @@ -3454,6 +3472,8 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } + CheckProxy(clone); + return clone; } @@ -3566,6 +3586,11 @@ JSObject::swap(JSContext *cx, JSObject *other) TradeGuts(this, otherClone); TradeGuts(other, thisClone); + CheckProxy(this); + CheckProxy(other); + CheckProxy(thisClone); + CheckProxy(otherClone); + return true; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 2d6673289955..076e7b242c85 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1184,6 +1184,11 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje else clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass; + if (!handler->isCrossCompartment() && priv.isObject()) { + if (priv.toObject().compartment() != cx->compartment) + JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); + } + JSObject *obj = NewNonFunction(cx, clasp, proto, parent); if (!obj || !obj->ensureInstanceReservedSlots(cx, 0)) return NULL; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 5f7b2769cfdd..8cf1b6a52432 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -92,6 +92,10 @@ class JS_FRIEND_API(JSProxyHandler) { return false; } + virtual bool isCrossCompartment() { + return false; + } + inline void *family() { return mFamily; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 7e0ca1327a50..2a54d36aebbc 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -46,7 +46,6 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" -#include "jscrashreport.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" @@ -288,26 +287,44 @@ Bindings::trace(JSTracer *trc) } /* namespace js */ +static void +volatile_memcpy(volatile char *dst, void *src, size_t n) +{ + for (size_t i = 0; i < n; i++) + dst[i] = ((char *)src)[i]; +} + static void CheckScript(JSScript *script, JSScript *prev) { -#ifdef JS_CRASH_DIAGNOSTICS + volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)]; if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) { - crash::StackBuffer buf1(script); - crash::StackBuffer buf2(prev); - JS_OPT_ASSERT(false); + volatile_memcpy(dbg1, script, sizeof(JSScript)); + if (prev) + volatile_memcpy(dbg2, prev, sizeof(JSScript)); } -#endif + JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE); } static void CheckScriptOwner(JSScript *script, JSObject *owner) { -#ifdef JS_CRASH_DIAGNOSTICS + if (script->ownerObject != owner) { + volatile char scriptData[sizeof(JSScript)]; + volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)]; + volatile char savedOwner[sizeof(JSObject *)]; + + volatile_memcpy(scriptData, script, sizeof(JSScript)); + volatile_memcpy(savedOwner, &owner, sizeof(JSObject *)); + if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT) + volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject)); + if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) + volatile_memcpy(owner2Data, owner, sizeof(JSObject)); + } JS_OPT_ASSERT(script->ownerObject == owner); + if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) JS_OPT_ASSERT(script->compartment == owner->compartment()); -#endif } #if JS_HAS_XDR @@ -964,10 +981,8 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom return NULL; PodZero(script); -#ifdef JS_CRASH_DIAGNOSTICS script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE; script->ownerObject = JS_NEW_SCRIPT; -#endif script->length = length; script->version = version; new (&script->bindings) Bindings(cx, emptyCallShape); @@ -1271,10 +1286,8 @@ JSScript::totalSize() void JSScript::setOwnerObject(JSObject *owner) { -#ifdef JS_CRASH_DIAGNOSTICS CheckScriptOwner(this, JS_NEW_SCRIPT); ownerObject = owner; -#endif } /* @@ -1315,6 +1328,22 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) JS_ClearScriptTraps(cx, script); } +namespace js { + +void +CheckCompartmentScripts(JSCompartment *comp) +{ + JSScript *prev = NULL; + for (JSScript *script = (JSScript *)comp->scripts.next; + &script->links != &comp->scripts; + prev = script, script = (JSScript *)script->links.next) + { + CheckScript(script, prev); + } +} + +} /* namespace js */ + static void DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) { @@ -1379,7 +1408,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) if (script->sourceMap) cx->free_(script->sourceMap); - JS_POISON(script, 0xdb, sizeof(JSScript)); + memset(script, 0xdb, script->totalSize()); *(uint32 *)script = caller; cx->free_(script); } @@ -1414,8 +1443,9 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) if (owner) CheckScriptOwner(script, owner); - DebugOnly rt = trc->context->runtime; - JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment); + JSRuntime *rt = trc->context->runtime; + if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment) + JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); JSAtomMap *map = &script->atomMap; MarkAtomRange(trc, map->length, map->vector, "atomMap"); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 8464dbd8652e..f0cad899a9c7 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -449,9 +449,7 @@ struct JSScript { jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ -#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie1; -#endif private: uint16 version; /* JS version under which script was compiled */ @@ -509,9 +507,7 @@ struct JSScript { JSPrincipals *principals;/* principals for this script */ jschar *sourceMap; /* source map file or null */ -#ifdef JS_CRASH_DIAGNOSTICS JSObject *ownerObject; -#endif void setOwnerObject(JSObject *owner); @@ -545,9 +541,7 @@ struct JSScript { /* array of execution counters for every JSOp in the script, by runmode */ JSPCCounters pcCounters; -#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie2; -#endif public: #ifdef JS_METHODJIT @@ -753,6 +747,19 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner); extern void js_DestroyCachedScript(JSContext *cx, JSScript *script); +namespace js { + +/* + * This diagnostic function checks that a compartment's list of scripts + * contains only valid scripts. It also searches for the target script + * in the list. If expected is true, it asserts that the target script + * is found. If expected is false, it asserts that it's not found. + */ +void +CheckCompartmentScripts(JSCompartment *comp); + +} /* namespace js */ + extern void js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner); diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 28bb73a77970..4b1682be3f80 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -45,6 +45,7 @@ #define jsutil_h___ #include "jstypes.h" +#include "jscrashreport.h" #include "mozilla/Util.h" #include #include @@ -61,24 +62,12 @@ JS_BEGIN_EXTERN_C #define JS_FREE_PATTERN 0xDA -#ifdef JS_CRASH_DIAGNOSTICS - -#define JS_POISON(p, val, size) memset((p), (val), (size)) - #define JS_OPT_ASSERT(expr) \ ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) #define JS_OPT_ASSERT_IF(cond, expr) \ ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) -#else - -#define JS_POISON(p, val, size) ((void) 0) -#define JS_OPT_ASSERT(expr) ((void) 0) -#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0) - -#endif /* JS_CRASH_DIAGNOSTICS */ - #ifdef DEBUG #define JS_ASSERT(expr) \ @@ -238,6 +227,12 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ #define JS_OOM_POSSIBLY_FAIL() do {} while(0) #endif +static JS_INLINE void *js_record_oom(void *p) { + if (!p) + js_SnapshotErrorStack(); + return p; +} + /* * SpiderMonkey code should not be calling these allocation functions directly. * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. @@ -245,17 +240,17 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ */ static JS_INLINE void* js_malloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return malloc(bytes); + return js_record_oom(malloc(bytes)); } static JS_INLINE void* js_calloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return calloc(bytes, 1); + return js_record_oom(calloc(bytes, 1)); } static JS_INLINE void* js_realloc(void* p, size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return realloc(p, bytes); + return js_record_oom(realloc(p, bytes)); } static JS_INLINE void js_free(void* p) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index 369518afa410..e7e2fcfb9b59 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -155,6 +155,10 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual void trace(JSTracer *trc, JSObject *wrapper); + virtual bool isCrossCompartment() { + return true; + } + static JSCrossCompartmentWrapper singleton; }; From 0fd3a6c524dd8d110c5feb23a35fba476570c477 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Fri, 12 Aug 2011 06:57:45 -0700 Subject: [PATCH 08/51] Bug 677773 - Allow JS crash diagnostics to be disabled; simplify them (r=dmandelin,ted) --- configure.in | 12 ++++++++ js/src/configure.in | 12 ++++++++ js/src/jscompartment.cpp | 2 -- js/src/jscrashreport.cpp | 44 ++++++++++++++--------------- js/src/jscrashreport.h | 44 +++++++++++++++++++++++------ js/src/jsgc.cpp | 9 ++---- js/src/jsgcmark.cpp | 19 ++----------- js/src/jsobj.cpp | 25 ----------------- js/src/jsproxy.cpp | 5 ---- js/src/jsproxy.h | 4 --- js/src/jsscript.cpp | 60 +++++++++++----------------------------- js/src/jsscript.h | 19 ++++--------- js/src/jsutil.h | 25 ++++++++++------- js/src/jswrapper.h | 4 --- 14 files changed, 125 insertions(+), 159 deletions(-) diff --git a/configure.in b/configure.in index c4a3716d526e..3556352f7ec7 100644 --- a/configure.in +++ b/configure.in @@ -7593,6 +7593,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/configure.in b/js/src/configure.in index 42a31ae154a6..74dcc1b14cbd 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -5022,6 +5022,18 @@ if test -n "$JS_GC_ZEAL"; then AC_DEFINE(JS_GC_ZEAL) fi +dnl ======================================================== +dnl JS opt-mode assertions and minidump instrumentation +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(js-diagnostics, +[ --enable-js-diagnostics + Enable JS diagnostic assertions and breakpad data], + JS_CRASH_DIAGNOSTICS=1, + JS_CRASH_DIAGNOSTICS= ) +if test -n "$JS_CRASH_DIAGNOSTICS"; then + AC_DEFINE(JS_CRASH_DIAGNOSTICS) +fi + dnl ====================================================== dnl = Enable compiling with ccache dnl ====================================================== diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b9077db2984d..b1f71676206c 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -562,8 +562,6 @@ JSCompartment::purge(JSContext *cx) #endif #ifdef JS_METHODJIT - js::CheckCompartmentScripts(this); - for (JSScript *script = (JSScript *)scripts.next; &script->links != &scripts; script = (JSScript *)script->links.next) { diff --git a/js/src/jscrashreport.cpp b/js/src/jscrashreport.cpp index 9a840b0cd1cd..ecc7c3f1185c 100644 --- a/js/src/jscrashreport.cpp +++ b/js/src/jscrashreport.cpp @@ -245,37 +245,37 @@ static Stack gGCStack(JS_CRASH_STACK_GC); static Stack gErrorStack(JS_CRASH_STACK_ERROR); static Ring gRingBuffer(JS_CRASH_RING); +void +SnapshotGCStack() +{ + if (gInitialized) + gGCStack.snapshot(); +} + +void +SnapshotErrorStack() +{ + if (gInitialized) + gErrorStack.snapshot(); +} + +void +SaveCrashData(uint64 tag, void *ptr, size_t size) +{ + if (gInitialized) + gRingBuffer.push(tag, ptr, size); +} + } /* namespace crash */ } /* namespace js */ using namespace js; using namespace js::crash; -JS_FRIEND_API(void) -js_SnapshotGCStack() -{ - if (gInitialized) - gGCStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SnapshotErrorStack() -{ - if (gInitialized) - gErrorStack.snapshot(); -} - -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size) -{ - if (gInitialized) - gRingBuffer.push(tag, ptr, size); -} - JS_PUBLIC_API(void) JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) { -#if 1 +#ifdef JS_CRASH_DIAGNOSTICS if (!gInitialized) { gInitialized = true; (*callback)(&gGCStack, sizeof(gGCStack)); diff --git a/js/src/jscrashreport.h b/js/src/jscrashreport.h index 293cd4396f02..378cbc94104f 100644 --- a/js/src/jscrashreport.h +++ b/js/src/jscrashreport.h @@ -42,18 +42,46 @@ #define jscrashreport_h___ #include "jstypes.h" +#include "jsutil.h" -JS_BEGIN_EXTERN_C +namespace js { +namespace crash { -JS_FRIEND_API(void) -js_SnapshotGCStack(); +void +SnapshotGCStack(); -JS_FRIEND_API(void) -js_SnapshotErrorStack(); +void +SnapshotErrorStack(); -JS_FRIEND_API(void) -js_SaveCrashData(uint64 tag, void *ptr, size_t size); +void +SaveCrashData(uint64 tag, void *ptr, size_t size); -JS_END_EXTERN_C +template +class StackBuffer { + private: + JS_DECL_USE_GUARD_OBJECT_NOTIFIER + volatile char buffer[size + 4]; + + public: + StackBuffer(void *data JS_GUARD_OBJECT_NOTIFIER_PARAM) { + JS_GUARD_OBJECT_NOTIFIER_INIT; + + buffer[0] = marker; + buffer[1] = '['; + + for (size_t i = 0; i < size; i++) { + if (data) + buffer[i + 2] = ((char *)data)[i]; + else + buffer[i + 2] = 0; + } + + buffer[size - 2] = ']'; + buffer[size - 1] = marker; + } +}; + +} /* namespace crash */ +} /* namespace js */ #endif /* jscrashreport_h___ */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 76d5939b4200..5e2e6776f877 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -241,7 +241,7 @@ Arena::finalize(JSContext *cx) if (!newFreeSpanStart) newFreeSpanStart = thing; t->finalize(cx); - memset(t, JS_FREE_PATTERN, sizeof(T)); + JS_POISON(t, JS_FREE_PATTERN, sizeof(T)); } } } @@ -2399,9 +2399,6 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM printf("GC HEAP SIZE %lu\n", (unsigned long)rt->gcBytes); } #endif - - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) - js::CheckCompartmentScripts(*c); } #ifdef JS_THREADSAFE @@ -2693,7 +2690,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) GCCrashData crashData; crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT; crashData.isCompartment = !!comp; - js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); + crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData)); GCTIMER_BEGIN(rt, comp); @@ -2744,7 +2741,7 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) rt->gcChunkAllocationSinceLastGC = false; GCTIMER_END(gckind == GC_LAST_CONTEXT); - js_SnapshotGCStack(); + crash::SnapshotGCStack(); } namespace js { diff --git a/js/src/jsgcmark.cpp b/js/src/jsgcmark.cpp index 52ff83b6381e..93766e5cf26f 100644 --- a/js/src/jsgcmark.cpp +++ b/js/src/jsgcmark.cpp @@ -100,13 +100,6 @@ PushMarkStack(GCMarker *gcmarker, JSShortString *thing); static inline void PushMarkStack(GCMarker *gcmarker, JSString *thing); -static void -volatile_memcpy(volatile unsigned char *dst, const void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - template void Mark(JSTracer *trc, T *thing) @@ -122,15 +115,9 @@ Mark(JSTracer *trc, T *thing) JS_ASSERT(thing->compartment()); JS_ASSERT(thing->compartment()->rt == rt); - if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment && - thing->compartment() != rt->atomsCompartment) - { - volatile unsigned char dbg[sizeof(T) + 2]; - dbg[0] = 0xab; - dbg[1] = 0xcd; - volatile_memcpy(dbg + 2, thing, sizeof(T)); - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); - } + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, + thing->compartment() == rt->gcCheckCompartment || + thing->compartment() == rt->atomsCompartment); /* * Don't mark things outside a compartment if we are in a per-compartment diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 25d22af52dd0..010299ede142 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3417,24 +3417,6 @@ CopySlots(JSContext *cx, JSObject *from, JSObject *to) return true; } -static void -CheckProxy(JSObject *obj) -{ - if (!obj->isProxy()) - return; - - JSProxyHandler *handler = obj->getProxyHandler(); - if (handler->isCrossCompartment()) - return; - - Value priv = obj->getProxyPrivate(); - if (!priv.isObject()) - return; - - if (priv.toObject().compartment() != obj->compartment()) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); -} - JSObject * JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) { @@ -3472,8 +3454,6 @@ JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent) return NULL; } - CheckProxy(clone); - return clone; } @@ -3586,11 +3566,6 @@ JSObject::swap(JSContext *cx, JSObject *other) TradeGuts(this, otherClone); TradeGuts(other, thisClone); - CheckProxy(this); - CheckProxy(other); - CheckProxy(thisClone); - CheckProxy(otherClone); - return true; } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index 076e7b242c85..2d6673289955 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -1184,11 +1184,6 @@ NewProxyObject(JSContext *cx, JSProxyHandler *handler, const Value &priv, JSObje else clasp = handler->isOuterWindow() ? &OuterWindowProxyClass : &ObjectProxyClass; - if (!handler->isCrossCompartment() && priv.isObject()) { - if (priv.toObject().compartment() != cx->compartment) - JS_Assert("compartment mismatch in proxy object", __FILE__, __LINE__); - } - JSObject *obj = NewNonFunction(cx, clasp, proto, parent); if (!obj || !obj->ensureInstanceReservedSlots(cx, 0)) return NULL; diff --git a/js/src/jsproxy.h b/js/src/jsproxy.h index 8cf1b6a52432..5f7b2769cfdd 100644 --- a/js/src/jsproxy.h +++ b/js/src/jsproxy.h @@ -92,10 +92,6 @@ class JS_FRIEND_API(JSProxyHandler) { return false; } - virtual bool isCrossCompartment() { - return false; - } - inline void *family() { return mFamily; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 2a54d36aebbc..93a3c4df5090 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -46,6 +46,7 @@ #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" +#include "jscrashreport.h" #include "jsprf.h" #include "jsapi.h" #include "jsatom.h" @@ -287,44 +288,26 @@ Bindings::trace(JSTracer *trc) } /* namespace js */ -static void -volatile_memcpy(volatile char *dst, void *src, size_t n) -{ - for (size_t i = 0; i < n; i++) - dst[i] = ((char *)src)[i]; -} - static void CheckScript(JSScript *script, JSScript *prev) { - volatile char dbg1[sizeof(JSScript)], dbg2[sizeof(JSScript)]; +#ifdef JS_CRASH_DIAGNOSTICS if (script->cookie1 != JS_SCRIPT_COOKIE || script->cookie2 != JS_SCRIPT_COOKIE) { - volatile_memcpy(dbg1, script, sizeof(JSScript)); - if (prev) - volatile_memcpy(dbg2, prev, sizeof(JSScript)); + crash::StackBuffer buf1(script); + crash::StackBuffer buf2(prev); + JS_OPT_ASSERT(false); } - JS_OPT_ASSERT(script->cookie1 == JS_SCRIPT_COOKIE && script->cookie2 == JS_SCRIPT_COOKIE); +#endif } static void CheckScriptOwner(JSScript *script, JSObject *owner) { - if (script->ownerObject != owner) { - volatile char scriptData[sizeof(JSScript)]; - volatile char owner1Data[sizeof(JSObject)], owner2Data[sizeof(JSObject)]; - volatile char savedOwner[sizeof(JSObject *)]; - - volatile_memcpy(scriptData, script, sizeof(JSScript)); - volatile_memcpy(savedOwner, &owner, sizeof(JSObject *)); - if (script->ownerObject != JS_NEW_SCRIPT && script->ownerObject != JS_CACHED_SCRIPT) - volatile_memcpy(owner1Data, script->ownerObject, sizeof(JSObject)); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) - volatile_memcpy(owner2Data, owner, sizeof(JSObject)); - } +#ifdef JS_CRASH_DIAGNOSTICS JS_OPT_ASSERT(script->ownerObject == owner); - if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT) JS_OPT_ASSERT(script->compartment == owner->compartment()); +#endif } #if JS_HAS_XDR @@ -981,8 +964,10 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom return NULL; PodZero(script); +#ifdef JS_CRASH_DIAGNOSTICS script->cookie1 = script->cookie2 = JS_SCRIPT_COOKIE; script->ownerObject = JS_NEW_SCRIPT; +#endif script->length = length; script->version = version; new (&script->bindings) Bindings(cx, emptyCallShape); @@ -1286,8 +1271,10 @@ JSScript::totalSize() void JSScript::setOwnerObject(JSObject *owner) { +#ifdef JS_CRASH_DIAGNOSTICS CheckScriptOwner(this, JS_NEW_SCRIPT); ownerObject = owner; +#endif } /* @@ -1328,22 +1315,6 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script) JS_ClearScriptTraps(cx, script); } -namespace js { - -void -CheckCompartmentScripts(JSCompartment *comp) -{ - JSScript *prev = NULL; - for (JSScript *script = (JSScript *)comp->scripts.next; - &script->links != &comp->scripts; - prev = script, script = (JSScript *)script->links.next) - { - CheckScript(script, prev); - } -} - -} /* namespace js */ - static void DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) { @@ -1408,7 +1379,7 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller) if (script->sourceMap) cx->free_(script->sourceMap); - memset(script, 0xdb, script->totalSize()); + JS_POISON(script, 0xdb, sizeof(JSScript)); *(uint32 *)script = caller; cx->free_(script); } @@ -1443,9 +1414,10 @@ js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner) if (owner) CheckScriptOwner(script, owner); +#ifdef JS_CRASH_DIAGNOSTICS JSRuntime *rt = trc->context->runtime; - if (rt->gcCheckCompartment && script->compartment != rt->gcCheckCompartment) - JS_Assert("compartment mismatch in GC", __FILE__, __LINE__); + JS_OPT_ASSERT_IF(rt->gcCheckCompartment, script->compartment == rt->gcCheckCompartment); +#endif JSAtomMap *map = &script->atomMap; MarkAtomRange(trc, map->length, map->vector, "atomMap"); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index f0cad899a9c7..8464dbd8652e 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -449,7 +449,9 @@ struct JSScript { jsbytecode *code; /* bytecodes and their immediate operands */ uint32 length; /* length of code vector */ +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie1; +#endif private: uint16 version; /* JS version under which script was compiled */ @@ -507,7 +509,9 @@ struct JSScript { JSPrincipals *principals;/* principals for this script */ jschar *sourceMap; /* source map file or null */ +#ifdef JS_CRASH_DIAGNOSTICS JSObject *ownerObject; +#endif void setOwnerObject(JSObject *owner); @@ -541,7 +545,9 @@ struct JSScript { /* array of execution counters for every JSOp in the script, by runmode */ JSPCCounters pcCounters; +#ifdef JS_CRASH_DIAGNOSTICS uint32 cookie2; +#endif public: #ifdef JS_METHODJIT @@ -747,19 +753,6 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner); extern void js_DestroyCachedScript(JSContext *cx, JSScript *script); -namespace js { - -/* - * This diagnostic function checks that a compartment's list of scripts - * contains only valid scripts. It also searches for the target script - * in the list. If expected is true, it asserts that the target script - * is found. If expected is false, it asserts that it's not found. - */ -void -CheckCompartmentScripts(JSCompartment *comp); - -} /* namespace js */ - extern void js_TraceScript(JSTracer *trc, JSScript *script, JSObject *owner); diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 4b1682be3f80..28bb73a77970 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -45,7 +45,6 @@ #define jsutil_h___ #include "jstypes.h" -#include "jscrashreport.h" #include "mozilla/Util.h" #include #include @@ -62,12 +61,24 @@ JS_BEGIN_EXTERN_C #define JS_FREE_PATTERN 0xDA +#ifdef JS_CRASH_DIAGNOSTICS + +#define JS_POISON(p, val, size) memset((p), (val), (size)) + #define JS_OPT_ASSERT(expr) \ ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) #define JS_OPT_ASSERT_IF(cond, expr) \ ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__)) +#else + +#define JS_POISON(p, val, size) ((void) 0) +#define JS_OPT_ASSERT(expr) ((void) 0) +#define JS_OPT_ASSERT_IF(cond, expr) ((void) 0) + +#endif /* JS_CRASH_DIAGNOSTICS */ + #ifdef DEBUG #define JS_ASSERT(expr) \ @@ -227,12 +238,6 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_counter; /* data race, who cares. */ #define JS_OOM_POSSIBLY_FAIL() do {} while(0) #endif -static JS_INLINE void *js_record_oom(void *p) { - if (!p) - js_SnapshotErrorStack(); - return p; -} - /* * SpiderMonkey code should not be calling these allocation functions directly. * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks. @@ -240,17 +245,17 @@ static JS_INLINE void *js_record_oom(void *p) { */ static JS_INLINE void* js_malloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(malloc(bytes)); + return malloc(bytes); } static JS_INLINE void* js_calloc(size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(calloc(bytes, 1)); + return calloc(bytes, 1); } static JS_INLINE void* js_realloc(void* p, size_t bytes) { JS_OOM_POSSIBLY_FAIL(); - return js_record_oom(realloc(p, bytes)); + return realloc(p, bytes); } static JS_INLINE void js_free(void* p) { diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e7e2fcfb9b59..369518afa410 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -155,10 +155,6 @@ class JS_FRIEND_API(JSCrossCompartmentWrapper) : public JSWrapper { virtual void trace(JSTracer *trc, JSObject *wrapper); - virtual bool isCrossCompartment() { - return true; - } - static JSCrossCompartmentWrapper singleton; }; From 555b4e6e9a7873a2e2e69c6ea460fc1dcc0578fd Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 12 Aug 2011 07:44:00 -0400 Subject: [PATCH 09/51] Bug 644993 - Undo close tab doesn't refresh the content in a SSL Error page [r=mark.finkle] --- mobile/chrome/content/browser.js | 20 ++++++++++++++++++-- mobile/chrome/content/content.js | 13 +++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index 1b200ca3f6ae..fbbf5faf3983 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -381,6 +381,7 @@ var Browser = { messageManager.addMessageListener("scroll", this); messageManager.addMessageListener("Browser:CertException", this); messageManager.addMessageListener("Browser:BlockedSite", this); + messageManager.addMessageListener("Browser:ErrorPage", this); // Broadcast a UIReady message so add-ons know we are finished with startup let event = document.createEvent("Events"); @@ -483,6 +484,7 @@ var Browser = { messageManager.removeMessageListener("scroll", this); messageManager.removeMessageListener("Browser:CertException", this); messageManager.removeMessageListener("Browser:BlockedSite", this); + messageManager.removeMessageListener("Browser:ErrorPage", this); var os = Services.obs; os.removeObserver(XPInstallObserver, "addon-install-blocked"); @@ -935,6 +937,14 @@ var Browser = { } }, + /** + * Handle error page message from the content. + */ + _handleErrorPage: function _handleErrorPage(aMessage) { + let tab = this.getTabForBrowser(aMessage.target); + tab.updateThumbnail({ force: true }); + }, + /** * Compute the sidebar percentage visibility. * @@ -1225,6 +1235,9 @@ var Browser = { case "Browser:BlockedSite": this._handleBlockedSite(aMessage); break; + case "Browser:ErrorPage": + this._handleErrorPage(aMessage); + break; } } }; @@ -2971,7 +2984,8 @@ Tab.prototype = { return this.metadata.allowZoom && !Util.isURLEmpty(this.browser.currentURI.spec); }, - updateThumbnail: function updateThumbnail() { + updateThumbnail: function updateThumbnail(options) { + let options = options || {}; let browser = this._browser; if (this._loading) { @@ -2979,9 +2993,11 @@ Tab.prototype = { return; } + let forceUpdate = ("force" in options && options.force); + // Do not repaint thumbnail if we already painted for this load. Bad things // happen when we do async canvas draws in quick succession. - if (!browser || this._thumbnailWindowId == browser.contentWindowId) + if (!forceUpdate && (!browser || this._thumbnailWindowId == browser.contentWindowId)) return; // Do not try to paint thumbnails if contentWindowWidth/Height have not been diff --git a/mobile/chrome/content/content.js b/mobile/chrome/content/content.js index fddb821773e2..2a23667bf643 100644 --- a/mobile/chrome/content/content.js +++ b/mobile/chrome/content/content.js @@ -279,6 +279,7 @@ let Content = { addEventListener("DOMActivate", this, true); addEventListener("MozApplicationManifest", this, false); + addEventListener("DOMContentLoaded", this, false); addEventListener("pagehide", this, false); addEventListener("keypress", this, false, false); @@ -384,6 +385,10 @@ let Content = { break; } + case "DOMContentLoaded": + this._maybeNotifyErroPage(); + break; + case "pagehide": if (aEvent.target == content.document) this._resetFontSize(); @@ -602,6 +607,14 @@ let Content = { } }, + _maybeNotifyErroPage: function _maybeNotifyErroPage() { + // Notify browser that an error page is being shown instead + // of the target location. Necessary to get proper thumbnail + // updates on chrome for error pages. + if (content.location.href !== content.document.documentURI) + sendAsyncMessage("Browser:ErrorPage", null); + }, + _resetFontSize: function _resetFontSize() { this._isZoomedToElement = false; this._setMinFontSize(0); From 17899dee15a8617344ac6307dbb83bd9cf7a4038 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 10 Aug 2011 17:40:39 -0500 Subject: [PATCH 10/51] Bug 672804 - "Assertion failure: parent" with trap right after JSOP_ENTERBLOCK. r=billm. --- js/src/jsinterp.cpp | 13 +++++++++++++ js/src/tests/js1_8_5/extensions/jstests.list | 3 +++ js/src/tests/js1_8_5/extensions/regress-672804-1.js | 12 ++++++++++++ js/src/tests/js1_8_5/extensions/regress-672804-2.js | 12 ++++++++++++ js/src/tests/js1_8_5/extensions/regress-672804-3.js | 11 +++++++++++ 5 files changed, 51 insertions(+) create mode 100644 js/src/tests/js1_8_5/extensions/regress-672804-1.js create mode 100644 js/src/tests/js1_8_5/extensions/regress-672804-2.js create mode 100644 js/src/tests/js1_8_5/extensions/regress-672804-3.js diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 5f94a5ddf11b..089c36c2ebad 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -154,6 +154,19 @@ js::GetBlockChain(JSContext *cx, StackFrame *fp) JSScript *script = fp->script(); jsbytecode *start = script->code; + + /* + * If the debugger asks for the scope chain at a pc where we are about to + * fix it up, advance target past the fixup. See bug 672804. + */ + JSOp op = js_GetOpcode(cx, script, target); + while (op == JSOP_NOP || op == JSOP_INDEXBASE || op == JSOP_INDEXBASE1 || + op == JSOP_INDEXBASE2 || op == JSOP_INDEXBASE3 || + op == JSOP_BLOCKCHAIN || op == JSOP_NULLBLOCKCHAIN) + { + target += js_CodeSpec[op].length; + op = js_GetOpcode(cx, script, target); + } JS_ASSERT(target >= start && target < start + script->length); JSObject *blockChain = NULL; diff --git a/js/src/tests/js1_8_5/extensions/jstests.list b/js/src/tests/js1_8_5/extensions/jstests.list index 915aeeb8bb20..63a0c4411e7a 100644 --- a/js/src/tests/js1_8_5/extensions/jstests.list +++ b/js/src/tests/js1_8_5/extensions/jstests.list @@ -53,4 +53,7 @@ script weakmap.js script regress-645160.js script regress-650753.js script regress-668438.js +require-or(debugMode,skip) script regress-672804-1.js +require-or(debugMode,skip) script regress-672804-2.js +require-or(debugMode,skip) script regress-672804-3.js script regress-677924.js diff --git a/js/src/tests/js1_8_5/extensions/regress-672804-1.js b/js/src/tests/js1_8_5/extensions/regress-672804-1.js new file mode 100644 index 000000000000..2197f89278e8 --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-672804-1.js @@ -0,0 +1,12 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 0; +function f() { + let (a = let (x = 1) x) {} +} + +trap(f, 3, 'assertEq(evalInFrame(1, "a"), 0)'); +f(); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-672804-2.js b/js/src/tests/js1_8_5/extensions/regress-672804-2.js new file mode 100644 index 000000000000..be8cc82f311a --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-672804-2.js @@ -0,0 +1,12 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var a = 0; +function f() { + let (a = let (x = 1) x) {} +} + +trap(f, 4, 'assertEq(evalInFrame(1, "a"), 0)'); +f(); + +reportCompare(0, 0, 'ok'); diff --git a/js/src/tests/js1_8_5/extensions/regress-672804-3.js b/js/src/tests/js1_8_5/extensions/regress-672804-3.js new file mode 100644 index 000000000000..029e82f07a3a --- /dev/null +++ b/js/src/tests/js1_8_5/extensions/regress-672804-3.js @@ -0,0 +1,11 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +var e = [], x = {b: []}; +function f() { + let (a = [[] for (x in e)], {b: []} = x) {} +} +trap(f, 4, ''); +f(); + +reportCompare(0, 0, 'ok'); From f046ef34acf1915410bde6c32e04685da0dfe583 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 12 Aug 2011 09:48:19 -0700 Subject: [PATCH 11/51] Back out rev 0bd518ded931 due to suspected Dromaeo(SunSpider) regression --- js/src/jswrapper.cpp | 17 ++++++++++++++-- js/src/vm/Stack-inl.h | 11 ++--------- js/src/vm/Stack.cpp | 46 +++++++++++++++++++++++-------------------- js/src/vm/Stack.h | 24 ++++------------------ 4 files changed, 46 insertions(+), 52 deletions(-) diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 40e7ba6259b4..120943e4d182 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -399,7 +399,7 @@ ForceFrame::enter() JSObject *scopeChain = target->getGlobal(); JS_ASSERT(scopeChain->isNative()); - return context->stack.pushDummyFrame(context, *scopeChain, frame); + return context->stack.pushDummyFrame(context, REPORT_ERROR, *scopeChain, frame); } AutoCompartment::AutoCompartment(JSContext *cx, JSObject *target) @@ -428,8 +428,21 @@ AutoCompartment::enter() JS_ASSERT(scopeChain->isNative()); frame.construct(); - if (!context->stack.pushDummyFrame(context, *scopeChain, &frame.ref())) + + /* + * Set the compartment eagerly so that pushDummyFrame associates the + * resource allocation request with 'destination' instead of 'origin'. + * (This is important when content has overflowed the stack and chrome + * is preparing to run JS to throw up a slow script dialog.) However, + * if an exception is thrown, we need it to be in origin's compartment + * so be careful to only report after restoring. + */ + context->compartment = destination; + if (!context->stack.pushDummyFrame(context, DONT_REPORT_ERROR, *scopeChain, &frame.ref())) { + context->compartment = origin; + js_ReportOverRecursed(context); return false; + } if (context->isExceptionPending()) context->wrapPendingException(); diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index da5925d84efb..5f70a55f3cbc 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -388,8 +388,7 @@ StackSpace::ensureEnoughSpaceToEnterTrace(JSContext *cx) STATIC_POSTCONDITION(!return || ubound(from) >= nvals) JS_ALWAYS_INLINE bool -StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, - JSCompartment *dest) const +StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const { assertInvariants(); JS_ASSERT(from >= firstUnused()); @@ -397,16 +396,10 @@ StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptr JS_ASSERT(from <= commitEnd_); #endif if (JS_UNLIKELY(conservativeEnd_ - from < nvals)) - return ensureSpaceSlow(cx, report, from, nvals, dest); + return ensureSpaceSlow(cx, report, from, nvals); return true; } -bool -StackSpace::ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const -{ - return ensureSpace(cx, report, from, nvals, cx->compartment); -} - inline Value * StackSpace::getStackLimit(JSContext *cx, MaybeReportError report) { diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index cab01bae913e..41ccdf8d5735 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -414,13 +414,13 @@ StackSpace::mark(JSTracer *trc) } JS_FRIEND_API(bool) -StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, - JSCompartment *dest) const +StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, + Value *from, ptrdiff_t nvals) const { assertInvariants(); - JS_ASSERT_IF(dest, cx); - bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals(); + bool trusted = !cx->compartment || + cx->compartment->principals == cx->runtime->trustedPrincipals(); Value *end = trusted ? trustedEnd_ : defaultEnd_; /* @@ -548,17 +548,17 @@ ContextStack::containsSlow(const StackFrame *target) const */ Value * ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, - MaybeExtend extend, bool *pushedSeg, JSCompartment *dest) + MaybeExtend extend, bool *pushedSeg) { Value *firstUnused = space().firstUnused(); if (onTop() && extend) { - if (!space().ensureSpace(cx, report, firstUnused, nvars, dest)) + if (!space().ensureSpace(cx, report, firstUnused, nvars)) return NULL; return firstUnused; } - if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest)) + if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars)) return NULL; FrameRegs *regs; @@ -577,13 +577,6 @@ ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, return seg_->slotsBegin(); } -Value * -ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, - MaybeExtend extend, bool *pushedSeg) -{ - return ensureOnTop(cx, report, nvars, extend, pushedSeg, cx->compartment); -} - void ContextStack::popSegment() { @@ -703,12 +696,11 @@ ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thi } bool -ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg) +ContextStack::pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain, + DummyFrameGuard *dfg) { - JSCompartment *dest = scopeChain.compartment(); - uintN nvars = VALUES_PER_STACK_FRAME; - Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest); + Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &dfg->pushedSeg_); if (!firstUnused) return NULL; @@ -716,7 +708,6 @@ ContextStack::pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuar fp->initDummyFrame(cx, scopeChain); dfg->regs_.initDummyFrame(*fp); - cx->compartment = dest; dfg->prevRegs_ = seg_->pushRegs(dfg->regs_); JS_ASSERT(space().firstUnused() == dfg->regs_.sp); dfg->setPushed(*this); @@ -799,11 +790,24 @@ ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg) bool ContextStack::saveFrameChain() { - JSCompartment *dest = NULL; + /* + * The StackSpace uses the context's current compartment to determine + * whether to allow access to the privileged end-of-stack buffer. + * However, we always want saveFrameChain to have access to this privileged + * buffer since it gets used to prepare calling trusted JS. To force this, + * we clear the current compartment (which is interpreted by ensureSpace as + * 'trusted') and either restore it on OOM or let resetCompartment() + * clobber it. + */ + JSCompartment *original = cx_->compartment; + cx_->compartment = NULL; bool pushedSeg; - if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest)) + if (!ensureOnTop(cx_, DONT_REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg)) { + cx_->compartment = original; + js_ReportOverRecursed(cx_); return false; + } JS_ASSERT(pushedSeg); JS_ASSERT(!hasfp()); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index bc10ce5bdf8b..e761cd3f5f9e 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -44,7 +44,6 @@ #include "jsfun.h" struct JSContext; -struct JSCompartment; namespace js { @@ -1330,15 +1329,10 @@ class StackSpace friend class ContextStack; friend class StackFrame; - inline bool ensureSpace(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals, - JSCompartment *dest) const; inline bool ensureSpace(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals) const; JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals, - JSCompartment *dest) const; - + Value *from, ptrdiff_t nvals) const; StackSegment &findContainingSegment(const StackFrame *target) const; public: @@ -1426,9 +1420,6 @@ class ContextStack /* Implementation details of push* public interface. */ StackSegment *pushSegment(JSContext *cx); enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false }; - Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, - MaybeExtend extend, bool *pushedSeg, - JSCompartment *dest); Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, MaybeExtend extend, bool *pushedSeg); @@ -1511,16 +1502,9 @@ class ContextStack */ bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg); - /* - * When changing the compartment of a cx, it is necessary to immediately - * change the scope chain to a global in the right compartment since any - * amount of general VM code can run before the first scripted frame is - * pushed (if at all). This is currently and hackily accomplished by - * pushing a "dummy frame" with the correct scope chain. On success, this - * function will change the compartment to 'scopeChain.compartment()' and - * push a dummy frame for 'scopeChain'. On failure, nothing is changed. - */ - bool pushDummyFrame(JSContext *cx, JSObject &scopeChain, DummyFrameGuard *dfg); + /* Pushes a "dummy" frame; should be removed one day. */ + bool pushDummyFrame(JSContext *cx, MaybeReportError report, JSObject &scopeChain, + DummyFrameGuard *dfg); /* * An "inline frame" may only be pushed from within the top, active From c3d8b83159d8edc82331169837569e7294d3d454 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 20 Sep 2010 12:43:52 -0700 Subject: [PATCH 12/51] Bug 574403 - Make --enable-dtrace work on all Linux and OSX builds OSX has the "real" DTrace available by default. On Linux, this uses systemtap's dtrace emulation tool and the header to convert static probes into special ELF sections describing the probe points so they may be enabled dynamically. systemtap-sdt-devel was installed onto all Linux build machines in bug 673753, so it should be valid to --enable-dtrace for both OSX and Linux. --HG-- extra : rebase_source : 480e81d27fe5c8fece172d52d16a37ae41e5993f --- config/rules.mk | 7 ++++--- js/src/config/rules.mk | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/config/rules.mk b/config/rules.mk index 548bc113d28c..4243d4fe77f5 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -1081,6 +1081,7 @@ endif ifdef DTRACE_PROBE_OBJ EXTRA_DEPS += $(DTRACE_PROBE_OBJ) +OBJS += $(DTRACE_PROBE_OBJ) endif $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) @@ -1121,8 +1122,8 @@ ifdef HAVE_DTRACE ifndef XP_MACOSX ifdef DTRACE_PROBE_OBJ ifndef DTRACE_LIB_DEPENDENT -$(DTRACE_PROBE_OBJ): $(OBJS) - dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS) +$(DTRACE_PROBE_OBJ): + dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) endif endif endif @@ -1144,7 +1145,7 @@ endif $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) @$(RM) $(DTRACE_PROBE_OBJ) else # ! DTRACE_LIB_DEPENDENT - $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) + $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) endif # DTRACE_LIB_DEPENDENT @$(call CHECK_STDCXX,$@) diff --git a/js/src/config/rules.mk b/js/src/config/rules.mk index 548bc113d28c..4243d4fe77f5 100644 --- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -1081,6 +1081,7 @@ endif ifdef DTRACE_PROBE_OBJ EXTRA_DEPS += $(DTRACE_PROBE_OBJ) +OBJS += $(DTRACE_PROBE_OBJ) endif $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(LOBJS) $(SHARED_LIBRARY_LIBS_DEPS) $(EXTRA_DEPS) $(GLOBAL_DEPS) @@ -1121,8 +1122,8 @@ ifdef HAVE_DTRACE ifndef XP_MACOSX ifdef DTRACE_PROBE_OBJ ifndef DTRACE_LIB_DEPENDENT -$(DTRACE_PROBE_OBJ): $(OBJS) - dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) $(OBJS) +$(DTRACE_PROBE_OBJ): + dtrace -G -C -s $(MOZILLA_DTRACE_SRC) -o $(DTRACE_PROBE_OBJ) endif endif endif @@ -1144,7 +1145,7 @@ endif $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(DTRACE_PROBE_OBJ) $(MOZILLA_PROBE_LIBS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) @$(RM) $(DTRACE_PROBE_OBJ) else # ! DTRACE_LIB_DEPENDENT - $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(DTRACE_PROBE_OBJ) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) + $(EXPAND_MKSHLIB) $(SHLIB_LDSTARTFILE) $(OBJS) $(LOBJS) $(SUB_SHLOBJS) $(RESFILE) $(LDFLAGS) $(SHARED_LIBRARY_LIBS) $(EXTRA_DSO_LDOPTS) $(OS_LIBS) $(EXTRA_LIBS) $(DEF_FILE) $(SHLIB_LDENDFILE) endif # DTRACE_LIB_DEPENDENT @$(call CHECK_STDCXX,$@) From 91538eb4e30df37fec6eb6a5f01aab9779fee3ab Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 12 Aug 2011 11:28:51 -0700 Subject: [PATCH 13/51] Bug 677993 followup: Use uintptr_t instead of size_t for casting unsigned values to/from void*, in JSObject::getArrayLength & setArrayLength. rs=waldo --- js/src/jsobjinlines.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index df09193e782e..bdf62e743624 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -343,14 +343,14 @@ inline uint32 JSObject::getArrayLength() const { JS_ASSERT(isArray()); - return (uint32)(size_t) getPrivate(); + return (uint32)(uintptr_t) getPrivate(); } inline void JSObject::setArrayLength(uint32 length) { JS_ASSERT(isArray()); - setPrivate((void*)(size_t)length); + setPrivate((void*)(uintptr_t) length); } inline uint32 From 33b813845a5b93f54287deb96156492a349fe96b Mon Sep 17 00:00:00 2001 From: jeremias bosch Date: Fri, 12 Aug 2011 11:30:21 -0700 Subject: [PATCH 14/51] Bug 677201 - Make use of Harmattan Backup System r=romaxa --- mobile/installer/Makefile.in | 18 ++++++++++++++++++ mobile/installer/debian/backup.in | 20 ++++++++++++++++++++ mobile/installer/debian/fennec-cud.sh.in | 3 +++ mobile/installer/debian/fennec-rfs.sh.in | 4 ++++ mobile/installer/debian/fennec.conf.in | 21 +++++++++++++++++++++ mobile/installer/debian/restore.in | 15 +++++++++++++++ 6 files changed, 81 insertions(+) create mode 100644 mobile/installer/debian/backup.in create mode 100644 mobile/installer/debian/fennec-cud.sh.in create mode 100644 mobile/installer/debian/fennec-rfs.sh.in create mode 100644 mobile/installer/debian/fennec.conf.in create mode 100644 mobile/installer/debian/restore.in diff --git a/mobile/installer/Makefile.in b/mobile/installer/Makefile.in index 8cb00d167286..52478d7da3fb 100644 --- a/mobile/installer/Makefile.in +++ b/mobile/installer/Makefile.in @@ -183,6 +183,14 @@ ifdef MOZ_ENABLE_CONTENTMANAGER PP_DEB_FILES += debian/fennec.aegis \ $(NULL) endif +ifeq ($(MOZ_PLATFORM_MAEMO),6) +PP_DEB_FILES += debian/backup \ + debian/restore \ + debian/fennec.conf \ + debian/fennec-cud.sh \ + debian/fennec-rfs.sh \ + $(NULL) +endif $(PP_DEB_FILES): @$(EXIT_ON_ERROR) \ @@ -207,6 +215,16 @@ ifeq ($(MOZ_PLATFORM_MAEMO),6) cp debian/$(MOZ_APP_NAME).service $(DEBDESTDIR)/usr/share/dbus-1/services/org.mozilla.$(MOZ_APP_NAME).service $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/themes/blanco/meegotouch/icons/ cp $(DIST)/branding/$(MOZ_APP_NAME)_scalable.png $(DEBDESTDIR)/usr/share/themes/blanco/meegotouch/icons/$(MOZ_APP_NAME).png + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/backup-framework/applications + $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME) + $(NSINSTALL) -D $(DEBDESTDIR)/etc/osso-cud-scripts + $(NSINSTALL) -D $(DEBDESTDIR)/etc/osso-rfs-scripts + $(NSINSTALL) -m 755 debian/backup $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME)/ + $(NSINSTALL) -m 755 debian/restore $(DEBDESTDIR)/usr/share/$(MOZ_APP_NAME)/ + cp debian/$(MOZ_APP_NAME).conf $(DEBDESTDIR)/usr/share/backup-framework/applications/$(MOZ_APP_NAME).conf + cp debian/$(MOZ_APP_NAME)-cud.sh $(DEBDESTDIR)/etc/osso-cud-scripts/$(MOZ_APP_NAME)-cud.sh + cp debian/$(MOZ_APP_NAME)-rfs.sh $(DEBDESTDIR)/etc/osso-rfs-scripts/$(MOZ_APP_NAME)-rfs.sh + else $(NSINSTALL) debian/$(MOZ_APP_NAME).desktop $(DEBDESTDIR)/usr/share/applications/hildon/ $(NSINSTALL) -D $(DEBDESTDIR)/usr/share/dbus-1/services/ diff --git a/mobile/installer/debian/backup.in b/mobile/installer/debian/backup.in new file mode 100644 index 000000000000..520820e14f2a --- /dev/null +++ b/mobile/installer/debian/backup.in @@ -0,0 +1,20 @@ +#filter substitution +#!/bin/sh + +# We do not care about parameters yet + +FENNEC_HOME=$HOME/.mozilla/@MOZ_APP_NAME@ +BACKUP=$HOME/.mozilla/backup + +rm -rf $BACKUP || exit 2 + +# do the backup only if there already is a profile +if [ -d $FENNEC_HOME ]; then + cp -a $FENNEC_HOME $BACKUP || exit 2 + find $BACKUP -name "*.dat" \ + -o -name "*.mfasl" \ + -o -name "Cache" \ + -o -name "OfflineCache" | xargs rm -rf || exit 2 +fi + +exit 0 diff --git a/mobile/installer/debian/fennec-cud.sh.in b/mobile/installer/debian/fennec-cud.sh.in new file mode 100644 index 000000000000..533d73bc7ef5 --- /dev/null +++ b/mobile/installer/debian/fennec-cud.sh.in @@ -0,0 +1,3 @@ +#filter substitution +#!/bin/sh +rm -rf $HOME/.mozilla/@MOZ_APP_NAME@ diff --git a/mobile/installer/debian/fennec-rfs.sh.in b/mobile/installer/debian/fennec-rfs.sh.in new file mode 100644 index 000000000000..4e732e6f3ea4 --- /dev/null +++ b/mobile/installer/debian/fennec-rfs.sh.in @@ -0,0 +1,4 @@ +#filter substitution +#!/bin/sh +rm -rf $HOME/.mozilla/@MOZ_APP_NAME@ +gconftool-2 --recursive-unset /apps/@MOZ_APP_NAME@ diff --git a/mobile/installer/debian/fennec.conf.in b/mobile/installer/debian/fennec.conf.in new file mode 100644 index 000000000000..a03dbc8ceaf3 --- /dev/null +++ b/mobile/installer/debian/fennec.conf.in @@ -0,0 +1,21 @@ +#filter substitution + + nokia + @MOZ_APP_NAME@ + backup-scripts + + + + /usr/share/@MOZ_APP_NAME@/backup + + + /usr/share/@MOZ_APP_NAME@/restore + + + + + + $HOME/.mozilla/backup + + + diff --git a/mobile/installer/debian/restore.in b/mobile/installer/debian/restore.in new file mode 100644 index 000000000000..baf0a03faa4c --- /dev/null +++ b/mobile/installer/debian/restore.in @@ -0,0 +1,15 @@ +#filter substitution +#!/bin/sh + +# We do not care about parameters yet + +FENNEC_HOME=$HOME/.mozilla/@MOZ_APP_NAME@ +BACKUP=$HOME/.mozilla/backup + +rm -rf $FENNEC_HOME || exit 2 + +# Fennec profile +cp -a $BACKUP $FENNEC_HOME || exit 2 + +exit 0 + From 4ca39c776babcbddb1a7e8f1bfa82240354aea46 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 12 Aug 2011 10:02:00 -0400 Subject: [PATCH 15/51] Bug 667438 - Choosing "Custom Page" option erases the text from the Start page button [r=mark.finkle] --- mobile/chrome/content/preferences.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mobile/chrome/content/preferences.js b/mobile/chrome/content/preferences.js index 342a688f7e57..d1b3ff5e3941 100644 --- a/mobile/chrome/content/preferences.js +++ b/mobile/chrome/content/preferences.js @@ -255,6 +255,11 @@ var PreferencesView = { display = Browser.selectedBrowser.contentTitle || currentURL; } break; + case "custom": + // If value is custom, this means the user is trying to + // set homepage to the same custom value. Do nothing in + // this case. + return; } // Show or hide the title or URL of the custom homepage From 6c668d27a828c9dd27de5126bdfb8f55f2ce2b7d Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 12 Aug 2011 13:43:21 -0500 Subject: [PATCH 16/51] Bug 672892 - CloneLeftHandSide should not also clone the right-hand side. Well sure it sounds obvious when you write it out like that. r=dvander. --HG-- extra : rebase_source : cfcaf1f4c681fc67fd70d20818bdccf5183cde94 --- js/src/jsparse.cpp | 14 ++++++++------ js/src/tests/js1_8_5/regress/jstests.list | 1 + js/src/tests/js1_8_5/regress/regress-672892.js | 8 ++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 js/src/tests/js1_8_5/regress/regress-672892.js diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 9c87e9e22082..d643caedbc25 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -4967,14 +4967,16 @@ CloneLeftHandSide(JSParseNode *opn, JSTreeContext *tc) pn->pn_link = dn->dn_uses; dn->dn_uses = pn; - } else if (opn->pn_defn) { - /* We copied some definition-specific state into pn. Clear it out. */ + } else { pn->pn_expr = NULL; - pn->pn_cookie.makeFree(); - pn->pn_dflags &= ~PND_BOUND; - pn->pn_defn = false; + if (opn->pn_defn) { + /* We copied some definition-specific state into pn. Clear it out. */ + pn->pn_cookie.makeFree(); + pn->pn_dflags &= ~PND_BOUND; + pn->pn_defn = false; - LinkUseToDef(pn, (JSDefinition *) opn, tc); + LinkUseToDef(pn, (JSDefinition *) opn, tc); + } } return pn; } diff --git a/js/src/tests/js1_8_5/regress/jstests.list b/js/src/tests/js1_8_5/regress/jstests.list index faee6d5b008d..3801a33e46bf 100644 --- a/js/src/tests/js1_8_5/regress/jstests.list +++ b/js/src/tests/js1_8_5/regress/jstests.list @@ -109,6 +109,7 @@ script regress-646820-3.js script regress-665355.js script regress-666599.js script regress-667047.js +script regress-672892.js script regress-673070-1.js script regress-673070-2.js script regress-673070-3.js diff --git a/js/src/tests/js1_8_5/regress/regress-672892.js b/js/src/tests/js1_8_5/regress/regress-672892.js new file mode 100644 index 000000000000..843af1d0efc7 --- /dev/null +++ b/js/src/tests/js1_8_5/regress/regress-672892.js @@ -0,0 +1,8 @@ +// Any copyright is dedicated to the Public Domain. +// http://creativecommons.org/licenses/publicdomain/ + +with (0) + for (var b = 0 in 0) // don't assert in parser + ; + +reportCompare(0, 0, 'ok'); From 56a37f401e6734e1dff95a8042bfb893e7809eeb Mon Sep 17 00:00:00 2001 From: arno renevier Date: Fri, 12 Aug 2011 15:12:45 -0400 Subject: [PATCH 17/51] Bug 338427 - Spellchecker should respect the langi attribute; r=ehsan --- .../base/content/test/test_contextmenu.html | 1 + content/base/public/nsIContent.h | 25 +++ editor/composer/src/nsEditorSpellCheck.cpp | 192 ++++++++++++------ editor/idl/nsIEditorSpellCheck.idl | 8 +- editor/idl/nsIHTMLEditor.idl | 9 +- editor/libeditor/base/nsEditor.cpp | 9 + editor/libeditor/base/nsEditor.h | 5 + .../libeditor/base/nsEditorEventListener.cpp | 2 +- editor/libeditor/html/nsHTMLEditor.h | 3 - .../libeditor/html/tests/test_bug484181.html | 18 +- .../txtsvc/public/nsIInlineSpellChecker.idl | 3 +- editor/txtsvc/public/nsISpellChecker.h | 3 +- .../spellcheck/src/mozInlineSpellChecker.cpp | 55 +++++ extensions/spellcheck/src/mozSpellChecker.cpp | 6 + layout/reftests/editor/338427-1-ref.html | 7 + layout/reftests/editor/338427-1.html | 6 + layout/reftests/editor/338427-2-ref.html | 18 ++ layout/reftests/editor/338427-2.html | 17 ++ layout/reftests/editor/338427-3-ref.html | 18 ++ layout/reftests/editor/338427-3.html | 18 ++ layout/reftests/editor/reftest.list | 3 + .../reftests/unicode/langattribute-ref.html | 26 +++ layout/reftests/unicode/langattribute.html | 26 +++ layout/reftests/unicode/reftest.list | 1 + layout/style/nsCSSRuleProcessor.cpp | 25 +-- toolkit/content/InlineSpellChecker.jsm | 13 +- 26 files changed, 411 insertions(+), 106 deletions(-) create mode 100644 layout/reftests/editor/338427-1-ref.html create mode 100644 layout/reftests/editor/338427-1.html create mode 100644 layout/reftests/editor/338427-2-ref.html create mode 100644 layout/reftests/editor/338427-2.html create mode 100644 layout/reftests/editor/338427-3-ref.html create mode 100644 layout/reftests/editor/338427-3.html create mode 100644 layout/reftests/unicode/langattribute-ref.html create mode 100644 layout/reftests/unicode/langattribute.html diff --git a/browser/base/content/test/test_contextmenu.html b/browser/base/content/test/test_contextmenu.html index a7df45d18058..4c3757c4ad6b 100644 --- a/browser/base/content/test/test_contextmenu.html +++ b/browser/base/content/test/test_contextmenu.html @@ -589,6 +589,7 @@ function startTest() { iframe = subwindow.document.getElementById("test-iframe"); textarea = subwindow.document.getElementById("test-textarea"); contenteditable = subwindow.document.getElementById("test-contenteditable"); + contenteditable.focus(); // content editable needs to be focused to enable spellcheck inputspell = subwindow.document.getElementById("test-input-spellcheck"); pagemenu = subwindow.document.getElementById("test-pagemenu"); diff --git a/content/base/public/nsIContent.h b/content/base/public/nsIContent.h index a10134ac5630..9af67aba2097 100644 --- a/content/base/public/nsIContent.h +++ b/content/base/public/nsIContent.h @@ -944,6 +944,31 @@ public: */ nsIContent* GetEditingHost(); + /** + * Determing language. Look at the nearest ancestor element that has a lang + * attribute in the XML namespace or is an HTML element and has a lang in + * no namespace attribute. + */ + void GetLang(nsAString& aResult) const { + for (const nsIContent* content = this; content; content = content->GetParent()) { + if (content->GetAttrCount() > 0) { + // xml:lang has precedence over lang on HTML elements (see + // XHTML1 section C.7). + PRBool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, + aResult); + if (!hasAttr && content->IsHTML()) { + hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, + aResult); + } + NS_ASSERTION(hasAttr || aResult.IsEmpty(), + "GetAttr that returns false should not make string non-empty"); + if (hasAttr) { + return; + } + } + } + } + // Overloaded from nsINode virtual already_AddRefed GetBaseURI() const; diff --git a/editor/composer/src/nsEditorSpellCheck.cpp b/editor/composer/src/nsEditorSpellCheck.cpp index 90a1b11f4ab0..9310f4b99132 100644 --- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -42,11 +42,15 @@ #include "nsEditorSpellCheck.h" +#include "nsStyleUtil.h" +#include "nsIContent.h" +#include "nsIDOMElement.h" #include "nsITextServicesDocument.h" #include "nsISpellChecker.h" #include "nsISelection.h" #include "nsIDOMRange.h" #include "nsIEditor.h" +#include "nsIHTMLEditor.h" #include "nsIComponentManager.h" #include "nsServiceManagerUtils.h" @@ -54,6 +58,7 @@ #include "nsString.h" #include "nsReadableUtils.h" #include "nsITextServicesFilter.h" +#include "nsUnicharUtils.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" @@ -183,61 +188,9 @@ nsEditorSpellCheck::InitSpellChecker(nsIEditor* aEditor, PRBool aEnableSelection rv = mSpellChecker->SetDocument(tsDoc, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); - // Tell the spellchecker what dictionary to use: - - nsAdoptingString dictName = - Preferences::GetLocalizedString("spellchecker.dictionary"); - - if (dictName.IsEmpty()) - { - // Prefs didn't give us a dictionary name, so just get the current - // locale and use that as the default dictionary name! - - nsCOMPtr packageRegistry = - mozilla::services::GetXULChromeRegistryService(); - - if (packageRegistry) { - nsCAutoString utf8DictName; - rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), - utf8DictName); - AppendUTF8toUTF16(utf8DictName, dictName); - } - } - - PRBool setDictionary = PR_FALSE; - if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) { - rv = SetCurrentDictionary(dictName.get()); - - // fall back to "en-US" if the current locale doesn't have a dictionary. - if (NS_FAILED(rv)) { - rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get()); - } - - if (NS_SUCCEEDED(rv)) - setDictionary = PR_TRUE; - } - - // If there was no dictionary specified by spellchecker.dictionary and setting it to the - // locale dictionary didn't work, try to use the first dictionary we find. This helps when - // the first dictionary is installed - if (! setDictionary) { - nsTArray dictList; - rv = mSpellChecker->GetDictionaryList(&dictList); - NS_ENSURE_SUCCESS(rv, rv); - if (dictList.Length() > 0) { - rv = SetCurrentDictionary(dictList[0].get()); - if (NS_SUCCEEDED(rv)) - SaveDefaultDictionary(); - } - } - - // If an error was thrown while checking the dictionary pref, just - // fail silently so that the spellchecker dialog is allowed to come - // up. The user can manually reset the language to their choice on - // the dialog if it is wrong. - - DeleteSuggestedWordList(); - + // do not fail if UpdateCurrentDictionary fails because this method may + // succeed later. + UpdateCurrentDictionary(aEditor); return NS_OK; } @@ -439,14 +392,6 @@ nsEditorSpellCheck::UninitSpellChecker() { NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); - // we preserve the last selected language, but ignore errors so we continue - // to uninitialize -#ifdef DEBUG - nsresult rv = -#endif - SaveDefaultDictionary(); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to set default dictionary"); - // Cleanup - kill the spell checker DeleteSuggestedWordList(); mDictionaryList.Clear(); @@ -489,3 +434,124 @@ nsEditorSpellCheck::DeleteSuggestedWordList() mSuggestedWordIndex = 0; return NS_OK; } + +NS_IMETHODIMP +nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor) +{ + nsresult rv; + + // Tell the spellchecker what dictionary to use: + nsAutoString dictName; + + // First, try to get language with html5 algorithm + nsAutoString editorLang; + + nsCOMPtr rootContent; + + nsCOMPtr htmlEditor = do_QueryInterface(aEditor); + if (htmlEditor) { + rootContent = htmlEditor->GetActiveEditingHost(); + } else { + nsCOMPtr rootElement; + rv = aEditor->GetRootElement(getter_AddRefs(rootElement)); + NS_ENSURE_SUCCESS(rv, rv); + rootContent = do_QueryInterface(rootElement); + } + NS_ENSURE_TRUE(rootContent, NS_ERROR_FAILURE); + + rootContent->GetLang(editorLang); + + if (editorLang.IsEmpty()) { + nsCOMPtr doc = rootContent->GetCurrentDoc(); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + doc->GetContentLanguage(editorLang); + } + + if (!editorLang.IsEmpty()) { + dictName.Assign(editorLang); + } + + // otherwise, get language from preferences + if (dictName.IsEmpty()) { + dictName.Assign(Preferences::GetLocalizedString("spellchecker.dictionary")); + } + + if (dictName.IsEmpty()) + { + // Prefs didn't give us a dictionary name, so just get the current + // locale and use that as the default dictionary name! + + nsCOMPtr packageRegistry = + mozilla::services::GetXULChromeRegistryService(); + + if (packageRegistry) { + nsCAutoString utf8DictName; + rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), + utf8DictName); + AppendUTF8toUTF16(utf8DictName, dictName); + } + } + + SetCurrentDictionary(NS_LITERAL_STRING("").get()); + + if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) { + rv = SetCurrentDictionary(dictName.get()); + if (NS_FAILED(rv)) { + // required dictionary was not available. Try to get a dictionary + // matching at least language part of dictName: If required dictionary is + // "aa-bb", we try "aa", then we try any available dictionary aa-XX + nsAutoString langCode; + PRInt32 dashIdx = dictName.FindChar('-'); + if (dashIdx != -1) { + langCode.Assign(Substring(dictName, 0, dashIdx)); + // try to use langCode + rv = SetCurrentDictionary(langCode.get()); + } else { + langCode.Assign(dictName); + } + if (NS_FAILED(rv)) { + // loop over avaible dictionaries; if we find one with required + // language, use it + nsTArray dictList; + rv = mSpellChecker->GetDictionaryList(&dictList); + NS_ENSURE_SUCCESS(rv, rv); + nsDefaultStringComparator comparator; + PRInt32 i, count = dictList.Length(); + for (i = 0; i < count; i++) { + nsAutoString dictStr(dictList.ElementAt(i)); + if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) && + NS_SUCCEEDED(SetCurrentDictionary(dictStr.get()))) { + break; + } + } + } + } + } + + // If we have not set dictionary, and the editable element doesn't have a + // lang attribute, we try to get a dictionary. First try, en-US. If it does + // not work, pick the first one. + if (editorLang.IsEmpty()) { + nsAutoString currentDictonary; + rv = mSpellChecker->GetCurrentDictionary(currentDictonary); + if (NS_FAILED(rv) || currentDictonary.IsEmpty()) { + rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get()); + if (NS_FAILED(rv)) { + nsTArray dictList; + rv = mSpellChecker->GetDictionaryList(&dictList); + if (NS_SUCCEEDED(rv) && dictList.Length() > 0) { + SetCurrentDictionary(dictList[0].get()); + } + } + } + } + + // If an error was thrown while setting the dictionary, just + // fail silently so that the spellchecker dialog is allowed to come + // up. The user can manually reset the language to their choice on + // the dialog if it is wrong. + + DeleteSuggestedWordList(); + + return NS_OK; +} diff --git a/editor/idl/nsIEditorSpellCheck.idl b/editor/idl/nsIEditorSpellCheck.idl index 02da2b10d29e..0370b1c3cb3c 100644 --- a/editor/idl/nsIEditorSpellCheck.idl +++ b/editor/idl/nsIEditorSpellCheck.idl @@ -41,7 +41,7 @@ interface nsIEditor; interface nsITextServicesFilter; -[scriptable, uuid(90c93610-c116-44ab-9793-62dccb9f43ce)] +[scriptable, uuid(803ff0dd-07f2-4438-b3a6-ab9c2fe4e1dd)] interface nsIEditorSpellCheck : nsISupports { @@ -188,4 +188,10 @@ interface nsIEditorSpellCheck : nsISupports */ boolean CheckCurrentWordNoSuggest(in wstring suggestedWord); + /** + * Update the dictionary in use to be sure it corresponds to what the editor + * needs. + */ + void UpdateCurrentDictionary(in nsIEditor editor); + }; diff --git a/editor/idl/nsIHTMLEditor.idl b/editor/idl/nsIHTMLEditor.idl index a9ba99250b2f..f43529dc3839 100644 --- a/editor/idl/nsIHTMLEditor.idl +++ b/editor/idl/nsIHTMLEditor.idl @@ -42,6 +42,7 @@ #include "domstubs.idl" interface nsIAtom; +interface nsIContent; interface nsISupportsArray; interface nsISelection; interface nsIContentFilter; @@ -51,7 +52,7 @@ interface nsIContentFilter; NS_ERROR_GENERATE_SUCCESS(NS_ERROR_MODULE_EDITOR, 1) %} -[scriptable, uuid(c964b8b0-e9e8-11df-9492-0800200c9a66)] +[scriptable, uuid(d58f35a7-c269-4292-b9aa-a79e200a7c99)] interface nsIHTMLEditor : nsISupports { @@ -613,5 +614,11 @@ interface nsIHTMLEditor : nsISupports * Checks whether a BR node is visible to the user. */ boolean breakIsVisible(in nsIDOMNode aNode); + + /** + * Get an active editor's editing host in DOM window. If this editor isn't + * active in the DOM window, this returns NULL. + */ + [noscript, notxpcom] nsIContent GetActiveEditingHost(); }; diff --git a/editor/libeditor/base/nsEditor.cpp b/editor/libeditor/base/nsEditor.cpp index a59214afc372..ef7c89f94f03 100644 --- a/editor/libeditor/base/nsEditor.cpp +++ b/editor/libeditor/base/nsEditor.cpp @@ -5311,3 +5311,12 @@ nsEditor::BeginKeypressHandling(nsIDOMNSEvent* aEvent) mLastKeypressEventWasTrusted = isTrusted ? eTriTrue : eTriFalse; } } + +void +nsEditor::OnFocus(nsIDOMEventTarget* aFocusEventTarget) +{ + InitializeSelection(aFocusEventTarget); + if (mInlineSpellChecker) { + mInlineSpellChecker->UpdateCurrentDictionary(); + } +} diff --git a/editor/libeditor/base/nsEditor.h b/editor/libeditor/base/nsEditor.h index cfcb88dfe2e4..1ef527abf67f 100644 --- a/editor/libeditor/base/nsEditor.h +++ b/editor/libeditor/base/nsEditor.h @@ -719,6 +719,11 @@ public: // nothing. nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget); + // This method has to be called by nsEditorEventListener::Focus. + // All actions that have to be done when the editor is focused needs to be + // added here. + void OnFocus(nsIDOMEventTarget* aFocusEventTarget); + protected: PRUint32 mModCount; // number of modifications (for undo/redo stack) diff --git a/editor/libeditor/base/nsEditorEventListener.cpp b/editor/libeditor/base/nsEditorEventListener.cpp index d9dfc2462381..b17d3145c1b2 100644 --- a/editor/libeditor/base/nsEditorEventListener.cpp +++ b/editor/libeditor/base/nsEditorEventListener.cpp @@ -818,7 +818,7 @@ nsEditorEventListener::Focus(nsIDOMEvent* aEvent) } } - mEditor->InitializeSelection(target); + mEditor->OnFocus(target); return NS_OK; } diff --git a/editor/libeditor/html/nsHTMLEditor.h b/editor/libeditor/html/nsHTMLEditor.h index efd7a641798d..d209bc1b5dcb 100644 --- a/editor/libeditor/html/nsHTMLEditor.h +++ b/editor/libeditor/html/nsHTMLEditor.h @@ -458,9 +458,6 @@ protected: // @return If the editor has focus, this returns the focused node. // Otherwise, returns null. already_AddRefed GetFocusedNode(); - // Get an active editor's editing host in DOM window. If this editor isn't - // active in the DOM window, this returns NULL. - nsIContent* GetActiveEditingHost(); // Return TRUE if aElement is a table-related elemet and caret was set PRBool SetCaretInTableCell(nsIDOMElement* aElement); diff --git a/editor/libeditor/html/tests/test_bug484181.html b/editor/libeditor/html/tests/test_bug484181.html index b5d8651c9368..62b40cc588a2 100644 --- a/editor/libeditor/html/tests/test_bug484181.html +++ b/editor/libeditor/html/tests/test_bug484181.html @@ -46,7 +46,6 @@ function append(str) { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); var edit = document.getElementById("edit"); - edit.focus(); var editor = getEditor(); var sel = editor.selection; sel.selectAllChildren(edit); @@ -59,16 +58,19 @@ function append(str) { function runTest() { gMisspeltWords = ["haz", "cheezburger"]; - is(isSpellingCheckOk(), true, "All misspellings before editing are accounted for."); - var edit = document.getElementById("edit"); - append(" becaz I'm a lolcat!"); + edit.focus(); SimpleTest.executeSoon(function() { - gMisspeltWords.push("becaz"); - gMisspeltWords.push("lolcat"); - is(isSpellingCheckOk(), true, "All misspellings after typing are accounted for."); + is(isSpellingCheckOk(), true, "All misspellings before editing are accounted for."); - SimpleTest.finish(); + append(" becaz I'm a lolcat!"); + SimpleTest.executeSoon(function() { + gMisspeltWords.push("becaz"); + gMisspeltWords.push("lolcat"); + is(isSpellingCheckOk(), true, "All misspellings after typing are accounted for."); + + SimpleTest.finish(); + }); }); } diff --git a/editor/txtsvc/public/nsIInlineSpellChecker.idl b/editor/txtsvc/public/nsIInlineSpellChecker.idl index 32735e8290b3..b34f6f57d54c 100644 --- a/editor/txtsvc/public/nsIInlineSpellChecker.idl +++ b/editor/txtsvc/public/nsIInlineSpellChecker.idl @@ -43,7 +43,7 @@ interface nsISelection; interface nsIEditor; interface nsIEditorSpellCheck; -[scriptable, uuid(07be036a-2355-4a92-b150-5c9b7e9fdf2f)] +[scriptable, uuid(f456dda1-965d-470c-8c55-e51b38e45212)] interface nsIInlineSpellChecker : nsISupports { @@ -71,6 +71,7 @@ interface nsIInlineSpellChecker : nsISupports void ignoreWord(in AString aWord); void ignoreWords([array, size_is(aCount)] in wstring aWordsToIgnore, in unsigned long aCount); + void updateCurrentDictionary(); }; %{C++ diff --git a/editor/txtsvc/public/nsISpellChecker.h b/editor/txtsvc/public/nsISpellChecker.h index 66765e9f26be..d1644df77fea 100644 --- a/editor/txtsvc/public/nsISpellChecker.h +++ b/editor/txtsvc/public/nsISpellChecker.h @@ -142,7 +142,8 @@ public: /** * Tells the spellchecker to use a specific dictionary. * @param aDictionary a string that is in the list returned - * by GetDictionaryList(). + * by GetDictionaryList() or an empty string. If aDictionary is + * empty string, spellchecker will be disabled. */ NS_IMETHOD SetCurrentDictionary(const nsAString &aDictionary) = 0; }; diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 1aa11eecd689..21c9340b0aca 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -1384,6 +1384,9 @@ nsresult mozInlineSpellChecker::DoSpellCheck(mozInlineSpellWordUtil& aWordUtil, PRBool isMisspelled; aWordUtil.NormalizeWord(wordText); rv = mSpellCheck->CheckCurrentWordNoSuggest(wordText.get(), &isMisspelled); + if (NS_FAILED(rv)) + continue; + if (isMisspelled) { // misspelled words count extra toward the max wordsSinceTimeCheck += MISSPELLED_WORD_COUNT_PENALTY; @@ -1441,6 +1444,23 @@ mozInlineSpellChecker::ResumeCheck(mozInlineSpellStatus* aStatus) nsCOMPtr spellCheckSelection; rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection)); NS_ENSURE_SUCCESS(rv, rv); + + PRUnichar *currentDictionary = nsnull; + rv = mSpellCheck->GetCurrentDictionary(¤tDictionary); + if (NS_FAILED(rv)) { + // no active dictionary + PRInt32 count; + spellCheckSelection->GetRangeCount(&count); + for (PRInt32 index = count - 1; index >= 0; index--) { + nsCOMPtr checkRange; + spellCheckSelection->GetRangeAt(index, getter_AddRefs(checkRange)); + if (checkRange) { + RemoveRange(spellCheckSelection, checkRange); + } + } + return NS_OK; + } + CleanupRangesInSelection(spellCheckSelection); rv = aStatus->FinishInitOnEvent(wordUtil); @@ -1733,3 +1753,38 @@ nsresult mozInlineSpellChecker::KeyPress(nsIDOMEvent* aKeyEvent) return NS_OK; } + +NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary() +{ + if (!mSpellCheck) { + return NS_OK; + } + + PRUnichar *previousDictionary = nsnull; + nsDependentString previousDictionaryStr; + if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(&previousDictionary))) { + previousDictionaryStr.Assign(previousDictionary); + } + + nsCOMPtr editor (do_QueryReferent(mEditor)); + nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor); + + PRUnichar *currentDictionary = nsnull; + nsDependentString currentDictionaryStr; + if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(¤tDictionary))) { + currentDictionaryStr.Assign(currentDictionary); + } + + if (!previousDictionary || !currentDictionary || !previousDictionaryStr.Equals(currentDictionaryStr)) { + rv = SpellCheckRange(nsnull); + } + + if (previousDictionary) { + nsMemory::Free(previousDictionary); + } + if (currentDictionary) { + nsMemory::Free(currentDictionary); + } + + return rv; +} diff --git a/extensions/spellcheck/src/mozSpellChecker.cpp b/extensions/spellcheck/src/mozSpellChecker.cpp index ee79054c5956..3ada4c029b57 100644 --- a/extensions/spellcheck/src/mozSpellChecker.cpp +++ b/extensions/spellcheck/src/mozSpellChecker.cpp @@ -369,6 +369,12 @@ mozSpellChecker::SetCurrentDictionary(const nsAString &aDictionary) nsresult rv; nsCString *contractId; + if (aDictionary.IsEmpty()) { + mCurrentEngineContractId = nsnull; + mSpellCheckingEngine = nsnull; + return NS_OK; + } + if (!mDictionariesMap.Get(aDictionary, &contractId)){ NS_WARNING("Dictionary not found"); return NS_ERROR_NOT_AVAILABLE; diff --git a/layout/reftests/editor/338427-1-ref.html b/layout/reftests/editor/338427-1-ref.html new file mode 100644 index 000000000000..d645ad9fb18d --- /dev/null +++ b/layout/reftests/editor/338427-1-ref.html @@ -0,0 +1,7 @@ + + + + + + + diff --git a/layout/reftests/editor/338427-1.html b/layout/reftests/editor/338427-1.html new file mode 100644 index 000000000000..17b6c391d694 --- /dev/null +++ b/layout/reftests/editor/338427-1.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/reftests/editor/338427-2-ref.html b/layout/reftests/editor/338427-2-ref.html new file mode 100644 index 000000000000..e13b9114c3ce --- /dev/null +++ b/layout/reftests/editor/338427-2-ref.html @@ -0,0 +1,18 @@ + + + + +
strangeimpossibleword
+ + + diff --git a/layout/reftests/editor/338427-2.html b/layout/reftests/editor/338427-2.html new file mode 100644 index 000000000000..af5d3506e236 --- /dev/null +++ b/layout/reftests/editor/338427-2.html @@ -0,0 +1,17 @@ + + + + +
strangeimpossibleword
+ + diff --git a/layout/reftests/editor/338427-3-ref.html b/layout/reftests/editor/338427-3-ref.html new file mode 100644 index 000000000000..e8949f3851df --- /dev/null +++ b/layout/reftests/editor/338427-3-ref.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/layout/reftests/editor/338427-3.html b/layout/reftests/editor/338427-3.html new file mode 100644 index 000000000000..7066ba662f34 --- /dev/null +++ b/layout/reftests/editor/338427-3.html @@ -0,0 +1,18 @@ + + + + + + + diff --git a/layout/reftests/editor/reftest.list b/layout/reftests/editor/reftest.list index d2bc3698710b..9e37ed5d507b 100644 --- a/layout/reftests/editor/reftest.list +++ b/layout/reftests/editor/reftest.list @@ -64,3 +64,6 @@ fails-if(Android) != spellcheck-hyphen-multiple-invalid.html spellcheck-hyphen-m != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html == 672709.html 672709-ref.html +== 338427-1.html 338427-1-ref.html +skip-if(Android) == 338427-2.html 338427-2-ref.html +skip-if(Android) == 338427-3.html 338427-3-ref.html diff --git a/layout/reftests/unicode/langattribute-ref.html b/layout/reftests/unicode/langattribute-ref.html new file mode 100644 index 000000000000..d3908825a681 --- /dev/null +++ b/layout/reftests/unicode/langattribute-ref.html @@ -0,0 +1,26 @@ + + + + testing lang attribute + + + +
fr language
+
+ +
+
+ dk language +
+ + diff --git a/layout/reftests/unicode/langattribute.html b/layout/reftests/unicode/langattribute.html new file mode 100644 index 000000000000..9630eb86ade2 --- /dev/null +++ b/layout/reftests/unicode/langattribute.html @@ -0,0 +1,26 @@ + + + + testing lang attribute + + + +
fr language
+
+ +
+
+ dk language +
+ + diff --git a/layout/reftests/unicode/reftest.list b/layout/reftests/unicode/reftest.list index c3b950dd111f..45a98b1c85d3 100644 --- a/layout/reftests/unicode/reftest.list +++ b/layout/reftests/unicode/reftest.list @@ -4,3 +4,4 @@ == unicode-media-query-media-type.html unicode-ref-print.html == unicode-media-query-query.html unicode-ref-print.html == unicode-pseudo-selector.html unicode-ref.html +== langattribute.html langattribute-ref.html diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index af501b3306ce..b808d9751e33 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -1195,29 +1195,6 @@ nsCSSRuleProcessor::GetWindowsThemeIdentifier() } #endif -// If we have a useful @lang, then aLang will end up nonempty. -static void GetLang(nsIContent* aContent, nsString& aLang) -{ - for (nsIContent* content = aContent; content; - content = content->GetParent()) { - if (content->GetAttrCount() > 0) { - // xml:lang has precedence over lang on HTML elements (see - // XHTML1 section C.7). - PRBool hasAttr = content->GetAttr(kNameSpaceID_XML, nsGkAtoms::lang, - aLang); - if (!hasAttr && content->IsHTML()) { - hasAttr = content->GetAttr(kNameSpaceID_None, nsGkAtoms::lang, - aLang); - } - NS_ASSERTION(hasAttr || aLang.IsEmpty(), - "GetAttr that returns false should not make string non-empty"); - if (hasAttr) { - return; - } - } - } -} - /* static */ nsEventStates nsCSSRuleProcessor::GetContentState(Element* aElement) @@ -1697,7 +1674,7 @@ static PRBool SelectorMatches(Element* aElement, // from the parent we have to be prepared to look at all parent // nodes. The language itself is encoded in the LANG attribute. nsAutoString language; - GetLang(aElement, language); + aElement->GetLang(language); if (!language.IsEmpty()) { if (!nsStyleUtil::DashMatchCompare(language, nsDependentString(pseudoClass->u.mString), diff --git a/toolkit/content/InlineSpellChecker.jsm b/toolkit/content/InlineSpellChecker.jsm index e4b0939e5889..010fbf400dbf 100644 --- a/toolkit/content/InlineSpellChecker.jsm +++ b/toolkit/content/InlineSpellChecker.jsm @@ -131,8 +131,12 @@ InlineSpellChecker.prototype = { return 0; // nothing to do var spellchecker = this.mInlineSpellChecker.spellChecker; - if (! spellchecker.CheckCurrentWord(this.mMisspelling)) - return 0; // word seems not misspelled after all (?) + try { + if (! spellchecker.CheckCurrentWord(this.mMisspelling)) + return 0; // word seems not misspelled after all (?) + } catch(e) { + return 0; + } this.mMenu = menu; this.mSpellSuggestions = []; @@ -192,7 +196,10 @@ InlineSpellChecker.prototype = { spellchecker.GetDictionaryList(o1, o2); var list = o1.value; var listcount = o2.value; - var curlang = spellchecker.GetCurrentDictionary(); + var curlang = ""; + try { + curlang = spellchecker.GetCurrentDictionary(); + } catch(e) {} var isoStrArray; for (var i = 0; i < list.length; i ++) { From 6a432acd320149bd7d79276ee009df0010d1fe37 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 10 Aug 2011 14:54:36 -0700 Subject: [PATCH 18/51] Bug 677820 - String.prototype.match should define properties for matches on the returned array, not set them. r=pbiggar --HG-- extra : rebase_source : 9001b1f918f403c50fa4bba57f54cbb22e887a70 --- js/src/jsstr.cpp | 7 +-- js/src/tests/ecma_5/String/jstests.list | 1 + .../String/match-defines-match-elements.js | 47 +++++++++++++++++++ 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 js/src/tests/ecma_5/String/match-defines-match-elements.js diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index f70bdae5a6a5..5ac46c7e84c6 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1535,11 +1535,8 @@ MatchCallback(JSContext *cx, RegExpStatics *res, size_t count, void *p) } Value v; - if (!res->createLastMatch(cx, &v)) - return false; - - JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING); - return !!arrayobj->setProperty(cx, INT_TO_JSID(count), &v, false); + return res->createLastMatch(cx, &v) && + arrayobj->defineProperty(cx, INT_TO_JSID(count), v); } static JSBool diff --git a/js/src/tests/ecma_5/String/jstests.list b/js/src/tests/ecma_5/String/jstests.list index eb469fbec3e2..96dc32650e1c 100644 --- a/js/src/tests/ecma_5/String/jstests.list +++ b/js/src/tests/ecma_5/String/jstests.list @@ -3,6 +3,7 @@ script 15.5.4.2.js script 15.5.4.7.js script 15.5.4.11-01.js script defaultvalue.js +script match-defines-match-elements.js script split-01.js script split-undefined-separator.js script split-xregexp.js diff --git a/js/src/tests/ecma_5/String/match-defines-match-elements.js b/js/src/tests/ecma_5/String/match-defines-match-elements.js new file mode 100644 index 000000000000..e1a38e30c8f6 --- /dev/null +++ b/js/src/tests/ecma_5/String/match-defines-match-elements.js @@ -0,0 +1,47 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var BUGNUMBER = 677820; +var summary = + "String.prototype.match must define matches on the returned array, not set " + + "them"; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +var called = false; +function setterFunction(v) { called = true; } +function getterFunction(v) { return "getter"; } + +Object.defineProperty(Array.prototype, 1, + { get: getterFunction, set: setterFunction }); + +assertEq(called, false); +var matches = "abcdef".match(/./g); +assertEq(called, false); +assertEq(matches.length, 6); +assertEq(matches[0], "a"); +assertEq(matches[1], "b"); +assertEq(matches[2], "c"); +assertEq(matches[3], "d"); +assertEq(matches[4], "e"); +assertEq(matches[5], "f"); + +var desc = Object.getOwnPropertyDescriptor(Array.prototype, 1); +assertEq(desc.get, getterFunction); +assertEq(desc.set, setterFunction); +assertEq(desc.enumerable, false); +assertEq(desc.configurable, false); +assertEq([][1], "getter"); + +assertEq(called, false); + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); From f6f2db44cb112466b0592b6136273b331dbb3924 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 10 Aug 2011 14:54:47 -0700 Subject: [PATCH 19/51] Bug 678074 - Implement js::PropertyName, a subclass of JSAtom which represents a string which does not contain an unsigned 32-bit index. This type will eventually be used internally (and possibly at some point externally) for all guaranteed-non-element property accesses. r=luke --HG-- extra : rebase_source : b5b7b975b7c5e3caa312784a0edc49c112d2aa09 --- js/src/jsapi-tests/Makefile.in | 6 +- js/src/jsapi-tests/testIndexToString.cpp | 113 ++++++++++++++++------- js/src/jsatom.h | 14 --- js/src/jsatominlines.h | 4 +- js/src/jspubtd.h | 1 + js/src/jsvalue.h | 5 + js/src/vm/String-inl.h | 15 +++ js/src/vm/String.cpp | 52 +++++++++++ js/src/vm/String.h | 68 +++++++++++++- 9 files changed, 225 insertions(+), 53 deletions(-) diff --git a/js/src/jsapi-tests/Makefile.in b/js/src/jsapi-tests/Makefile.in index d3898b4e0211..734cdb71cf54 100644 --- a/js/src/jsapi-tests/Makefile.in +++ b/js/src/jsapi-tests/Makefile.in @@ -51,15 +51,18 @@ CPPSRCS = \ selfTest.cpp \ testArgumentsObject.cpp \ testBug604087.cpp \ + testChromeBuffer.cpp \ testClassGetter.cpp \ testCloneScript.cpp \ testConservativeGC.cpp \ testContexts.cpp \ + testCustomIterator.cpp \ testDebugger.cpp \ testDeepFreeze.cpp \ testDefineGetterSetterNonEnumerable.cpp \ testDefineProperty.cpp \ testExtendedEq.cpp \ + testExternalStrings.cpp \ testFuncCallback.cpp \ testFunctionProperties.cpp \ testGCChunkAlloc.cpp \ @@ -84,9 +87,6 @@ CPPSRCS = \ testUTF8.cpp \ testVersion.cpp \ testXDR.cpp \ - testCustomIterator.cpp \ - testExternalStrings.cpp \ - testChromeBuffer.cpp \ $(NULL) # Disabled: an entirely unrelated test seems to cause this to fail. Moreover, diff --git a/js/src/jsapi-tests/testIndexToString.cpp b/js/src/jsapi-tests/testIndexToString.cpp index 20701d51df52..c1f5c3c4b198 100644 --- a/js/src/jsapi-tests/testIndexToString.cpp +++ b/js/src/jsapi-tests/testIndexToString.cpp @@ -7,43 +7,50 @@ #include "jscntxt.h" #include "jscompartment.h" #include "jsnum.h" +#include "jsstr.h" #include "vm/String-inl.h" +template JSFlatString * +NewString(JSContext *cx, const jschar (&chars)[N]) +{ + return js_NewStringCopyN(cx, chars, N); +} + +static const struct TestPair { + uint32 num; + const char *expected; +} tests[] = { + { 0, "0" }, + { 1, "1" }, + { 2, "2" }, + { 9, "9" }, + { 10, "10" }, + { 15, "15" }, + { 16, "16" }, + { 17, "17" }, + { 99, "99" }, + { 100, "100" }, + { 255, "255" }, + { 256, "256" }, + { 257, "257" }, + { 999, "999" }, + { 1000, "1000" }, + { 4095, "4095" }, + { 4096, "4096" }, + { 9999, "9999" }, + { 1073741823, "1073741823" }, + { 1073741824, "1073741824" }, + { 1073741825, "1073741825" }, + { 2147483647, "2147483647" }, + { 2147483648, "2147483648" }, + { 2147483649, "2147483649" }, + { 4294967294, "4294967294" }, + { 4294967295, "4294967295" }, +}; + BEGIN_TEST(testIndexToString) { - const struct TestPair { - uint32 num; - const char *expected; - } tests[] = { - { 0, "0" }, - { 1, "1" }, - { 2, "2" }, - { 9, "9" }, - { 10, "10" }, - { 15, "15" }, - { 16, "16" }, - { 17, "17" }, - { 99, "99" }, - { 100, "100" }, - { 255, "255" }, - { 256, "256" }, - { 257, "257" }, - { 999, "999" }, - { 1000, "1000" }, - { 4095, "4095" }, - { 4096, "4096" }, - { 9999, "9999" }, - { 1073741823, "1073741823" }, - { 1073741824, "1073741824" }, - { 1073741825, "1073741825" }, - { 2147483647, "2147483647" }, - { 2147483648, "2147483648" }, - { 2147483649, "2147483649" }, - { 4294967294, "4294967294" }, - { 4294967295, "4294967295" }, - }; - for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) { uint32 u = tests[i].num; JSString *str = js::IndexToString(cx, u); @@ -60,3 +67,45 @@ BEGIN_TEST(testIndexToString) return true; } END_TEST(testIndexToString) + +BEGIN_TEST(testStringIsElement) +{ + for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) { + uint32 u = tests[i].num; + JSFlatString *str = js::IndexToString(cx, u); + CHECK(str); + + uint32 n; + CHECK(str->isElement(&n)); + CHECK(u == n); + } + + return true; +} +END_TEST(testStringIsElement) + +BEGIN_TEST(testStringToPropertyName) +{ + uint32 index; + + static const jschar hiChars[] = { 'h', 'i' }; + JSFlatString *hiStr = NewString(cx, hiChars); + CHECK(hiStr); + CHECK(!hiStr->isElement(&index)); + CHECK(hiStr->toPropertyName(cx) != NULL); + + static const jschar maxChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '5' }; + JSFlatString *maxStr = NewString(cx, maxChars); + CHECK(maxStr); + CHECK(maxStr->isElement(&index)); + CHECK(index == UINT32_MAX); + + static const jschar maxPlusOneChars[] = { '4', '2', '9', '4', '9', '6', '7', '2', '9', '6' }; + JSFlatString *maxPlusOneStr = NewString(cx, maxPlusOneChars); + CHECK(maxPlusOneStr); + CHECK(!maxPlusOneStr->isElement(&index)); + CHECK(maxPlusOneStr->toPropertyName(cx) != NULL); + + return true; +} +END_TEST(testStringToPropertyName) diff --git a/js/src/jsatom.h b/js/src/jsatom.h index 011f6af59923..fb41c14ab6a1 100644 --- a/js/src/jsatom.h +++ b/js/src/jsatom.h @@ -169,13 +169,6 @@ struct JSAtomMap { namespace js { -/* N.B. must correspond to boolean tagging behavior. */ -enum InternBehavior -{ - DoNotInternAtom = false, - InternAtom = true -}; - typedef TaggedPointerEntry AtomStateEntry; struct AtomHasher @@ -504,13 +497,6 @@ js_InitCommonAtoms(JSContext *cx); extern void js_FinishCommonAtoms(JSContext *cx); -/* - * Find or create the atom for a string. Return null on failure to allocate - * memory. - */ -extern JSAtom * -js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom); - extern JSAtom * js_Atomize(JSContext *cx, const char *bytes, size_t length, js::InternBehavior ib = js::DoNotInternAtom, diff --git a/js/src/jsatominlines.h b/js/src/jsatominlines.h index e78b63de5c9a..26163c8e7a4a 100644 --- a/js/src/jsatominlines.h +++ b/js/src/jsatominlines.h @@ -135,12 +135,10 @@ js_Int32ToId(JSContext* cx, int32 index, jsid* id) namespace js { -static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1; - /* * Write out character representing |index| to the memory just before |end|. * Thus |*end| is not touched, but |end[-1]| and earlier are modified as - * appropriate. There must be at least UINT32_CHAR_BUFFER_LENGTH elements + * appropriate. There must be at least js::UINT32_CHAR_BUFFER_LENGTH elements * before |end| to avoid buffer underflow. The start of the characters written * is returned and is necessarily before |end|. */ diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index 1b09eb0e4f2e..72dcb3455c90 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -166,6 +166,7 @@ typedef struct JSCrossCompartmentCall JSCrossCompartmentCall; typedef struct JSStructuredCloneWriter JSStructuredCloneWriter; typedef struct JSStructuredCloneReader JSStructuredCloneReader; typedef struct JSStructuredCloneCallbacks JSStructuredCloneCallbacks; +typedef struct JSPropertyName JSPropertyName; #ifdef __cplusplus typedef class JSWrapper JSWrapper; diff --git a/js/src/jsvalue.h b/js/src/jsvalue.h index 6ea99461bd89..d82c8ec80806 100644 --- a/js/src/jsvalue.h +++ b/js/src/jsvalue.h @@ -956,6 +956,8 @@ typedef void class AutoIdVector; +class PropertyName; + /* * Prepare to make |obj| non-extensible; in particular, fully resolve its properties. * On error, return false. @@ -983,6 +985,9 @@ static inline JSCheckAccessOp Jsvalify(CheckAccessOp f) { return (JSChec static inline EqualityOp Valueify(JSEqualityOp f); /* Same type as JSHasInstanceOp */ static inline JSEqualityOp Jsvalify(EqualityOp f); /* Same type as HasInstanceOp */ +static inline PropertyName *Valueify(JSPropertyName *n) { return (PropertyName *)n; } +static inline JSPropertyName *Jsvalify(PropertyName *n) { return (JSPropertyName *)n; } + static const PropertyOp PropertyStub = (PropertyOp)JS_PropertyStub; static const StrictPropertyOp StrictPropertyStub = (StrictPropertyOp)JS_StrictPropertyStub; static const JSEnumerateOp EnumerateStub = JS_EnumerateStub; diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 434b92b2a3b3..117b874d6b34 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -89,6 +89,21 @@ JSDependentString::new_(JSContext *cx, JSLinearString *base, const jschar *chars return str; } +inline js::PropertyName * +JSFlatString::toPropertyName(JSContext *cx) +{ +#ifdef DEBUG + uint32 dummy; + JS_ASSERT(!isElement(&dummy)); +#endif + if (isAtom()) + return asAtom().asPropertyName(); + JSAtom *atom = js_AtomizeString(cx, this); + if (!atom) + return NULL; + return atom->asPropertyName(); +} + JS_ALWAYS_INLINE void JSFixedString::init(const jschar *chars, size_t length) { diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index dc34c5060e0f..0974c65e64b6 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -38,9 +38,12 @@ * * ***** END LICENSE BLOCK ***** */ +#include "mozilla/RangedPtr.h" + #include "String.h" #include "String-inl.h" +using namespace mozilla; using namespace js; bool @@ -317,3 +320,52 @@ JSDependentString::undepend(JSContext *cx) JSStringFinalizeOp JSExternalString::str_finalizers[JSExternalString::TYPE_LIMIT] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + +bool +JSFlatString::isElement(uint32 *indexp) const +{ + const jschar *s = charsZ(); + jschar ch = *s; + + if (!JS7_ISDEC(ch)) + return false; + + size_t n = length(); + if (n > UINT32_CHAR_BUFFER_LENGTH) + return false; + + /* + * Make sure to account for the '\0' at the end of characters, dereferenced + * in the loop below. + */ + RangedPtr cp(s, n + 1); + const RangedPtr end(s + n, s, n + 1); + + uint32 index = JS7_UNDEC(*cp++); + uint32 oldIndex = 0; + uint32 c = 0; + + if (index != 0) { + while (JS7_ISDEC(*cp)) { + oldIndex = index; + c = JS7_UNDEC(*cp); + index = 10 * index + c; + cp++; + } + } + + /* It's not an element if there are characters after the number. */ + if (cp != end) + return false; + + /* + * Look out for "4294967296" and larger-number strings that fit in + * UINT32_CHAR_BUFFER_LENGTH: only unsigned 32-bit integers shall pass. + */ + if (oldIndex < UINT32_MAX / 10 || (oldIndex == UINT32_MAX / 10 && c <= (UINT32_MAX % 10))) { + *indexp = index; + return true; + } + + return false; +} diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 2bbffebe908e..30b9c1e89b7d 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -43,6 +43,7 @@ #include "jscell.h" +class JSString; class JSDependentString; class JSExtensibleString; class JSExternalString; @@ -52,6 +53,29 @@ class JSStaticAtom; class JSRope; class JSAtom; +namespace js { + +class PropertyName; + +/* The buffer length required to contain any unsigned 32-bit integer. */ +static const size_t UINT32_CHAR_BUFFER_LENGTH = sizeof("4294967295") - 1; + +/* N.B. must correspond to boolean tagging behavior. */ +enum InternBehavior +{ + DoNotInternAtom = false, + InternAtom = true +}; + +} /* namespace js */ + +/* + * Find or create the atom for a string. Return null on failure to allocate + * memory. + */ +extern JSAtom * +js_AtomizeString(JSContext *cx, JSString *str, js::InternBehavior ib = js::DoNotInternAtom); + /* * JavaScript strings * @@ -474,6 +498,21 @@ class JSFlatString : public JSLinearString return chars(); } + /* + * Returns true if this string's characters store an unsigned 32-bit + * integer value, initializing *indexp to that value if so. (Thus if + * calling isElement returns true, js::IndexToString(cx, *indexp) will be a + * string equal to this string.) + */ + bool isElement(uint32 *indexp) const; + + /* + * Returns a property name represented by this string, or null on failure. + * You must verify that this is not an element per isElement before calling + * this method. + */ + inline js::PropertyName *toPropertyName(JSContext *cx); + /* Only called by the GC for strings with the FINALIZE_STRING kind. */ inline void finalize(JSRuntime *rt); @@ -678,6 +717,9 @@ class JSAtom : public JSFixedString /* Return null if no static atom exists for the given (chars, length). */ static inline JSStaticAtom *lookupStatic(const jschar *chars, size_t length); + /* Returns the PropertyName for this. isElement() must be false. */ + inline js::PropertyName *asPropertyName(); + inline void finalize(JSRuntime *rt); }; @@ -708,7 +750,21 @@ class JSStaticAtom : public JSAtom JS_STATIC_ASSERT(sizeof(JSStaticAtom) == sizeof(JSString)); -/* Avoid requring vm/String-inl.h just to call getChars. */ +namespace js { + +/* + * Represents an atomized string which does not contain an unsigned 32-bit + * value. That is, it is never the case that for a PropertyName propname, + * ToString(ToUint32(propname)) is equal to propname. + */ +class PropertyName : public JSAtom +{}; + +JS_STATIC_ASSERT(sizeof(PropertyName) == sizeof(JSString)); + +} /* namespace js */ + +/* Avoid requiring vm/String-inl.h just to call getChars. */ JS_ALWAYS_INLINE const jschar * JSString::getChars(JSContext *cx) @@ -758,4 +814,14 @@ JSString::ensureFixed(JSContext *cx) return &asFixed(); } +inline js::PropertyName * +JSAtom::asPropertyName() +{ +#ifdef DEBUG + uint32 dummy; + JS_ASSERT(!isElement(&dummy)); +#endif + return static_cast(this); +} + #endif From f14955fe449845eb9a628e564deaa61324d78a4b Mon Sep 17 00:00:00 2001 From: Graeme McCutcheon Date: Fri, 12 Aug 2011 15:53:10 -0400 Subject: [PATCH 20/51] Bug 483651 - Trailing
node not removed when it should be; r=ehsan --- editor/libeditor/text/nsTextEditRules.cpp | 67 +++++++++++++- editor/libeditor/text/nsTextEditRules.h | 3 + editor/libeditor/text/tests/Makefile.in | 1 + .../libeditor/text/tests/test_bug483651.html | 92 +++++++++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 editor/libeditor/text/tests/test_bug483651.html diff --git a/editor/libeditor/text/nsTextEditRules.cpp b/editor/libeditor/text/nsTextEditRules.cpp index d1ad81ce1f79..b317d8b278fd 100644 --- a/editor/libeditor/text/nsTextEditRules.cpp +++ b/editor/libeditor/text/nsTextEditRules.cpp @@ -221,11 +221,16 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsnull, 0, nsnull, 0); NS_ENSURE_SUCCESS(res, res); + // if only trailing
remaining remove it + res = RemoveRedundantTrailingBR(); + if (NS_FAILED(res)) + return res; + // detect empty doc res = CreateBogusNodeIfNeeded(selection); NS_ENSURE_SUCCESS(res, res); - // insure trailing br node + // ensure trailing br node res = CreateTrailingBRIfNeeded(); NS_ENSURE_SUCCESS(res, res); @@ -1045,6 +1050,66 @@ nsTextEditRules::DidOutputText(nsISelection *aSelection, nsresult aResult) return NS_OK; } +nsresult +nsTextEditRules::RemoveRedundantTrailingBR() +{ + // If the bogus node exists, we have no work to do + if (mBogusNode) + return NS_OK; + + // Likewise, nothing to be done if we could never have inserted a trailing br + if (IsSingleLineEditor()) + return NS_OK; + + nsIDOMNode* body = mEditor->GetRoot(); + if (!body) + return NS_ERROR_NULL_POINTER; + + PRBool hasChildren; + nsresult res = body->HasChildNodes(&hasChildren); + NS_ENSURE_SUCCESS(res, res); + + if (hasChildren) { + nsCOMPtr childList; + res = body->GetChildNodes(getter_AddRefs(childList)); + NS_ENSURE_SUCCESS(res, res); + + if (!childList) + return NS_ERROR_NULL_POINTER; + + PRUint32 childCount; + res = childList->GetLength(&childCount); + NS_ENSURE_SUCCESS(res, res); + + // The trailing br is redundant if it is the only remaining child node + if (childCount != 1) + return NS_OK; + + nsCOMPtr child; + res = body->GetFirstChild(getter_AddRefs(child)); + NS_ENSURE_SUCCESS(res, res); + + if (nsTextEditUtils::IsMozBR(child)) { + // Rather than deleting this node from the DOM tree we should instead + // morph this br into the bogus node + nsCOMPtr elem = do_QueryInterface(child); + if (elem) { + elem->RemoveAttribute(NS_LITERAL_STRING("type")); + NS_ENSURE_SUCCESS(res, res); + + // set mBogusNode to be this
+ mBogusNode = elem; + + // give it the bogus node attribute + nsCOMPtr content = do_QueryInterface(elem); + content->SetAttr(kNameSpaceID_None, kMOZEditorBogusNodeAttrAtom, + kMOZEditorBogusNodeValue, PR_FALSE); + } + } + } + return NS_OK; +} + nsresult nsTextEditRules::CreateTrailingBRIfNeeded() { diff --git a/editor/libeditor/text/nsTextEditRules.h b/editor/libeditor/text/nsTextEditRules.h index 0d249a35a6d6..186bcfbbb43e 100644 --- a/editor/libeditor/text/nsTextEditRules.h +++ b/editor/libeditor/text/nsTextEditRules.h @@ -205,6 +205,9 @@ protected: // helper functions + + /** check for and replace a redundant trailing break */ + nsresult RemoveRedundantTrailingBR(); /** creates a trailing break in the text doc if there is not one already */ nsresult CreateTrailingBRIfNeeded(); diff --git a/editor/libeditor/text/tests/Makefile.in b/editor/libeditor/text/tests/Makefile.in index a2e6c5fdd39b..acd84cc7b70f 100644 --- a/editor/libeditor/text/tests/Makefile.in +++ b/editor/libeditor/text/tests/Makefile.in @@ -73,6 +73,7 @@ _TEST_FILES += \ endif _CHROME_TEST_FILES = \ + test_bug483651.html \ test_bug636465.xul \ $(NULL) diff --git a/editor/libeditor/text/tests/test_bug483651.html b/editor/libeditor/text/tests/test_bug483651.html new file mode 100644 index 000000000000..3b9aa72e9479 --- /dev/null +++ b/editor/libeditor/text/tests/test_bug483651.html @@ -0,0 +1,92 @@ + + + + + + + Test for Bug 483651 + + + + + + + + Mozilla Bug 483651 +

+ + +
+    
+  
+ + + + From 92a037b32c7e4dbb13cdab13df652b5baec7bf8a Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 12 Aug 2011 16:25:24 +0200 Subject: [PATCH 21/51] Bug 678039 - Limit the top titlebar line overdrawing workaround to Lion. It's not needed on Leopard / Snow Leopard. r=josh --- widget/src/cocoa/nsCocoaWindow.mm | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/widget/src/cocoa/nsCocoaWindow.mm b/widget/src/cocoa/nsCocoaWindow.mm index edbce459d087..bd189ee11c0c 100644 --- a/widget/src/cocoa/nsCocoaWindow.mm +++ b/widget/src/cocoa/nsCocoaWindow.mm @@ -2417,11 +2417,14 @@ DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect, nil], nil); - // At some window widths the call to CUIDraw doesn't draw the top pixel strip. - // We don't want to have a flickering transparent line, so we overdraw it. - CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1); - CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1, - aTitlebarRect.size.width, 1)); + if (nsToolkit::OnLionOrLater()) { + // On Lion the call to CUIDraw doesn't draw the top pixel strip at some + // window widths. We don't want to have a flickering transparent line, so + // we overdraw it. + CGContextSetRGBFillColor(aContext, 0.95, 0.95, 0.95, 1); + CGContextFillRect(aContext, CGRectMake(0, CGRectGetMaxY(aTitlebarRect) - 1, + aTitlebarRect.size.width, 1)); + } } // Pattern draw callback for standard titlebar gradients and solid titlebar colors From ca516da524c5f8df7aff9eaf72ae225d7815ac5e Mon Sep 17 00:00:00 2001 From: Markus Stange Date: Fri, 12 Aug 2011 16:26:53 +0200 Subject: [PATCH 22/51] Bug 678333 - Don't set a height on the zoom button. r=dao --- browser/themes/pinstripe/browser/browser.css | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css index 69b707f03d94..158e18725a23 100644 --- a/browser/themes/pinstripe/browser/browser.css +++ b/browser/themes/pinstripe/browser/browser.css @@ -741,7 +741,6 @@ toolbar[mode="icons"] #zoom-out-button { toolbar[mode="icons"] #zoom-in-button { -moz-border-start: none; -moz-margin-start: 0; - height: 16px; } #zoom-out-button:-moz-locale-dir(ltr), From 1e7f1b5a2780c6b3a8085c6b575131d853d82bd8 Mon Sep 17 00:00:00 2001 From: Landry Breuil Date: Fri, 12 Aug 2011 13:47:22 -0700 Subject: [PATCH 23/51] Bug 545222: treat powerpc-*-openbsd* as powerpc-*-freebsd*, r=gal --- js/src/ctypes/libffi/configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure index 93ba5843fd35..2c08e1bdb12d 100755 --- a/js/src/ctypes/libffi/configure +++ b/js/src/ctypes/libffi/configure @@ -11277,7 +11277,7 @@ case "$host" in powerpc-*-aix* | rs6000-*-aix*) TARGET=POWERPC_AIX; TARGETDIR=powerpc ;; - powerpc-*-freebsd*) + powerpc-*-freebsd* | powerpc-*-openbsd*) TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc ;; powerpc*-*-rtems*) From d5fe7530460fd09503490abcdb4146b22149fa8c Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Fri, 12 Aug 2011 14:14:52 -0700 Subject: [PATCH 24/51] Bug 678588 - Drop singleDeviceInterval to 1 hour. r=mconnor --- services/sync/services-sync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/sync/services-sync.js b/services/sync/services-sync.js index 20d0ecbe9b5d..c748071defe9 100644 --- a/services/sync/services-sync.js +++ b/services/sync/services-sync.js @@ -9,7 +9,7 @@ pref("services.sync.syncKeyHelpURL", "https://services.mozilla.com/help/synckey" pref("services.sync.lastversion", "firstrun"); pref("services.sync.sendVersionInfo", true); -pref("services.sync.scheduler.singleDeviceInterval", 86400); // 1 day +pref("services.sync.scheduler.singleDeviceInterval", 3600); // 1 hour pref("services.sync.scheduler.idleInterval", 3600); // 1 hour pref("services.sync.scheduler.activeInterval", 300); // 5 minutes pref("services.sync.scheduler.immediateInterval", 60); // 1 minute From 48bacb45f557359818f98751305bf8060d1be595 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 12 Aug 2011 10:15:29 +0200 Subject: [PATCH 25/51] bug 678457 - remove assert about a pending exception in the scanner as OOM does not throw. r=luke --HG-- extra : rebase_source : f71a1bac1d0271758ef9623156cd53d396346a2a --- js/src/jsscan.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index abe64d3498d3..338147b80986 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -2131,8 +2131,6 @@ TokenStream::getTokenInternal() return tt; error: - JS_ASSERT(cx->isExceptionPending()); - /* * For erroneous multi-line tokens we won't have changed end.lineno (it'll * still be equal to begin.lineno) so we revert end.index to be equal to From cca8baa8e36997ca2218d5928fc170263a241e5d Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 12 Aug 2011 14:51:01 -0700 Subject: [PATCH 26/51] Bug 661297 followup: Update toolkit-makefiles.sh to remove reference to no-longer-existing Makefile. rs=ted --- toolkit/toolkit-makefiles.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index 74c07cf368d5..45b70cdb3bed 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -49,7 +49,6 @@ MAKEFILES_dom=" ipc/glue/Makefile ipc/ipdl/Makefile dom/Makefile - dom/public/coreEvents/Makefile dom/interfaces/base/Makefile dom/interfaces/canvas/Makefile dom/interfaces/core/Makefile From 750a0f359f1343baaa3f4aec2a569cb6de58b7f9 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Mon, 25 Jul 2011 13:04:02 +0200 Subject: [PATCH 27/51] bug 673795 - part1, no empty chunk hashing. r=anygregor --HG-- extra : rebase_source : 5700156556287ae6e72b78640451c5ceb1013df4 --- js/src/jsapi.cpp | 4 +- js/src/jscntxt.h | 11 ++-- js/src/jsgc.cpp | 131 +++++++++++++++++++++++++---------------------- js/src/jsgc.h | 27 +++++++--- 4 files changed, 97 insertions(+), 76 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0c1b9a84ba5f..4325299ef59b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2718,9 +2718,9 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) case JSGC_MODE: return uint32(rt->gcMode); case JSGC_UNUSED_CHUNKS: - return uint32(rt->gcChunksWaitingToExpire); + return uint32(rt->gcEmptyChunkCount); case JSGC_TOTAL_CHUNKS: - return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count()); + return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count() + rt->gcEmptyChunkCount); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 9310a5886a58..89cbd6191645 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -138,7 +138,7 @@ struct GSNCache { void purge(); }; - + inline GSNCache * GetGSNCache(JSContext *cx); @@ -212,7 +212,7 @@ struct ThreadData { ~ThreadData(); bool init(); - + void mark(JSTracer *trc) { stackSpace.mark(trc); } @@ -263,7 +263,7 @@ struct JSThread { suspendCount(0) # ifdef DEBUG , checkRequestDepth(0) -# endif +# endif { JS_INIT_CLIST(&contextList); } @@ -392,6 +392,8 @@ struct JSRuntime /* Garbage collector state, used by jsgc.c. */ js::GCChunkSet gcUserChunkSet; js::GCChunkSet gcSystemChunkSet; + js::gc::Chunk *gcEmptyChunkListHead; + size_t gcEmptyChunkCount; js::RootedValueMap gcRootsHash; js::GCLocks gcLocksHash; @@ -401,7 +403,6 @@ struct JSRuntime size_t gcLastBytes; size_t gcMaxBytes; size_t gcMaxMallocBytes; - size_t gcChunksWaitingToExpire; uint32 gcEmptyArenaPoolLifespan; uint32 gcNumber; js::GCMarker *gcMarkingTracer; @@ -672,7 +673,7 @@ struct JSRuntime return infoEnabled; } - /* + /* * Circular buffer with GC data. * count may grow >= INFO_LIMIT, which would indicate data loss. */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5e2e6776f877..2457bc37ed54 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -409,8 +409,21 @@ Chunk::releaseArena(ArenaHeader *aheader) aheader->next = info.emptyArenaListHead; info.emptyArenaListHead = aheader; ++info.numFree; - if (unused()) + if (unused()) { + rt->gcUserChunkSet.remove(this); + rt->gcSystemChunkSet.remove(this); + + /* + * We keep empty chunks until we are done with finalization to allow + * calling IsAboutToBeFinalized/Cell::isMarked for finalized GC things + * in empty chunks. So we add the chunk to the empty set even during + * GC_SHRINK. + */ info.age = 0; + info.link = rt->gcEmptyChunkListHead; + rt->gcEmptyChunkListHead = this; + rt->gcEmptyChunkCount++; + } } inline Chunk * @@ -448,9 +461,8 @@ PickChunk(JSContext *cx) * The chunk used for the last allocation is full, search all chunks for * free arenas. */ - GCChunkSet::Range - r(isSystemCompartment ? rt->gcSystemChunkSet.all() : rt->gcUserChunkSet.all()); - for (; !r.empty(); r.popFront()) { + GCChunkSet *chunkSet = isSystemCompartment ? &rt->gcSystemChunkSet : &rt->gcUserChunkSet; + for (GCChunkSet::Range r(chunkSet->all()); !r.empty(); r.popFront()) { chunk = r.front(); if (chunk->hasAvailableArenas()) { cx->compartment->chunk = chunk; @@ -458,79 +470,73 @@ PickChunk(JSContext *cx) } } - chunk = AllocateGCChunk(rt); - if (!chunk) { - /* Our last chance is to find an empty chunk in the other chunk set. */ - GCChunkSet::Enum e(isSystemCompartment ? rt->gcUserChunkSet : rt->gcSystemChunkSet); - for (; !e.empty(); e.popFront()) { - if (e.front()->info.numFree == ArenasPerChunk) { - chunk = e.front(); - e.removeFront(); - break; - } - } - + /* Use an empty chunk when available or allocate a new one. */ + chunk = rt->gcEmptyChunkListHead; + if (chunk) { + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcUserChunkSet.has(chunk)); + JS_ASSERT(!rt->gcSystemChunkSet.has(chunk)); + JS_ASSERT(rt->gcEmptyChunkCount >= 1); + rt->gcEmptyChunkListHead = chunk->info.link; + rt->gcEmptyChunkCount--; + } else { + chunk = AllocateGCChunk(rt); if (!chunk) return NULL; + + chunk->init(rt); + rt->gcChunkAllocationSinceLastGC = true; } /* * FIXME bug 583732 - chunk is newly allocated and cannot be present in * the table so using ordinary lookupForAdd is suboptimal here. */ - GCChunkSet::AddPtr p = isSystemCompartment ? - rt->gcSystemChunkSet.lookupForAdd(chunk) : - rt->gcUserChunkSet.lookupForAdd(chunk); + GCChunkSet::AddPtr p = chunkSet->lookupForAdd(chunk); JS_ASSERT(!p); - if (isSystemCompartment) { - if (!rt->gcSystemChunkSet.add(p, chunk)) { - ReleaseGCChunk(rt, chunk); - return NULL; - } - } else { - if (!rt->gcUserChunkSet.add(p, chunk)) { - ReleaseGCChunk(rt, chunk); - return NULL; - } + if (!chunkSet->add(p, chunk)) { + ReleaseGCChunk(rt, chunk); + return NULL; } - chunk->init(rt); cx->compartment->chunk = chunk; - rt->gcChunkAllocationSinceLastGC = true; return chunk; } +static void +ReleaseEmptyGCChunks(JSRuntime *rt) +{ + for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) { + Chunk *next = chunk->info.link; + ReleaseGCChunk(rt, chunk); + chunk = next; + } + rt->gcEmptyChunkListHead = NULL; + rt->gcEmptyChunkCount = 0; +} + static void ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind) { - static const size_t MaxAge = 3; - - /* Remove unused chunks. */ AutoLockGC lock(rt); - rt->gcChunksWaitingToExpire = 0; - for (GCChunkSet::Enum e(rt->gcUserChunkSet); !e.empty(); e.popFront()) { - Chunk *chunk = e.front(); - JS_ASSERT(chunk->info.runtime == rt); - if (chunk->unused()) { - if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) { - e.removeFront(); + if (gckind == GC_SHRINK) { + ReleaseEmptyGCChunks(rt); + } else { + /* Return old empty chunks to the system. */ + for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) { + JS_ASSERT(rt->gcEmptyChunkCount); + Chunk *chunk = *chunkp; + JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); + if (chunk->info.age == MAX_EMPTY_CHUNK_AGE) { + *chunkp = chunk->info.link; + --rt->gcEmptyChunkCount; ReleaseGCChunk(rt, chunk); - continue; + } else { + /* Keep the chunk but increase its age. */ + ++chunk->info.age; + chunkp = &chunk->info.link; } - rt->gcChunksWaitingToExpire++; - } - } - for (GCChunkSet::Enum e(rt->gcSystemChunkSet); !e.empty(); e.popFront()) { - Chunk *chunk = e.front(); - JS_ASSERT(chunk->info.runtime == rt); - if (chunk->unused()) { - if (gckind == GC_SHRINK || chunk->info.age++ > MaxAge) { - e.removeFront(); - ReleaseGCChunk(rt, chunk); - continue; - } - rt->gcChunksWaitingToExpire++; } } } @@ -574,10 +580,10 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes) * Make room for at least 16 chunks so the table would not grow before * the browser starts up. */ - if (!rt->gcUserChunkSet.init(16)) + if (!rt->gcUserChunkSet.init(INITIAL_CHUNK_CAPACITY)) return false; - if (!rt->gcSystemChunkSet.init(16)) + if (!rt->gcSystemChunkSet.init(INITIAL_CHUNK_CAPACITY)) return false; if (!rt->gcRootsHash.init(256)) @@ -933,6 +939,7 @@ js_FinishGC(JSRuntime *rt) ReleaseGCChunk(rt, r.front()); rt->gcUserChunkSet.clear(); rt->gcSystemChunkSet.clear(); + ReleaseEmptyGCChunks(rt); #ifdef JS_THREADSAFE rt->gcHelperThread.finish(rt); @@ -1088,7 +1095,7 @@ JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) { gcLastBytes = lastBytes; - size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER); + size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD); float trigger = float(base) * GC_HEAP_GROWTH_FACTOR; gcTriggerBytes = size_t(Min(float(gcMaxBytes), trigger)); } @@ -1097,7 +1104,7 @@ void JSRuntime::reduceGCTriggerBytes(uint32 amount) { JS_ASSERT(amount > 0); JS_ASSERT(gcTriggerBytes - amount >= 0); - if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) + if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; } @@ -1107,7 +1114,7 @@ JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) { gcLastBytes = lastBytes; - size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ARENA_ALLOCATION_TRIGGER); + size_t base = gckind == GC_SHRINK ? lastBytes : Max(lastBytes, GC_ALLOCATION_THRESHOLD); float trigger = float(base) * GC_HEAP_GROWTH_FACTOR; gcTriggerBytes = size_t(Min(float(rt->gcMaxBytes), trigger)); } @@ -1116,7 +1123,7 @@ void JSCompartment::reduceGCTriggerBytes(uint32 amount) { JS_ASSERT(amount > 0); JS_ASSERT(gcTriggerBytes - amount >= 0); - if (gcTriggerBytes - amount < GC_ARENA_ALLOCATION_TRIGGER * GC_HEAP_GROWTH_FACTOR) + if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; } @@ -1932,7 +1939,7 @@ MaybeGC(JSContext *cx) */ int64 now = PRMJ_Now(); if (rt->gcNextFullGCTime && rt->gcNextFullGCTime <= now) { - if (rt->gcChunkAllocationSinceLastGC || rt->gcChunksWaitingToExpire) { + if (rt->gcChunkAllocationSinceLastGC || rt->gcEmptyChunkListHead) { GCREASON(MAYBEGC); js_GC(cx, NULL, GC_SHRINK); } else { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d25920b2d3af..d4b86ab4a17a 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -613,6 +613,12 @@ struct Chunk { return (addr & GC_CHUNK_MASK) >> ArenaShift; } + uintptr_t address() const { + uintptr_t addr = reinterpret_cast(this); + JS_ASSERT(!(addr & GC_CHUNK_MASK)); + return addr; + } + void init(JSRuntime *rt); bool unused(); bool hasAvailableArenas(); @@ -750,13 +756,12 @@ Cell::compartment() const /* * Lower limit after which we limit the heap growth */ -const size_t GC_ARENA_ALLOCATION_TRIGGER = 30 * js::GC_CHUNK_SIZE; +const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024; /* - * A GC is triggered once the number of newly allocated arenas - * is GC_HEAP_GROWTH_FACTOR times the number of live arenas after - * the last GC starting after the lower limit of - * GC_ARENA_ALLOCATION_TRIGGER. + * A GC is triggered once the number of newly allocated arenas is + * GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC + * starting after the lower limit of GC_ALLOCATION_THRESHOLD. */ const float GC_HEAP_GROWTH_FACTOR = 3.0f; @@ -998,9 +1003,17 @@ struct FreeLists { extern void * RefillFinalizableFreeList(JSContext *cx, unsigned thingKind); -} /* namespace gc */ +/* + * Initial allocation size for data structures holding chunks is set to hold + * chunks with total capacity of 16MB to avoid buffer resizes during browser + * startup. + */ +const size_t INITIAL_CHUNK_CAPACITY = 16 * 1024 * 1024 / GC_CHUNK_SIZE; -typedef Vector GCChunks; +/* The number of GC cycles an empty chunk can survive before been released. */ +const size_t MAX_EMPTY_CHUNK_AGE = 4; + +} /* namespace gc */ struct GCPtrHasher { From 2ce78ccd3229a6d608aaa9cc3f79fa906480adf5 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Tue, 26 Jul 2011 09:55:23 +0200 Subject: [PATCH 28/51] bug 673795 - part2, using lists of avaiulable chunks for faster chunk selection. r=wmccloskey --HG-- extra : rebase_source : ae4f5a82bc4042e341fdb5c08e3f0fe4b4ae8935 --- js/src/jsapi.cpp | 2 +- js/src/jscntxt.h | 25 +++++- js/src/jscompartment.cpp | 3 - js/src/jscompartment.h | 1 - js/src/jsgc.cpp | 179 ++++++++++++++++++++------------------- js/src/jsgc.h | 17 +++- 6 files changed, 128 insertions(+), 99 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4325299ef59b..9e576e631f14 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2720,7 +2720,7 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key) case JSGC_UNUSED_CHUNKS: return uint32(rt->gcEmptyChunkCount); case JSGC_TOTAL_CHUNKS: - return uint32(rt->gcUserChunkSet.count() + rt->gcSystemChunkSet.count() + rt->gcEmptyChunkCount); + return uint32(rt->gcChunkSet.count() + rt->gcEmptyChunkCount); default: JS_ASSERT(key == JSGC_NUMBER); return rt->gcNumber; diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 89cbd6191645..391cf3a9e03e 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -390,8 +390,29 @@ struct JSRuntime uint32 protoHazardShape; /* Garbage collector state, used by jsgc.c. */ - js::GCChunkSet gcUserChunkSet; - js::GCChunkSet gcSystemChunkSet; + + /* + * Set of all GC chunks with at least one allocated thing. The + * conservative GC uses it to quickly check if a possible GC thing points + * into an allocated chunk. + */ + js::GCChunkSet gcChunkSet; + + /* + * Doubly-linked lists of chunks from user and system compartments. The GC + * allocates its arenas from the corresponding list and when all arenas + * in the list head are taken, then the chunk is removed from the list. + * During the GC when all arenas in a chunk become free, that chunk is + * removed from the list and scheduled for release. + */ + js::gc::Chunk *gcSystemAvailableChunkListHead; + js::gc::Chunk *gcUserAvailableChunkListHead; + + /* + * Singly-linked list of empty chunks and its length. We use the list not + * to release empty chunks immediately so they can be used for future + * allocations. This avoids very high overhead of chunk release/allocation. + */ js::gc::Chunk *gcEmptyChunkListHead; size_t gcEmptyChunkCount; diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index b1f71676206c..b3c47c931577 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -127,7 +127,6 @@ JSCompartment::~JSCompartment() bool JSCompartment::init() { - chunk = NULL; for (unsigned i = 0; i < FINALIZE_LIMIT; i++) arenas[i].init(); freeLists.init(); @@ -467,8 +466,6 @@ JSCompartment::markCrossCompartmentWrappers(JSTracer *trc) void JSCompartment::sweep(JSContext *cx, uint32 releaseInterval) { - chunk = NULL; - /* Remove dead wrappers from the table. */ for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) { JS_ASSERT_IF(IsAboutToBeFinalized(cx, e.front().key.toGCThing()) && diff --git a/js/src/jscompartment.h b/js/src/jscompartment.h index ed5edffa9545..3c8f11d27327 100644 --- a/js/src/jscompartment.h +++ b/js/src/jscompartment.h @@ -390,7 +390,6 @@ typedef HashSetrt; + return comp->isSystemCompartment + ? &rt->gcSystemAvailableChunkListHead + : &rt->gcUserAvailableChunkListHead; } -bool -Chunk::hasAvailableArenas() +inline void +Chunk::addToAvailableList(JSCompartment *comp) { - return info.numFree > 0; + Chunk **listHeadp = GetAvailableChunkList(comp); + JS_ASSERT(!info.prevp); + JS_ASSERT(!info.next); + info.prevp = listHeadp; + Chunk *head = *listHeadp; + if (head) { + JS_ASSERT(head->info.prevp == listHeadp); + head->info.prevp = &info.next; + } + info.next = head; + *listHeadp = this; } -bool -Chunk::withinArenasRange(Cell *cell) +inline void +Chunk::removeFromAvailableList() { - uintptr_t addr = uintptr_t(cell); - if (addr >= uintptr_t(&arenas[0]) && addr < uintptr_t(&arenas[ArenasPerChunk])) - return true; - return false; + JS_ASSERT(info.prevp); + *info.prevp = info.next; + if (info.next) { + JS_ASSERT(info.next->info.prevp == &info.next); + info.next->info.prevp = info.prevp; + } + info.prevp = NULL; + info.next = NULL; } template @@ -371,6 +393,9 @@ Chunk::allocateArena(JSContext *cx, unsigned thingKind) aheader->init(comp, thingKind, thingSize); --info.numFree; + if (!hasAvailableArenas()) + removeFromAvailableList(); + JSRuntime *rt = info.runtime; Probes::resizeHeap(comp, rt->gcBytes, rt->gcBytes + ArenaSize); JS_ATOMIC_ADD(&rt->gcBytes, ArenaSize); @@ -409,9 +434,15 @@ Chunk::releaseArena(ArenaHeader *aheader) aheader->next = info.emptyArenaListHead; info.emptyArenaListHead = aheader; ++info.numFree; - if (unused()) { - rt->gcUserChunkSet.remove(this); - rt->gcSystemChunkSet.remove(this); + if (info.numFree == 1) { + JS_ASSERT(!info.prevp); + JS_ASSERT(!info.next); + addToAvailableList(aheader->compartment); + } else if (!unused()) { + JS_ASSERT(info.prevp); + } else { + rt->gcChunkSet.remove(this); + removeFromAvailableList(); /* * We keep empty chunks until we are done with finalization to allow @@ -420,7 +451,7 @@ Chunk::releaseArena(ArenaHeader *aheader) * GC_SHRINK. */ info.age = 0; - info.link = rt->gcEmptyChunkListHead; + info.next = rt->gcEmptyChunkListHead; rt->gcEmptyChunkListHead = this; rt->gcEmptyChunkCount++; } @@ -450,34 +481,23 @@ ReleaseGCChunk(JSRuntime *rt, Chunk *p) inline Chunk * PickChunk(JSContext *cx) { - Chunk *chunk = cx->compartment->chunk; - if (chunk && chunk->hasAvailableArenas()) + JSCompartment *comp = cx->compartment; + JSRuntime *rt = comp->rt; + Chunk **listHeadp = GetAvailableChunkList(comp); + Chunk *chunk = *listHeadp; + if (chunk) return chunk; - JSRuntime *rt = cx->runtime; - bool isSystemCompartment = cx->compartment->isSystemCompartment; - /* - * The chunk used for the last allocation is full, search all chunks for - * free arenas. + * We do not have available chunks, either get one from the empty set or + * allocate one. */ - GCChunkSet *chunkSet = isSystemCompartment ? &rt->gcSystemChunkSet : &rt->gcUserChunkSet; - for (GCChunkSet::Range r(chunkSet->all()); !r.empty(); r.popFront()) { - chunk = r.front(); - if (chunk->hasAvailableArenas()) { - cx->compartment->chunk = chunk; - return chunk; - } - } - - /* Use an empty chunk when available or allocate a new one. */ chunk = rt->gcEmptyChunkListHead; if (chunk) { JS_ASSERT(chunk->unused()); - JS_ASSERT(!rt->gcUserChunkSet.has(chunk)); - JS_ASSERT(!rt->gcSystemChunkSet.has(chunk)); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); JS_ASSERT(rt->gcEmptyChunkCount >= 1); - rt->gcEmptyChunkListHead = chunk->info.link; + rt->gcEmptyChunkListHead = chunk->info.next; rt->gcEmptyChunkCount--; } else { chunk = AllocateGCChunk(rt); @@ -492,27 +512,18 @@ PickChunk(JSContext *cx) * FIXME bug 583732 - chunk is newly allocated and cannot be present in * the table so using ordinary lookupForAdd is suboptimal here. */ - GCChunkSet::AddPtr p = chunkSet->lookupForAdd(chunk); + GCChunkSet::AddPtr p = rt->gcChunkSet.lookupForAdd(chunk); JS_ASSERT(!p); - if (!chunkSet->add(p, chunk)) { + if (!rt->gcChunkSet.add(p, chunk)) { ReleaseGCChunk(rt, chunk); return NULL; } - cx->compartment->chunk = chunk; - return chunk; -} + chunk->info.prevp = NULL; + chunk->info.next = NULL; + chunk->addToAvailableList(comp); -static void -ReleaseEmptyGCChunks(JSRuntime *rt) -{ - for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) { - Chunk *next = chunk->info.link; - ReleaseGCChunk(rt, chunk); - chunk = next; - } - rt->gcEmptyChunkListHead = NULL; - rt->gcEmptyChunkCount = 0; + return chunk; } static void @@ -520,23 +531,21 @@ ExpireGCChunks(JSRuntime *rt, JSGCInvocationKind gckind) { AutoLockGC lock(rt); - if (gckind == GC_SHRINK) { - ReleaseEmptyGCChunks(rt); - } else { - /* Return old empty chunks to the system. */ - for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) { - JS_ASSERT(rt->gcEmptyChunkCount); - Chunk *chunk = *chunkp; - JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); - if (chunk->info.age == MAX_EMPTY_CHUNK_AGE) { - *chunkp = chunk->info.link; - --rt->gcEmptyChunkCount; - ReleaseGCChunk(rt, chunk); - } else { - /* Keep the chunk but increase its age. */ - ++chunk->info.age; - chunkp = &chunk->info.link; - } + /* Return old empty chunks to the system. */ + for (Chunk **chunkp = &rt->gcEmptyChunkListHead; *chunkp; ) { + JS_ASSERT(rt->gcEmptyChunkCount); + Chunk *chunk = *chunkp; + JS_ASSERT(chunk->unused()); + JS_ASSERT(!rt->gcChunkSet.has(chunk)); + JS_ASSERT(chunk->info.age <= MAX_EMPTY_CHUNK_AGE); + if (gckind == GC_SHRINK || chunk->info.age == MAX_EMPTY_CHUNK_AGE) { + *chunkp = chunk->info.next; + --rt->gcEmptyChunkCount; + ReleaseGCChunk(rt, chunk); + } else { + /* Keep the chunk but increase its age. */ + ++chunk->info.age; + chunkp = &chunk->info.next; } } } @@ -576,14 +585,7 @@ static const int64 JIT_SCRIPT_EIGHTH_LIFETIME = 120 * 1000 * 1000; JSBool js_InitGC(JSRuntime *rt, uint32 maxbytes) { - /* - * Make room for at least 16 chunks so the table would not grow before - * the browser starts up. - */ - if (!rt->gcUserChunkSet.init(INITIAL_CHUNK_CAPACITY)) - return false; - - if (!rt->gcSystemChunkSet.init(INITIAL_CHUNK_CAPACITY)) + if (!rt->gcChunkSet.init(INITIAL_CHUNK_CAPACITY)) return false; if (!rt->gcRootsHash.init(256)) @@ -725,8 +727,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) Chunk *chunk = Chunk::fromAddress(addr); - if (!trc->context->runtime->gcUserChunkSet.has(chunk) && - !trc->context->runtime->gcSystemChunkSet.has(chunk)) + if (!trc->context->runtime->gcChunkSet.has(chunk)) return CGCT_NOTCHUNK; /* @@ -933,13 +934,18 @@ js_FinishGC(JSRuntime *rt) rt->compartments.clear(); rt->atomsCompartment = NULL; - for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront()) + rt->gcSystemAvailableChunkListHead = NULL; + rt->gcUserAvailableChunkListHead = NULL; + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) ReleaseGCChunk(rt, r.front()); - for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront()) - ReleaseGCChunk(rt, r.front()); - rt->gcUserChunkSet.clear(); - rt->gcSystemChunkSet.clear(); - ReleaseEmptyGCChunks(rt); + rt->gcChunkSet.clear(); + for (Chunk *chunk = rt->gcEmptyChunkListHead; chunk; ) { + Chunk *next = chunk->info.next; + ReleaseGCChunk(rt, chunk); + chunk = next; + } + rt->gcEmptyChunkListHead = NULL; + rt->gcEmptyChunkCount = 0; #ifdef JS_THREADSAFE rt->gcHelperThread.finish(rt); @@ -2265,10 +2271,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM JS_ASSERT(gcmarker.getMarkColor() == BLACK); rt->gcMarkingTracer = &gcmarker; - for (GCChunkSet::Range r(rt->gcUserChunkSet.all()); !r.empty(); r.popFront()) - r.front()->bitmap.clear(); - - for (GCChunkSet::Range r(rt->gcSystemChunkSet.all()); !r.empty(); r.popFront()) + for (GCChunkSet::Range r(rt->gcChunkSet.all()); !r.empty(); r.popFront()) r.front()->bitmap.clear(); if (comp) { diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d4b86ab4a17a..761ef58ab731 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -514,8 +514,9 @@ struct MarkingDelay { /* The chunk header (located at the end of the chunk to preserve arena alignment). */ struct ChunkInfo { - Chunk *link; JSRuntime *runtime; + Chunk *next; + Chunk **prevp; ArenaHeader *emptyArenaListHead; size_t age; size_t numFree; @@ -620,9 +621,17 @@ struct Chunk { } void init(JSRuntime *rt); - bool unused(); - bool hasAvailableArenas(); - bool withinArenasRange(Cell *cell); + + bool unused() const { + return info.numFree == ArenasPerChunk; + } + + bool hasAvailableArenas() const { + return info.numFree > 0; + } + + inline void addToAvailableList(JSCompartment *compartment); + inline void removeFromAvailableList(); template ArenaHeader *allocateArena(JSContext *cx, unsigned thingKind); From 3c7d7cdcad0f0c938dda1f2626d9c2b4f5817391 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Tue, 26 Jul 2011 15:56:09 -0700 Subject: [PATCH 29/51] Bug 673631 - Clean up probes and register (almost) all profiler control entry points in JS_DefineProfilingFunctions (r=luke,waldo,jst) Previously, JS_DefineProfilingFunctions only defined a very basic set of functions (startProfiling and stopProfiling), and various scattered places added more specific ones (start/stop vtune, dumpProfile, etc.) This patch makes jsdbgapi do all of it, so that all users get the same set. Also rename JS_DumpProfile -> JS_DumpBytecode to avoid name conflict. The bytecode dumps are how the counters ("profiles") are displayed, so the DumpProfile name was bogus anyway. --HG-- extra : rebase_source : 0e5ae9748cd497b9a0d2f51527799c4092f51fd0 --- dom/base/nsJSEnvironment.cpp | 29 -- ipc/testshell/XPCShellEnvironment.cpp | 5 - js/src/jsdbgapi.cpp | 418 +++++++++++++++--- js/src/jsdbgapi.h | 79 +++- js/src/jsgc.cpp | 3 +- js/src/jsprobes.cpp | 156 +++---- js/src/jsprobes.h | 356 ++++++++------- js/src/shell/js.cpp | 73 +-- .../xpconnect/loader/mozJSComponentLoader.cpp | 11 - js/src/xpconnect/shell/xpcshell.cpp | 5 - xpcom/tests/TestHarness.h | 94 +--- 11 files changed, 708 insertions(+), 521 deletions(-) diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 3b3746a4ffec..be1f1b88f2c3 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2963,25 +2963,6 @@ static JSFunctionSpec JProfFunctions[] = { #endif /* defined(MOZ_JPROF) */ -#ifdef MOZ_CALLGRIND -static JSFunctionSpec CallgrindFunctions[] = { - {"startCallgrind", js_StartCallgrind, 0, 0}, - {"stopCallgrind", js_StopCallgrind, 0, 0}, - {"dumpCallgrind", js_DumpCallgrind, 1, 0}, - {nsnull, nsnull, 0, 0} -}; -#endif - -#ifdef MOZ_VTUNE -static JSFunctionSpec VtuneFunctions[] = { - {"startVtune", js_StartVtune, 1, 0}, - {"stopVtune", js_StopVtune, 0, 0}, - {"pauseVtune", js_PauseVtune, 0, 0}, - {"resumeVtune", js_ResumeVtune, 0, 0}, - {nsnull, nsnull, 0, 0} -}; -#endif - #ifdef MOZ_TRACEVIS static JSFunctionSpec EthogramFunctions[] = { {"initEthogram", js_InitEthogram, 0, 0}, @@ -3017,16 +2998,6 @@ nsJSContext::InitClasses(void *aGlobalObj) ::JS_DefineFunctions(mContext, globalObj, JProfFunctions); #endif -#ifdef MOZ_CALLGRIND - // Attempt to initialize Callgrind functions - ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions); -#endif - -#ifdef MOZ_VTUNE - // Attempt to initialize Vtune functions - ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions); -#endif - #ifdef MOZ_TRACEVIS // Attempt to initialize Ethogram functions ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions); diff --git a/ipc/testshell/XPCShellEnvironment.cpp b/ipc/testshell/XPCShellEnvironment.cpp index 8060610d004b..6466e9ca2420 100644 --- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -558,11 +558,6 @@ JSFunctionSpec gGlobalFunctions[] = {"clear", Clear, 1,0}, #ifdef DEBUG {"dumpHeap", DumpHeap, 5,0}, -#endif -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, #endif {nsnull,nsnull,0,0} }; diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index 22fa1e41ab57..35565a708cce 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -43,6 +43,7 @@ * JS debugging API. */ #include +#include #include "jsprvtd.h" #include "jstypes.h" #include "jsstdint.h" @@ -1505,32 +1506,251 @@ JS_ClearContextDebugHooks(JSContext *cx) return JS_SetContextDebugHooks(cx, &js_NullDebugHooks); } -JS_PUBLIC_API(JSBool) -JS_StartProfiling() +/************************************************************************/ + +/* Profiling-related API */ + +/* Thread-unsafe error management */ + +static char gLastError[2000]; + +static void +#ifdef _GNU_SOURCE +__attribute__((unused,format(printf,1,2))) +#endif +UnsafeError(const char *format, ...) { - return Probes::startProfiling(); + va_list args; + va_start(args, format); + (void) vsnprintf(gLastError, sizeof(gLastError), format, args); + va_end(args); + + gLastError[sizeof(gLastError) - 1] = '\0'; } -JS_PUBLIC_API(void) -JS_StopProfiling() +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError() { - Probes::stopProfiling(); + return gLastError; +} + +JS_PUBLIC_API(JSBool) +JS_StartProfiling(const char *profileName) +{ + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark for %s", profileName); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (!js_StartVtune(profileName)) + ok = JS_FALSE; +#endif + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName) +{ + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_VTUNE + if (!js_StopVtune()) + ok = JS_FALSE; +#endif + return ok; +} + +/* + * Start or stop whatever platform- and configuration-specific profiling + * backends are available. + */ +static JSBool +ControlProfilers(bool toState) +{ + JSBool ok = JS_TRUE; + + if (! Probes::ProfilingActive && toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark for %s", profileName); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_CALLGRIND + if (! js_StartCallgrind()) { + UnsafeError("Failed to start Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_ResumeVtune()) + ok = JS_FALSE; +#endif + } else if (Probes::ProfilingActive && ! toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_CALLGRIND + if (! js_StopCallgrind()) { + UnsafeError("failed to stop Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_PauseVtune()) + ok = JS_FALSE; +#endif + } + + Probes::ProfilingActive = toState; + + return ok; +} + +/* + * Pause/resume whatever profiling mechanism is currently compiled + * in, if applicable. This will not affect things like dtrace. + * + * Do not mix calls to these APIs with calls to the individual + * profilers' pause/resume functions, because only overall state is + * tracked, not the state of each profiler. + */ +JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName) +{ + return ControlProfilers(false); +} + +JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName) +{ + return ControlProfilers(true); +} + +JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName) +{ + JSBool ok = JS_TRUE; +#ifdef MOZ_CALLGRIND + js_DumpCallgrind(outfile); +#endif + return ok; } #ifdef MOZ_PROFILING +struct RequiredStringArg { + JSContext *mCx; + char *mBytes; + RequiredStringArg(JSContext *cx, uintN argc, jsval *vp, size_t argi, const char *caller) + : mCx(cx), mBytes(NULL) + { + if (argc <= argi) { + JS_ReportError(cx, "%s: not enough arguments", caller); + } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) { + JS_ReportError(cx, "%s: invalid arguments (string expected)", caller); + } else { + mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi])); + } + } + operator void*() { + return (void*) mBytes; + } + ~RequiredStringArg() { + if (mBytes) + mCx->free_(mBytes); + } +}; + static JSBool StartProfiling(JSContext *cx, uintN argc, jsval *vp) { - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling())); - return true; + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes))); + return JS_TRUE; } static JSBool StopProfiling(JSContext *cx, uintN argc, jsval *vp) { - JS_StopProfiling(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +PauseProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +ResumeProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes))); + return JS_TRUE; +} + +/* Usage: DumpProfile([filename[, profileName]]) */ +static JSBool +DumpProfile(JSContext *cx, uintN argc, jsval *vp) +{ + bool ret; + if (argc == 0) { + ret = JS_DumpProfile(NULL, NULL); + } else { + RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile"); + if (!filename) + return JS_FALSE; + + if (argc == 1) { + ret = JS_DumpProfile(filename.mBytes, NULL); + } else { + RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile"); + if (!profileName) + return JS_FALSE; + + ret = JS_DumpProfile(filename.mBytes, profileName.mBytes); + } + } + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret)); return true; } @@ -1545,15 +1765,94 @@ IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp) #endif +#ifdef MOZ_CALLGRIND +static JSBool +StartCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind())); + return JS_TRUE; +} + +static JSBool +StopCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind())); + return JS_TRUE; +} + +static JSBool +DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL))); + return JS_TRUE; + } + + RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind"); + if (!outFile) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes))); + return JS_TRUE; +} +#endif + +#ifdef MOZ_VTUNE +static JSBool +StartVtune(JSContext *cx, uintN argc, jsval *vp) +{ + RequiredStringArg profileName(cx, argc, vp, 0, "startVtune"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +StopVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune())); + return JS_TRUE; +} + +static JSBool +PauseVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune())); + return JS_TRUE; +} + +static JSBool +ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune())); + return JS_TRUE; +} +#endif + static JSFunctionSpec profiling_functions[] = { - JS_FN("startProfiling", StartProfiling, 0,0), - JS_FN("stopProfiling", StopProfiling, 0,0), + JS_FN("startProfiling", StartProfiling, 1,0), + JS_FN("stopProfiling", StopProfiling, 1,0), + JS_FN("pauseProfilers", PauseProfilers, 1,0), + JS_FN("resumeProfilers", ResumeProfilers, 1,0), + JS_FN("dumpProfile", DumpProfile, 2,0), #ifdef MOZ_SHARK /* Keep users of the old shark API happy. */ JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), JS_FN("startShark", StartProfiling, 0,0), JS_FN("stopShark", StopProfiling, 0,0), +#endif +#ifdef MOZ_CALLGRIND + JS_FN("startCallgrind", StartCallgrind, 0,0), + JS_FN("stopCallgrind", StopCallgrind, 0,0), + JS_FN("dumpCallgrind", DumpCallgrind, 1,0), +#endif +#ifdef MOZ_VTUNE + JS_FN("startVtune", js_StartVtune, 1,0), + JS_FN("stopVtune", js_StopVtune, 0,0), + JS_FN("pauseVtune", js_PauseVtune, 0,0), + JS_FN("resumeVtune", js_ResumeVtune, 0,0), #endif JS_FS_END }; @@ -1576,40 +1875,30 @@ JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj) #include JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StartCallgrind() { CALLGRIND_START_INSTRUMENTATION; CALLGRIND_ZERO_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StopCallgrind() { CALLGRIND_STOP_INSTRUMENTATION; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_DumpCallgrind(const char *outfile) { - JSString *str; - - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - JSAutoByteString bytes(cx, str); - if (!!bytes) { - CALLGRIND_DUMP_STATS_AT(bytes.ptr()); - return JS_TRUE; - } + if (outfile) { + CALLGRIND_DUMP_STATS_AT(outfile); + } else { + CALLGRIND_DUMP_STATS; } - CALLGRIND_DUMP_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } #endif /* MOZ_CALLGRIND */ @@ -1644,8 +1933,8 @@ static const char *vtuneErrorMessages[] = { }; -JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StartVtune(const char *profileName) { VTUNE_EVENT events[] = { { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, @@ -1672,62 +1961,54 @@ js_StartVtune(JSContext *cx, uintN argc, jsval *vp) default_filename, }; - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - params.tb5Filename = DeflateString(cx, str->chars(), str->length()); + if (profileName) { + char filename[strlen(profileName) + strlen("-vtune.tb5") + 1]; + snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName); + params.tb5Filename = filename; } status = VTStartSampling(¶ms); if (params.tb5Filename != default_filename) - cx->free_(params.tb5Filename); + Foreground::free_(params.tb5Filename); if (status != 0) { if (status == VTAPI_MULTIPLE_RUNS) VTStopSampling(0); if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune setup error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune setup error: %d", - status); + UnsafeError("Vtune setup error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StopVtune() { U32 status = VTStopSampling(1); if (status) { if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune shutdown error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune shutdown error: %d", - status); + UnsafeError("Vtune shutdown error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_PauseVtune() { VTPause(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_ResumeVtune() { VTResume(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } @@ -2198,32 +2479,29 @@ JS_GetFunctionCallback(JSContext *cx) #endif /* MOZ_TRACE_JSCALLS */ JS_PUBLIC_API(void) -JS_DumpProfile(JSContext *cx, JSScript *script) +JS_DumpBytecode(JSContext *cx, JSScript *script) { JS_ASSERT(!cx->runtime->gcRunning); #if defined(DEBUG) - if (script->pcCounters) { - // Display hit counts for every JS code line - AutoArenaAllocator mark(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + AutoArenaAllocator mark(&cx->tempPool); + Sprinter sprinter; + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - fprintf(stdout, "--- PC COUNTS %s:%d ---\n", script->filename, script->lineno); - js_Disassemble(cx, script, true, &sprinter); - fprintf(stdout, "%s\n", sprinter.base); - fprintf(stdout, "--- END PC COUNTS %s:%d ---\n", script->filename, script->lineno); - } + fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); + js_Disassemble(cx, script, true, &sprinter); + fprintf(stdout, "%s\n", sprinter.base); + fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif } JS_PUBLIC_API(void) -JS_DumpAllProfiles(JSContext *cx) +JS_DumpCompartmentBytecode(JSContext *cx) { for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts); script != (JSScript *) &cx->compartment->scripts; script = (JSScript *) JS_NEXT_LINK((JSCList *)script)) { - JS_DumpProfile(cx, script); + JS_DumpBytecode(cx, script); } } diff --git a/js/src/jsdbgapi.h b/js/src/jsdbgapi.h index f2c730174d87..2b76ea2e79b7 100644 --- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -500,41 +500,88 @@ JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks); extern JS_PUBLIC_API(JSDebugHooks *) JS_ClearContextDebugHooks(JSContext *cx); +/** + * Start any profilers that are available and have been configured on for this + * platform. This is NOT thread safe. + * + * The profileName is used by some profilers to describe the current profiling + * run. It may be used for part of the filename of the output, but the + * specifics depend on the profiler. Many profilers will ignore it. Passing in + * NULL is legal; some profilers may use it to output to stdout or similar. + * + * Returns true if no profilers fail to start. + */ extern JS_PUBLIC_API(JSBool) -JS_StartProfiling(); +JS_StartProfiling(const char *profileName); -extern JS_PUBLIC_API(void) -JS_StopProfiling(); +/** + * Stop any profilers that were previously started with JS_StartProfiling. + * Returns true if no profilers fail to stop. + */ +extern JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName); +/** + * Write the current profile data to the given file, if applicable to whatever + * profiler is being used. + */ +extern JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName); + +/** + * Pause currently active profilers (only supported by some profilers). Returns + * whether any profilers failed to pause. (Profilers that do not support + * pause/resume do not count.) + */ +extern JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName); + +/** + * Resume suspended profilers + */ +extern JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName); + +/** + * Add various profiling-related functions as properties of the given object. + */ extern JS_PUBLIC_API(JSBool) JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj); +/** + * The profiling API calls are not able to report errors, so they use a + * thread-unsafe global memory buffer to hold the last error encountered. This + * should only be called after something returns false. + */ +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError(); + #ifdef MOZ_CALLGRIND extern JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StopCallgrind(); extern JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StartCallgrind(); extern JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_DumpCallgrind(const char *outfile); #endif /* MOZ_CALLGRIND */ #ifdef MOZ_VTUNE -extern JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StartVtune(const char *profileName); -extern JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StopVtune(); -extern JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_PauseVtune(); -extern JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_ResumeVtune(); #endif /* MOZ_VTUNE */ @@ -568,10 +615,10 @@ JS_GetFunctionCallback(JSContext *cx); #endif /* MOZ_TRACE_JSCALLS */ extern JS_PUBLIC_API(void) -JS_DumpProfile(JSContext *cx, JSScript *script); +JS_DumpBytecode(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(void) -JS_DumpAllProfiles(JSContext *cx); +JS_DumpCompartmentBytecode(JSContext *cx); JS_END_EXTERN_C diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index cb1ac5fcbcac..04b5422456e8 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2202,7 +2202,6 @@ SweepCompartments(JSContext *cx, JSGCInvocationKind gckind) (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) { compartment->freeLists.checkEmpty(); - Probes::GCEndSweepPhase(compartment); if (callback) JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); if (compartment->principals) @@ -2337,6 +2336,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM * unreachable compartments. */ if (comp) { + Probes::GCStartSweepPhase(comp); comp->sweep(cx, 0); comp->finalizeObjectArenaLists(cx); GCTIMESTAMP(sweepObjectEnd); @@ -2344,6 +2344,7 @@ MarkAndSweep(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind GCTIM GCTIMESTAMP(sweepStringEnd); comp->finalizeShapeArenaLists(cx); GCTIMESTAMP(sweepShapeEnd); + Probes::GCEndSweepPhase(comp); } else { /* * Some sweeping is not compartment-specific. Start a NULL-compartment diff --git a/js/src/jsprobes.cpp b/js/src/jsprobes.cpp index 2d809f4828b5..1c1ad0cab966 100644 --- a/js/src/jsprobes.cpp +++ b/js/src/jsprobes.cpp @@ -69,48 +69,65 @@ const char Probes::anonymousName[] = "(anonymous)"; bool Probes::ProfilingActive = true; -bool -Probes::controlProfilers(JSContext *cx, bool toState) +#ifdef INCLUDE_MOZILLA_DTRACE +static const char * +ScriptFilename(const JSScript *script) { - JSBool ok = JS_TRUE; -#if defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) - jsval dummy; -#endif + if (!script) + return Probes::nullName; + if (!script->filename) + return Probes::anonymousName; + return script->filename; +} - if (! ProfilingActive && toState) { -#if defined(MOZ_SHARK) && defined(__APPLE__) - if (!Shark::Start()) - ok = JS_FALSE; -#endif -#ifdef MOZ_CALLGRIND - if (! js_StartCallgrind(cx, 0, &dummy)) - ok = JS_FALSE; -#endif -#ifdef MOZ_VTUNE - if (! js_ResumeVtune(cx, 0, &dummy)) - ok = JS_FALSE; -#endif - } else if (ProfilingActive && ! toState) { -#if defined(MOZ_SHARK) && defined(__APPLE__) - Shark::Stop(); -#endif -#ifdef MOZ_CALLGRIND - if (! js_StopCallgrind(cx, 0, &dummy)) - ok = JS_FALSE; -#endif -#ifdef MOZ_VTUNE - if (! js_PauseVtune(cx, 0, &dummy)) - ok = JS_FALSE; -#endif - } +static const char * +FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes) +{ + if (!fun) + return Probes::nullName; + JSAtom *atom = const_cast(fun->atom); + if (!atom) + return Probes::anonymousName; + return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName; +} - ProfilingActive = toState; +static const char * +FunctionClassname(const JSFunction *fun) +{ + if (!fun || FUN_INTERPRETED(fun)) + return Probes::nullName; + if (!(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun)) + return (char *)FUN_CLASP(fun)->name; + return Probes::nullName; +} - return ok; +/* + * These functions call the DTrace macros for the JavaScript USDT probes. + * Originally this code was inlined in the JavaScript code; however since + * a number of operations are called, these have been placed into functions + * to reduce any negative compiler optimization effect that the addition of + * a number of usually unused lines of code would cause. + */ +void +Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script) +{ + JSAutoByteString funNameBytes; + JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun), + FunctionName(cx, fun, &funNameBytes)); } void -Probes::current_location(JSContext *cx, int* lineno, char const **filename) +Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script) +{ + JSAutoByteString funNameBytes; + JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun), + FunctionName(cx, fun, &funNameBytes)); +} +#endif + +#ifdef MOZ_ETW +static void +current_location(JSContext *cx, int* lineno, char const **filename) { JSScript *script = js_GetCurrentScript(cx); if (! script) { @@ -122,75 +139,6 @@ Probes::current_location(JSContext *cx, int* lineno, char const **filename) *filename = ScriptFilename(script); } -const char * -Probes::FunctionClassname(const JSFunction *fun) -{ - return (fun && !FUN_INTERPRETED(fun) && !(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun)) - ? (char *)FUN_CLASP(fun)->name - : nullName; -} - -const char * -Probes::ScriptFilename(JSScript *script) -{ - return (script && script->filename) ? (char *)script->filename : nullName; -} - -int -Probes::FunctionLineNumber(JSContext *cx, const JSFunction *fun) -{ - if (fun && FUN_INTERPRETED(fun)) - return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun)); - - return 0; -} - -#ifdef INCLUDE_MOZILLA_DTRACE -/* - * These functions call the DTrace macros for the JavaScript USDT probes. - * Originally this code was inlined in the JavaScript code; however since - * a number of operations are called, these have been placed into functions - * to reduce any negative compiler optimization effect that the addition of - * a number of usually unused lines of code would cause. - */ -void -Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script) -{ - JSAutoByteString funNameBytes; - JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun), - FunctionName(cx, fun, &funNameBytes)); -} - -void -Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script) -{ - JSAutoByteString funNameBytes; - JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun), - FunctionName(cx, fun, &funNameBytes)); -} - -#endif - -bool -Probes::startProfiling() -{ -#if defined(MOZ_SHARK) && defined(__APPLE__) - if (Shark::Start()) - return true; -#endif - return false; -} - -void -Probes::stopProfiling() -{ -#if defined(MOZ_SHARK) && defined(__APPLE__) - Shark::Stop(); -#endif -} - - -#ifdef MOZ_ETW /* * ETW (Event Tracing for Windows) * diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h index d73fe5fafe39..dce663a23fab 100644 --- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -14,10 +14,14 @@ * for the specific language governing rights and limitations under the * License. * - * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. * * Contributor(s): - * Brendan Eich + * Steve Fink * * 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"), @@ -44,168 +48,204 @@ namespace js { -class Probes { - static bool ProfilingActive; - static bool controlProfilers(JSContext *cx, bool toState); +namespace Probes { - static const char nullName[]; - static const char anonymousName[]; +/* + * Static probes + * + * The probe points defined in this file are scattered around the SpiderMonkey + * source tree. The presence of Probes::someEvent() means that someEvent is + * about to happen or has happened. To the extent possible, probes should be + * inserted in all paths associated with a given event, regardless of the + * active runmode (interpreter/traceJIT/methodJIT/ionJIT). + * + * When a probe fires, it is handled by any probe handling backends that have + * been compiled in. By default, most probes do nothing or at least do nothing + * expensive, so the presence of the probe should have negligible effect on + * running time. (Probes in slow paths may do something by default, as long as + * there is no noticeable slowdown.) + * + * For some probes, the mere existence of the probe is too expensive even if it + * does nothing when called. For example, just having consistent information + * available for a function call entry/exit probe causes the JITs to + * de-optimize function calls. In those cases, the JITs may query at compile + * time whether a probe is desired, and omit the probe invocation if not. If a + * probe is runtime-disabled at compilation time, it is not guaranteed to fire + * within a compiled function if it is later enabled. + * + * Not all backends handle all of the probes listed here. + */ - static const char *FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes) - { - if (!fun) - return nullName; - JSAtom *atom = const_cast(fun->atom); - if (!atom) - return anonymousName; - return bytes->encode(cx, atom) ? bytes->ptr() : nullName; - } +/* + * Internal use only: remember whether "profiling", whatever that means, is + * currently active. Used for state management. + */ +extern bool ProfilingActive; - static const char *ScriptFilename(const JSScript *script) { - if (! script) - return "(null)"; - if (! script->filename) - return "(anonymous)"; - return script->filename; - } +extern const char nullName[]; +extern const char anonymousName[]; - static const char *ObjectClassname(JSObject *obj) { - if (! obj) - return "(null object)"; - Class *clasp = obj->getClass(); - if (! clasp) - return "(null)"; - const char *class_name = clasp->name; - if (! class_name) - return "(null class name)"; - return class_name; - } +/* JSRuntime created, with currently valid fields */ +bool createRuntime(JSRuntime *rt); - static void current_location(JSContext *cx, int* lineno, char const **filename); +/* JSRuntime about to be destroyed */ +bool destroyRuntime(JSRuntime *rt); - static const char *FunctionClassname(const JSFunction *fun); - static const char *ScriptFilename(JSScript *script); - static int FunctionLineNumber(JSContext *cx, const JSFunction *fun); +/* Total JS engine shutdown */ +bool shutdown(); - static void enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script); - static void handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script); - static void finalizeObjectImpl(JSObject *obj); - public: - static bool createRuntime(JSRuntime *rt); - static bool destroyRuntime(JSRuntime *rt); - static bool shutdown(); +/* + * Test whether we are tracking JS function call enter/exit. The JITs use this + * to decide whether they can optimize in a way that would prevent probes from + * firing. + */ +bool callTrackingActive(JSContext *); - /* - * Pause/resume whatever profiling mechanism is currently compiled - * in, if applicable. This will not affect things like dtrace. - * - * Do not mix calls to these APIs with calls to the individual - * profilers' pase/resume functions, because only overall state is - * tracked, not the state of each profiler. - * - * Return the previous state. - */ - static bool pauseProfilers(JSContext *cx) { - bool prevState = ProfilingActive; - controlProfilers(cx, false); - return prevState; - } - static bool resumeProfilers(JSContext *cx) { - bool prevState = ProfilingActive; - controlProfilers(cx, true); - return prevState; - } +/* Entering a JS function */ +bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1); - static bool callTrackingActive(JSContext *); +/* About to leave a JS function */ +bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0); - static bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1); - static bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0); +/* Executing a script */ +bool startExecution(JSContext *cx, JSScript *script); - static bool startExecution(JSContext *cx, JSScript *script); - static bool stopExecution(JSContext *cx, JSScript *script); +/* Script has completed execution */ +bool stopExecution(JSContext *cx, JSScript *script); - static bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); +/* Heap has been resized */ +bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); - /* |obj| must exist (its class and size are computed) */ - static bool createObject(JSContext *cx, JSObject *obj); +/* + * Object has been created. |obj| must exist (its class and size are read) + */ +bool createObject(JSContext *cx, JSObject *obj); - static bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); +/* Object has been resized */ +bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); - /* |obj| must still exist (its class is accessed) */ - static bool finalizeObject(JSObject *obj); +/* + * Object is about to be finalized. |obj| must still exist (its class is + * read) + */ +bool finalizeObject(JSObject *obj); - /* - * |string| does not need to contain any content yet; only its - * pointer value is used. |length| is the length of the string and - * does not imply anything about the amount of storage consumed to - * store the string. (It may be a short string, an external - * string, or a rope, and the encoding is not taken into - * consideration.) - */ - static bool createString(JSContext *cx, JSString *string, size_t length); +/* + * String has been created. + * + * |string|'s content is not (yet) valid. |length| is the length of the string + * and does not imply anything about the amount of storage consumed to store + * the string. (It may be a short string, an external string, or a rope, and + * the encoding is not taken into consideration.) + */ +bool createString(JSContext *cx, JSString *string, size_t length); - /* - * |string| must still have a valid length. - */ - static bool finalizeString(JSString *string); +/* + * String is about to be finalized + * + * |string| must still have a valid length. + */ +bool finalizeString(JSString *string); - static bool compileScriptBegin(JSContext *cx, const char *filename, int lineno); - static bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno); +/* Script is about to be compiled */ +bool compileScriptBegin(JSContext *cx, const char *filename, int lineno); - static bool calloutBegin(JSContext *cx, JSFunction *fun); - static bool calloutEnd(JSContext *cx, JSFunction *fun); +/* Script has just finished compilation */ +bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno); - static bool acquireMemory(JSContext *cx, void *address, size_t nbytes); - static bool releaseMemory(JSContext *cx, void *address, size_t nbytes); +/* About to make a call from JS into native code */ +bool calloutBegin(JSContext *cx, JSFunction *fun); - static bool GCStart(JSCompartment *compartment); - static bool GCEnd(JSCompartment *compartment); - static bool GCStartMarkPhase(JSCompartment *compartment); +/* Native code called by JS has terminated */ +bool calloutEnd(JSContext *cx, JSFunction *fun); - static bool GCEndMarkPhase(JSCompartment *compartment); - static bool GCStartSweepPhase(JSCompartment *compartment); - static bool GCEndSweepPhase(JSCompartment *compartment); +/* Unimplemented */ +bool acquireMemory(JSContext *cx, void *address, size_t nbytes); +bool releaseMemory(JSContext *cx, void *address, size_t nbytes); - static bool CustomMark(JSString *string); - static bool CustomMark(const char *string); - static bool CustomMark(int marker); +/* + * Garbage collection probes + * + * GC timing is tricky and at the time of this writing is changing frequently. + * GCStart(NULL)/GCEnd(NULL) are intended to bracket the entire garbage + * collection (either global or single-compartment), but a separate thread may + * continue doing work after GCEnd. + * + * Multiple compartments' GC will be interleaved during a global collection + * (eg, compartment 1 starts, compartment 2 starts, compartment 1 ends, ...) + */ +bool GCStart(JSCompartment *compartment); +bool GCEnd(JSCompartment *compartment); - static bool startProfiling(); - static void stopProfiling(); +bool GCStartMarkPhase(JSCompartment *compartment); +bool GCEndMarkPhase(JSCompartment *compartment); +bool GCStartSweepPhase(JSCompartment *compartment); +bool GCEndSweepPhase(JSCompartment *compartment); + +/* + * Various APIs for inserting custom probe points. These might be used to mark + * when something starts and stops, or for various other purposes the user has + * in mind. These are useful to export to JS so that JS code can mark + * application-meaningful events and phases of execution. + * + * Not all backends support these. + */ +bool CustomMark(JSString *string); +bool CustomMark(const char *string); +bool CustomMark(int marker); + +/* + * Internal: DTrace-specific functions to be called during Probes::enterJSFun + * and Probes::exitJSFun. These will not be inlined, but the argument + * marshalling required for these probe points is expensive enough that it + * shouldn't really matter. + */ +void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script); +void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script); + +/* + * Internal: ETW-specific probe functions + */ #ifdef MOZ_ETW - // ETW Handlers - static bool ETWCreateRuntime(JSRuntime *rt); - static bool ETWDestroyRuntime(JSRuntime *rt); - static bool ETWShutdown(); - static bool ETWCallTrackingActive(JSContext *cx); - static bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); - static bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); - static bool ETWCreateObject(JSContext *cx, JSObject *obj); - static bool ETWFinalizeObject(JSObject *obj); - static bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); - static bool ETWCreateString(JSContext *cx, JSString *string, size_t length); - static bool ETWFinalizeString(JSString *string); - static bool ETWCompileScriptBegin(const char *filename, int lineno); - static bool ETWCompileScriptEnd(const char *filename, int lineno); - static bool ETWCalloutBegin(JSContext *cx, JSFunction *fun); - static bool ETWCalloutEnd(JSContext *cx, JSFunction *fun); - static bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes); - static bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes); - static bool ETWGCStart(JSCompartment *compartment); - static bool ETWGCEnd(JSCompartment *compartment); - static bool ETWGCStartMarkPhase(JSCompartment *compartment); - static bool ETWGCEndMarkPhase(JSCompartment *compartment); - static bool ETWGCStartSweepPhase(JSCompartment *compartment); - static bool ETWGCEndSweepPhase(JSCompartment *compartment); - static bool ETWCustomMark(JSString *string); - static bool ETWCustomMark(const char *string); - static bool ETWCustomMark(int marker); - static bool ETWStartExecution(JSContext *cx, JSScript *script); - static bool ETWStopExecution(JSContext *cx, JSScript *script); - static bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); +// ETW Handlers +bool ETWCreateRuntime(JSRuntime *rt); +bool ETWDestroyRuntime(JSRuntime *rt); +bool ETWShutdown(); +bool ETWCallTrackingActive(JSContext *cx); +bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); +bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); +bool ETWCreateObject(JSContext *cx, JSObject *obj); +bool ETWFinalizeObject(JSObject *obj); +bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); +bool ETWCreateString(JSContext *cx, JSString *string, size_t length); +bool ETWFinalizeString(JSString *string); +bool ETWCompileScriptBegin(const char *filename, int lineno); +bool ETWCompileScriptEnd(const char *filename, int lineno); +bool ETWCalloutBegin(JSContext *cx, JSFunction *fun); +bool ETWCalloutEnd(JSContext *cx, JSFunction *fun); +bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes); +bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes); +bool ETWGCStart(JSCompartment *compartment); +bool ETWGCEnd(JSCompartment *compartment); +bool ETWGCStartMarkPhase(JSCompartment *compartment); +bool ETWGCEndMarkPhase(JSCompartment *compartment); +bool ETWGCStartSweepPhase(JSCompartment *compartment); +bool ETWGCEndSweepPhase(JSCompartment *compartment); +bool ETWCustomMark(JSString *string); +bool ETWCustomMark(const char *string); +bool ETWCustomMark(int marker); +bool ETWStartExecution(JSContext *cx, JSScript *script); +bool ETWStopExecution(JSContext *cx, JSScript *script); +bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); #endif -}; + +} /* namespace Probes */ + +/* + * Probe handlers are implemented inline for minimal performance impact, + * especially important when no backends are enabled. + */ inline bool Probes::createRuntime(JSRuntime *rt) @@ -258,30 +298,13 @@ Probes::callTrackingActive(JSContext *cx) return false; } -extern inline JS_FRIEND_API(JSBool) -js_PauseProfilers(JSContext *cx, uintN argc, jsval *vp) -{ - Probes::pauseProfilers(cx); - return JS_TRUE; -} - -extern inline JS_FRIEND_API(JSBool) -js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp) -{ - Probes::resumeProfilers(cx); - return JS_TRUE; -} - -extern JS_FRIEND_API(JSBool) -js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp); - inline bool Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter) { bool ok = true; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) - enterJSFunImpl(cx, fun, script); + DTraceEnterJSFun(cx, fun, script); #endif #ifdef MOZ_TRACE_JSCALLS cx->doFunctionCallback(fun, script, counter); @@ -301,7 +324,7 @@ Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter) #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) - handleFunctionReturn(cx, fun, script); + DTraceExitJSFun(cx, fun, script); #endif #ifdef MOZ_TRACE_JSCALLS if (counter > 0) @@ -329,6 +352,20 @@ Probes::resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize) return ok; } +#ifdef INCLUDE_MOZILLA_DTRACE +static const char *ObjectClassname(JSObject *obj) { + if (! obj) + return "(null object)"; + Class *clasp = obj->getClass(); + if (! clasp) + return "(null)"; + const char *class_name = clasp->name; + if (! class_name) + return "(null class name)"; + return class_name; +} +#endif + inline bool Probes::createObject(JSContext *cx, JSObject *obj) { @@ -657,5 +694,10 @@ struct AutoFunctionCallProbe { }; } /* namespace js */ - + +/* + * Internal functions for controlling various profilers. The profiler-specific + * implementations of these are mostly in jsdbgapi.cpp. + */ + #endif /* _JSPROBES_H */ diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index b74c680b67c8..49d05f8fa1f3 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3936,19 +3936,6 @@ static JSFunctionSpec shell_functions[] = { JS_FN("evalInFrame", EvalInFrame, 2,0), JS_FN("shapeOf", ShapeOf, 1,0), JS_FN("resolver", Resolver, 1,0), - JS_FN("pauseProfilers", js_PauseProfilers, 0,0), - JS_FN("resumeProfilers", js_ResumeProfilers, 0,0), -#ifdef MOZ_CALLGRIND - JS_FN("startCallgrind", js_StartCallgrind, 0,0), - JS_FN("stopCallgrind", js_StopCallgrind, 0,0), - JS_FN("dumpCallgrind", js_DumpCallgrind, 1,0), -#endif -#ifdef MOZ_VTUNE - JS_FN("startVtune", js_StartVtune, 1,0), - JS_FN("stopVtune", js_StopVtune, 0,0), - JS_FN("pauseVtune", js_PauseVtune, 0,0), - JS_FN("resumeVtune", js_ResumeVtune, 0,0), -#endif #ifdef MOZ_TRACEVIS JS_FN("startTraceVis", StartTraceVisNative, 1,0), JS_FN("stopTraceVis", StopTraceVisNative, 0,0), @@ -4075,19 +4062,6 @@ static const char *const shell_help_messages[] = { "shapeOf(obj) Get the shape of obj (an implementation detail)", "resolver(src[, proto]) Create object with resolve hook that copies properties\n" " from src. If proto is omitted, use Object.prototype.", -"pauseProfilers() Pause all profilers that can be paused", -"resumeProfilers() Resume profilers if they are paused", -#ifdef MOZ_CALLGRIND -"startCallgrind() Start callgrind instrumentation", -"stopCallgrind() Stop callgrind instrumentation", -"dumpCallgrind([name]) Dump callgrind counters", -#endif -#ifdef MOZ_VTUNE -"startVtune([filename]) Start vtune instrumentation", -"stopVtune() Stop vtune instrumentation", -"pauseVtune() Pause vtune collection", -"resumeVtune() Resume vtune collection", -#endif #ifdef MOZ_TRACEVIS "startTraceVis(filename) Start TraceVis recording (stops any current recording)", "stopTraceVis() Stop TraceVis recording", @@ -4129,20 +4103,50 @@ static const char *const shell_help_messages[] = { /* Keep these last: see the static assertion below. */ #ifdef MOZ_PROFILING -"startProfiling() Start a profiling session.\n" +"startProfiling([profileName])\n" +" Start a profiling session\n" " Profiler must be running with programatic sampling", -"stopProfiling() Stop a running profiling session\n" +"stopProfiling([profileName])\n" +" Stop a running profiling session", +"pauseProfilers([profileName])\n" +" Pause a running profiling session", +"resumeProfilers([profileName])\n" +" Resume a paused profiling session", +"dumpProfile([outfile[, profileName]])\n" +" Dump out current profile info (only valid for callgrind)", +# ifdef MOZ_CALLGRIND +"startCallgrind() Start Callgrind instrumentation", +"stopCallgrind() Stop Callgrind instrumentation", +"dumpCallgrind([outfile]) Dump current Callgrind counters to file or stdout", +# endif +# ifdef MOZ_VTUNE +"startVtune() Start Vtune instrumentation", +"stopVtune() Stop Vtune instrumentation", +"pauseVtune() Pause Vtune collection", +"resumeVtune() Resume Vtune collection", +# endif #endif }; #ifdef MOZ_PROFILING -#define PROFILING_FUNCTION_COUNT 2 +# define PROFILING_FUNCTION_COUNT 5 +# ifdef MOZ_CALLGRIND +# define CALLGRIND_FUNCTION_COUNT 3 +# else +# define CALLGRIND_FUNCTION_COUNT 0 +# endif +# ifdef MOZ_VTUNE +# define VTUNE_FUNCTION_COUNT 4 +# else +# define VTUNE_FUNCTION_COUNT 0 +# endif +# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT) #else -#define PROFILING_FUNCTION_COUNT 0 +# define EXTERNAL_FUNCTION_COUNT 0 #endif /* Help messages must match shell functions. */ -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT == +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - EXTERNAL_FUNCTION_COUNT == JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); #ifdef DEBUG @@ -4153,7 +4157,7 @@ CheckHelpMessages() const char *lp; /* Messages begin with "function_name(" prefix and don't end with \n. */ - for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - PROFILING_FUNCTION_COUNT; ++m) { + for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - EXTERNAL_FUNCTION_COUNT; ++m) { lp = strchr(*m, '('); JS_ASSERT(lp); JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, @@ -4166,6 +4170,9 @@ CheckHelpMessages() #endif #undef PROFILING_FUNCTION_COUNT +#undef CALLGRIND_FUNCTION_COUNT +#undef VTUNE_FUNCTION_COUNT +#undef EXTERNAL_FUNCTION_COUNT static JSBool Help(JSContext *cx, uintN argc, jsval *vp) @@ -5188,7 +5195,7 @@ Shell(JSContext *cx, OptionParser *op, char **envp) #endif /* JSDEBUGGER */ if (enableDisassemblyDumps) - JS_DumpAllProfiles(cx); + JS_DumpCompartmentBytecode(cx); return result; } diff --git a/js/src/xpconnect/loader/mozJSComponentLoader.cpp b/js/src/xpconnect/loader/mozJSComponentLoader.cpp index 4edb2e0ea4b3..985fc04cfc46 100644 --- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -291,17 +291,6 @@ static JSFunctionSpec gGlobalFun[] = { {"atob", Atob, 1,0}, {"btoa", Btoa, 1,0}, {"File", File, 1,JSFUN_CONSTRUCTOR}, -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, -#endif -#ifdef MOZ_VTUNE - {"startVtune", js_StartVtune, 1,0}, - {"stopVtune", js_StopVtune, 0,0}, - {"pauseVtune", js_PauseVtune, 0,0}, - {"resumeVtune", js_ResumeVtune, 0,0}, -#endif #ifdef MOZ_TRACEVIS {"initEthogram", js_InitEthogram, 0,0}, {"shutdownEthogram", js_ShutdownEthogram, 0,0}, diff --git a/js/src/xpconnect/shell/xpcshell.cpp b/js/src/xpconnect/shell/xpcshell.cpp index fa604b7459a1..e1725a2a63ea 100644 --- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -847,11 +847,6 @@ static JSFunctionSpec glob_functions[] = { #endif {"sendCommand", SendCommand, 1,0}, {"getChildGlobalObject", GetChildGlobalObject, 0,0}, -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, -#endif {nsnull,nsnull,0,0} }; diff --git a/xpcom/tests/TestHarness.h b/xpcom/tests/TestHarness.h index c3aa366a367b..fa80494baca4 100644 --- a/xpcom/tests/TestHarness.h +++ b/xpcom/tests/TestHarness.h @@ -56,6 +56,7 @@ #include "nsIFile.h" #include "nsIProperties.h" #include "nsXULAppAPI.h" +#include "jsdbgapi.h" #include #include #include @@ -95,17 +96,6 @@ void passed(const char* test) // Code profiling // static const char* gCurrentProfile; -static PRBool gProfilerTriedInit = PR_FALSE; -static PRBool gProfilerInited = PR_FALSE; - -// Platform profilers must implement these functions. -// Init and deinit are guaranteed to only be called once, and -// StartProfile/StopProfile may assume that they are only called -// when the profiler has successfully been initialized. -static PRBool _PlatformInitProfiler(); -static PRBool _PlatformStartProfile(const char* profileName); -static PRBool _PlatformStopProfile(const char* profileName); -static PRBool _PlatformDeinitProfiler(); /** * If the build has been configured properly, start the best code profiler @@ -124,19 +114,12 @@ static PRBool _PlatformDeinitProfiler(); inline PRBool StartProfiling(const char* profileName) { - if (!gProfilerTriedInit) { - gProfilerTriedInit = PR_TRUE; - gProfilerInited = _PlatformInitProfiler(); - } - if (!gProfilerInited) - return PR_FALSE; - NS_ASSERTION(profileName, "need a name for this profile"); NS_PRECONDITION(!gCurrentProfile, "started a new profile before stopping another"); - PRBool rv = _PlatformStartProfile(profileName); + JSBool ok = JS_StartProfiling(profileName); gCurrentProfile = profileName; - return rv; + return ok ? PR_TRUE : PR_FALSE; } /** @@ -153,78 +136,13 @@ StartProfiling(const char* profileName) inline PRBool StopProfiling() { - NS_ASSERTION(gProfilerTriedInit, "tried to stop profile before starting one"); - if (!gProfilerInited) - return PR_FALSE; - NS_PRECONDITION(gCurrentProfile, "tried to stop profile before starting one"); const char* profileName = gCurrentProfile; gCurrentProfile = 0; - return _PlatformStopProfile(profileName); + return JS_StopProfiling(profileName) ? PR_TRUE : PR_FALSE; } -//-------------------------------------------------- -// Shark impl -#if defined(MOZ_SHARK) -#include "jsdbgapi.h" - -static PRBool -_PlatformInitProfiler() -{ - return PR_TRUE; -} - -static PRBool -_PlatformStartProfile(const char* profileName) -{ - return JS_StartProfiling() ? PR_TRUE : PR_FALSE; -} - -static PRBool -_PlatformStopProfile(const char* profileName) -{ - JS_StopProfiling(); - return PR_TRUE; -} - -static PRBool -_PlatformDeinitProfiler() -{ - return PR_TRUE; -} - -//-------------------------------------------------- -// Default, no-profiler impl -#else - -static PRBool -_PlatformInitProfiler() -{ - NS_WARNING("Profiling is not available/configured for your platform."); - return PR_FALSE; -} -static PRBool -_PlatformStartProfile(const char* profileName) -{ - NS_WARNING("Profiling is not available/configured for your platform."); - return PR_FALSE; -} -static PRBool -_PlatformStopProfile(const char* profileName) -{ - NS_WARNING("Profiling is not available/configured for your platform."); - return PR_FALSE; -} -static PRBool -_PlatformDeinitProfiler() -{ - NS_WARNING("Profiling is not available/configured for your platform."); - return PR_FALSE; -} - -#endif - //----------------------------------------------------------------------------- class ScopedLogging @@ -264,10 +182,6 @@ class ScopedXPCOM : public nsIDirectoryServiceProvider2 ~ScopedXPCOM() { - if (gProfilerInited) - if (!_PlatformDeinitProfiler()) - NS_WARNING("Problem shutting down profiler"); - // If we created a profile directory, we need to remove it. if (mProfD) { if (NS_FAILED(mProfD->Remove(PR_TRUE))) From 5d41678b4a9ff817a48e44b22990d4ac742b0f6b Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Thu, 11 Aug 2011 19:35:48 -0700 Subject: [PATCH 30/51] Bug 677424: If a tab is closed between quit-application-requested and quit-application-granted then it is still restored when the application restarts. r=zpao --- browser/components/sessionstore/src/nsSessionStore.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/browser/components/sessionstore/src/nsSessionStore.js b/browser/components/sessionstore/src/nsSessionStore.js index 5066f00e93a7..9532fcfeb022 100644 --- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -1065,6 +1065,14 @@ SessionStoreService.prototype = { if (length > maxTabsUndo) this._windows[aWindow.__SSi]._closedTabs.splice(maxTabsUndo, length - maxTabsUndo); } + + // Remove the tab from the saved window state if it matches what is already + // there. This solves bug 677424 + if (aTab._tPos < this._windows[aWindow.__SSi].tabs.length) { + var oldTab = this._windows[aWindow.__SSi].tabs[aTab._tPos]; + if (oldTab.entries[oldTab.index - 1].url == aTab.linkedBrowser.currentURI.spec) + this._windows[aWindow.__SSi].tabs.splice(aTab._tPos, 1); + } }, /** From 790cf2a235989c3993e715c9350b1fd0146ea00f Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Fri, 12 Aug 2011 09:12:59 -0400 Subject: [PATCH 31/51] Bug 650494: Remove nsIXULPrototypeCache. r=smaug --- chrome/src/nsChromeProtocolHandler.cpp | 8 -- content/base/public/nsContentCID.h | 4 - content/xul/document/public/Makefile.in | 1 - .../document/public/nsIXULPrototypeCache.h | 91 ------------------- content/xul/document/src/nsXULDocument.h | 1 - .../xul/document/src/nsXULPrototypeCache.cpp | 18 +--- .../xul/document/src/nsXULPrototypeCache.h | 9 +- .../xul/templates/src/nsXULContentUtils.cpp | 1 - layout/build/nsLayoutModule.cpp | 5 - 9 files changed, 6 insertions(+), 132 deletions(-) delete mode 100644 content/xul/document/public/nsIXULPrototypeCache.h diff --git a/chrome/src/nsChromeProtocolHandler.cpp b/chrome/src/nsChromeProtocolHandler.cpp index ac88986e8f1d..9c0997b93d89 100644 --- a/chrome/src/nsChromeProtocolHandler.cpp +++ b/chrome/src/nsChromeProtocolHandler.cpp @@ -69,14 +69,6 @@ #include "nsString.h" #include "prlog.h" -#ifdef MOZ_XUL -#include "nsIXULPrototypeCache.h" -#endif - -//---------------------------------------------------------------------- - -static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); - //////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler, diff --git a/content/base/public/nsContentCID.h b/content/base/public/nsContentCID.h index 1933d2c91cf6..a65f781f07fb 100644 --- a/content/base/public/nsContentCID.h +++ b/content/base/public/nsContentCID.h @@ -186,10 +186,6 @@ #define NS_XULDOCUMENT_CID \ { 0x541afcb2, 0xa9a3, 0x11d2, { 0x8e, 0xc5, 0x0, 0x80, 0x5f, 0x29, 0xf3, 0x70 } } -// {3A0A0FC1-8349-11d3-BE47-00104BDE6048} -#define NS_XULPROTOTYPECACHE_CID \ -{ 0x3a0a0fc1, 0x8349, 0x11d3, { 0xbe, 0x47, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 } } - // {a6cf9126-15b3-11d2-932e-00805f8add32} #define NS_RANGEUTILS_CID \ { 0xa6cf9126, 0x15b3, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } diff --git a/content/xul/document/public/Makefile.in b/content/xul/document/public/Makefile.in index cb46aebd8113..d148aa40030c 100644 --- a/content/xul/document/public/Makefile.in +++ b/content/xul/document/public/Makefile.in @@ -57,7 +57,6 @@ XPIDLSRCS += \ EXPORTS = \ nsIXULDocument.h \ - nsIXULPrototypeCache.h \ $(NULL) endif diff --git a/content/xul/document/public/nsIXULPrototypeCache.h b/content/xul/document/public/nsIXULPrototypeCache.h deleted file mode 100644 index ba34f29c508f..000000000000 --- a/content/xul/document/public/nsIXULPrototypeCache.h +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ben Goodger - * - * 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 ***** */ - -#ifndef nsIXULPrototypeCache_h__ -#define nsIXULPrototypeCache_h__ - -#include "nsISupports.h" -class nsIURI; - -// {3A0A0FC1-8349-11d3-BE47-00104BDE6048} -#define NS_XULPROTOTYPECACHE_CID \ -{ 0x3a0a0fc1, 0x8349, 0x11d3, { 0xbe, 0x47, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 } } - -// {f8bee3d7-4be8-46ae-92c2-60c25d5cd647} -#define NS_IXULPROTOTYPECACHE_IID \ -{ 0xf8bee3d7, 0x4be8, 0x46ae, \ - { 0x92, 0xc2, 0x60, 0xc2, 0x5d, 0x5c, 0xd6, 0x47 } } - -/** - * This interface lets code from outside gklayout access the prototype cache. - */ -class nsIXULPrototypeCache : public nsISupports -{ -public: - NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULPROTOTYPECACHE_IID) - - /** - * Whether the XUL document at the specified URI is in the cache. - */ - virtual PRBool IsCached(nsIURI* aURI) = 0; - - /** - * Stop the caching process abruptly, removing the cache file. - */ - virtual void AbortCaching() = 0; -}; - -NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPrototypeCache, NS_IXULPROTOTYPECACHE_IID) - -nsresult -NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult); - - -const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL"; - -// Increase the subtractor when changing version, say when changing the -// (opaque to XPCOM FastLoad code) format of XUL-specific XDR serializations. -// See also JSXDR_BYTECODE_VERSION in jsxdrapi.h, which tracks incompatible JS -// bytecode version changes. -#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 25) - -#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024) -#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024) - - -#endif // nsIXULPrototypeCache_h__ diff --git a/content/xul/document/src/nsXULDocument.h b/content/xul/document/src/nsXULDocument.h index 22bd805ae350..f6c44823907f 100644 --- a/content/xul/document/src/nsXULDocument.h +++ b/content/xul/document/src/nsXULDocument.h @@ -60,7 +60,6 @@ class nsIRDFResource; class nsIRDFService; -class nsIXULPrototypeCache; class nsPIWindowRoot; #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript)) class nsIObjectInputStream; diff --git a/content/xul/document/src/nsXULPrototypeCache.cpp b/content/xul/document/src/nsXULPrototypeCache.cpp index d6698597b4e0..edcf0ad12919 100644 --- a/content/xul/document/src/nsXULPrototypeCache.cpp +++ b/content/xul/document/src/nsXULPrototypeCache.cpp @@ -69,8 +69,6 @@ using namespace mozilla; using namespace mozilla::scache; -static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); - static PRBool gDisableXULCache = PR_FALSE; // enabled by default static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache"; static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache"; @@ -109,10 +107,7 @@ nsXULPrototypeCache::~nsXULPrototypeCache() } -NS_IMPL_THREADSAFE_ISUPPORTS2(nsXULPrototypeCache, - nsIXULPrototypeCache, - nsIObserver) - +NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULPrototypeCache, nsIObserver) nsresult NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult) @@ -161,13 +156,8 @@ NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult) /* static */ nsXULPrototypeCache* nsXULPrototypeCache::GetInstance() { - // Theoretically this can return nsnull and callers should handle that. if (!sInstance) { - nsIXULPrototypeCache* cache; - - CallGetService(kXULPrototypeCacheCID, &cache); - - sInstance = static_cast(cache); + NS_ADDREF(sInstance = new nsXULPrototypeCache()); } return sInstance; } @@ -587,9 +577,7 @@ CachePrefChangedCallback(const char* aPref, void* aClosure) gDisableXULDiskCache); if (wasEnabled && gDisableXULDiskCache) { - static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); - nsCOMPtr cache = - do_GetService(kXULPrototypeCacheCID); + nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); if (cache) cache->AbortCaching(); diff --git a/content/xul/document/src/nsXULPrototypeCache.h b/content/xul/document/src/nsXULPrototypeCache.h index 38e1ff183212..4683068aebea 100644 --- a/content/xul/document/src/nsXULPrototypeCache.h +++ b/content/xul/document/src/nsXULPrototypeCache.h @@ -46,7 +46,6 @@ #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsXBLDocumentInfo.h" -#include "nsIXULPrototypeCache.h" #include "nsDataHashtable.h" #include "nsInterfaceHashtable.h" #include "nsRefPtrHashtable.h" @@ -74,19 +73,17 @@ struct CacheScriptEntry * 1. In-memory hashtables * 2. The on-disk cache file. */ -class nsXULPrototypeCache : public nsIXULPrototypeCache, - nsIObserver +class nsXULPrototypeCache : public nsIObserver { public: // nsISupports NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - // nsIXULPrototypeCache - virtual PRBool IsCached(nsIURI* aURI) { + PRBool IsCached(nsIURI* aURI) { return GetPrototype(aURI) != nsnull; } - virtual void AbortCaching(); + void AbortCaching(); /** diff --git a/content/xul/templates/src/nsXULContentUtils.cpp b/content/xul/templates/src/nsXULContentUtils.cpp index c64665474a7b..2f2160886ee0 100644 --- a/content/xul/templates/src/nsXULContentUtils.cpp +++ b/content/xul/templates/src/nsXULContentUtils.cpp @@ -71,7 +71,6 @@ #include "nsIServiceManager.h" #include "nsIURL.h" #include "nsXULContentUtils.h" -#include "nsIXULPrototypeCache.h" #include "nsLayoutCID.h" #include "nsNetUtil.h" #include "nsRDFCID.h" diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 8f8138fdd6f0..187e6abf32eb 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -260,7 +260,6 @@ static NS_DEFINE_CID(kWindowCommandTableCID, NS_WINDOWCOMMANDTABLE_CID); #ifdef MOZ_XUL #include "nsIXULDocument.h" -#include "nsIXULPrototypeCache.h" #include "nsIXULSortService.h" nsresult @@ -539,7 +538,6 @@ MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXUL // NS_NewXULTreeBuilder MAKE_CTOR(CreateXULDocument, nsIXULDocument, NS_NewXULDocument) // NS_NewXULControllers -// NS_NewXULPrototypeCache MAKE_CTOR(CreateXULPopupManager, nsISupports, NS_NewXULPopupManager) #endif #ifdef MOZ_XTF @@ -785,7 +783,6 @@ NS_DEFINE_NAMED_CID(NS_XULTEMPLATEBUILDER_CID); NS_DEFINE_NAMED_CID(NS_XULTREEBUILDER_CID); NS_DEFINE_NAMED_CID(NS_XULPOPUPMANAGER_CID); NS_DEFINE_NAMED_CID(NS_XULDOCUMENT_CID); -NS_DEFINE_NAMED_CID(NS_XULPROTOTYPECACHE_CID); #endif #ifdef MOZ_XTF NS_DEFINE_NAMED_CID(NS_XTFSERVICE_CID); @@ -920,7 +917,6 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_XULTREEBUILDER_CID, false, NULL, NS_NewXULTreeBuilder }, { &kNS_XULPOPUPMANAGER_CID, false, NULL, CreateXULPopupManager }, { &kNS_XULDOCUMENT_CID, false, NULL, CreateXULDocument }, - { &kNS_XULPROTOTYPECACHE_CID, false, NULL, NS_NewXULPrototypeCache }, #endif #ifdef MOZ_XTF { &kNS_XTFSERVICE_CID, false, NULL, CreateXTFService }, @@ -1056,7 +1052,6 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { "@mozilla.org/xul/xul-tree-builder;1", &kNS_XULTREEBUILDER_CID }, { "@mozilla.org/xul/xul-popup-manager;1", &kNS_XULPOPUPMANAGER_CID }, { "@mozilla.org/xul/xul-document;1", &kNS_XULDOCUMENT_CID }, - { "@mozilla.org/xul/xul-prototype-cache;1", &kNS_XULPROTOTYPECACHE_CID }, #endif #ifdef MOZ_XTF { NS_XTFSERVICE_CONTRACTID, &kNS_XTFSERVICE_CID }, From 36d6f9a425bb65e8ec685bee681ea43d243d4857 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 12 Aug 2011 12:06:46 -0400 Subject: [PATCH 32/51] Bug 678479: Add a --regen argument to header.py and typelib.py to regenerate the IDL parser. r=ted --- xpcom/idl-parser/header.py | 10 ++++++++++ xpcom/idl-parser/typelib.py | 10 ++++++++++ xpcom/idl-parser/xpidl.py | 6 +++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/xpcom/idl-parser/header.py b/xpcom/idl-parser/header.py index 7ecfa30e4240..8d43013134d1 100644 --- a/xpcom/idl-parser/header.py +++ b/xpcom/idl-parser/header.py @@ -483,6 +483,8 @@ if __name__ == '__main__': help="Output file (default is stdout)") o.add_option('-d', dest='depfile', default=None, help="Generate a make dependency file") + o.add_option('--regen', action='store_true', dest='regen', default=False, + help="Regenerate IDL Parser cache") options, args = o.parse_args() file, = args @@ -491,6 +493,14 @@ if __name__ == '__main__': os.mkdir(options.cachedir) sys.path.append(options.cachedir) + if options.regen: + if options.cachedir is None: + print >>sys.stderr, "--regen requires --cachedir" + sys.exit(1) + + p = xpidl.IDLParser(outputdir=options.cachedir, regen=True) + sys.exit(0) + if options.depfile is not None and options.outfile is None: print >>sys.stderr, "-d requires -o" sys.exit(1) diff --git a/xpcom/idl-parser/typelib.py b/xpcom/idl-parser/typelib.py index 2b55673f1e77..0c6bc386dfd1 100644 --- a/xpcom/idl-parser/typelib.py +++ b/xpcom/idl-parser/typelib.py @@ -281,6 +281,8 @@ if __name__ == '__main__': help="Output file") o.add_option('-d', dest='depfile', default=None, help="Generate a make dependency file") + o.add_option('--regen', action='store_true', dest='regen', default=False, + help="Regenerate IDL Parser cache") options, args = o.parse_args() file, = args @@ -289,6 +291,14 @@ if __name__ == '__main__': os.mkdir(options.cachedir) sys.path.append(options.cachedir) + if options.regen: + if options.cachedir is None: + print >>sys.stderr, "--regen requires --cachedir" + sys.exit(1) + + p = xpidl.IDLParser(outputdir=options.cachedir, regen=True) + sys.exit(0) + if options.depfile is not None and options.outfile is None: print >>sys.stderr, "-d requires -o" sys.exit(1) diff --git a/xpcom/idl-parser/xpidl.py b/xpcom/idl-parser/xpidl.py index d3cbff743fe0..f568befc5c89 100644 --- a/xpcom/idl-parser/xpidl.py +++ b/xpcom/idl-parser/xpidl.py @@ -1336,17 +1336,17 @@ class IDLParser(object): location = Location(self.lexer, t.lineno, t.lexpos) raise IDLError("invalid syntax", location) - def __init__(self, outputdir=''): + def __init__(self, outputdir='', regen=False): self._doccomments = [] self.lexer = lex.lex(object=self, outputdir=outputdir, lextab='xpidllex', - optimize=1) + optimize=0 if regen else 1) self.parser = yacc.yacc(module=self, outputdir=outputdir, debugfile='xpidl_debug', tabmodule='xpidlyacc', - optimize=1) + optimize=0 if regen else 1) def clearComments(self): self._doccomments = [] From ccbb26c3abfca788e548d499a113ce463075e98a Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 12 Aug 2011 12:07:09 -0400 Subject: [PATCH 33/51] Bug 678479: Add a makefile target in xpcom/ to regenerate the IDL parser. r=ted --- xpcom/Makefile.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/xpcom/Makefile.in b/xpcom/Makefile.in index 8af06ee85bd2..ac811ddf906d 100644 --- a/xpcom/Makefile.in +++ b/xpcom/Makefile.in @@ -90,3 +90,9 @@ endif include $(topsrcdir)/config/rules.mk +regenerate-idl-parser: + $(PYTHON_PATH) \ + -I$(topsrcdir)/other-licenses/ply \ + -I$(topsrcdir)/xpcom/idl-parser \ + $(topsrcdir)/xpcom/idl-parser/header.py --cachedir=$(topsrcdir)/xpcom/idl-parser --regen + From 92fccd2a28446bc8caa092069f276afa8e2707a9 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 12 Aug 2011 12:58:06 -0400 Subject: [PATCH 34/51] Backed out changeset b021d1f9f57d --- chrome/src/nsChromeProtocolHandler.cpp | 8 ++ content/base/public/nsContentCID.h | 4 + content/xul/document/public/Makefile.in | 1 + .../document/public/nsIXULPrototypeCache.h | 91 +++++++++++++++++++ content/xul/document/src/nsXULDocument.h | 1 + .../xul/document/src/nsXULPrototypeCache.cpp | 18 +++- .../xul/document/src/nsXULPrototypeCache.h | 9 +- .../xul/templates/src/nsXULContentUtils.cpp | 1 + layout/build/nsLayoutModule.cpp | 5 + 9 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 content/xul/document/public/nsIXULPrototypeCache.h diff --git a/chrome/src/nsChromeProtocolHandler.cpp b/chrome/src/nsChromeProtocolHandler.cpp index 9c0997b93d89..ac88986e8f1d 100644 --- a/chrome/src/nsChromeProtocolHandler.cpp +++ b/chrome/src/nsChromeProtocolHandler.cpp @@ -69,6 +69,14 @@ #include "nsString.h" #include "prlog.h" +#ifdef MOZ_XUL +#include "nsIXULPrototypeCache.h" +#endif + +//---------------------------------------------------------------------- + +static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); + //////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS2(nsChromeProtocolHandler, diff --git a/content/base/public/nsContentCID.h b/content/base/public/nsContentCID.h index a65f781f07fb..1933d2c91cf6 100644 --- a/content/base/public/nsContentCID.h +++ b/content/base/public/nsContentCID.h @@ -186,6 +186,10 @@ #define NS_XULDOCUMENT_CID \ { 0x541afcb2, 0xa9a3, 0x11d2, { 0x8e, 0xc5, 0x0, 0x80, 0x5f, 0x29, 0xf3, 0x70 } } +// {3A0A0FC1-8349-11d3-BE47-00104BDE6048} +#define NS_XULPROTOTYPECACHE_CID \ +{ 0x3a0a0fc1, 0x8349, 0x11d3, { 0xbe, 0x47, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 } } + // {a6cf9126-15b3-11d2-932e-00805f8add32} #define NS_RANGEUTILS_CID \ { 0xa6cf9126, 0x15b3, 0x11d2, {0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32 } } diff --git a/content/xul/document/public/Makefile.in b/content/xul/document/public/Makefile.in index d148aa40030c..cb46aebd8113 100644 --- a/content/xul/document/public/Makefile.in +++ b/content/xul/document/public/Makefile.in @@ -57,6 +57,7 @@ XPIDLSRCS += \ EXPORTS = \ nsIXULDocument.h \ + nsIXULPrototypeCache.h \ $(NULL) endif diff --git a/content/xul/document/public/nsIXULPrototypeCache.h b/content/xul/document/public/nsIXULPrototypeCache.h new file mode 100644 index 000000000000..ba34f29c508f --- /dev/null +++ b/content/xul/document/public/nsIXULPrototypeCache.h @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Goodger + * + * 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 ***** */ + +#ifndef nsIXULPrototypeCache_h__ +#define nsIXULPrototypeCache_h__ + +#include "nsISupports.h" +class nsIURI; + +// {3A0A0FC1-8349-11d3-BE47-00104BDE6048} +#define NS_XULPROTOTYPECACHE_CID \ +{ 0x3a0a0fc1, 0x8349, 0x11d3, { 0xbe, 0x47, 0x0, 0x10, 0x4b, 0xde, 0x60, 0x48 } } + +// {f8bee3d7-4be8-46ae-92c2-60c25d5cd647} +#define NS_IXULPROTOTYPECACHE_IID \ +{ 0xf8bee3d7, 0x4be8, 0x46ae, \ + { 0x92, 0xc2, 0x60, 0xc2, 0x5d, 0x5c, 0xd6, 0x47 } } + +/** + * This interface lets code from outside gklayout access the prototype cache. + */ +class nsIXULPrototypeCache : public nsISupports +{ +public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXULPROTOTYPECACHE_IID) + + /** + * Whether the XUL document at the specified URI is in the cache. + */ + virtual PRBool IsCached(nsIURI* aURI) = 0; + + /** + * Stop the caching process abruptly, removing the cache file. + */ + virtual void AbortCaching() = 0; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsIXULPrototypeCache, NS_IXULPROTOTYPECACHE_IID) + +nsresult +NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + +const char XUL_FASTLOAD_FILE_BASENAME[] = "XUL"; + +// Increase the subtractor when changing version, say when changing the +// (opaque to XPCOM FastLoad code) format of XUL-specific XDR serializations. +// See also JSXDR_BYTECODE_VERSION in jsxdrapi.h, which tracks incompatible JS +// bytecode version changes. +#define XUL_FASTLOAD_FILE_VERSION (0xfeedbeef - 25) + +#define XUL_SERIALIZATION_BUFFER_SIZE (64 * 1024) +#define XUL_DESERIALIZATION_BUFFER_SIZE (8 * 1024) + + +#endif // nsIXULPrototypeCache_h__ diff --git a/content/xul/document/src/nsXULDocument.h b/content/xul/document/src/nsXULDocument.h index f6c44823907f..22bd805ae350 100644 --- a/content/xul/document/src/nsXULDocument.h +++ b/content/xul/document/src/nsXULDocument.h @@ -60,6 +60,7 @@ class nsIRDFResource; class nsIRDFService; +class nsIXULPrototypeCache; class nsPIWindowRoot; #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript)) class nsIObjectInputStream; diff --git a/content/xul/document/src/nsXULPrototypeCache.cpp b/content/xul/document/src/nsXULPrototypeCache.cpp index edcf0ad12919..d6698597b4e0 100644 --- a/content/xul/document/src/nsXULPrototypeCache.cpp +++ b/content/xul/document/src/nsXULPrototypeCache.cpp @@ -69,6 +69,8 @@ using namespace mozilla; using namespace mozilla::scache; +static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); + static PRBool gDisableXULCache = PR_FALSE; // enabled by default static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache"; static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache"; @@ -107,7 +109,10 @@ nsXULPrototypeCache::~nsXULPrototypeCache() } -NS_IMPL_THREADSAFE_ISUPPORTS1(nsXULPrototypeCache, nsIObserver) +NS_IMPL_THREADSAFE_ISUPPORTS2(nsXULPrototypeCache, + nsIXULPrototypeCache, + nsIObserver) + nsresult NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult) @@ -156,8 +161,13 @@ NS_NewXULPrototypeCache(nsISupports* aOuter, REFNSIID aIID, void** aResult) /* static */ nsXULPrototypeCache* nsXULPrototypeCache::GetInstance() { + // Theoretically this can return nsnull and callers should handle that. if (!sInstance) { - NS_ADDREF(sInstance = new nsXULPrototypeCache()); + nsIXULPrototypeCache* cache; + + CallGetService(kXULPrototypeCacheCID, &cache); + + sInstance = static_cast(cache); } return sInstance; } @@ -577,7 +587,9 @@ CachePrefChangedCallback(const char* aPref, void* aClosure) gDisableXULDiskCache); if (wasEnabled && gDisableXULDiskCache) { - nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance(); + static NS_DEFINE_CID(kXULPrototypeCacheCID, NS_XULPROTOTYPECACHE_CID); + nsCOMPtr cache = + do_GetService(kXULPrototypeCacheCID); if (cache) cache->AbortCaching(); diff --git a/content/xul/document/src/nsXULPrototypeCache.h b/content/xul/document/src/nsXULPrototypeCache.h index 4683068aebea..38e1ff183212 100644 --- a/content/xul/document/src/nsXULPrototypeCache.h +++ b/content/xul/document/src/nsXULPrototypeCache.h @@ -46,6 +46,7 @@ #include "nsCOMPtr.h" #include "nsIObserver.h" #include "nsXBLDocumentInfo.h" +#include "nsIXULPrototypeCache.h" #include "nsDataHashtable.h" #include "nsInterfaceHashtable.h" #include "nsRefPtrHashtable.h" @@ -73,17 +74,19 @@ struct CacheScriptEntry * 1. In-memory hashtables * 2. The on-disk cache file. */ -class nsXULPrototypeCache : public nsIObserver +class nsXULPrototypeCache : public nsIXULPrototypeCache, + nsIObserver { public: // nsISupports NS_DECL_ISUPPORTS NS_DECL_NSIOBSERVER - PRBool IsCached(nsIURI* aURI) { + // nsIXULPrototypeCache + virtual PRBool IsCached(nsIURI* aURI) { return GetPrototype(aURI) != nsnull; } - void AbortCaching(); + virtual void AbortCaching(); /** diff --git a/content/xul/templates/src/nsXULContentUtils.cpp b/content/xul/templates/src/nsXULContentUtils.cpp index 2f2160886ee0..c64665474a7b 100644 --- a/content/xul/templates/src/nsXULContentUtils.cpp +++ b/content/xul/templates/src/nsXULContentUtils.cpp @@ -71,6 +71,7 @@ #include "nsIServiceManager.h" #include "nsIURL.h" #include "nsXULContentUtils.h" +#include "nsIXULPrototypeCache.h" #include "nsLayoutCID.h" #include "nsNetUtil.h" #include "nsRDFCID.h" diff --git a/layout/build/nsLayoutModule.cpp b/layout/build/nsLayoutModule.cpp index 187e6abf32eb..8f8138fdd6f0 100644 --- a/layout/build/nsLayoutModule.cpp +++ b/layout/build/nsLayoutModule.cpp @@ -260,6 +260,7 @@ static NS_DEFINE_CID(kWindowCommandTableCID, NS_WINDOWCOMMANDTABLE_CID); #ifdef MOZ_XUL #include "nsIXULDocument.h" +#include "nsIXULPrototypeCache.h" #include "nsIXULSortService.h" nsresult @@ -538,6 +539,7 @@ MAKE_CTOR(CreateXULSortService, nsIXULSortService, NS_NewXUL // NS_NewXULTreeBuilder MAKE_CTOR(CreateXULDocument, nsIXULDocument, NS_NewXULDocument) // NS_NewXULControllers +// NS_NewXULPrototypeCache MAKE_CTOR(CreateXULPopupManager, nsISupports, NS_NewXULPopupManager) #endif #ifdef MOZ_XTF @@ -783,6 +785,7 @@ NS_DEFINE_NAMED_CID(NS_XULTEMPLATEBUILDER_CID); NS_DEFINE_NAMED_CID(NS_XULTREEBUILDER_CID); NS_DEFINE_NAMED_CID(NS_XULPOPUPMANAGER_CID); NS_DEFINE_NAMED_CID(NS_XULDOCUMENT_CID); +NS_DEFINE_NAMED_CID(NS_XULPROTOTYPECACHE_CID); #endif #ifdef MOZ_XTF NS_DEFINE_NAMED_CID(NS_XTFSERVICE_CID); @@ -917,6 +920,7 @@ static const mozilla::Module::CIDEntry kLayoutCIDs[] = { { &kNS_XULTREEBUILDER_CID, false, NULL, NS_NewXULTreeBuilder }, { &kNS_XULPOPUPMANAGER_CID, false, NULL, CreateXULPopupManager }, { &kNS_XULDOCUMENT_CID, false, NULL, CreateXULDocument }, + { &kNS_XULPROTOTYPECACHE_CID, false, NULL, NS_NewXULPrototypeCache }, #endif #ifdef MOZ_XTF { &kNS_XTFSERVICE_CID, false, NULL, CreateXTFService }, @@ -1052,6 +1056,7 @@ static const mozilla::Module::ContractIDEntry kLayoutContracts[] = { { "@mozilla.org/xul/xul-tree-builder;1", &kNS_XULTREEBUILDER_CID }, { "@mozilla.org/xul/xul-popup-manager;1", &kNS_XULPOPUPMANAGER_CID }, { "@mozilla.org/xul/xul-document;1", &kNS_XULDOCUMENT_CID }, + { "@mozilla.org/xul/xul-prototype-cache;1", &kNS_XULPROTOTYPECACHE_CID }, #endif #ifdef MOZ_XTF { NS_XTFSERVICE_CONTRACTID, &kNS_XTFSERVICE_CID }, From 4bf1621dbc6bbc03133e133c2bab34bc5f577ea2 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Fri, 12 Aug 2011 12:58:43 -0400 Subject: [PATCH 35/51] Bug 673742: Allow postMessage()ing File and FileLists between same-origin windows. r=sicking --- dom/base/Makefile.in | 5 ++ dom/base/StructuredCloneTags.h | 55 ++++++++++++++++ dom/base/nsGlobalWindow.cpp | 116 ++++++++++++++++++++++++++++++++- 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 dom/base/StructuredCloneTags.h diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index d2d81f42744e..c492f3ec5128 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -88,6 +88,11 @@ EXPORTS = \ nsDOMMemoryReporter.h \ $(NULL) +EXPORTS_NAMESPACES = mozilla/dom +EXPORTS_mozilla/dom = \ + StructuredCloneTags.h \ + $(NULL) + CPPSRCS = \ nsBarProps.cpp \ nsDOMException.cpp \ diff --git a/dom/base/StructuredCloneTags.h b/dom/base/StructuredCloneTags.h new file mode 100644 index 000000000000..d866c75f86ee --- /dev/null +++ b/dom/base/StructuredCloneTags.h @@ -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 Mozilla Structured Clone Code. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Kyle Huey + * + * 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 StructuredCloneTags_h__ +#define StructuredCloneTags_h__ + +#include "jsapi.h" + +namespace mozilla { +namespace dom { + +enum StructuredCloneTags { + SCTAG_BASE = JS_SCTAG_USER_MIN, + SCTAG_DOM_BLOB, + SCTAG_DOM_FILELIST, + SCTAG_DOM_MAX +}; + +} // namespace dom +} // namespace mozilla + +#endif // StructuredCloneTags_h__ diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index adbd43debb10..2fcfb5631d7c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -179,6 +179,7 @@ #include "nsCSSProps.h" #include "nsFileDataProtocolHandler.h" #include "nsIDOMFile.h" +#include "nsIDOMFileList.h" #include "nsIURIFixup.h" #include "mozilla/FunctionTimer.h" #include "nsCDefaultURIFixup.h" @@ -240,6 +241,8 @@ #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h" +#include "mozilla/dom/StructuredCloneTags.h" + #include "nsRefreshDriver.h" #include "mozAutoDocUpdate.h" @@ -5911,7 +5914,6 @@ nsGlobalWindow::CallerInnerWindow() return static_cast(win.get()); } - /** * Class used to represent events generated by calls to Window.postMessage, * which asynchronously creates and dispatches events. @@ -5949,6 +5951,12 @@ class PostMessageEvent : public nsRunnable aBuffer.steal(&mMessage, &mMessageLen); } + bool StoreISupports(nsISupports* aSupports) + { + mSupportsArray.AppendElement(aSupports); + return true; + } + private: nsRefPtr mSource; nsString mCallerOrigin; @@ -5957,8 +5965,102 @@ class PostMessageEvent : public nsRunnable nsRefPtr mTargetWindow; nsCOMPtr mProvidedOrigin; PRBool mTrustedCaller; + nsTArray > mSupportsArray; }; +namespace { + +struct StructuredCloneInfo { + PostMessageEvent* event; + PRBool subsumes; +}; + +static JSObject* +PostMessageReadStructuredClone(JSContext* cx, + JSStructuredCloneReader* reader, + uint32 tag, + uint32 data, + void* closure) +{ + StructuredCloneInfo* scInfo = static_cast(closure); + NS_ASSERTION(scInfo, "Must have scInfo!"); + + if (tag == SCTAG_DOM_BLOB || tag == SCTAG_DOM_FILELIST) { + NS_ASSERTION(!data, "Data should be empty"); + + nsISupports* supports; + if (JS_ReadBytes(reader, &supports, sizeof(supports))) { + JSObject* global = JS_GetGlobalForObject(cx, JS_GetScopeChain(cx)); + if (global) { + jsval val; + nsCOMPtr wrapper; + if (NS_SUCCEEDED(nsContentUtils::WrapNative(cx, global, supports, + &val, + getter_AddRefs(wrapper)))) { + return JSVAL_TO_OBJECT(val); + } + } + } + } + + const JSStructuredCloneCallbacks* runtimeCallbacks = + cx->runtime->structuredCloneCallbacks; + + if (runtimeCallbacks) { + return runtimeCallbacks->read(cx, reader, tag, data, nsnull); + } + + return JS_FALSE; +} + +static JSBool +PostMessageWriteStructuredClone(JSContext* cx, + JSStructuredCloneWriter* writer, + JSObject* obj, + void *closure) +{ + StructuredCloneInfo* scInfo = static_cast(closure); + NS_ASSERTION(scInfo, "Must have scInfo!"); + + nsCOMPtr wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative)); + if (wrappedNative) { + PRUint32 scTag = 0; + nsISupports* supports = wrappedNative->Native(); + + nsCOMPtr blob = do_QueryInterface(supports); + if (blob && scInfo->subsumes) + scTag = SCTAG_DOM_BLOB; + + nsCOMPtr list = do_QueryInterface(supports); + if (list && scInfo->subsumes) + scTag = SCTAG_DOM_FILELIST; + + if (scTag) + return JS_WriteUint32Pair(writer, scTag, 0) && + JS_WriteBytes(writer, &supports, sizeof(supports)) && + scInfo->event->StoreISupports(supports); + } + + const JSStructuredCloneCallbacks* runtimeCallbacks = + cx->runtime->structuredCloneCallbacks; + + if (runtimeCallbacks) { + return runtimeCallbacks->write(cx, writer, obj, nsnull); + } + + return JS_FALSE; +} + +JSStructuredCloneCallbacks kPostMessageCallbacks = { + PostMessageReadStructuredClone, + PostMessageWriteStructuredClone, + nsnull +}; + +} // anonymous namespace + NS_IMETHODIMP PostMessageEvent::Run() { @@ -6046,8 +6148,10 @@ PostMessageEvent::Run() jsval messageData; { JSAutoRequest ar(cx); + StructuredCloneInfo scInfo; + scInfo.event = this; - if (!buffer.read(cx, &messageData, nsnull)) + if (!buffer.read(cx, &messageData, &kPostMessageCallbacks, &scInfo)) return NS_ERROR_DOM_DATA_CLONE_ERR; } @@ -6173,8 +6277,14 @@ nsGlobalWindow::PostMessageMoz(const jsval& aMessage, // We *must* clone the data here, or the jsval could be modified // by script JSAutoStructuredCloneBuffer buffer; + StructuredCloneInfo scInfo; + scInfo.event = event; - if (!buffer.write(aCx, aMessage, nsnull, nsnull)) + nsIPrincipal* principal = GetPrincipal(); + if (NS_FAILED(callerPrin->Subsumes(principal, &scInfo.subsumes))) + return NS_ERROR_DOM_DATA_CLONE_ERR; + + if (!buffer.write(aCx, aMessage, &kPostMessageCallbacks, &scInfo)) return NS_ERROR_DOM_DATA_CLONE_ERR; event->SetJSData(buffer); From 308b60ecc8643781181461e3750af7b06698d34e Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Fri, 12 Aug 2011 13:50:47 -0700 Subject: [PATCH 36/51] Bug 476430: Disable third-party add-ons by default and offer them to the user. r=Unfocused, r=robstrong --- browser/app/profile/firefox.js | 5 + browser/components/nsBrowserGlue.js | 16 ++ docshell/base/nsAboutRedirector.cpp | 2 + docshell/build/nsDocShellModule.cpp | 1 + .../chrome/mozapps/extensions/newaddon.dtd | 11 ++ .../mozapps/extensions/newaddon.properties | 6 + toolkit/locales/jar.mn | 2 + .../mozapps/extensions/content/newaddon.js | 162 +++++++++++++++ .../mozapps/extensions/content/newaddon.xul | 99 ++++++++++ toolkit/mozapps/extensions/jar.mn | 2 + .../extensions/test/browser/Makefile.in | 1 + .../test/browser/browser_newaddon.js | 186 ++++++++++++++++++ .../mozapps/extensions/test/browser/head.js | 4 + .../mozapps/extensions/newaddon.css | 136 +++++++++++++ toolkit/themes/gnomestripe/mozapps/jar.mn | 1 + .../pinstripe/mozapps/extensions/newaddon.css | 138 +++++++++++++ toolkit/themes/pinstripe/mozapps/jar.mn | 1 + .../mozapps/extensions/newaddon-aero.css | 3 + .../winstripe/mozapps/extensions/newaddon.css | 160 +++++++++++++++ toolkit/themes/winstripe/mozapps/jar.mn | 2 + 20 files changed, 938 insertions(+) create mode 100644 toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd create mode 100644 toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties create mode 100644 toolkit/mozapps/extensions/content/newaddon.js create mode 100644 toolkit/mozapps/extensions/content/newaddon.xul create mode 100644 toolkit/mozapps/extensions/test/browser/browser_newaddon.js create mode 100644 toolkit/themes/gnomestripe/mozapps/extensions/newaddon.css create mode 100644 toolkit/themes/pinstripe/mozapps/extensions/newaddon.css create mode 100644 toolkit/themes/winstripe/mozapps/extensions/newaddon-aero.css create mode 100644 toolkit/themes/winstripe/mozapps/extensions/newaddon.css diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 4760737bdaf2..ef656c2b9699 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -74,6 +74,11 @@ pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/ pref("extensions.update.autoUpdateDefault", true); +// Disable add-ons installed into the shared user and shared system areas by +// default. This does not include the application directory. See the SCOPE +// constants in AddonManager.jsm for values to use here +pref("extensions.autoDisableScopes", 10); + // Dictionary download preference pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/"); diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 167d51597357..c2f9c9fef015 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -51,6 +51,7 @@ const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/AddonManager.jsm"); XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { Cu.import("resource://gre/modules/NetUtil.jsm"); @@ -399,6 +400,21 @@ BrowserGlue.prototype = { // been warned about them yet, open the plugins update page. if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER)) this._showPluginUpdatePage(); + + // For any add-ons that were installed disabled and can be enabled offer + // them to the user + var win = this.getMostRecentBrowserWindow(); + var browser = win.gBrowser; + var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED); + AddonManager.getAddonsByIDs(changedIDs, function(aAddons) { + aAddons.forEach(function(aAddon) { + // If the add-on isn't user disabled or can't be enabled then skip it + if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) + return; + + browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id); + }) + }); }, _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp index c910b0a9d700..2be60ba80c8f 100644 --- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp @@ -90,6 +90,8 @@ static RedirEntry kRedirMap[] = { nsIAboutModule::ALLOW_SCRIPT }, { "addons", "chrome://mozapps/content/extensions/extensions.xul", nsIAboutModule::ALLOW_SCRIPT }, + { "newaddon", "chrome://mozapps/content/extensions/newaddon.xul", + nsIAboutModule::ALLOW_SCRIPT }, { "support", "chrome://global/content/aboutSupport.xhtml", nsIAboutModule::ALLOW_SCRIPT } }; diff --git a/docshell/build/nsDocShellModule.cpp b/docshell/build/nsDocShellModule.cpp index e1afa1e6e93c..1f883205ffcc 100644 --- a/docshell/build/nsDocShellModule.cpp +++ b/docshell/build/nsDocShellModule.cpp @@ -212,6 +212,7 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = { { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "addons", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, + { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID }, { NS_URI_LOADER_CONTRACTID, &kNS_URI_LOADER_CID }, { NS_DOCUMENTLOADER_SERVICE_CONTRACTID, &kNS_DOCUMENTLOADER_SERVICE_CID }, diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd new file mode 100644 index 000000000000..632b75edc769 --- /dev/null +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.dtd @@ -0,0 +1,11 @@ + + + + + + + + + diff --git a/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties new file mode 100644 index 000000000000..1cab31a84860 --- /dev/null +++ b/toolkit/locales/en-US/chrome/mozapps/extensions/newaddon.properties @@ -0,0 +1,6 @@ +#LOCALIZATION NOTE (name) %1$S is the add-on name, %2$S is the add-on version +name=%1$S %2$S +#LOCALIZATION NOTE (author) %S is the author of the add-on +author=By %S +#LOCALIZATION NOTE (location) %S is the path the add-on is installed in +location=Location: %S diff --git a/toolkit/locales/jar.mn b/toolkit/locales/jar.mn index 0375de2d4b33..543fa4505567 100644 --- a/toolkit/locales/jar.mn +++ b/toolkit/locales/jar.mn @@ -85,6 +85,8 @@ locale/@AB_CD@/mozapps/extensions/about.dtd (%chrome/mozapps/extensions/about.dtd) locale/@AB_CD@/mozapps/extensions/update.dtd (%chrome/mozapps/extensions/update.dtd) locale/@AB_CD@/mozapps/extensions/update.properties (%chrome/mozapps/extensions/update.properties) + locale/@AB_CD@/mozapps/extensions/newaddon.dtd (%chrome/mozapps/extensions/newaddon.dtd) + locale/@AB_CD@/mozapps/extensions/newaddon.properties (%chrome/mozapps/extensions/newaddon.properties) locale/@AB_CD@/mozapps/handling/handling.dtd (%chrome/mozapps/handling/handling.dtd) locale/@AB_CD@/mozapps/handling/handling.properties (%chrome/mozapps/handling/handling.properties) locale/@AB_CD@/mozapps/plugins/plugins.dtd (%chrome/mozapps/plugins/plugins.dtd) diff --git a/toolkit/mozapps/extensions/content/newaddon.js b/toolkit/mozapps/extensions/content/newaddon.js new file mode 100644 index 000000000000..05f081703052 --- /dev/null +++ b/toolkit/mozapps/extensions/content/newaddon.js @@ -0,0 +1,162 @@ +/* ***** 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 Extension Manager UI. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Townsend + * + * 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 ***** */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/AddonManager.jsm"); + +var gAddon = null; + +// If the user enables the add-on through some other UI close this window +var EnableListener = { + onEnabling: function(aAddon) { + if (aAddon.id == gAddon.id) + window.close(); + } +} +AddonManager.addAddonListener(EnableListener); + +function initialize() { + // About URIs don't implement nsIURL so we have to find the query string + // manually + let spec = document.documentURIObject.spec; + let pos = spec.indexOf("?"); + let query = ""; + if (pos >= 0) + query = spec.substring(pos + 1); + + let bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/newaddon.properties"); + + // Just assume the query is "id=" + let id = query.substring(3); + if (!id) { + window.close(); + return; + } + + AddonManager.getAddonByID(id, function(aAddon) { + // If the add-on doesn't exist or it is already enabled or it cannot be + // enabled then this UI is useless, just close it. This shouldn't normally + // happen unless session restore restores the tab + if (!aAddon || !aAddon.userDisabled || + !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) { + window.close(); + return; + } + + gAddon = aAddon; + + document.getElementById("addon-info").setAttribute("type", aAddon.type); + + let icon = document.getElementById("icon"); + if (aAddon.icon64URL) + icon.src = aAddon.icon64URL; + else if (aAddon.iconURL) + icon.src = aAddon.iconURL; + + let name = bundle.formatStringFromName("name", [aAddon.name, aAddon.version], + 2); + document.getElementById("name").value = name + + if (aAddon.creator) { + let creator = bundle.formatStringFromName("author", [aAddon.creator], 1); + document.getElementById("author").value = creator; + } else { + document.getElementById("author").hidden = true; + } + + let uri = "getResourceURI" in aAddon ? aAddon.getResourceURI() : null; + let locationLabel = document.getElementById("location"); + if (uri instanceof Ci.nsIFileURL) { + let location = bundle.formatStringFromName("location", [uri.file.path], 1); + locationLabel.value = location; + locationLabel.setAttribute("tooltiptext", location); + } else { + document.getElementById("location").hidden = true; + } + + var event = document.createEvent("Events"); + event.initEvent("AddonDisplayed", true, true); + document.dispatchEvent(event); + }); +} + +function unload() { + AddonManager.removeAddonListener(EnableListener); +} + +function continueClicked() { + AddonManager.removeAddonListener(EnableListener); + + if (document.getElementById("allow").checked) { + gAddon.userDisabled = false; + + if (gAddon.pendingOperations & AddonManager.PENDING_ENABLE) { + document.getElementById("allow").disabled = true; + document.getElementById("buttonDeck").selectedPanel = document.getElementById("restartPanel"); + return; + } + } + + window.close(); +} + +function restartClicked() { + let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]. + createInstance(Ci.nsISupportsPRBool); + Services.obs.notifyObservers(cancelQuit, "quit-application-requested", + "restart"); + if (cancelQuit.data) + return; // somebody canceled our quit request + + window.close(); + + let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"]. + getService(Components.interfaces.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart); +} + +function cancelClicked() { + gAddon.userDisabled = true; + AddonManager.addAddonListener(EnableListener); + + document.getElementById("allow").disabled = false; + document.getElementById("buttonDeck").selectedPanel = document.getElementById("continuePanel"); +} diff --git a/toolkit/mozapps/extensions/content/newaddon.xul b/toolkit/mozapps/extensions/content/newaddon.xul new file mode 100644 index 000000000000..d845387e6789 --- /dev/null +++ b/toolkit/mozapps/extensions/content/newaddon.xul @@ -0,0 +1,99 @@ + + + + + + + +%brandDTD; + +%newaddonDTD; +]> + + + + + +