diff --git a/accessible/Makefile.in b/accessible/Makefile.in index 21a5893b3500..634557084f40 100644 --- a/accessible/Makefile.in +++ b/accessible/Makefile.in @@ -45,9 +45,7 @@ include $(DEPTH)/config/autoconf.mk MODULE = accessibility DIRS = public src build -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/accessible/src/base/Statistics.h b/accessible/src/base/Statistics.h index a2660a301723..0c59dd838124 100644 --- a/accessible/src/base/Statistics.h +++ b/accessible/src/base/Statistics.h @@ -56,7 +56,13 @@ namespace statistics { * Report that ISimpleDOM* has been used. */ inline void ISimpleDOMUsed() - { Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1); } + { + static bool firstTime = true; + if (firstTime) { + Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1); + firstTime = false; + } + } /** * Report that IAccessibleTable has been used. diff --git a/browser/base/Makefile.in b/browser/base/Makefile.in index af88ed7d0429..c8f856f3a2bf 100644 --- a/browser/base/Makefile.in +++ b/browser/base/Makefile.in @@ -49,9 +49,7 @@ abs_srcdir = $(call core_abspath,$(srcdir)) CHROME_DEPS += $(abs_srcdir)/content/overrides/app-license.html -ifdef ENABLE_TESTS -DIRS += content/test -endif +TEST_DIRS += content/test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/certerror/Makefile.in b/browser/components/certerror/Makefile.in index a9217958a61b..43aef5003de0 100644 --- a/browser/components/certerror/Makefile.in +++ b/browser/components/certerror/Makefile.in @@ -43,8 +43,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/dirprovider/Makefile.in b/browser/components/dirprovider/Makefile.in index e401ae301e1d..3ce4b8184f3f 100644 --- a/browser/components/dirprovider/Makefile.in +++ b/browser/components/dirprovider/Makefile.in @@ -45,9 +45,7 @@ include $(DEPTH)/config/autoconf.mk MODULE = browserdir LIBRARY_NAME = browserdir_s -ifdef ENABLE_TESTS -DIRS = tests -endif +TEST_DIRS += tests FORCE_STATIC_LIB = 1 diff --git a/browser/components/feeds/Makefile.in b/browser/components/feeds/Makefile.in index 93681d67f15e..a4eaeb3c4c7a 100644 --- a/browser/components/feeds/Makefile.in +++ b/browser/components/feeds/Makefile.in @@ -43,8 +43,6 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/places/Makefile.in b/browser/components/places/Makefile.in index 29306f554212..81a7ddc29616 100644 --- a/browser/components/places/Makefile.in +++ b/browser/components/places/Makefile.in @@ -45,9 +45,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = src -ifdef ENABLE_TESTS - DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/browser/components/preferences/Makefile.in b/browser/components/preferences/Makefile.in index cda73ceb7896..e3fe90492f24 100644 --- a/browser/components/preferences/Makefile.in +++ b/browser/components/preferences/Makefile.in @@ -43,9 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/browser/components/privatebrowsing/Makefile.in b/browser/components/privatebrowsing/Makefile.in index 4540affae78e..64058207d62d 100644 --- a/browser/components/privatebrowsing/Makefile.in +++ b/browser/components/privatebrowsing/Makefile.in @@ -46,8 +46,6 @@ MODULE = privatebrowsing DIRS = src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/safebrowsing/Makefile.in b/browser/components/safebrowsing/Makefile.in index 2d526c2933cc..38338147abc4 100644 --- a/browser/components/safebrowsing/Makefile.in +++ b/browser/components/safebrowsing/Makefile.in @@ -43,9 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS += content/test -endif +TEST_DIRS += content/test ifdef MOZILLA_OFFICIAL DEFINES += -DOFFICIAL_BUILD=1 diff --git a/browser/components/search/Makefile.in b/browser/components/search/Makefile.in index a2f7dfe3ab5a..8d4cebc0d771 100644 --- a/browser/components/search/Makefile.in +++ b/browser/components/search/Makefile.in @@ -43,8 +43,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS = test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/shell/Makefile.in b/browser/components/shell/Makefile.in index 7f3883694b0c..1f4c1ff7c871 100644 --- a/browser/components/shell/Makefile.in +++ b/browser/components/shell/Makefile.in @@ -44,8 +44,6 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/components/tabview/Makefile.in b/browser/components/tabview/Makefile.in index c86fb752212d..2a266a8e684e 100644 --- a/browser/components/tabview/Makefile.in +++ b/browser/components/tabview/Makefile.in @@ -43,9 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/highlighter/Makefile.in b/browser/devtools/highlighter/Makefile.in index fbe321671b1b..258a055ab0d2 100644 --- a/browser/devtools/highlighter/Makefile.in +++ b/browser/devtools/highlighter/Makefile.in @@ -52,8 +52,6 @@ EXTRA_JS_MODULES = \ highlighter.jsm \ $(NULL) -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/scratchpad/Makefile.in b/browser/devtools/scratchpad/Makefile.in index 376ffb1ef0cd..bd17c2d0dc1c 100644 --- a/browser/devtools/scratchpad/Makefile.in +++ b/browser/devtools/scratchpad/Makefile.in @@ -43,9 +43,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/shared/Makefile.in b/browser/devtools/shared/Makefile.in index 074d954280ae..dea9f1cafd38 100644 --- a/browser/devtools/shared/Makefile.in +++ b/browser/devtools/shared/Makefile.in @@ -45,9 +45,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/sourceeditor/Makefile.in b/browser/devtools/sourceeditor/Makefile.in index d8b36e80539f..a8e45d310f8e 100644 --- a/browser/devtools/sourceeditor/Makefile.in +++ b/browser/devtools/sourceeditor/Makefile.in @@ -44,9 +44,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test EXTRA_JS_MODULES = \ source-editor.jsm \ diff --git a/browser/devtools/styleeditor/Makefile.in b/browser/devtools/styleeditor/Makefile.in index 5dc39453e206..77a89f998536 100644 --- a/browser/devtools/styleeditor/Makefile.in +++ b/browser/devtools/styleeditor/Makefile.in @@ -42,9 +42,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/tilt/Makefile.in b/browser/devtools/tilt/Makefile.in index 60f934d0357d..0ae639ead1ce 100644 --- a/browser/devtools/tilt/Makefile.in +++ b/browser/devtools/tilt/Makefile.in @@ -42,9 +42,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS - DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/fuel/Makefile.in b/browser/fuel/Makefile.in index 87f996957237..5930d116a0ac 100644 --- a/browser/fuel/Makefile.in +++ b/browser/fuel/Makefile.in @@ -44,9 +44,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/browser/modules/Makefile.in b/browser/modules/Makefile.in index 721d812c6e00..ba2063663abe 100644 --- a/browser/modules/Makefile.in +++ b/browser/modules/Makefile.in @@ -44,9 +44,7 @@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/config.mk -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test EXTRA_JS_MODULES = \ openLocationLastURL.jsm \ diff --git a/build/Makefile.in b/build/Makefile.in index 46d0f9e3ef42..c521b7b8fd62 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -55,17 +55,15 @@ endif DIRS += pgo -ifdef ENABLE_TESTS - DIRS += autoconf/test +TEST_DIRS += autoconf/test ifeq (android,$(MOZ_WIDGET_TOOLKIT)) - DIRS += mobile/sutagent/android \ +TEST_DIRS += mobile/sutagent/android \ mobile/sutagent/android/watcher \ mobile/sutagent/android/ffxcp \ mobile/sutagent/android/fencp \ mobile/robocop \ $(NULL) endif -endif ifdef MOZ_APP_BASENAME DIST_FILES = application.ini diff --git a/caps/Makefile.in b/caps/Makefile.in index b89c840681c6..ffd06a035ee5 100644 --- a/caps/Makefile.in +++ b/caps/Makefile.in @@ -45,9 +45,7 @@ include $(DEPTH)/config/autoconf.mk MODULE = caps DIRS = idl include src -ifdef ENABLE_TESTS -DIRS += tests/mochitest -endif +TEST_DIRS += tests/mochitest include $(topsrcdir)/config/rules.mk diff --git a/chrome/Makefile.in b/chrome/Makefile.in index bbb7c2303597..df7b442ff1f5 100644 --- a/chrome/Makefile.in +++ b/chrome/Makefile.in @@ -44,9 +44,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/content/base/test/test_XHR.html b/content/base/test/test_XHR.html index 82717fb7c642..4fe0ef8356e8 100644 --- a/content/base/test/test_XHR.html +++ b/content/base/test/test_XHR.html @@ -2,16 +2,17 @@
+ diff --git a/layout/reftests/svg/suspend-02.svg b/layout/reftests/svg/suspend-02.svg new file mode 100644 index 000000000000..5b74008c3daf --- /dev/null +++ b/layout/reftests/svg/suspend-02.svg @@ -0,0 +1,23 @@ + + + diff --git a/layout/reftests/svg/suspend-03.svg b/layout/reftests/svg/suspend-03.svg new file mode 100644 index 000000000000..e3c9d185c0b5 --- /dev/null +++ b/layout/reftests/svg/suspend-03.svg @@ -0,0 +1,24 @@ + + + diff --git a/layout/reftests/svg/suspend-04.svg b/layout/reftests/svg/suspend-04.svg new file mode 100644 index 000000000000..b6c33e38ed17 --- /dev/null +++ b/layout/reftests/svg/suspend-04.svg @@ -0,0 +1,27 @@ + + + diff --git a/layout/reftests/svg/suspend-05.svg b/layout/reftests/svg/suspend-05.svg new file mode 100644 index 000000000000..eee051d8f3fc --- /dev/null +++ b/layout/reftests/svg/suspend-05.svg @@ -0,0 +1,21 @@ + + + diff --git a/layout/reftests/svg/suspend-06.svg b/layout/reftests/svg/suspend-06.svg new file mode 100644 index 000000000000..46551a25daae --- /dev/null +++ b/layout/reftests/svg/suspend-06.svg @@ -0,0 +1,24 @@ + + + diff --git a/layout/reftests/svg/suspend-07.svg b/layout/reftests/svg/suspend-07.svg new file mode 100644 index 000000000000..25f6e337f7f7 --- /dev/null +++ b/layout/reftests/svg/suspend-07.svg @@ -0,0 +1,25 @@ + + + diff --git a/layout/reftests/svg/suspend-08.svg b/layout/reftests/svg/suspend-08.svg new file mode 100644 index 000000000000..363842911e68 --- /dev/null +++ b/layout/reftests/svg/suspend-08.svg @@ -0,0 +1,28 @@ + + + diff --git a/layout/reftests/transform-3d/matrix3d-1-ref.html b/layout/reftests/transform-3d/matrix3d-1-ref.html index fac2def083b3..ef27cb984ba6 100644 --- a/layout/reftests/transform-3d/matrix3d-1-ref.html +++ b/layout/reftests/transform-3d/matrix3d-1-ref.html @@ -2,7 +2,7 @@ -+Test Textdiff --git a/layout/reftests/transform-3d/matrix3d-1a.html b/layout/reftests/transform-3d/matrix3d-1a.html index 5f9798368dd0..38b98b274069 100644 --- a/layout/reftests/transform-3d/matrix3d-1a.html +++ b/layout/reftests/transform-3d/matrix3d-1a.html @@ -2,7 +2,7 @@ -+Test Textdiff --git a/layout/reftests/transform-3d/matrix3d-2-ref.html b/layout/reftests/transform-3d/matrix3d-2-ref.html index 628bb934b629..ac665fb66dc9 100644 --- a/layout/reftests/transform-3d/matrix3d-2-ref.html +++ b/layout/reftests/transform-3d/matrix3d-2-ref.html @@ -2,7 +2,7 @@ -+Test Textdiff --git a/layout/reftests/transform-3d/matrix3d-2a.html b/layout/reftests/transform-3d/matrix3d-2a.html index a101d8094c02..222dd6a82a79 100644 --- a/layout/reftests/transform-3d/matrix3d-2a.html +++ b/layout/reftests/transform-3d/matrix3d-2a.html @@ -2,7 +2,7 @@ -+Test Textdiff --git a/layout/reftests/transform/reftest.list b/layout/reftests/transform/reftest.list index 16f4f5b30ca4..5d5da5a804e0 100644 --- a/layout/reftests/transform/reftest.list +++ b/layout/reftests/transform/reftest.list @@ -37,9 +37,11 @@ random == rotate-1b.html rotate-1-ref.html random == rotate-1c.html rotate-1-ref.html random == rotate-1d.html rotate-1-ref.html random == rotate-1e.html rotate-1-ref.html +random == rotate-1f.html rotate-1-ref.html # rotate: 90deg rotations should be indistinguishable from objects constructed # to look the same. == rotate-2a.html rotate-2-ref.html +== rotate-2b.html rotate-2-ref.html # -moz-transform-origin: We should NOT get the same images when using different # -moz-transform-origins. != origin-1a.html origin-1-ref.html diff --git a/layout/reftests/transform/rotate-1f.html b/layout/reftests/transform/rotate-1f.html new file mode 100644 index 000000000000..1dd88ed00c40 --- /dev/null +++ b/layout/reftests/transform/rotate-1f.html @@ -0,0 +1,9 @@ + + + + ++ Test Text ++ + diff --git a/layout/reftests/transform/rotate-2b.html b/layout/reftests/transform/rotate-2b.html new file mode 100644 index 000000000000..040d8a99c185 --- /dev/null +++ b/layout/reftests/transform/rotate-2b.html @@ -0,0 +1,9 @@ + + + + ++ ++ + diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 70fcb81152aa..e774246eb3fc 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -4295,6 +4295,7 @@ const UnitInfo UnitData[] = { { STR_WITH_LEN("deg"), eCSSUnit_Degree, VARIANT_ANGLE }, { STR_WITH_LEN("grad"), eCSSUnit_Grad, VARIANT_ANGLE }, { STR_WITH_LEN("rad"), eCSSUnit_Radian, VARIANT_ANGLE }, + { STR_WITH_LEN("turn"), eCSSUnit_Turn, VARIANT_ANGLE }, { STR_WITH_LEN("hz"), eCSSUnit_Hertz, VARIANT_FREQUENCY }, { STR_WITH_LEN("khz"), eCSSUnit_Kilohertz, VARIANT_FREQUENCY }, { STR_WITH_LEN("s"), eCSSUnit_Seconds, VARIANT_TIME }, diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index ea3e010ded95..1afb7913d3ec 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -262,6 +262,7 @@ double nsCSSValue::GetAngleValueInRadians() const switch (GetUnit()) { case eCSSUnit_Radian: return angle; + case eCSSUnit_Turn: return angle * 2 * M_PI; case eCSSUnit_Degree: return angle * M_PI / 180.0; case eCSSUnit_Grad: return angle * M_PI / 200.0; @@ -1105,6 +1106,7 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const case eCSSUnit_Degree: aResult.AppendLiteral("deg"); break; case eCSSUnit_Grad: aResult.AppendLiteral("grad"); break; case eCSSUnit_Radian: aResult.AppendLiteral("rad"); break; + case eCSSUnit_Turn: aResult.AppendLiteral("turn"); break; case eCSSUnit_Hertz: aResult.AppendLiteral("Hz"); break; case eCSSUnit_Kilohertz: aResult.AppendLiteral("kHz"); break; diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index 7baad7ba797a..53779f40427e 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -183,6 +183,7 @@ enum nsCSSUnit { eCSSUnit_Degree = 1000, // (float) 360 per circle eCSSUnit_Grad = 1001, // (float) 400 per circle eCSSUnit_Radian = 1002, // (float) 2*pi per circle + eCSSUnit_Turn = 1003, // (float) 1 per circle // Frequency units eCSSUnit_Hertz = 2000, // (float) 1/seconds @@ -274,7 +275,7 @@ public: bool IsPixelLengthUnit() const { return eCSSUnit_Point <= mUnit && mUnit <= eCSSUnit_Pixel; } bool IsAngularUnit() const - { return eCSSUnit_Degree <= mUnit && mUnit <= eCSSUnit_Radian; } + { return eCSSUnit_Degree <= mUnit && mUnit <= eCSSUnit_Turn; } bool IsFrequencyUnit() const { return eCSSUnit_Hertz <= mUnit && mUnit <= eCSSUnit_Kilohertz; } bool IsTimeUnit() const @@ -311,7 +312,7 @@ public: float GetAngleValue() const { NS_ABORT_IF_FALSE(eCSSUnit_Degree <= mUnit && - mUnit <= eCSSUnit_Radian, "not an angle value"); + mUnit <= eCSSUnit_Turn, "not an angle value"); return mValue.mFloat; } diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index ce5c65b2fc65..dedb4c0ffa96 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1497,6 +1497,7 @@ nsComputedDOMStyle::GetCSSGradientString(const nsStyleGradient* aGradient, case eStyleUnit_Degree: aString.AppendLiteral("deg"); break; case eStyleUnit_Grad: aString.AppendLiteral("grad"); break; case eStyleUnit_Radian: aString.AppendLiteral("rad"); break; + case eStyleUnit_Turn: aString.AppendLiteral("turn"); break; default: NS_NOTREACHED("unrecognized angle unit"); } needSep = true; diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index af97e0e2b0ed..5aabfcae45d3 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -888,6 +888,7 @@ static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext, case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; + case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; default: NS_NOTREACHED("unrecognized angular unit"); unit = eStyleUnit_Degree; } diff --git a/layout/style/nsStyleCoord.cpp b/layout/style/nsStyleCoord.cpp index db1b320e329d..9353fa4d568a 100644 --- a/layout/style/nsStyleCoord.cpp +++ b/layout/style/nsStyleCoord.cpp @@ -116,6 +116,7 @@ bool nsStyleCoord::operator==(const nsStyleCoord& aOther) const case eStyleUnit_Degree: case eStyleUnit_Grad: case eStyleUnit_Radian: + case eStyleUnit_Turn: return mValue.mFloat == aOther.mValue.mFloat; case eStyleUnit_Coord: case eStyleUnit_Integer: @@ -170,7 +171,8 @@ void nsStyleCoord::SetAngleValue(float aValue, nsStyleUnit aUnit) { if (aUnit == eStyleUnit_Degree || aUnit == eStyleUnit_Grad || - aUnit == eStyleUnit_Radian) { + aUnit == eStyleUnit_Radian || + aUnit == eStyleUnit_Turn) { mUnit = aUnit; mValue.mFloat = aValue; } else { @@ -212,6 +214,7 @@ nsStyleCoord::GetAngleValueInRadians() const switch (GetUnit()) { case eStyleUnit_Radian: return angle; + case eStyleUnit_Turn: return angle * 2 * M_PI; case eStyleUnit_Degree: return angle * M_PI / 180.0; case eStyleUnit_Grad: return angle * M_PI / 200.0; diff --git a/layout/style/nsStyleCoord.h b/layout/style/nsStyleCoord.h index 6d73039a2dd7..9cdd4bc571f5 100644 --- a/layout/style/nsStyleCoord.h +++ b/layout/style/nsStyleCoord.h @@ -57,6 +57,7 @@ enum nsStyleUnit { eStyleUnit_Degree = 12, // (float) angle in degrees eStyleUnit_Grad = 13, // (float) angle in grads eStyleUnit_Radian = 14, // (float) angle in radians + eStyleUnit_Turn = 15, // (float) angle in turns eStyleUnit_Coord = 20, // (nscoord) value is twips eStyleUnit_Integer = 30, // (int) value is simple integer eStyleUnit_Enumerated = 32, // (int) value has enumerated meaning @@ -120,7 +121,7 @@ public: } bool IsAngleValue() const { - return eStyleUnit_Degree <= mUnit && mUnit <= eStyleUnit_Radian; + return eStyleUnit_Degree <= mUnit && mUnit <= eStyleUnit_Turn; } bool IsCalcUnit() const { @@ -323,8 +324,8 @@ inline float nsStyleCoord::GetFactorValue() const inline float nsStyleCoord::GetAngleValue() const { NS_ASSERTION(mUnit >= eStyleUnit_Degree && - mUnit <= eStyleUnit_Radian, "not an angle value"); - if (mUnit >= eStyleUnit_Degree && mUnit <= eStyleUnit_Radian) { + mUnit <= eStyleUnit_Turn, "not an angle value"); + if (mUnit >= eStyleUnit_Degree && mUnit <= eStyleUnit_Turn) { return mValue.mFloat; } return 0.0f; diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 036cf0af0c18..da2a00acbd13 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -1001,7 +1001,7 @@ var gCSSProperties = { type: CSS_TYPE_LONGHAND, prerequisites: { "width": "300px", "height": "50px" }, initial_values: [ "none" ], - other_values: [ "translatex(1px)", "translatex(4em)", "translatex(-4px)", "translatex(3px)", "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", "translatey(4em)", "translate(3px)", "translate(10px, -3px)", "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", "rotate(0)", "scalex(10)", "scaley(10)", "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "matrix(1, 2, 3, 4, 5px, 6em)", "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", "translatex(50%)", "translatey(50%)", "translate(50%)", "translate(3%, 5px)", "translate(5px, 3%)", "matrix(1, 2, 3, 4, 5px, 6%)", "matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)", "matrix(1, 2, 3, 4, 5, 6)", + other_values: [ "translatex(1px)", "translatex(4em)", "translatex(-4px)", "translatex(3px)", "translatex(0px) translatex(1px) translatex(2px) translatex(3px) translatex(4px)", "translatey(4em)", "translate(3px)", "translate(10px, -3px)", "rotate(45deg)", "rotate(45grad)", "rotate(45rad)", "rotate(0.25turn)", "rotate(0)", "scalex(10)", "scaley(10)", "scale(10)", "scale(10, 20)", "skewx(30deg)", "skewx(0)", "skewy(0)", "skewx(30grad)", "skewx(30rad)", "skewx(0.08turn)", "skewy(30deg)", "skewy(30grad)", "skewy(30rad)", "skewy(0.08turn)", "matrix(1, 2, 3, 4, 5px, 6em)", "rotate(45deg) scale(2, 1)", "skewx(45deg) skewx(-50grad)", "translate(0, 0) scale(1, 1) skewx(0) skewy(0) matrix(1, 0, 0, 1, 0, 0)", "translatex(50%)", "translatey(50%)", "translate(50%)", "translate(3%, 5px)", "translate(5px, 3%)", "matrix(1, 2, 3, 4, 5px, 6%)", "matrix(1, 2, 3, 4, 5%, 6px)", "matrix(1, 2, 3, 4, 5%, 6%)", "matrix(1, 2, 3, 4, 5, 6)", /* valid calc() values */ "translatex(-moz-calc(5px + 10%))", "translatey(-moz-calc(0.25 * 5px + 10% / 3))", @@ -1010,7 +1010,7 @@ var gCSSProperties = { "translate(-50px, -moz-calc(5px - 10% * 3))", "matrix(1, 0, 0, 1, -moz-calc(5px * 3), -moz-calc(10% - 3px))" ].concat(SpecialPowers.getBoolPref("layout.3d-transforms.enabled") ? [ - "translatez(1px)", "translatez(4em)", "translatez(-4px)", "translatez(0px)", "translatez(2px) translatez(5px)", "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)", "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)", "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)", "scalez(-6)", "rotate3d(2, 3, 4, 45deg)", "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)", "rotatez(72rad)", "perspective(1000px)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)" + "translatez(1px)", "translatez(4em)", "translatez(-4px)", "translatez(0px)", "translatez(2px) translatez(5px)", "translate3d(3px, 4px, 5px)", "translate3d(2em, 3px, 1em)", "translatex(2px) translate3d(4px, 5px, 6px) translatey(1px)", "scale3d(4, 4, 4)", "scale3d(-2, 3, -7)", "scalez(4)", "scalez(-6)", "rotate3d(2, 3, 4, 45deg)", "rotate3d(-3, 7, 0, 12rad)", "rotatex(15deg)", "rotatey(-12grad)", "rotatez(72rad)", "rotatex(0.125turn)", "perspective(1000px)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13px, 14em, 15px, 16)", "matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 20%, 10%, 15, 16)" ] : []), invalid_values: ["1px", "#0000ff", "red", "auto", "translatex(1px 1px)", "translatex(translatex(1px))", "translatex(#0000ff)", "translatex(red)", "translatey()", "matrix(1px, 2px, 3px, 4px, 5px, 6px)", "scale(150%)", "skewx(red)", "matrix(1%, 0, 0, 0, 0px, 0px)", "matrix(0, 1%, 2, 3, 4px,5px)", "matrix(0, 1, 2%, 3, 4px, 5px)", "matrix(0, 1, 2, 3%, 4%, 5%)", /* invalid calc() values */ @@ -1170,6 +1170,7 @@ var gCSSProperties = { "50%", "-moz-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat", "-moz-linear-gradient(10px 10px -45deg, red, blue) repeat", + "-moz-linear-gradient(10px 10px -0.125turn, red, blue) repeat", "-moz-repeating-radial-gradient(10% bottom, #ffffff, black) scroll no-repeat", "-moz-repeating-linear-gradient(10px 10px -45deg, red, blue) repeat", "-moz-element(#test) lime", @@ -1286,12 +1287,14 @@ var gCSSProperties = { "-moz-linear-gradient(20% bottom -300deg, red, blue)", "-moz-linear-gradient(center 20% 1.95929rad, red, blue)", "-moz-linear-gradient(left 35px 30grad, red, blue)", + "-moz-linear-gradient(left 35px 0.1turn, red, blue)", "-moz-linear-gradient(10% 10em 99999deg, red, blue)", "-moz-linear-gradient(44px top -33deg, red, blue)", "-moz-linear-gradient(-33deg, red, blue)", "-moz-linear-gradient(30grad left 35px, red, blue)", "-moz-linear-gradient(10deg 20px, red, blue)", + "-moz-linear-gradient(1turn 20px, red, blue)", "-moz-linear-gradient(.414rad bottom, red, blue)", "-moz-radial-gradient(red, blue)", diff --git a/layout/style/test/test_computed_style.html b/layout/style/test/test_computed_style.html index 6002272da8fe..04daf1c21e87 100644 --- a/layout/style/test/test_computed_style.html +++ b/layout/style/test/test_computed_style.html @@ -198,6 +198,32 @@ var noframe_container = document.getElementById("content"); p.parentNode.removeChild(p); })(); +(function test_bug_716628() { + // Test that various gradient styles round-trip correctly + var backgroundImages = [ + [ "-moz-radial-gradient(10% bottom, #ffffff, black)", + "-moz-radial-gradient(10% 100%, ellipse, rgb(255, 255, 255), rgb(0, 0, 0))", + "radial gradient" ], + [ "-moz-linear-gradient(10px 10px -45deg, red, blue)", + "-moz-linear-gradient(10px 10px -45deg, rgb(255, 0, 0), rgb(0, 0, 255))", + "linear gradient with angle in degrees" ], + [ "-moz-linear-gradient(10px 10px -0.125turn, red, blue)", + "-moz-linear-gradient(10px 10px -0.125turn, rgb(255, 0, 0), rgb(0, 0, 255))", + "linear gradient with angle in turns" ], + ]; + + var p = document.createElement("p"); + var cs = getComputedStyle(p, ""); + frame_container.appendChild(p); + + for (var i = 0; i < backgroundImages.length; ++i) { + var test = backgroundImages[i]; + p.style.backgroundImage = test[0]; + is(cs.backgroundImage, test[1], "computed value of " + test[2] + " background-image"); + } + + p.parentNode.removeChild(p); +})(); diff --git a/layout/svg/base/src/nsISVGChildFrame.h b/layout/svg/base/src/nsISVGChildFrame.h index 2b7f0d47405e..0680804a0004 100644 --- a/layout/svg/base/src/nsISVGChildFrame.h +++ b/layout/svg/base/src/nsISVGChildFrame.h @@ -102,8 +102,8 @@ public: COORD_CONTEXT_CHANGED = 0x04 }; virtual void NotifySVGChanged(PRUint32 aFlags)=0; - NS_IMETHOD NotifyRedrawSuspended()=0; - NS_IMETHOD NotifyRedrawUnsuspended()=0; + virtual void NotifyRedrawSuspended()=0; + virtual void NotifyRedrawUnsuspended()=0; /** * Get this frame's contribution to the rect returned by a GetBBox() call diff --git a/layout/svg/base/src/nsISVGSVGFrame.h b/layout/svg/base/src/nsISVGSVGFrame.h index e27e5824e2f4..07abeb6dd785 100644 --- a/layout/svg/base/src/nsISVGSVGFrame.h +++ b/layout/svg/base/src/nsISVGSVGFrame.h @@ -46,9 +46,9 @@ class nsISVGSVGFrame public: NS_DECL_QUERYFRAME_TARGET(nsISVGSVGFrame) - NS_IMETHOD SuspendRedraw()=0; - NS_IMETHOD UnsuspendRedraw()=0; - NS_IMETHOD NotifyViewportChange()=0; + virtual void SuspendRedraw()=0; + virtual void UnsuspendRedraw()=0; + virtual void NotifyViewportChange()=0; }; #endif // __NS_ISVGSVGFRAME_H__ diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index 547e2f66de95..f5c092bf918c 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -105,7 +105,8 @@ nsSVGDisplayContainerFrame::Init(nsIContent* aContent, { if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) { AddStateBits(aParent->GetStateBits() & - (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)); + (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD | + NS_STATE_SVG_REDRAW_SUSPENDED)); } nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow); return rv; @@ -145,11 +146,14 @@ NS_IMETHODIMP nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID, nsIFrame* aOldFrame) { + // Force the invalidation before it's too late + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + nsSVGUtils::InvalidateCoveredRegion(aOldFrame); nsresult rv = nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame); - if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { + if (!(GetStateBits() & (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_IS_OUTER_SVG))) { nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this); } @@ -234,30 +238,16 @@ nsSVGDisplayContainerFrame::NotifySVGChanged(PRUint32 aFlags) nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags); } -NS_IMETHODIMP +void nsSVGDisplayContainerFrame::NotifyRedrawSuspended() { - for (nsIFrame* kid = mFrames.FirstChild(); kid; - kid = kid->GetNextSibling()) { - nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); - if (SVGFrame) { - SVGFrame->NotifyRedrawSuspended(); - } - } - return NS_OK; + nsSVGUtils::NotifyRedrawSuspended(this); } -NS_IMETHODIMP +void nsSVGDisplayContainerFrame::NotifyRedrawUnsuspended() { - for (nsIFrame* kid = mFrames.FirstChild(); kid; - kid = kid->GetNextSibling()) { - nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); - if (SVGFrame) { - SVGFrame->NotifyRedrawUnsuspended(); - } - } - return NS_OK; + nsSVGUtils::NotifyRedrawUnsuspended(this); } gfxRect diff --git a/layout/svg/base/src/nsSVGContainerFrame.h b/layout/svg/base/src/nsSVGContainerFrame.h index 10861b78c2ef..c035a62f60b2 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.h +++ b/layout/svg/base/src/nsSVGContainerFrame.h @@ -110,8 +110,8 @@ public: NS_IMETHOD UpdateCoveredRegion(); NS_IMETHOD InitialUpdate(); virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD NotifyRedrawSuspended(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawSuspended(); + virtual void NotifyRedrawUnsuspended(); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags); NS_IMETHOD_(bool) IsDisplayContainer() { return true; } diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index 908906b89229..57236840eb71 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -91,7 +91,8 @@ nsSVGForeignObjectFrame::Init(nsIContent* aContent, nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); AddStateBits(aParent->GetStateBits() & - (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)); + (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD | + NS_STATE_SVG_REDRAW_SUSPENDED)); if (NS_SUCCEEDED(rv)) { nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); } @@ -439,15 +440,17 @@ nsSVGForeignObjectFrame::NotifySVGChanged(PRUint32 aFlags) } } -NS_IMETHODIMP +void nsSVGForeignObjectFrame::NotifyRedrawSuspended() { - return NS_OK; + AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); } -NS_IMETHODIMP +void nsSVGForeignObjectFrame::NotifyRedrawUnsuspended() { + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) { if (GetStateBits() & NS_STATE_SVG_DIRTY) { UpdateGraphic(); // invalidate our entire area @@ -455,7 +458,6 @@ nsSVGForeignObjectFrame::NotifyRedrawUnsuspended() FlushDirtyRegion(0); // only invalidate areas dirtied by our descendants } } - return NS_OK; } gfxRect @@ -665,7 +667,8 @@ void nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags) { if ((mSameDocDirtyRegion.IsEmpty() && mSubDocDirtyRegion.IsEmpty()) || - mInReflow) + mInReflow || + (GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED)) return; nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this); @@ -674,9 +677,6 @@ nsSVGForeignObjectFrame::FlushDirtyRegion(PRUint32 aFlags) return; } - if (outerSVGFrame->IsRedrawSuspended()) - return; - InvalidateDirtyRect(outerSVGFrame, mSameDocDirtyRegion.GetBounds(), aFlags); InvalidateDirtyRect(outerSVGFrame, mSubDocDirtyRegion.GetBounds(), aFlags | INVALIDATE_CROSS_DOC); diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index 6f5914f71ce6..2bc685b95faf 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -128,8 +128,8 @@ public: NS_IMETHOD UpdateCoveredRegion(); NS_IMETHOD InitialUpdate(); virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD NotifyRedrawSuspended(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawSuspended(); + virtual void NotifyRedrawUnsuspended(); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags); NS_IMETHOD_(bool) IsDisplayContainer() { return true; } diff --git a/layout/svg/base/src/nsSVGGeometryFrame.cpp b/layout/svg/base/src/nsSVGGeometryFrame.cpp index 5452865a0d40..ea7ef9143948 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGGeometryFrame.cpp @@ -54,7 +54,8 @@ nsSVGGeometryFrame::Init(nsIContent* aContent, nsIFrame* aPrevInFlow) { AddStateBits(aParent->GetStateBits() & - (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)); + (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD | + NS_STATE_SVG_REDRAW_SUSPENDED)); nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow); return rv; } @@ -165,28 +166,9 @@ SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext, nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, float aOpacity) { - const nsStyleSVGPaint &paint = aStyleContext->GetStyleSVG()->*aFillOrStroke; - nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited(); - bool isServer = paint.mType == eStyleSVGPaintType_Server; - nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor; - if (styleIfVisited) { - const nsStyleSVGPaint &paintIfVisited = - styleIfVisited->GetStyleSVG()->*aFillOrStroke; - // To prevent Web content from detecting if a user has visited a URL - // (via URL loading triggered by paint servers or performance - // differences between paint servers or between a paint server and a - // color), we do not allow whether links are visited to change which - // paint server is used or switch between paint servers and simple - // colors. A :visited style may only override a simple color with - // another simple color. - if (paintIfVisited.mType == eStyleSVGPaintType_Color && - paint.mType == eStyleSVGPaintType_Color) { - nscolor colorIfVisited = paintIfVisited.mPaint.mColor; - nscolor colors[2] = { color, colorIfVisited }; - color = nsStyleContext::CombineVisitedColors(colors, - aStyleContext->RelevantLinkVisited()); - } - } + nscolor color; + nsSVGUtils::GetFallbackOrPaintColor(aContext, aStyleContext, aFillOrStroke, + &aOpacity, &color); SetupCairoColor(aContext, color, aOpacity); } diff --git a/layout/svg/base/src/nsSVGGeometryFrame.h b/layout/svg/base/src/nsSVGGeometryFrame.h index 45b000092dc6..15065c75b6b3 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.h +++ b/layout/svg/base/src/nsSVGGeometryFrame.h @@ -115,9 +115,6 @@ protected: */ virtual PRUint16 GetHitTestFlags(); -private: - bool GetStrokeDashData(FallibleTArray& dashes, gfxFloat *dashOffset); - /** * Returns the given 'fill-opacity' or 'stroke-opacity' value multiplied by * the value of the 'opacity' property if it's possible to avoid the expense @@ -126,6 +123,9 @@ private: * given 'fill-opacity'/'stroke-opacity' is returned unmodified. */ float MaybeOptimizeOpacity(float aFillOrStrokeOpacity); + +private: + bool GetStrokeDashData(FallibleTArray & dashes, gfxFloat *dashOffset); }; #endif // __NS_SVGGEOMETRYFRAME_H__ diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index 00c23a4da132..307a8e361f91 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -53,6 +53,8 @@ #include "gfxContext.h" #include "gfxMatrix.h" #include "gfxPlatform.h" +#include "nsSVGEffects.h" +#include "nsSVGPaintServerFrame.h" using namespace mozilla; @@ -354,9 +356,9 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, if (renderMode == nsSVGRenderState::CLIP_MASK) { gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f)); - FillCharacters(&iter, gfx); + DrawCharacters(&iter, gfx, gfxFont::GLYPH_FILL); } else { - AddCharactersToPath(&iter, gfx); + DrawCharacters(&iter, gfx, gfxFont::GLYPH_PATH); } gfx->SetMatrix(matrix); @@ -371,19 +373,13 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext, CharacterIterator iter(this, true); iter.SetInitialMatrix(gfx); - if (SetupCairoFill(gfx)) { - gfxMatrix matrix = gfx->CurrentMatrix(); - FillCharacters(&iter, gfx); - gfx->SetMatrix(matrix); - } + nsRefPtr strokePattern; + DrawMode drawMode = SetupCairoState(gfx, &strokePattern); - if (SetupCairoStroke(gfx)) { - // SetupCairoStroke will clear mTextRun whenever - // there is a pattern or gradient on the text - iter.Reset(); - - StrokeCharacters(&iter, gfx); + if (drawMode) { + DrawCharacters(&iter, gfx, drawMode, strokePattern); } + gfx->Restore(); return NS_OK; @@ -529,38 +525,19 @@ nsSVGGlyphFrame::NotifySVGChanged(PRUint32 aFlags) } } -NS_IMETHODIMP +void nsSVGGlyphFrame::NotifyRedrawSuspended() { - // XXX should we cache the fact that redraw is suspended? - return NS_OK; -} - -NS_IMETHODIMP -nsSVGGlyphFrame::NotifyRedrawUnsuspended() -{ - if (GetStateBits() & NS_STATE_SVG_DIRTY) - nsSVGUtils::UpdateGraphic(this); - - return NS_OK; + AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); } void -nsSVGGlyphFrame::AddCharactersToPath(CharacterIterator *aIter, - gfxContext *aContext) +nsSVGGlyphFrame::NotifyRedrawUnsuspended() { - if (aIter->SetupForDirectTextRunDrawing(aContext)) { - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_PATH, 0, - mTextRun->GetLength(), nsnull, nsnull); - return; - } + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); - PRUint32 i; - while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) { - aIter->SetupForDrawing(aContext); - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_PATH, i, - aIter->ClusterLength(), nsnull, nsnull); - } + if (GetStateBits() & NS_STATE_SVG_DIRTY) + nsSVGUtils::UpdateGraphic(this); } void @@ -586,39 +563,26 @@ nsSVGGlyphFrame::AddBoundingBoxesToPath(CharacterIterator *aIter, } void -nsSVGGlyphFrame::FillCharacters(CharacterIterator *aIter, - gfxContext *aContext) +nsSVGGlyphFrame::DrawCharacters(CharacterIterator *aIter, + gfxContext *aContext, + DrawMode aDrawMode, + gfxPattern *aStrokePattern) { + if (aDrawMode & gfxFont::GLYPH_STROKE) { + aIter->SetLineWidthAndDashesForDrawing(aContext); + } + if (aIter->SetupForDirectTextRunDrawing(aContext)) { - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_FILL, 0, - mTextRun->GetLength(), nsnull, nsnull); + mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, 0, + mTextRun->GetLength(), nsnull, nsnull, aStrokePattern); return; } PRUint32 i; while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) { aIter->SetupForDrawing(aContext); - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_FILL, i, - aIter->ClusterLength(), nsnull, nsnull); - } -} - -void -nsSVGGlyphFrame::StrokeCharacters(CharacterIterator *aIter, - gfxContext *aContext) -{ - aIter->SetLineWidthAndDashesForDrawing(aContext); - if (aIter->SetupForDirectTextRunDrawing(aContext)) { - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_STROKE, 0, - mTextRun->GetLength(), nsnull, nsnull); - return; - } - - PRUint32 i; - while ((i = aIter->NextCluster()) != aIter->InvalidCluster()) { - aIter->SetupForDrawing(aContext); - mTextRun->Draw(aContext, gfxPoint(0, 0), gfxFont::GLYPH_STROKE, i, - aIter->ClusterLength(), nsnull, nsnull); + mTextRun->Draw(aContext, gfxPoint(0, 0), aDrawMode, i, + aIter->ClusterLength(), nsnull, nsnull, aStrokePattern); } } @@ -913,6 +877,46 @@ nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale) return baselineAppUnits * aMetricsScale; } +DrawMode +nsSVGGlyphFrame::SetupCairoState(gfxContext *context, nsRefPtr *strokePattern) { + DrawMode toDraw = DrawMode(0); + const nsStyleSVG* style = GetStyleSVG(); + + if (HasStroke()) { + gfxContextMatrixAutoSaveRestore matrixRestore(context); + context->IdentityMatrix(); + + toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE); + + SetupCairoStrokeHitGeometry(context); + float opacity = style->mStrokeOpacity; + nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke, + nsSVGEffects::StrokeProperty()); + + if (ps) { + // Gradient or Pattern: can get pattern directly from frame + *strokePattern = ps->GetPaintServerPattern(this, opacity); + + NS_ASSERTION(*strokePattern, "No pattern returned from paint server"); + } else { + nscolor color; + nsSVGUtils::GetFallbackOrPaintColor(context, GetStyleContext(), + &nsStyleSVG::mStroke, &opacity, + &color); + *strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0, + NS_GET_G(color) / 255.0, + NS_GET_B(color) / 255.0, + NS_GET_A(color) / 255.0 * opacity)); + } + } + + if (SetupCairoFill(context)) { + toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL); + } + + return toDraw; +} + //---------------------------------------------------------------------- // Utilities for converting from indices in the uncompressed content diff --git a/layout/svg/base/src/nsSVGGlyphFrame.h b/layout/svg/base/src/nsSVGGlyphFrame.h index c47600c3a3ee..c96986357126 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.h +++ b/layout/svg/base/src/nsSVGGlyphFrame.h @@ -53,6 +53,8 @@ class nsSVGGlyphFrame; class CharacterIterator; struct CharacterPosition; +typedef gfxFont::DrawMode DrawMode; + typedef nsSVGGeometryFrame nsSVGGlyphFrameBase; class nsSVGGlyphFrame : public nsSVGGlyphFrameBase, @@ -180,8 +182,8 @@ public: NS_IMETHOD_(nsRect) GetCoveredRegion(); NS_IMETHOD InitialUpdate(); virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD NotifyRedrawSuspended(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawSuspended(); + virtual void NotifyRedrawUnsuspended(); NS_IMETHOD_(bool) IsDisplayContainer() { return false; } NS_IMETHOD_(bool) HasValidCoveredRect() { return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD); @@ -236,10 +238,10 @@ protected: gfxContext *aContext); void AddBoundingBoxesToPath(CharacterIterator *aIter, gfxContext *aContext); - void FillCharacters(CharacterIterator *aIter, - gfxContext *aContext); - void StrokeCharacters(CharacterIterator *aIter, - gfxContext *aContext); + void DrawCharacters(CharacterIterator *aIter, + gfxContext *aContext, + DrawMode aDrawMode, + gfxPattern *aStrokePattern = nsnull); void NotifyGlyphMetricsChange(); void SetupGlobalTransform(gfxContext *aContext); @@ -264,6 +266,9 @@ protected: bool mCompressWhitespace; bool mTrimLeadingWhitespace; bool mTrimTrailingWhitespace; + +private: + DrawMode SetupCairoState(gfxContext *context, nsRefPtr *strokePattern); }; #endif diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp index 0305778d83ff..67016895cdf3 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.cpp @@ -39,7 +39,6 @@ #include "nsSVGInnerSVGFrame.h" #include "nsIFrame.h" #include "nsISVGChildFrame.h" -#include "nsSVGOuterSVGFrame.h" #include "nsIDOMSVGAnimatedRect.h" #include "nsSVGSVGElement.h" #include "nsSVGContainerFrame.h" @@ -216,33 +215,28 @@ nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint) //---------------------------------------------------------------------- // nsISVGSVGFrame methods: -NS_IMETHODIMP +void nsSVGInnerSVGFrame::SuspendRedraw() { - nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this); - if (!outerSVGFrame) { - NS_ERROR("no outer svg frame"); - return NS_ERROR_FAILURE; - } - return outerSVGFrame->SuspendRedraw(); + if (GetParent()->GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) + return; + + nsSVGUtils::NotifyRedrawSuspended(this); } -NS_IMETHODIMP +void nsSVGInnerSVGFrame::UnsuspendRedraw() { - nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this); - if (!outerSVGFrame) { - NS_ERROR("no outer svg frame"); - return NS_ERROR_FAILURE; - } - return outerSVGFrame->UnsuspendRedraw(); + if (GetParent()->GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) + return; + + nsSVGUtils::NotifyRedrawUnsuspended(this); } -NS_IMETHODIMP +void nsSVGInnerSVGFrame::NotifyViewportChange() { NS_ERROR("Inner SVG frames should not get Viewport changes."); - return NS_ERROR_FAILURE; } //---------------------------------------------------------------------- diff --git a/layout/svg/base/src/nsSVGInnerSVGFrame.h b/layout/svg/base/src/nsSVGInnerSVGFrame.h index 762a39b84279..31db89169ce4 100644 --- a/layout/svg/base/src/nsSVGInnerSVGFrame.h +++ b/layout/svg/base/src/nsSVGInnerSVGFrame.h @@ -89,9 +89,9 @@ public: virtual gfxMatrix GetCanvasTM(); // nsISVGSVGFrame interface: - NS_IMETHOD SuspendRedraw(); - NS_IMETHOD UnsuspendRedraw(); - NS_IMETHOD NotifyViewportChange(); + virtual void SuspendRedraw(); + virtual void UnsuspendRedraw(); + virtual void NotifyViewportChange(); protected: diff --git a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp index 0d88e56e1d5a..6c80a8ddc849 100644 --- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp @@ -141,7 +141,7 @@ NS_IMPL_FRAMEARENA_HELPERS(nsSVGOuterSVGFrame) nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext) : nsSVGOuterSVGFrameBase(aContext) - , mRedrawSuspendCount(0) + , mRedrawSuspendCount(0) , mFullZoom(0) , mViewportInitialized(false) #ifdef XP_MACOSX @@ -681,97 +681,35 @@ nsSVGOuterSVGFrame::GetType() const return nsGkAtoms::svgOuterSVGFrame; } -//---------------------------------------------------------------------- -// nsSVGOuterSVGFrame methods: - -void -nsSVGOuterSVGFrame::InvalidateCoveredRegion(nsIFrame *aFrame) -{ - // Make sure elements styled by :hover get updated if script/animation moves - // them under or out from under the pointer: - PresContext()->PresShell()->SynthesizeMouseMove(false); - - nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); - if (!svgFrame) - return; - - nsRect rect = nsSVGUtils::FindFilterInvalidation(aFrame, svgFrame->GetCoveredRegion()); - Invalidate(rect); -} - -bool -nsSVGOuterSVGFrame::UpdateAndInvalidateCoveredRegion(nsIFrame *aFrame) -{ - // Make sure elements styled by :hover get updated if script/animation moves - // them under or out from under the pointer: - PresContext()->PresShell()->SynthesizeMouseMove(false); - - nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); - if (!svgFrame) - return false; - - nsRect oldRegion = svgFrame->GetCoveredRegion(); - Invalidate(nsSVGUtils::FindFilterInvalidation(aFrame, oldRegion)); - svgFrame->UpdateCoveredRegion(); - nsRect newRegion = svgFrame->GetCoveredRegion(); - if (oldRegion.IsEqualInterior(newRegion)) - return false; - - Invalidate(nsSVGUtils::FindFilterInvalidation(aFrame, newRegion)); - return true; -} - -bool -nsSVGOuterSVGFrame::IsRedrawSuspended() -{ - return (mRedrawSuspendCount>0) || !mViewportInitialized; -} - //---------------------------------------------------------------------- // nsISVGSVGFrame methods: - -NS_IMETHODIMP +void nsSVGOuterSVGFrame::SuspendRedraw() { if (++mRedrawSuspendCount != 1) - return NS_OK; + return; - for (nsIFrame* kid = mFrames.FirstChild(); kid; - kid = kid->GetNextSibling()) { - nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); - if (SVGFrame) { - SVGFrame->NotifyRedrawSuspended(); - } - } - return NS_OK; + nsSVGUtils::NotifyRedrawSuspended(this); } -NS_IMETHODIMP +void nsSVGOuterSVGFrame::UnsuspendRedraw() { NS_ASSERTION(mRedrawSuspendCount >=0, "unbalanced suspend count!"); if (--mRedrawSuspendCount > 0) - return NS_OK; + return; - for (nsIFrame* kid = mFrames.FirstChild(); kid; - kid = kid->GetNextSibling()) { - nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); - if (SVGFrame) { - SVGFrame->NotifyRedrawUnsuspended(); - } - } - - return NS_OK; + nsSVGUtils::NotifyRedrawUnsuspended(this); } -NS_IMETHODIMP +void nsSVGOuterSVGFrame::NotifyViewportChange() { // no point in doing anything when were not init'ed yet: if (!mViewportInitialized) { - return NS_OK; + return; } PRUint32 flags = COORD_CONTEXT_CHANGED; @@ -790,10 +728,7 @@ nsSVGOuterSVGFrame::NotifyViewportChange() } // inform children - SuspendRedraw(); nsSVGUtils::NotifyChildrenOfSVGChange(this, flags); - UnsuspendRedraw(); - return NS_OK; } //---------------------------------------------------------------------- diff --git a/layout/svg/base/src/nsSVGOuterSVGFrame.h b/layout/svg/base/src/nsSVGOuterSVGFrame.h index 02bc30f12d1c..c5f6cf872086 100644 --- a/layout/svg/base/src/nsSVGOuterSVGFrame.h +++ b/layout/svg/base/src/nsSVGOuterSVGFrame.h @@ -124,21 +124,10 @@ public: nsIAtom* aAttribute, PRInt32 aModType); - // nsSVGOuterSVGFrame methods: - - void InvalidateCoveredRegion(nsIFrame *aFrame); - // Calls aSVG->UpdateCoveredRegion and returns true if the covered - // region actually changed. If it changed, invalidates the old and new - // covered regions, taking filters into account, like - // InvalidateCoveredRegion. - bool UpdateAndInvalidateCoveredRegion(nsIFrame *aFrame); - - bool IsRedrawSuspended(); - // nsISVGSVGFrame interface: - NS_IMETHOD SuspendRedraw(); - NS_IMETHOD UnsuspendRedraw(); - NS_IMETHOD NotifyViewportChange(); + virtual void SuspendRedraw(); + virtual void UnsuspendRedraw(); + virtual void NotifyViewportChange(); // nsSVGContainerFrame methods: virtual gfxMatrix GetCanvasTM(); @@ -169,9 +158,9 @@ protected: // subtree if we were to use a list (see bug 381285 comment 20). nsTHashtable mForeignObjectHash; - PRUint32 mRedrawSuspendCount; nsAutoPtr mCanvasTM; + PRUint32 mRedrawSuspendCount; float mFullZoom; bool mViewportInitialized; diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index 6d171fcfeb00..c7f2d5ae3e59 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -236,20 +236,19 @@ nsSVGPathGeometryFrame::NotifySVGChanged(PRUint32 aFlags) } } -NS_IMETHODIMP +void nsSVGPathGeometryFrame::NotifyRedrawSuspended() { - // XXX should we cache the fact that redraw is suspended? - return NS_OK; + AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); } -NS_IMETHODIMP +void nsSVGPathGeometryFrame::NotifyRedrawUnsuspended() { + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + if (GetStateBits() & NS_STATE_SVG_DIRTY) nsSVGUtils::UpdateGraphic(this); - - return NS_OK; } gfxRect diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.h b/layout/svg/base/src/nsSVGPathGeometryFrame.h index dee1c836c4ad..5a2114779a6d 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.h +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.h @@ -96,8 +96,8 @@ protected: NS_IMETHOD UpdateCoveredRegion(); NS_IMETHOD InitialUpdate(); virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD NotifyRedrawSuspended(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawSuspended(); + virtual void NotifyRedrawUnsuspended(); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags); NS_IMETHOD_(bool) IsDisplayContainer() { return false; } diff --git a/layout/svg/base/src/nsSVGSwitchFrame.cpp b/layout/svg/base/src/nsSVGSwitchFrame.cpp index 61cc4e5e01b1..a8cb6bbde6e3 100644 --- a/layout/svg/base/src/nsSVGSwitchFrame.cpp +++ b/layout/svg/base/src/nsSVGSwitchFrame.cpp @@ -80,7 +80,7 @@ public: NS_IMETHODIMP_(nsRect) GetCoveredRegion(); NS_IMETHOD UpdateCoveredRegion(); NS_IMETHOD InitialUpdate(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawUnsuspended(); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags); @@ -186,13 +186,15 @@ nsSVGSwitchFrame::InitialUpdate() return nsSVGSwitchFrameBase::InitialUpdate(); } -NS_IMETHODIMP +void nsSVGSwitchFrame::NotifyRedrawUnsuspended() { + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + if (GetStateBits() & NS_STATE_SVG_DIRTY) nsSVGUtils::UpdateGraphic(this); - return nsSVGSwitchFrameBase::NotifyRedrawUnsuspended(); + nsSVGSwitchFrameBase::NotifyRedrawUnsuspended(); } gfxRect diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index ec9907c27715..5c90cd1b89fa 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -209,20 +209,13 @@ nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags) } } -NS_IMETHODIMP -nsSVGTextFrame::NotifyRedrawSuspended() -{ - mMetricsState = suspended; - - return nsSVGTextFrameBase::NotifyRedrawSuspended(); -} - -NS_IMETHODIMP +void nsSVGTextFrame::NotifyRedrawUnsuspended() { - mMetricsState = unsuspended; + RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + UpdateGlyphPositioning(false); - return nsSVGTextFrameBase::NotifyRedrawUnsuspended(); + nsSVGTextFrameBase::NotifyRedrawUnsuspended(); } NS_IMETHODIMP @@ -338,7 +331,7 @@ nsSVGTextFrame::SetWhitespaceHandling(nsSVGGlyphFrame *aFrame) void nsSVGTextFrame::UpdateGlyphPositioning(bool aForceGlobalTransform) { - if (mMetricsState == suspended || !mPositioningDirty) + if ((GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) || !mPositioningDirty) return; mPositioningDirty = false; diff --git a/layout/svg/base/src/nsSVGTextFrame.h b/layout/svg/base/src/nsSVGTextFrame.h index 8f39ff5f7e2c..ba8b6f02e3a0 100644 --- a/layout/svg/base/src/nsSVGTextFrame.h +++ b/layout/svg/base/src/nsSVGTextFrame.h @@ -52,7 +52,6 @@ class nsSVGTextFrame : public nsSVGTextFrameBase protected: nsSVGTextFrame(nsStyleContext* aContext) : nsSVGTextFrameBase(aContext), - mMetricsState(unsuspended), mPositioningDirty(true) {} public: @@ -85,8 +84,7 @@ public: // nsISVGChildFrame interface: virtual void NotifySVGChanged(PRUint32 aFlags); - NS_IMETHOD NotifyRedrawSuspended(); - NS_IMETHOD NotifyRedrawUnsuspended(); + virtual void NotifyRedrawUnsuspended(); // Override these four to ensure that UpdateGlyphPositioning is called // to bring glyph positions up to date NS_IMETHOD PaintSVG(nsSVGRenderState* aContext, @@ -126,9 +124,6 @@ private: nsAutoPtr mCanvasTM; - enum UpdateState { unsuspended, suspended }; - UpdateState mMetricsState; - bool mPositioningDirty; }; diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 0b36603b7ff3..9d9885b18df8 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -598,45 +598,67 @@ nsSVGUtils::InvalidateCoveredRegion(nsIFrame *aFrame) nsSVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(aFrame); NS_ASSERTION(outerSVGFrame, "no outer svg frame"); - if (outerSVGFrame) - outerSVGFrame->InvalidateCoveredRegion(aFrame); + if (outerSVGFrame) { + nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); + if (!svgFrame) + return; + + // Make sure elements styled by :hover get updated if script/animation moves + // them under or out from under the pointer: + aFrame->PresContext()->PresShell()->SynthesizeMouseMove(false); + + nsRect rect = FindFilterInvalidation(aFrame, svgFrame->GetCoveredRegion()); + outerSVGFrame->Invalidate(rect); + } } void -nsSVGUtils::UpdateGraphic(nsISVGChildFrame *aSVGFrame) +nsSVGUtils::UpdateGraphic(nsIFrame *aFrame) { - nsIFrame *frame = do_QueryFrame(aSVGFrame); + nsSVGEffects::InvalidateRenderingObservers(aFrame); - nsSVGEffects::InvalidateRenderingObservers(frame); - - if (frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) + if (aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) return; - nsSVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame(frame); + if (aFrame->GetStateBits() & NS_STATE_SVG_REDRAW_SUSPENDED) { + aFrame->AddStateBits(NS_STATE_SVG_DIRTY); + return; + } + + aFrame->RemoveStateBits(NS_STATE_SVG_DIRTY); + + nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); + if (!svgFrame) + return; + + nsSVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame(aFrame); if (!outerSVGFrame) { NS_ERROR("null outerSVGFrame"); return; } - if (outerSVGFrame->IsRedrawSuspended()) { - frame->AddStateBits(NS_STATE_SVG_DIRTY); - } else { - frame->RemoveStateBits(NS_STATE_SVG_DIRTY); + // Make sure elements styled by :hover get updated if script/animation moves + // them under or out from under the pointer: + aFrame->PresContext()->PresShell()->SynthesizeMouseMove(false); - bool changed = outerSVGFrame->UpdateAndInvalidateCoveredRegion(frame); - if (changed) { - NotifyAncestorsOfFilterRegionChange(frame); - } + nsRect oldRegion = svgFrame->GetCoveredRegion(); + outerSVGFrame->Invalidate(FindFilterInvalidation(aFrame, oldRegion)); + svgFrame->UpdateCoveredRegion(); + nsRect newRegion = svgFrame->GetCoveredRegion(); + if (oldRegion.IsEqualInterior(newRegion)) + return; + + outerSVGFrame->Invalidate(FindFilterInvalidation(aFrame, newRegion)); + if (!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) { + NotifyAncestorsOfFilterRegionChange(aFrame); } } void nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame) { - if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) { - // It would be better if we couldn't get here - return; - } + NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG), + "Not expecting to be called on the outer SVG Frame"); aFrame = aFrame->GetParent(); @@ -883,6 +905,38 @@ nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags) } } +void +nsSVGUtils::NotifyRedrawSuspended(nsIFrame *aFrame) +{ + aFrame->AddStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + + nsIFrame *kid = aFrame->GetFirstPrincipalChild(); + + while (kid) { + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); + if (SVGFrame) { + SVGFrame->NotifyRedrawSuspended(); + } + kid = kid->GetNextSibling(); + } +} + +void +nsSVGUtils::NotifyRedrawUnsuspended(nsIFrame *aFrame) +{ + aFrame->RemoveStateBits(NS_STATE_SVG_REDRAW_SUSPENDED); + + nsIFrame *kid = aFrame->GetFirstPrincipalChild(); + + while (kid) { + nsISVGChildFrame* SVGFrame = do_QueryFrame(kid); + if (SVGFrame) { + SVGFrame->NotifyRedrawUnsuspended(); + } + kid = kid->GetNextSibling(); + } +} + // ************************************************************ class SVGPaintCallback : public nsSVGFilterPaintCallback @@ -1522,3 +1576,32 @@ nsSVGUtils::RootSVGElementHasViewbox(const nsIContent *aRootSVGElem) return svgSvgElem->HasValidViewbox(); } + +/* static */ void +nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + float *aOpacity, nscolor *color) +{ + const nsStyleSVGPaint &paint = aStyleContext->GetStyleSVG()->*aFillOrStroke; + nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited(); + bool isServer = paint.mType == eStyleSVGPaintType_Server; + *color = isServer ? paint.mFallbackColor : paint.mPaint.mColor; + if (styleIfVisited) { + const nsStyleSVGPaint &paintIfVisited = + styleIfVisited->GetStyleSVG()->*aFillOrStroke; + // To prevent Web content from detecting if a user has visited a URL + // (via URL loading triggered by paint servers or performance + // differences between paint servers or between a paint server and a + // color), we do not allow whether links are visited to change which + // paint server is used or switch between paint servers and simple + // colors. A :visited style may only override a simple color with + // another simple color. + if (paintIfVisited.mType == eStyleSVGPaintType_Color && + paint.mType == eStyleSVGPaintType_Color) { + nscolor colorIfVisited = paintIfVisited.mPaint.mColor; + nscolor colors[2] = { *color, colorIfVisited }; + *color = nsStyleContext::CombineVisitedColors(colors, + aStyleContext->RelevantLinkVisited()); + } + } +} diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 9dafbd628b52..41673f314da6 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -48,6 +48,7 @@ #include "nsRenderingContext.h" #include "gfxRect.h" #include "gfxMatrix.h" +#include "nsStyleStruct.h" class nsIDocument; class nsPresContext; @@ -101,6 +102,9 @@ class Element; // If this bit is set, we are a element or descendant. #define NS_STATE_SVG_CLIPPATH_CHILD NS_FRAME_STATE_BIT(23) +// If this bit is set, redraw is suspended. +#define NS_STATE_SVG_REDRAW_SUSPENDED NS_FRAME_STATE_BIT(24) + /** * Byte offsets of channels in a native packed gfxColor or cairo image surface. */ @@ -326,9 +330,10 @@ public: static void InvalidateCoveredRegion(nsIFrame *aFrame); /* - * Update the area covered by the frame + * Update the area covered by the frame allowing for the frame to + * have moved. */ - static void UpdateGraphic(nsISVGChildFrame *aSVGFrame); + static void UpdateGraphic(nsIFrame *aFrame); /* * Update the filter invalidation region for ancestor frames, if relevant. @@ -423,6 +428,19 @@ public: static void NotifyChildrenOfSVGChange(nsIFrame *aFrame, PRUint32 aFlags); + /* + * Tells child frames that redraw is suspended + */ + static void + NotifyRedrawSuspended(nsIFrame *aFrame); + + /* + * Tells child frames that redraw is no longer suspended + * @return true if any of the child frames are dirty + */ + static void + NotifyRedrawUnsuspended(nsIFrame *aFrame); + /* * Get frame's covered region by walking the children and doing union. */ @@ -586,6 +604,11 @@ public: * builds, it will trigger a false return-value as a safe fallback.) */ static bool RootSVGElementHasViewbox(const nsIContent *aRootSVGElem); + + static void GetFallbackOrPaintColor(gfxContext *aContext, + nsStyleContext *aStyleContext, + nsStyleSVGPaint nsStyleSVG::*aFillOrStroke, + float *aOpacity, nscolor *color); }; #endif diff --git a/layout/tables/Makefile.in b/layout/tables/Makefile.in index 220b5e27acd2..36c1de1a01d1 100644 --- a/layout/tables/Makefile.in +++ b/layout/tables/Makefile.in @@ -72,9 +72,8 @@ CPPSRCS = \ # we don't want the shared lib, but we want to force the creation of a static lib. FORCE_STATIC_LIB = 1 -ifdef ENABLE_TESTS -DIRS = test -endif +TEST_DIRS += test + include $(topsrcdir)/config/rules.mk diff --git a/mfbt/LinkedList.h b/mfbt/LinkedList.h index f187294215af..70244b89671c 100644 --- a/mfbt/LinkedList.h +++ b/mfbt/LinkedList.h @@ -116,6 +116,25 @@ class LinkedListElement * But the goal here isn't to win an award for the fastest or slimmest * linked list; rather, we want a *convenient* linked list. So we won't * waste time guessing which micro-optimization strategy is best. + * + * + * Speaking of unnecessary work, it's worth addressing here why we wrote + * mozilla::LinkedList in the first place, instead of using stl::list. + * + * The key difference between mozilla::LinkedList and stl::list is that + * mozilla::LinkedList stores the prev/next pointers in the object itself, + * while stl::list stores the prev/next pointers in a list element which + * itself points to the object being stored. + * + * mozilla::LinkedList's approach makes it harder to store an object in more + * than one list. But the upside is that you can call next() / prev() / + * remove() directly on the object. With stl::list, you'd need to store a + * pointer to its iterator in the object in order to accomplish this. Not + * only would this waste space, but you'd have to remember to update that + * pointer every time you added or removed the object from a list. + * + * In-place, constant-time removal is a killer feature of doubly-linked + * lists, and supporting this painlessly was a key design criterion. */ private: @@ -200,14 +219,14 @@ private: friend class LinkedList ; enum NodeKind { - NODE_TYPE_NORMAL, - NODE_TYPE_SENTINEL + NODE_KIND_NORMAL, + NODE_KIND_SENTINEL }; - LinkedListElement(NodeKind nodeType) + LinkedListElement(NodeKind nodeKind) : next(this) , prev(this) - , isSentinel(nodeType == NODE_TYPE_SENTINEL) + , isSentinel(nodeKind == NODE_KIND_SENTINEL) { } @@ -265,7 +284,7 @@ public: LinkedList(const LinkedList & other) MOZ_DELETE; LinkedList() - : sentinel(LinkedListElement ::NODE_TYPE_SENTINEL) + : sentinel(LinkedListElement ::NODE_KIND_SENTINEL) { } @@ -372,7 +391,10 @@ public: MOZ_ASSERT(slow != fast2); } - /* Check that |sentinel| is the only root in the list. */ + /* + * Check that |sentinel| is the only node in the list with + * isSentinel == true. + */ for (LinkedListElement * elem = sentinel.next; elem != sentinel; elem = elem->next) { diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index 63112101595c..9d31870844c8 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -90,9 +90,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL /* The viewport that Gecko will display when drawing is finished */ private ViewportMetrics mNewGeckoViewport; - /* The offset used to make sure tiles are snapped to the pixel grid */ - private Point mRenderOffset; - private CairoImage mCairoImage; private static final IntSize TILE_SIZE = new IntSize(256, 256); @@ -123,7 +120,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mScreenSize = new IntSize(0, 0); mBufferSize = new IntSize(0, 0); mFormat = CairoImage.FORMAT_RGB16_565; - mRenderOffset = new Point(0, 0); mCairoImage = new CairoImage() { @Override @@ -179,7 +175,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL if (mHasDirectTexture) { Log.i(LOGTAG, "Creating WidgetTileLayer"); mTileLayer = new WidgetTileLayer(mCairoImage); - mRenderOffset.set(0, 0); } else { Log.i(LOGTAG, "Creating MultiTileLayer"); mTileLayer = new MultiTileLayer(mCairoImage, TILE_SIZE); @@ -195,10 +190,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } public boolean beginDrawing(int width, int height, int tileWidth, int tileHeight, String metadata, boolean hasDirectTexture) { - // If we've changed surface types, cancel this draw - if (setHasDirectTexture(hasDirectTexture)) { - return false; - } + setHasDirectTexture(hasDirectTexture); // Make sure the tile-size matches. If it doesn't, we could crash trying // to access invalid memory. @@ -231,43 +223,18 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL beginTransaction(mTileLayer); - // We only need to set a render offset/allocate buffer memory if - // we're using MultiTileLayer. Otherwise, just synchronise the - // buffer size and return. - if (!(mTileLayer instanceof MultiTileLayer)) { - if (mBufferSize.width != width || mBufferSize.height != height) - mBufferSize = new IntSize(width, height); - return true; - } - - // If the origin has changed, alter the rendering offset so that - // rendering is snapped to the tile grid and clear the invalid area. - boolean originChanged = true; - Point origin = PointUtils.round(mNewGeckoViewport.getDisplayportOrigin()); - - if (mGeckoViewport != null) { - Point oldOrigin = PointUtils.round(mGeckoViewport.getDisplayportOrigin()); - originChanged = !origin.equals(oldOrigin); - } - - if (originChanged) { - Point tileOrigin = new Point((origin.x / TILE_SIZE.width) * TILE_SIZE.width, - (origin.y / TILE_SIZE.height) * TILE_SIZE.height); - mRenderOffset.set(origin.x - tileOrigin.x, origin.y - tileOrigin.y); - } - - // If the window size has changed, reallocate the buffer to match. + // Synchronise the buffer size with Gecko. if (mBufferSize.width != width || mBufferSize.height != height) { mBufferSize = new IntSize(width, height); - // We over-allocate to allow for the render offset. nsWindow - // assumes that this will happen. - IntSize realBufferSize = new IntSize(width + TILE_SIZE.width, - height + TILE_SIZE.height); + // We only need to allocate buffer memory if we're using MultiTileLayer. + if (!(mTileLayer instanceof MultiTileLayer)) { + return true; + } // Reallocate the buffer if necessary int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8; - int size = realBufferSize.getArea() * bpp; + int size = mBufferSize.getArea() * bpp; if (mBuffer == null || mBuffer.capacity() != size) { // Free the old buffer if (mBuffer != null) { @@ -320,9 +287,7 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL if (mTileLayer instanceof MultiTileLayer) { Rect rect = new Rect(x, y, x + width, y + height); - rect.offset(mRenderOffset.x, mRenderOffset.y); ((MultiTileLayer)mTileLayer).invalidate(rect); - ((MultiTileLayer)mTileLayer).setRenderOffset(mRenderOffset); } } finally { endTransaction(mTileLayer); @@ -348,19 +313,23 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL ByteBuffer tileBuffer = mBuffer.slice(); int bpp = CairoUtils.bitsPerPixelForCairoFormat(mFormat) / 8; - for (int y = 0; y <= mBufferSize.height; y += TILE_SIZE.height) { - for (int x = 0; x <= mBufferSize.width; x += TILE_SIZE.width) { + for (int y = 0; y < mBufferSize.height; y += TILE_SIZE.height) { + for (int x = 0; x < mBufferSize.width; x += TILE_SIZE.width) { + // Calculate tile size + IntSize tileSize = new IntSize(Math.min(mBufferSize.width - x, TILE_SIZE.width), + Math.min(mBufferSize.height - y, TILE_SIZE.height)); + // Create a Bitmap from this tile - Bitmap tile = Bitmap.createBitmap(TILE_SIZE.width, TILE_SIZE.height, + Bitmap tile = Bitmap.createBitmap(tileSize.width, tileSize.height, CairoUtils.cairoFormatTobitmapConfig(mFormat)); tile.copyPixelsFromBuffer(tileBuffer.asIntBuffer()); // Copy the tile to the master Bitmap and recycle it - c.drawBitmap(tile, x - mRenderOffset.x, y - mRenderOffset.y, null); + c.drawBitmap(tile, x, y, null); tile.recycle(); // Progress the buffer to the next tile - tileBuffer.position(TILE_SIZE.getArea() * bpp); + tileBuffer.position(tileSize.getArea() * bpp); tileBuffer = tileBuffer.slice(); } } @@ -402,10 +371,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL return mBuffer; } - public Point getRenderOffset() { - return mRenderOffset; - } - /** * Gecko calls this function to signal that it is done with the back buffer. After this call, * it is forbidden for Gecko to touch the buffer. diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index ae98e7ef7061..095bb2cbb0ec 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -65,31 +65,23 @@ public abstract class Layer { /** * Updates the layer. This returns false if there is still work to be done - * after this update. If the layer is not already in a transaction, the - * lock will be acquired and a transaction will automatically begin and - * end around the update. + * after this update. */ public final boolean update(GL10 gl, RenderContext context) { - boolean startTransaction = true; if (mTransactionLock.isHeldByCurrentThread()) { - startTransaction = false; + throw new RuntimeException("draw() called while transaction lock held by this " + + "thread?!"); } - // If we're not already in a transaction and we can't acquire the lock, - // bail out. - if (startTransaction && !mTransactionLock.tryLock()) { - return false; - } - - mInTransaction = true; - try { - return performUpdates(gl, context); - } finally { - if (startTransaction) { - mInTransaction = false; + if (mTransactionLock.tryLock()) { + try { + return performUpdates(gl, context); + } finally { mTransactionLock.unlock(); } } + + return false; } /** Subclasses override this function to draw the layer. */ @@ -128,6 +120,7 @@ public abstract class Layer { mTransactionLock.lock(); mView = aView; mInTransaction = true; + mNewResolution = mResolution; } public void beginTransaction() { diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index ae72f92c5096..9081a19e4ecb 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -118,30 +118,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - mBackgroundLayer.beginTransaction(null); - try { - mBackgroundLayer.invalidate(); - } finally { - mBackgroundLayer.endTransaction(); - } mCheckerboardImage = new CheckerboardImage(); mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage); - mCheckerboardLayer.beginTransaction(null); - try { - mCheckerboardLayer.invalidate(); - } finally { - mCheckerboardLayer.endTransaction(); - } CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); - mShadowLayer.beginTransaction(null); - try { - mShadowLayer.invalidate(); - } finally { - mShadowLayer.endTransaction(); - } IntSize frameRateLayerSize = new IntSize(FRAME_RATE_METER_WIDTH, FRAME_RATE_METER_HEIGHT); mFrameRateLayer = TextLayer.create(frameRateLayerSize, "-- ms/--"); diff --git a/mobile/android/base/gfx/MultiTileLayer.java b/mobile/android/base/gfx/MultiTileLayer.java index 57c7244e0b5a..0f3f1f58b2db 100644 --- a/mobile/android/base/gfx/MultiTileLayer.java +++ b/mobile/android/base/gfx/MultiTileLayer.java @@ -37,21 +37,16 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.FloatUtils; import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.SingleTileLayer; import android.graphics.Point; -import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; import android.util.Log; -import java.lang.Long; import java.nio.ByteBuffer; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.ListIterator; +import java.util.ArrayList; import javax.microedition.khronos.opengles.GL10; /** @@ -63,18 +58,9 @@ public class MultiTileLayer extends Layer { private static final String LOGTAG = "GeckoMultiTileLayer"; private final CairoImage mImage; - private final IntSize mTileSize; + private IntSize mTileSize; private IntSize mBufferSize; - private Region mDirtyRegion; - private Region mValidRegion; - private Point mRenderOffset; - private final LinkedList mTiles; - private final HashMap mPositionHash; - - // Copies of the last set origin/resolution, to decide when to invalidate - // the buffer - private Point mOrigin; - private float mResolution; + private final ArrayList mTiles; public MultiTileLayer(CairoImage image, IntSize tileSize) { super(); @@ -82,38 +68,29 @@ public class MultiTileLayer extends Layer { mImage = image; mTileSize = tileSize; mBufferSize = new IntSize(0, 0); - mDirtyRegion = new Region(); - mValidRegion = new Region(); - mRenderOffset = new Point(); - mTiles = new LinkedList (); - mPositionHash = new HashMap (); + mTiles = new ArrayList (); } - /** - * Invalidates a sub-region of the layer. Data will be uploaded from the - * backing buffer over subsequent calls to update(). - * This method is only valid inside a transaction. - */ public void invalidate(Rect dirtyRect) { if (!inTransaction()) { throw new RuntimeException("invalidate() is only valid inside a transaction"); } - mDirtyRegion.union(dirtyRect); - mValidRegion.union(dirtyRect); + for (SubTile layer : mTiles) { + IntSize tileSize = layer.getSize(); + Rect tileRect = new Rect(layer.x, layer.y, layer.x + tileSize.width, layer.y + tileSize.height); + + if (tileRect.intersect(dirtyRect)) { + tileRect.offset(-layer.x, -layer.y); + layer.invalidate(tileRect); + } + } } - /** - * Invalidates the backing buffer. Data will not be uploaded from an invalid - * backing buffer. This method is only valid inside a transaction. - */ - protected void invalidateBuffer() { - if (!inTransaction()) { - throw new RuntimeException("invalidateBuffer() is only valid inside a transaction"); + public void invalidate() { + for (SubTile layer : mTiles) { + layer.invalidate(); } - - mDirtyRegion.setEmpty(); - mValidRegion.setEmpty(); } @Override @@ -121,109 +98,62 @@ public class MultiTileLayer extends Layer { return mImage.getSize(); } - /** - * Makes sure there are enough tiles to accommodate the buffer image. - */ private void validateTiles() { IntSize size = getSize(); - if (size.equals(mBufferSize)) + if (size.equals(mBufferSize)) { return; + } + + // Regenerate tiles + mTiles.clear(); + int offset = 0; + final int format = mImage.getFormat(); + final ByteBuffer buffer = mImage.getBuffer().slice(); + final int bpp = CairoUtils.bitsPerPixelForCairoFormat(format) / 8; + for (int y = 0; y < size.height; y += mTileSize.height) { + for (int x = 0; x < size.width; x += mTileSize.width) { + // Create a CairoImage implementation that returns a + // tile from the parent CairoImage. It's assumed that + // the tiles are stored in series. + final IntSize layerSize = + new IntSize(Math.min(mTileSize.width, size.width - x), + Math.min(mTileSize.height, size.height - y)); + final int tileOffset = offset; + + CairoImage subImage = new CairoImage() { + @Override + public ByteBuffer getBuffer() { + // Create a ByteBuffer that shares the data of the original + // buffer, but is positioned and limited so that only the + // tile data is accessible. + buffer.position(tileOffset); + ByteBuffer tileBuffer = buffer.slice(); + tileBuffer.limit(layerSize.getArea() * bpp); + + return tileBuffer; + } + + @Override + public IntSize getSize() { + return layerSize; + } + + @Override + public int getFormat() { + return format; + } + }; + + mTiles.add(new SubTile(subImage, x, y)); + offset += layerSize.getArea() * bpp; + } + } + + // Set tile origins and resolution + refreshTileMetrics(getOrigin(), getResolution(), false); mBufferSize = size; - - // Shrink/grow tile pool - int nTiles = (Math.round(size.width / (float)mTileSize.width) + 1) * - (Math.round(size.height / (float)mTileSize.height) + 1); - if (mTiles.size() < nTiles) { - Log.i(LOGTAG, "Tile pool growing from " + mTiles.size() + " to " + nTiles); - - for (int i = 0; i < nTiles; i++) { - mTiles.add(new SubTile(new SubImage(mImage, mTileSize))); - } - } else if (mTiles.size() > nTiles) { - Log.i(LOGTAG, "Tile pool shrinking from " + mTiles.size() + " to " + nTiles); - - // Remove tiles from the beginning of the list, as these are - // least recently used tiles - for (int i = mTiles.size(); i > nTiles; i--) { - SubTile tile = mTiles.get(0); - if (tile.key != null) { - mPositionHash.remove(tile.key); - } - mTiles.remove(0); - } - } - - // A buffer size probably means a layout change, so invalidate all tiles. - invalidateTiles(); - } - - /** - * Returns a Long representing the given Point. Used for hashing. - */ - private Long longFromPoint(Point point) { - // Assign 32 bits for each dimension of the point. - return new Long((((long)point.x) << 32) | point.y); - } - - private Point getOffsetOrigin() { - Point origin = new Point(getOrigin()); - origin.offset(-mRenderOffset.x, -mRenderOffset.y); - return origin; - } - - /** - * Performs the necessary functions to update the specified properties of - * a sub-tile. - */ - private void updateTile(GL10 gl, RenderContext context, SubTile tile, Point tileOrigin, Rect dirtyRect, boolean reused) { - tile.beginTransaction(null); - try { - if (reused) { - // Invalidate any area that isn't represented in the current - // buffer. This is done as SingleTileLayer always updates the - // entire width, regardless of the dirty-rect's width, and so - // can override existing data. - Point origin = getOffsetOrigin(); - Region validRegion = new Region(tile.getValidTextureArea()); - validRegion.translate(tileOrigin.x - origin.x, tileOrigin.y - origin.y); - validRegion.op(mValidRegion, Region.Op.INTERSECT); - - // SingleTileLayer can't draw complex regions, so in that case, - // just invalidate the entire area. - tile.invalidateTexture(); - if (!validRegion.isEmpty() && !validRegion.isComplex()) { - validRegion.translate(origin.x - tileOrigin.x, origin.y - tileOrigin.y); - tile.getValidTextureArea().set(validRegion.getBounds()); - } - } else { - // Update tile metrics - tile.setOrigin(tileOrigin); - tile.setResolution(getResolution()); - - // Make sure that non-reused tiles are marked as invalid before - // uploading new content. - tile.invalidateTexture(); - - // (Re)Place in the position hash for quick retrieval. - if (tile.key != null) { - mPositionHash.remove(tile.key); - } - tile.key = longFromPoint(tileOrigin); - mPositionHash.put(tile.key, tile); - } - - // Invalidate the area we want to upload. - tile.invalidate(dirtyRect); - - // Perform updates and mark texture as valid. - if (!tile.performUpdates(gl, context)) { - Log.e(LOGTAG, "Sub-tile failed to update fully"); - } - } finally { - tile.endTransaction(); - } } @Override @@ -232,154 +162,86 @@ public class MultiTileLayer extends Layer { validateTiles(); - // Bail out early if we have nothing to do. - if (mDirtyRegion.isEmpty() || mTiles.isEmpty()) { - return true; - } + // Iterate over the tiles and decide which ones we'll be drawing + int dirtyTiles = 0; + boolean screenUpdateDone = false; + SubTile firstDirtyTile = null; + for (SubTile layer : mTiles) { + // First do a non-texture update to make sure coordinates are + // up-to-date. + boolean invalid = layer.getSkipTextureUpdate(); + layer.setSkipTextureUpdate(true); + layer.performUpdates(gl, context); - // Check that we're capable of updating from this origin. - Point origin = getOffsetOrigin(); - if ((origin.x % mTileSize.width) != 0 || (origin.y % mTileSize.height) != 0) { - Log.e(LOGTAG, "MultiTileLayer doesn't support non tile-aligned buffers! (" + - origin.x + ", " + origin.y + ")"); - return true; - } + RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize())); + boolean isDirty = layer.isDirty(); - // Transform the viewport into tile-space so we can see what part of the - // dirty region intersects with it. - // We update any tiles intersecting with the screen before tiles - // intersecting with the viewport. - // XXX Maybe we want to to split this update even further to update - // checkerboard area before updating screen regions with old data. - // Note that this could provide inconsistent views, so we may not - // want to do this. - Rect tilespaceViewport; - float scaleFactor = getResolution() / context.zoomFactor; - tilespaceViewport = RectUtils.roundOut(RectUtils.scale(context.viewport, scaleFactor)); - tilespaceViewport.offset(-origin.x, -origin.y); - - // Expand tile-space viewport to tile boundaries - tilespaceViewport.left = (tilespaceViewport.left / mTileSize.width) * mTileSize.width; - tilespaceViewport.right += mTileSize.width - 1; - tilespaceViewport.right = (tilespaceViewport.right / mTileSize.width) * mTileSize.width; - tilespaceViewport.top = (tilespaceViewport.top / mTileSize.height) * mTileSize.height; - tilespaceViewport.bottom += mTileSize.height - 1; - tilespaceViewport.bottom = (tilespaceViewport.bottom / mTileSize.height) * mTileSize.height; - - // Declare a region for storing the results of Region operations - Region opRegion = new Region(); - - // Test if the dirty region intersects with the screen - boolean updateVisible = false; - Region updateRegion = mDirtyRegion; - if (opRegion.op(tilespaceViewport, mDirtyRegion, Region.Op.INTERSECT)) { - updateVisible = true; - updateRegion = new Region(opRegion); - } - - // Invalidate any tiles that are due to be replaced if their resolution - // doesn't match the parent layer resolution, and any tiles that are - // off-screen and off-buffer, as we cannot guarantee their validity. - // - // Note that we also cannot guarantee the validity of on-screen, - // off-buffer tiles, but this is a rare case that we allow for - // optimisation purposes. - // - // XXX Ideally, we want to remove this second invalidation clause - // somehow. It may be possible to know if off-screen tiles are - // valid by monitoring reflows on the browser element, or - // something along these lines. - LinkedList invalidTiles = new LinkedList (); - for (ListIterator i = mTiles.listIterator(); i.hasNext();) { - SubTile tile = i.next(); - - if (tile.key == null) { - continue; - } - - RectF tileBounds = tile.getBounds(context, new FloatSize(tile.getSize())); - Rect tilespaceTileBounds = - RectUtils.round(RectUtils.scale(tileBounds, scaleFactor)); - tilespaceTileBounds.offset(-origin.x, -origin.y); - - // First bracketed clause: Invalidate off-screen, off-buffer tiles - // Second: Invalidate visible tiles at the wrong resolution that have updates - if ((!opRegion.op(tilespaceTileBounds, mValidRegion, Region.Op.INTERSECT) && - !Rect.intersects(tilespaceViewport, tilespaceTileBounds)) || - (!FloatUtils.fuzzyEquals(tile.getResolution(), getResolution()) && - opRegion.op(tilespaceTileBounds, updateRegion, Region.Op.INTERSECT))) { - tile.invalidateTexture(); - - // Add to the list of invalid tiles and remove from the main list - invalidTiles.add(tile); - i.remove(); - - // Remove from the position hash - mPositionHash.remove(tile.key); - tile.key = null; - } - } - - // Push invalid tiles to the head of the queue so they get used first - mTiles.addAll(0, invalidTiles); - - // Update tiles - // Note, it's <= as the buffer is over-allocated due to render-offsetting. - for (int y = origin.y; y <= origin.y + mBufferSize.height; y += mTileSize.height) { - for (int x = origin.x; x <= origin.x + mBufferSize.width; x += mTileSize.width) { - // Does this tile intersect with the dirty region? - Rect tilespaceTileRect = new Rect(x - origin.x, y - origin.y, - (x - origin.x) + mTileSize.width, - (y - origin.y) + mTileSize.height); - if (!opRegion.op(tilespaceTileRect, updateRegion, Region.Op.INTERSECT)) { - continue; - } - - // Dirty tile, find out if we already have this tile to reuse. - boolean reusedTile; - Point tileOrigin = new Point(x, y); - SubTile tile = mPositionHash.get(longFromPoint(tileOrigin)); - - // If we don't, get an unused tile (we store these at the head of the list). - if (tile == null) { - tile = mTiles.removeFirst(); - reusedTile = false; + if (isDirty) { + if (!RectF.intersects(layerBounds, context.viewport)) { + if (firstDirtyTile == null) + firstDirtyTile = layer; + dirtyTiles ++; + invalid = true; } else { - mTiles.remove(tile); - - // Reuse the tile (i.e. keep the texture data and metrics) - // only if the resolution matches - reusedTile = FloatUtils.fuzzyEquals(tile.getResolution(), getResolution()); - } - - // Place tile at the end of the tile-list so it isn't re-used. - mTiles.add(tile); - - // Work out the tile's invalid area in this tile's space. - if (opRegion.isComplex()) { - Log.w(LOGTAG, "MultiTileLayer encountered complex dirty region"); - } - Rect dirtyRect = opRegion.getBounds(); - dirtyRect.offset(origin.x - x, origin.y - y); - - // Update tile metrics and texture data - tile.x = (x - origin.x) / mTileSize.width; - tile.y = (y - origin.y) / mTileSize.height; - updateTile(gl, context, tile, tileOrigin, dirtyRect, reusedTile); - - // If this update isn't visible, we only want to update one - // tile at a time. - if (!updateVisible) { - mDirtyRegion.op(opRegion, Region.Op.XOR); - return mDirtyRegion.isEmpty(); + // This tile intersects with the screen and is dirty, + // update it immediately. + layer.setSkipTextureUpdate(false); + screenUpdateDone = true; + layer.performUpdates(gl, context); + invalid = false; } } + + // We use the SkipTextureUpdate flag as a marker of a tile's + // validity. This is required, as sometimes layers are drawn + // without updating first, and we mustn't draw tiles that have + // been marked as invalid that we haven't updated. + layer.setSkipTextureUpdate(invalid); } - // Remove the update region from the dirty region - mDirtyRegion.op(updateRegion, Region.Op.XOR); + // Now if no tiles that intersect with the screen were updated, update + // a single tile that doesn't (if there are any). This has the effect + // of spreading out non-critical texture upload over time, and smoothing + // upload-related hitches. + if (!screenUpdateDone && firstDirtyTile != null) { + firstDirtyTile.setSkipTextureUpdate(false); + firstDirtyTile.performUpdates(gl, context); + dirtyTiles --; + } - return mDirtyRegion.isEmpty(); + return (dirtyTiles == 0); + } + + private void refreshTileMetrics(Point origin, float resolution, boolean inTransaction) { + IntSize size = getSize(); + for (SubTile layer : mTiles) { + if (!inTransaction) { + layer.beginTransaction(null); + } + + if (origin != null) { + layer.setOrigin(new Point(origin.x + layer.x, origin.y + layer.y)); + } + if (resolution >= 0.0f) { + layer.setResolution(resolution); + } + + if (!inTransaction) { + layer.endTransaction(); + } + } + } + + @Override + public void setOrigin(Point newOrigin) { + super.setOrigin(newOrigin); + refreshTileMetrics(newOrigin, -1, true); + } + + @Override + public void setResolution(float newResolution) { + super.setResolution(newResolution); + refreshTileMetrics(null, newResolution, true); } @Override @@ -403,46 +265,23 @@ public class MultiTileLayer extends Layer { @Override public void draw(RenderContext context) { for (SubTile layer : mTiles) { - // Skip invalid tiles - if (layer.key == null) { + // We use the SkipTextureUpdate flag as a validity flag. If it's false, + // the contents of this tile are invalid and we shouldn't draw it. + if (layer.getSkipTextureUpdate()) continue; - } // Avoid work, only draw tiles that intersect with the viewport RectF layerBounds = layer.getBounds(context, new FloatSize(layer.getSize())); - if (RectF.intersects(layerBounds, context.viewport)) { + if (RectF.intersects(layerBounds, context.viewport)) layer.draw(context); - } } } - @Override - public void setOrigin(Point origin) { - if (mOrigin == null || !origin.equals(mOrigin)) { - mOrigin = origin; - super.setOrigin(origin); - invalidateBuffer(); - } - } - - @Override - public void setResolution(float resolution) { - if (!FloatUtils.fuzzyEquals(resolution, mResolution)) { - mResolution = resolution; - super.setResolution(resolution); - invalidateBuffer(); - } - } - - public void setRenderOffset(Point offset) { - mRenderOffset.set(offset.x, offset.y); - } - @Override public Region getValidRegion(RenderContext context) { Region validRegion = new Region(); for (SubTile tile : mTiles) { - if (tile.key == null || tile.getValidTextureArea().isEmpty()) + if (tile.getSkipTextureUpdate()) continue; validRegion.op(tile.getValidRegion(context), Region.Op.UNION); } @@ -450,90 +289,14 @@ public class MultiTileLayer extends Layer { return validRegion; } - /** - * Invalidates all sub-tiles. This should be called if the source backing - * this layer has changed. This method is only valid inside a transaction. - */ - protected void invalidateTiles() { - if (!inTransaction()) { - throw new RuntimeException("invalidateTiles() is only valid inside a transaction"); - } - - for (SubTile tile : mTiles) { - // Remove tile from position hash and mark it as invalid - if (tile.key != null) { - mPositionHash.remove(tile.key); - tile.key = null; - } - tile.invalidateTexture(); - } - } - - /** - * A SingleTileLayer extension with fields for relevant tile data that - * MultiTileLayer requires. - */ - private static class SubTile extends SingleTileLayer { + class SubTile extends SingleTileLayer { public int x; public int y; - public Long key; - - public SubTile(SubImage aImage) { - super(aImage); - - aImage.tile = this; - } - } - - /** - * A CairoImage implementation that returns a tile from a parent CairoImage. - * This assumes that the parent image has a size that is a multiple of the - * tile size. - */ - private static class SubImage extends CairoImage { - public SubTile tile; - - private IntSize mTileSize; - private CairoImage mImage; - - public SubImage(CairoImage image, IntSize tileSize) { - mTileSize = tileSize; - mImage = image; - } - - @Override - public ByteBuffer getBuffer() { - // Create a ByteBuffer that shares the data of the original - // buffer, but is positioned and limited so that only the - // tile data is accessible. - IntSize bufferSize = mImage.getSize(); - int bpp = CairoUtils.bitsPerPixelForCairoFormat(getFormat()) / 8; - int index = (tile.y * (bufferSize.width / mTileSize.width + 1)) + tile.x; - - ByteBuffer buffer = mImage.getBuffer().slice(); - - try { - buffer.position(index * mTileSize.getArea() * bpp); - buffer = buffer.slice(); - buffer.limit(mTileSize.getArea() * bpp); - } catch (IllegalArgumentException e) { - Log.e(LOGTAG, "Tile image-data out of bounds! Tile: (" + - tile.x + ", " + tile.y + "), image (" + bufferSize + ")"); - return null; - } - - return buffer; - } - - @Override - public IntSize getSize() { - return mTileSize; - } - - @Override - public int getFormat() { - return mImage.getFormat(); + public SubTile(CairoImage mImage, int mX, int mY) { + super(mImage); + x = mX; + y = mY; } } } diff --git a/mobile/android/base/gfx/RectUtils.java b/mobile/android/base/gfx/RectUtils.java index ea606af20faf..ce25f40d5ce7 100644 --- a/mobile/android/base/gfx/RectUtils.java +++ b/mobile/android/base/gfx/RectUtils.java @@ -105,12 +105,6 @@ public final class RectUtils { Math.round(rect.right), Math.round(rect.bottom)); } - /** Returns the smallest integer rect that encapsulates the given rect. */ - public static Rect roundOut(RectF rect) { - return new Rect((int)Math.floor(rect.left), (int)Math.floor(rect.top), - (int)Math.ceil(rect.right), (int)Math.ceil(rect.bottom)); - } - public static IntSize getSize(Rect rect) { return new IntSize(rect.width(), rect.height()); } diff --git a/mobile/android/base/gfx/SingleTileLayer.java b/mobile/android/base/gfx/SingleTileLayer.java index 940527df2a91..39d063d57955 100644 --- a/mobile/android/base/gfx/SingleTileLayer.java +++ b/mobile/android/base/gfx/SingleTileLayer.java @@ -43,7 +43,6 @@ import org.mozilla.gecko.gfx.IntSize; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.TileLayer; import android.graphics.PointF; -import android.graphics.Rect; import android.graphics.RectF; import android.opengl.GLES11; import android.opengl.GLES11Ext; @@ -59,8 +58,6 @@ import javax.microedition.khronos.opengles.GL10; * TODO: Repeating textures really should be their own type of layer. */ public class SingleTileLayer extends TileLayer { - private static final String LOGTAG = "GeckoSingleTileLayer"; - public SingleTileLayer(CairoImage image) { this(false, image); } public SingleTileLayer(boolean repeat, CairoImage image) { @@ -74,11 +71,6 @@ public class SingleTileLayer extends TileLayer { if (!initialized()) return; - // If the texture contents is entirely invalid, we have nothing to draw. - Rect validTexture = getValidTextureArea(); - if (validTexture.isEmpty()) - return; - GLES11.glBindTexture(GL10.GL_TEXTURE_2D, getTextureID()); RectF bounds; @@ -87,27 +79,13 @@ public class SingleTileLayer extends TileLayer { RectF viewport = context.viewport; if (repeats()) { - if (!validTexture.equals(new Rect(0, 0, size.width, size.height))) { - Log.e(LOGTAG, "Drawing partial repeating textures is unsupported!"); - } - bounds = new RectF(0.0f, 0.0f, viewport.width(), viewport.height()); int width = Math.round(viewport.width()); int height = Math.round(-viewport.height()); cropRect = new int[] { 0, size.height, width, height }; } else { bounds = getBounds(context, new FloatSize(size)); - - float scaleFactor = bounds.width() / (float)size.width; - bounds.left += validTexture.left * scaleFactor; - bounds.top += validTexture.top * scaleFactor; - bounds.right -= (size.width - validTexture.right) * scaleFactor; - bounds.bottom -= (size.height - validTexture.bottom) * scaleFactor; - - cropRect = new int[] { validTexture.left, - validTexture.bottom, - validTexture.width(), - -validTexture.height() }; + cropRect = new int[] { 0, size.height, size.width, -size.height }; } GLES11.glTexParameteriv(GL10.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_CROP_RECT_OES, cropRect, diff --git a/mobile/android/base/gfx/TileLayer.java b/mobile/android/base/gfx/TileLayer.java index f96d3a7fabba..9ec820266811 100644 --- a/mobile/android/base/gfx/TileLayer.java +++ b/mobile/android/base/gfx/TileLayer.java @@ -61,14 +61,14 @@ public abstract class TileLayer extends Layer { private final CairoImage mImage; private final boolean mRepeat; private IntSize mSize; - private Rect mValidTextureRect; + private boolean mSkipTextureUpdate; private int[] mTextureIDs; public TileLayer(boolean repeat, CairoImage image) { mRepeat = repeat; mImage = image; mSize = new IntSize(0, 0); - mValidTextureRect = new Rect(); + mSkipTextureUpdate = false; IntSize bufferSize = mImage.getSize(); mDirtyRect = new Rect(); @@ -134,43 +134,23 @@ public abstract class TileLayer extends Layer { } } - /** - * Tells the tile that its texture contents are invalid. This will also - * clear any invalidated area. - */ - public void invalidateTexture() { - mValidTextureRect.setEmpty(); - mDirtyRect.setEmpty(); + /** Tells the tile not to update the texture on the next update. */ + public void setSkipTextureUpdate(boolean skip) { + mSkipTextureUpdate = skip; } - /** - * Returns a handle to the valid texture area rectangle. Modifying this - * Rect will modify the valid texture area for this layer. - */ - public Rect getValidTextureArea() { - return mValidTextureRect; - } - - @Override - public Region getValidRegion(RenderContext context) { - if (mValidTextureRect.isEmpty()) - return new Region(); - - Point origin = getOrigin(); - float scaleFactor = context.zoomFactor / getResolution(); - float x = (origin.x + mValidTextureRect.left) * scaleFactor; - float y = (origin.y + mValidTextureRect.top) * scaleFactor; - float width = mValidTextureRect.width() * scaleFactor; - float height = mValidTextureRect.height() * scaleFactor; - - return new Region(Math.round(x), Math.round(y), - Math.round(x + width), Math.round(y + height)); + public boolean getSkipTextureUpdate() { + return mSkipTextureUpdate; } @Override protected boolean performUpdates(GL10 gl, RenderContext context) { super.performUpdates(gl, context); + if (mSkipTextureUpdate) { + return false; + } + // Reallocate the texture if the size has changed validateTexture(gl); @@ -178,20 +158,24 @@ public abstract class TileLayer extends Layer { if (!mImage.getSize().isPositive()) return true; - // Update the dirty region - uploadDirtyRect(gl, mDirtyRect); + // If we haven't allocated a texture, assume the whole region is dirty + if (mTextureIDs == null) { + uploadFullTexture(gl); + } else { + uploadDirtyRect(gl, mDirtyRect); + } + mDirtyRect.setEmpty(); return true; } - private void uploadDirtyRect(GL10 gl, Rect dirtyRect) { + private void uploadFullTexture(GL10 gl) { IntSize bufferSize = mImage.getSize(); - Rect bufferRect = new Rect(0, 0, bufferSize.width, bufferSize.height); - - // Make sure the dirty region intersects with the buffer - dirtyRect.intersect(bufferRect); + uploadDirtyRect(gl, new Rect(0, 0, bufferSize.width, bufferSize.height)); + } + private void uploadDirtyRect(GL10 gl, Rect dirtyRect) { // If we have nothing to upload, just return for now if (dirtyRect.isEmpty()) return; @@ -201,11 +185,6 @@ public abstract class TileLayer extends Layer { if (imageBuffer == null) return; - // Mark the dirty region as valid. Note, we assume that the valid area - // can be enclosed by a rectangle (ideally we'd use a Region, but it'd - // be slower and it probably isn't necessary). - mValidTextureRect.union(dirtyRect); - boolean newlyCreated = false; if (mTextureIDs == null) { @@ -214,12 +193,15 @@ public abstract class TileLayer extends Layer { newlyCreated = true; } + IntSize bufferSize = mImage.getSize(); + Rect bufferRect = new Rect(0, 0, bufferSize.width, bufferSize.height); + int cairoFormat = mImage.getFormat(); CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); bindAndSetGLParameters(gl); - if (newlyCreated || dirtyRect.equals(bufferRect)) { + if (newlyCreated || dirtyRect.contains(bufferRect)) { if (mSize.equals(bufferSize)) { gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); @@ -233,6 +215,12 @@ public abstract class TileLayer extends Layer { } } + // Make sure that the dirty region intersects with the buffer rect, + // otherwise we'll end up with an invalid buffer pointer. + if (!Rect.intersects(dirtyRect, bufferRect)) { + return; + } + /* * Upload the changed rect. We have to widen to the full width of the texture * because we can't count on the device having support for GL_EXT_unpack_subimage, @@ -249,8 +237,8 @@ public abstract class TileLayer extends Layer { } viewBuffer.position(position); - gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, - bufferSize.width, dirtyRect.height(), + gl.glTexSubImage2D(gl.GL_TEXTURE_2D, 0, 0, dirtyRect.top, bufferSize.width, + Math.min(bufferSize.height - dirtyRect.top, dirtyRect.height()), glInfo.format, glInfo.type, viewBuffer); } diff --git a/mobile/android/chrome/Makefile.in b/mobile/android/chrome/Makefile.in index ca93fec14b66..599b5b42668d 100644 --- a/mobile/android/chrome/Makefile.in +++ b/mobile/android/chrome/Makefile.in @@ -48,8 +48,6 @@ DEFINES += -DAB_CD=$(MOZ_UI_LOCALE) \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e1bc39dc808d..a4acfd3989c8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2135,7 +2135,6 @@ Tab.prototype = { // Is it on the top level? let contentDocument = aSubject; if (contentDocument == this.browser.contentDocument) { - sendMessageToJava({ gecko: { type: "Document:Shown" } }); ViewportHandler.updateMetadata(this); this.documentIdForCurrentViewport = ViewportHandler.getIdForDocument(contentDocument); } diff --git a/mobile/android/sync/manifests/SyncAndroidManifest_activities.xml.in b/mobile/android/sync/manifests/SyncAndroidManifest_activities.xml.in index 64a2b633ac1a..bec0ad8e0bb7 100644 --- a/mobile/android/sync/manifests/SyncAndroidManifest_activities.xml.in +++ b/mobile/android/sync/manifests/SyncAndroidManifest_activities.xml.in @@ -4,6 +4,7 @@ android:screenOrientation="portrait" android:configChanges="orientation" android:windowSoftInputMode="adjustResize|stateHidden" + android:taskAffinity="org.mozilla.gecko.sync.setup" android:name="org.mozilla.gecko.sync.setup.activities.SetupSyncActivity" > diff --git a/mobile/xul/chrome/Makefile.in b/mobile/xul/chrome/Makefile.in index ca93fec14b66..599b5b42668d 100644 --- a/mobile/xul/chrome/Makefile.in +++ b/mobile/xul/chrome/Makefile.in @@ -48,8 +48,6 @@ DEFINES += -DAB_CD=$(MOZ_UI_LOCALE) \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/modules/libjar/zipwriter/Makefile.in b/modules/libjar/zipwriter/Makefile.in index ec852fd20126..a6f0bd52717f 100644 --- a/modules/libjar/zipwriter/Makefile.in +++ b/modules/libjar/zipwriter/Makefile.in @@ -46,8 +46,6 @@ MODULE = zipwriter DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/parser/xml/Makefile.in b/parser/xml/Makefile.in index 68aef738e857..0d1bdff55c98 100644 --- a/parser/xml/Makefile.in +++ b/parser/xml/Makefile.in @@ -43,8 +43,6 @@ include $(DEPTH)/config/autoconf.mk DIRS = public src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/security/manager/ssl/Makefile.in b/security/manager/ssl/Makefile.in index 4ffcc43f58b1..e9db54d7d5c6 100644 --- a/security/manager/ssl/Makefile.in +++ b/security/manager/ssl/Makefile.in @@ -46,8 +46,6 @@ include $(DEPTH)/config/autoconf.mk MODULE = pipnss DIRS = src public -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/services/crypto/Makefile.in b/services/crypto/Makefile.in index e9a04c76a819..2796fb2ef9ed 100644 --- a/services/crypto/Makefile.in +++ b/services/crypto/Makefile.in @@ -48,8 +48,6 @@ MODULE = services-crypto libs:: $(PYTHON) $(topsrcdir)/config/nsinstall.py $(srcdir)/modules/* $(FINAL_TARGET)/modules/services-crypto -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/services/crypto/component/Makefile.in b/services/crypto/component/Makefile.in index 12184a28834e..ebc5b458573f 100644 --- a/services/crypto/component/Makefile.in +++ b/services/crypto/component/Makefile.in @@ -63,8 +63,6 @@ CPPSRCS = \ nsSyncJPAKE.cpp \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/services/sync/Makefile.in b/services/sync/Makefile.in index 53eba58e1b6b..7d7f74514792 100644 --- a/services/sync/Makefile.in +++ b/services/sync/Makefile.in @@ -86,8 +86,6 @@ ifndef NO_DIST_INSTALL endif -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/storage/src/TelemetryVFS.cpp b/storage/src/TelemetryVFS.cpp index 6120ed17bd43..2636195282a7 100644 --- a/storage/src/TelemetryVFS.cpp +++ b/storage/src/TelemetryVFS.cpp @@ -323,6 +323,8 @@ xOpen(sqlite3_vfs* vfs, const char *zName, sqlite3_file* pFile, } p->histograms = h; rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags); + if( rc != SQLITE_OK ) + return rc; if( p->pReal->pMethods ){ sqlite3_io_methods *pNew = new sqlite3_io_methods; const sqlite3_io_methods *pSub = p->pReal->pMethods; diff --git a/testing/xpcshell/Makefile.in b/testing/xpcshell/Makefile.in index d1a37405526e..ee6ce20b63a0 100644 --- a/testing/xpcshell/Makefile.in +++ b/testing/xpcshell/Makefile.in @@ -47,9 +47,7 @@ MODULE = testing_xpcshell # Here's how you let the build system know there are tests in the # "example" folder: -ifdef ENABLE_TESTS -DIRS += example -endif +TEST_DIRS += example include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/alerts/Makefile.in b/toolkit/components/alerts/Makefile.in index f8f5624379ca..1c9330d2ccd9 100644 --- a/toolkit/components/alerts/Makefile.in +++ b/toolkit/components/alerts/Makefile.in @@ -65,9 +65,7 @@ DIRS += \ $(NULL) endif -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test ifneq (,$(filter $(MOZ_WIDGET_TOOLKIT),windows gtk2 qt android)) include $(topsrcdir)/config/config.mk diff --git a/toolkit/components/autocomplete/Makefile.in b/toolkit/components/autocomplete/Makefile.in index e45a9e1b1323..22987839870a 100644 --- a/toolkit/components/autocomplete/Makefile.in +++ b/toolkit/components/autocomplete/Makefile.in @@ -64,8 +64,6 @@ CPPSRCS = \ nsAutoCompleteSimpleResult.cpp \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/commandlines/Makefile.in b/toolkit/components/commandlines/Makefile.in index 51970b5ff183..c6c49e65aa99 100644 --- a/toolkit/components/commandlines/Makefile.in +++ b/toolkit/components/commandlines/Makefile.in @@ -62,8 +62,6 @@ CPPSRCS = \ nsCommandLine.cpp \ $(NULL) -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/contentprefs/Makefile.in b/toolkit/components/contentprefs/Makefile.in index 8f5ac43c9808..a679b53ebf9b 100644 --- a/toolkit/components/contentprefs/Makefile.in +++ b/toolkit/components/contentprefs/Makefile.in @@ -45,8 +45,6 @@ MODULE = contentprefs EXTRA_COMPONENTS = nsContentPrefService.js nsContentPrefService.manifest -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/ctypes/Makefile.in b/toolkit/components/ctypes/Makefile.in index 89a8924e80ba..b5d20c061cc9 100644 --- a/toolkit/components/ctypes/Makefile.in +++ b/toolkit/components/ctypes/Makefile.in @@ -60,8 +60,6 @@ CPPSRCS = \ ctypes.cpp \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/downloads/Makefile.in b/toolkit/components/downloads/Makefile.in index 5620a19d9419..3915016a29a7 100644 --- a/toolkit/components/downloads/Makefile.in +++ b/toolkit/components/downloads/Makefile.in @@ -75,8 +75,6 @@ EXTRA_COMPONENTS = \ $(NULL) endif -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/microformats/Makefile.in b/toolkit/components/microformats/Makefile.in index 756af0ce133b..aa00396b0df9 100644 --- a/toolkit/components/microformats/Makefile.in +++ b/toolkit/components/microformats/Makefile.in @@ -45,8 +45,6 @@ MODULE = microformats EXTRA_JS_MODULES = Microformats.js -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/passwordmgr/Makefile.in b/toolkit/components/passwordmgr/Makefile.in index 583f91867131..2e021333ef98 100644 --- a/toolkit/components/passwordmgr/Makefile.in +++ b/toolkit/components/passwordmgr/Makefile.in @@ -65,8 +65,6 @@ EXTRA_COMPONENTS = \ storage-mozStorage.js \ $(NULL) -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/places/Helpers.cpp b/toolkit/components/places/Helpers.cpp index 49ba9da6a528..6e70f2dcfd0d 100644 --- a/toolkit/components/places/Helpers.cpp +++ b/toolkit/components/places/Helpers.cpp @@ -423,5 +423,34 @@ NS_IMPL_THREADSAFE_ISUPPORTS2( , nsIRunnable ) +//////////////////////////////////////////////////////////////////////////////// +//// AsyncStatementCallbackNotifier + +NS_IMETHODIMP +AsyncStatementCallbackNotifier::HandleCompletion(PRUint16 aReason) +{ + if (aReason != mozIStorageStatementCallback::REASON_FINISHED) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr obs = services::GetObserverService(); + if (obs) { + (void)obs->NotifyObservers(nsnull, mTopic, nsnull); + } + + return NS_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +//// AsyncStatementCallbackNotifier + +NS_IMETHODIMP +AsyncStatementTelemetryTimer::HandleCompletion(PRUint16 aReason) +{ + if (aReason == mozIStorageStatementCallback::REASON_FINISHED) { + Telemetry::AccumulateTimeDelta(mHistogramId, mStart); + } + return NS_OK; +} + } // namespace places } // namespace mozilla diff --git a/toolkit/components/places/Helpers.h b/toolkit/components/places/Helpers.h index cf6253fc9db0..a8d00f26f3e0 100644 --- a/toolkit/components/places/Helpers.h +++ b/toolkit/components/places/Helpers.h @@ -47,6 +47,7 @@ #include "nsIURI.h" #include "nsThreadUtils.h" #include "nsProxyRelease.h" +#include "mozilla/Telemetry.h" namespace mozilla { namespace places { @@ -260,6 +261,42 @@ protected: const char* const mTopic; }; +/** + * Used to notify a topic to system observers on async execute completion. + */ +class AsyncStatementCallbackNotifier : public AsyncStatementCallback +{ +public: + AsyncStatementCallbackNotifier(const char* aTopic) + : mTopic(aTopic) + { + } + + NS_IMETHOD HandleCompletion(PRUint16 aReason); + +private: + const char* mTopic; +}; + +/** + * Used to notify a topic to system observers on async execute completion. + */ +class AsyncStatementTelemetryTimer : public AsyncStatementCallback +{ +public: + AsyncStatementTelemetryTimer(Telemetry::ID aHistogramId, + TimeStamp aStart = TimeStamp::Now()) + : mHistogramId(aHistogramId) + , mStart(aStart) + { + } + + NS_IMETHOD HandleCompletion(PRUint16 aReason); + +private: + const Telemetry::ID mHistogramId; + const TimeStamp mStart; +}; } // namespace places } // namespace mozilla diff --git a/toolkit/components/places/Makefile.in b/toolkit/components/places/Makefile.in index fd385c689314..0235027184c0 100644 --- a/toolkit/components/places/Makefile.in +++ b/toolkit/components/places/Makefile.in @@ -128,9 +128,7 @@ EXTRA_PP_JS_MODULES = \ PlacesUtils.jsm \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/toolkit/components/places/PlacesDBUtils.jsm b/toolkit/components/places/PlacesDBUtils.jsm index eb684952b402..712b7568cdf1 100644 --- a/toolkit/components/places/PlacesDBUtils.jsm +++ b/toolkit/components/places/PlacesDBUtils.jsm @@ -79,7 +79,7 @@ let PlacesDBUtils = { _executeTasks: function PDBU__executeTasks(aTasks) { if (PlacesDBUtils._isShuttingDown) { - tasks.log("- We are shutting down. Will not schedule the tasks."); + aTasks.log("- We are shutting down. Will not schedule the tasks."); aTasks.clear(); } @@ -88,6 +88,14 @@ let PlacesDBUtils = { task.call(PlacesDBUtils, aTasks); } else { + // All tasks have been completed. + // Telemetry the time it took for maintenance, if a start time exists. + if (aTasks._telemetryStart) { + Services.telemetry.getHistogramById("PLACES_IDLE_MAINTENANCE_TIME_MS") + .add(Date.now() - aTasks._telemetryStart); + aTasks._telemetryStart = 0; + } + if (aTasks.callback) { let scope = aTasks.scope || Cu.getGlobalForObject(aTasks.callback); aTasks.callback.call(scope, aTasks.messages); @@ -128,6 +136,7 @@ let PlacesDBUtils = { , this.checkCoherence , this._refreshUI ]); + tasks._telemetryStart = Date.now(); tasks.callback = aCallback; tasks.scope = aScope; this._executeTasks(tasks); @@ -1021,11 +1030,12 @@ function Tasks(aTasks) if (Array.isArray(aTasks)) { this._list = aTasks.slice(0, aTasks.length); } - else if ("list" in aTasks) { + else if (typeof(aTasks) == "object" && aTasks instanceof Tasks) { this._list = aTasks.list; this._log = aTasks.messages; this.callback = aTasks.callback; this.scope = aTasks.scope; + this._telemetryStart = aTasks._telemetryStart; } } } @@ -1035,6 +1045,7 @@ Tasks.prototype = { _log: [], callback: null, scope: null, + _telemetryStart: 0, /** * Adds a task to the top of the list. diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index 0344253e779d..d9f66a51a944 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -277,36 +277,6 @@ protected: nsNavHistory& mNavHistory; }; - -// Used to notify a topic to system observers on async execute completion. -class AsyncStatementCallbackNotifier : public AsyncStatementCallback -{ -public: - AsyncStatementCallbackNotifier(const char* aTopic) - : mTopic(aTopic) - { - } - - NS_IMETHOD HandleCompletion(PRUint16 aReason); - -private: - const char* mTopic; -}; - -NS_IMETHODIMP -AsyncStatementCallbackNotifier::HandleCompletion(PRUint16 aReason) -{ - if (aReason != mozIStorageStatementCallback::REASON_FINISHED) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr obs = services::GetObserverService(); - if (obs) { - (void)obs->NotifyObservers(nsnull, mTopic, nsnull); - } - - return NS_OK; -} - } // anonymouse namespace @@ -3988,8 +3958,10 @@ nsNavHistory::DecayFrecency() deleteAdaptive.get() }; nsCOMPtr ps; - rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), nsnull, - getter_AddRefs(ps)); + nsRefPtr cb = + new AsyncStatementTelemetryTimer(Telemetry::PLACES_IDLE_FRECENCY_DECAY_TIME_MS); + rv = mDB->MainConn()->ExecuteAsync(stmts, ArrayLength(stmts), cb, + getter_AddRefs(ps)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; diff --git a/toolkit/components/places/tests/unit/test_telemetry.js b/toolkit/components/places/tests/unit/test_telemetry.js index 126f91094768..2719dd66cba1 100644 --- a/toolkit/components/places/tests/unit/test_telemetry.js +++ b/toolkit/components/places/tests/unit/test_telemetry.js @@ -20,35 +20,10 @@ let histograms = { PLACES_DATABASE_SIZE_PER_PAGE_B: function (val) do_check_true(val > 0), PLACES_EXPIRATION_STEPS_TO_CLEAN: function (val) do_check_true(val > 1), //PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS: function (val) do_check_true(val > 1), + PLACES_IDLE_FRECENCY_DECAY_TIME_MS: function (val) do_check_true(val > 0), + PLACES_IDLE_MAINTENANCE_TIME_MS: function (val) do_check_true(val > 0), } -// This sucks, but due to nsITelemetry using [implicit_jscontext], it's -// impossible to implement it in js, so no fancy service factory replacements. -// This mock implements only the telemetry methods used by Places. -XPCOMUtils.defineLazyGetter(Services, "telemetry", function () { - return { - getHistogramById: function FT_getHistogramById(id) { - if (id in histograms) { - return { - add: function FH_add(val) { - do_log_info("Testing probe " + id); - histograms[id](val); - delete histograms[id]; - if (Object.keys(histograms).length == 0) - do_test_finished(); - } - }; - } - - return { - add: function FH_add(val) { - do_log_info("Unknown probe " + id); - } - }; - }, - }; -}); - function run_test() { do_test_pending(); @@ -120,4 +95,24 @@ function continue_test() { controller.input = new AutoCompleteInput(["history"]); controller.startSearch("moz"); */ + + // Test idle probes. + PlacesUtils.history.QueryInterface(Ci.nsIObserver) + .observe(null, "idle-daily", null); + PlacesDBUtils.maintenanceOnIdle(); + + Services.obs.addObserver(function maintenanceObserver() { + Services.obs.removeObserver(maintenanceObserver, + "places-maintenance-finished"); + check_telemetry(); + }, "places-maintenance-finished", false); +} + +function check_telemetry() { + for (let histogramId in histograms) { + do_log_info("checking histogram " + histogramId); + let validate = histograms[histogramId]; + validate(Services.telemetry.getHistogramById(histogramId).snapshot().sum); + } + do_test_finished(); } diff --git a/toolkit/components/prompts/Makefile.in b/toolkit/components/prompts/Makefile.in index 3cad06daefb8..c321a3912a5c 100644 --- a/toolkit/components/prompts/Makefile.in +++ b/toolkit/components/prompts/Makefile.in @@ -43,8 +43,6 @@ include $(DEPTH)/config/autoconf.mk DIRS = src -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/satchel/Makefile.in b/toolkit/components/satchel/Makefile.in index 2c6ac5eb471d..5a8256b0d1d9 100644 --- a/toolkit/components/satchel/Makefile.in +++ b/toolkit/components/satchel/Makefile.in @@ -76,8 +76,6 @@ EXTRA_JS_MODULES = \ nsFormAutoCompleteResult.jsm \ $(NULL) -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/search/Makefile.in b/toolkit/components/search/Makefile.in index d89bda16f83d..1275e807cfba 100644 --- a/toolkit/components/search/Makefile.in +++ b/toolkit/components/search/Makefile.in @@ -49,8 +49,6 @@ EXTRA_PP_COMPONENTS = nsSearchService.js \ DEFINES += -DMOZ_DISTRIBUTION_ID=$(MOZ_DISTRIBUTION_ID) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/telemetry/Makefile.in b/toolkit/components/telemetry/Makefile.in index c0a8883c18e4..681601bfb803 100644 --- a/toolkit/components/telemetry/Makefile.in +++ b/toolkit/components/telemetry/Makefile.in @@ -79,9 +79,7 @@ EXTRA_DSO_LDOPTS += \ $(MOZ_JS_LIBS) \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/config.mk include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 764b20185be9..684d64f69270 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -287,6 +287,8 @@ HISTOGRAM(PLACES_DATABASE_PAGESIZE_B, 1024, 32768, 10, EXPONENTIAL, "PLACES: Dat HISTOGRAM(PLACES_DATABASE_SIZE_PER_PAGE_B, 500, 10240, 20, EXPONENTIAL, "PLACES: Average size of a place in the database (bytes)") HISTOGRAM(PLACES_EXPIRATION_STEPS_TO_CLEAN, 1, 10, 10, LINEAR, "PLACES: Expiration steps to cleanup the database") HISTOGRAM(PLACES_AUTOCOMPLETE_1ST_RESULT_TIME_MS, 50, 500, 10, EXPONENTIAL, "PLACES: Time for first autocomplete result if > 50ms (ms)") +HISTOGRAM(PLACES_IDLE_FRECENCY_DECAY_TIME_MS, 50, 10000, 10, EXPONENTIAL, "PLACES: Time to decay all frecencies values on idle (ms)") +HISTOGRAM(PLACES_IDLE_MAINTENANCE_TIME_MS, 1000, 30000, 10, EXPONENTIAL, "PLACES: Time to execute maintenance tasks on idle (ms)") /** * Updater telemetry. diff --git a/toolkit/components/urlformatter/Makefile.in b/toolkit/components/urlformatter/Makefile.in index b88c959f99b2..e48d7d55a6de 100644 --- a/toolkit/components/urlformatter/Makefile.in +++ b/toolkit/components/urlformatter/Makefile.in @@ -56,8 +56,6 @@ EXTRA_PP_COMPONENTS = \ nsURLFormatter.js \ $(NULL) -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/components/viewsource/Makefile.in b/toolkit/components/viewsource/Makefile.in index 73ef499ba8de..43aef5003de0 100644 --- a/toolkit/components/viewsource/Makefile.in +++ b/toolkit/components/viewsource/Makefile.in @@ -43,8 +43,6 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk -ifdef ENABLE_TESTS -DIRS = test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/content/Makefile.in b/toolkit/content/Makefile.in index 6d8d6f44a7dc..38ae0a81df09 100644 --- a/toolkit/content/Makefile.in +++ b/toolkit/content/Makefile.in @@ -81,9 +81,7 @@ ifdef MOZ_TOOLKIT_SEARCH DEFINES += -DMOZ_TOOLKIT_SEARCH endif -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests EXTRA_JS_MODULES = \ Geometry.jsm \ diff --git a/toolkit/mozapps/downloads/Makefile.in b/toolkit/mozapps/downloads/Makefile.in index 1465ff0e76b3..38be5478c4b7 100644 --- a/toolkit/mozapps/downloads/Makefile.in +++ b/toolkit/mozapps/downloads/Makefile.in @@ -59,8 +59,6 @@ EXTRA_JS_MODULES += \ $(NULL) endif -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/mozapps/extensions/Makefile.in b/toolkit/mozapps/extensions/Makefile.in index b4b01793f000..dc9d45a1e785 100644 --- a/toolkit/mozapps/extensions/Makefile.in +++ b/toolkit/mozapps/extensions/Makefile.in @@ -76,9 +76,7 @@ EXTRA_JS_MODULES = \ SpellCheckDictionaryBootstrap.js \ $(NULL) -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test EXTRA_DSO_LDOPTS = \ $(MOZ_JS_LIBS) \ diff --git a/toolkit/mozapps/plugins/Makefile.in b/toolkit/mozapps/plugins/Makefile.in index b0fa1151ca3d..796c52d5f041 100644 --- a/toolkit/mozapps/plugins/Makefile.in +++ b/toolkit/mozapps/plugins/Makefile.in @@ -44,8 +44,6 @@ include $(DEPTH)/config/autoconf.mk EXTRA_COMPONENTS = pluginGlue.manifest -ifdef ENABLE_TESTS -DIRS += tests -endif +TEST_DIRS += tests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/profile/Makefile.in b/toolkit/profile/Makefile.in index bddf7f137037..d0f473e1cd78 100644 --- a/toolkit/profile/Makefile.in +++ b/toolkit/profile/Makefile.in @@ -68,9 +68,7 @@ DEFINES += -DIMPL_XREAPI GARBAGE += nsProfileLock.cpp -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/rules.mk diff --git a/toolkit/themes/pinstripe/Makefile.in b/toolkit/themes/pinstripe/Makefile.in index 305dc5faefa3..3c8399b8d0c3 100644 --- a/toolkit/themes/pinstripe/Makefile.in +++ b/toolkit/themes/pinstripe/Makefile.in @@ -48,8 +48,6 @@ ifdef MOZ_HELP_VIEWER DIRS += help endif -ifdef ENABLE_TESTS -DIRS += mochitests -endif +TEST_DIRS += mochitests include $(topsrcdir)/config/rules.mk diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index 8c347c964d06..a240d24ae827 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -180,9 +180,7 @@ SHARED_LIBRARY_LIBS += \ endif endif -ifdef ENABLE_TESTS -DIRS += test -endif +TEST_DIRS += test include $(topsrcdir)/config/config.mk include $(topsrcdir)/ipc/chromium/chromium-config.mk diff --git a/uriloader/exthandler/Makefile.in b/uriloader/exthandler/Makefile.in index 5b29d2a52b3e..23553616667e 100644 --- a/uriloader/exthandler/Makefile.in +++ b/uriloader/exthandler/Makefile.in @@ -44,9 +44,7 @@ include $(DEPTH)/config/autoconf.mk DIRS = \ $(NULL) -ifdef ENABLE_TESTS - DIRS += tests -endif +TEST_DIRS += tests ifeq ($(MOZ_WIDGET_TOOLKIT),os2) OSDIR = os2 diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 957ab847ced8..af181e368a32 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -111,7 +111,6 @@ jmethodID AndroidAddress::jGetThoroughfareMethod; jclass AndroidGeckoSoftwareLayerClient::jGeckoSoftwareLayerClientClass = 0; jmethodID AndroidGeckoSoftwareLayerClient::jLockBufferMethod = 0; jmethodID AndroidGeckoSoftwareLayerClient::jUnlockBufferMethod = 0; -jmethodID AndroidGeckoSoftwareLayerClient::jGetRenderOffsetMethod = 0; jmethodID AndroidGeckoSoftwareLayerClient::jBeginDrawingMethod = 0; jmethodID AndroidGeckoSoftwareLayerClient::jEndDrawingMethod = 0; jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0; @@ -330,7 +329,6 @@ AndroidGeckoSoftwareLayerClient::InitGeckoSoftwareLayerClientClass(JNIEnv *jEnv) jLockBufferMethod = getMethod("lockBuffer", "()Ljava/nio/ByteBuffer;"); jUnlockBufferMethod = getMethod("unlockBuffer", "()V"); - jGetRenderOffsetMethod = getMethod("getRenderOffset", "()Landroid/graphics/Point;"); jBeginDrawingMethod = getMethod("beginDrawing", "(IIIILjava/lang/String;Z)Z"); jEndDrawingMethod = getMethod("endDrawing", "(IIII)V"); #endif @@ -684,18 +682,6 @@ AndroidGeckoSoftwareLayerClient::UnlockBuffer() env->CallVoidMethod(wrapped_obj, jUnlockBufferMethod); } -void -AndroidGeckoSoftwareLayerClient::GetRenderOffset(nsIntPoint &aOffset) -{ - JNIEnv *env = AndroidBridge::GetJNIEnv(); - if (!env) - return; - - AndroidPoint offset(env, env->CallObjectMethod(wrapped_obj, jGetRenderOffsetMethod)); - aOffset.x = offset.X(); - aOffset.y = offset.Y(); -} - bool AndroidGeckoSoftwareLayerClient::BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, const nsAString &aMetadata, bool aHasDirectTexture) { diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 060910d18e51..76b552fa4ae4 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -161,7 +161,6 @@ public: jobject LockBuffer(); unsigned char *LockBufferBits(); void UnlockBuffer(); - void GetRenderOffset(nsIntPoint &aOffset); bool BeginDrawing(int aWidth, int aHeight, int aTileWidth, int aTileHeight, const nsAString &aMetadata, bool aHasDirectTexture); void EndDrawing(const nsIntRect &aRect); @@ -171,7 +170,6 @@ private: static jmethodID jUnlockBufferMethod; protected: - static jmethodID jGetRenderOffsetMethod; static jmethodID jBeginDrawingMethod; static jmethodID jEndDrawingMethod; }; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 45a48f898419..cdfea0c706bc 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1216,9 +1216,6 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) return; } - nsIntPoint renderOffset; - client.GetRenderOffset(renderOffset); - nsIntRect dirtyRect = ae->Rect().Intersect(nsIntRect(0, 0, gAndroidBounds.width, gAndroidBounds.height)); unsigned char *bits = NULL; @@ -1241,26 +1238,24 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) int offset = 0; - // It is assumed that the buffer has been over-allocated so that not - // only is the tile-size constant, but that a render-offset of anything - // up to (but not including) the tile size could be accommodated. - for (int y = 0; y < gAndroidBounds.height + gAndroidTileSize.height; y += tileHeight) { - for (int x = 0; x < gAndroidBounds.width + gAndroidTileSize.width; x += tileWidth) { + for (int y = 0; y < gAndroidBounds.height; y += tileHeight) { + for (int x = 0; x < gAndroidBounds.width; x += tileWidth) { + int width = NS_MIN(tileWidth, gAndroidBounds.width - x); + int height = NS_MIN(tileHeight, gAndroidBounds.height - y); nsRefPtr targetSurface = new gfxImageSurface(bits + offset, - gfxIntSize(tileWidth, tileHeight), - tileWidth * 2, + gfxIntSize(width, height), + width * 2, gfxASurface::ImageFormatRGB16_565); - offset += tileWidth * tileHeight * 2; + offset += width * height * 2; if (targetSurface->CairoStatus()) { ALOG("### Failed to create a valid surface from the bitmap"); break; } else { - targetSurface->SetDeviceOffset(gfxPoint(renderOffset.x - x, - renderOffset.y - y)); + targetSurface->SetDeviceOffset(gfxPoint(-x, -y)); DrawTo(targetSurface, dirtyRect); } }