diff --git a/.hgtags b/.hgtags index d3529a539c8..151d88046cf 100644 --- a/.hgtags +++ b/.hgtags @@ -73,3 +73,6 @@ c0983049bcaa9551e5f276d5a77ce154c151e0b0 AURORA_BASE_20110927 54bfd8bf682e295ffd7f22fa921ca343957b6c1c AURORA_BASE_20111108 a8506ab2c65480cf2f85f54e203ea746522c62bb AURORA_BASE_20111220 462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R16 +bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 +bbc7014db2de49e2301680d2a86be8a53108a88a AURORA_BASE_20120131 +0000000000000000000000000000000000000000 AURORA_BASE_20120131 diff --git a/accessible/src/base/nsRootAccessible.cpp b/accessible/src/base/nsRootAccessible.cpp index a0511f38a1d..355effda1b5 100644 --- a/accessible/src/base/nsRootAccessible.cpp +++ b/accessible/src/base/nsRootAccessible.cpp @@ -574,59 +574,6 @@ nsRootAccessible::Shutdown() nsDocAccessibleWrap::Shutdown(); } -// nsRootAccessible protected member -already_AddRefed -nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart) -{ - if (!aStart) { - return nsnull; - } - - PRInt32 itemType; - aStart->GetItemType(&itemType); - if (itemType == nsIDocShellTreeItem::typeContent) { - nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart); - - // Hidden documents don't have accessibles (like SeaMonkey's sidebar), - // they are of no interest for a11y. - if (!accDoc) - return nsnull; - - // If ancestor chain of accessibles is not completely visible, - // don't use this one. This happens for example if it's inside - // a background tab (tabbed browsing) - nsAccessible* parent = accDoc->Parent(); - while (parent) { - if (parent->State() & states::INVISIBLE) - return nsnull; - - if (parent == this) - break; // Don't check past original root accessible we started with - - parent = parent->Parent(); - } - - NS_ADDREF(aStart); - return aStart; - } - nsCOMPtr treeNode(do_QueryInterface(aStart)); - if (treeNode) { - PRInt32 subDocuments; - treeNode->GetChildCount(&subDocuments); - for (PRInt32 count = 0; count < subDocuments; count ++) { - nsCOMPtr treeItemChild, contentTreeItem; - treeNode->GetChildAt(count, getter_AddRefs(treeItemChild)); - NS_ENSURE_TRUE(treeItemChild, nsnull); - contentTreeItem = GetContentDocShell(treeItemChild); - if (contentTreeItem) { - NS_ADDREF(aStart = contentTreeItem); - return aStart; - } - } - } - return nsnull; -} - // nsIAccessible method Relation nsRootAccessible::RelationByType(PRUint32 aType) @@ -634,14 +581,25 @@ nsRootAccessible::RelationByType(PRUint32 aType) if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS) return nsDocAccessibleWrap::RelationByType(aType); - nsCOMPtr treeItem = - nsCoreUtils::GetDocShellTreeItemFor(mDocument); - nsCOMPtr contentTreeItem = GetContentDocShell(treeItem); - // there may be no content area, so we need a null check - if (!contentTreeItem) - return Relation(); + nsIDOMWindow* rootWindow = mDocument->GetWindow(); + if (rootWindow) { + nsCOMPtr contentWindow; + rootWindow->GetContent(getter_AddRefs(contentWindow)); + if (contentWindow) { + nsCOMPtr contentDOMDocument; + contentWindow->GetDocument(getter_AddRefs(contentDOMDocument)); + nsCOMPtr contentDocumentNode = + do_QueryInterface(contentDOMDocument); + if (contentDocumentNode) { + nsDocAccessible* contentDocument = + GetAccService()->GetDocAccessible(contentDocumentNode); + if (contentDocument) + return Relation(contentDocument); + } + } + } - return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem)); + return Relation(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/src/base/nsRootAccessible.h b/accessible/src/base/nsRootAccessible.h index 6feb79a9100..b7f02ffddbc 100644 --- a/accessible/src/base/nsRootAccessible.h +++ b/accessible/src/base/nsRootAccessible.h @@ -127,8 +127,7 @@ protected: PRUint32 GetChromeFlags(); #endif - already_AddRefed - GetContentDocShell(nsIDocShellTreeItem *aStart); + nsRefPtr mCaretAccessible; }; diff --git a/accessible/tests/mochitest/relations/Makefile.in b/accessible/tests/mochitest/relations/Makefile.in index 5a7654412d3..5fc8bf6d878 100644 --- a/accessible/tests/mochitest/relations/Makefile.in +++ b/accessible/tests/mochitest/relations/Makefile.in @@ -48,6 +48,7 @@ include $(topsrcdir)/config/rules.mk # test_tabbrowser.xul disabled for misusing (bug 715857) _TEST_FILES =\ + test_embeds.xul \ test_general.html \ test_general.xul \ test_tree.xul \ diff --git a/accessible/tests/mochitest/relations/test_embeds.xul b/accessible/tests/mochitest/relations/test_embeds.xul new file mode 100644 index 00000000000..5d0ddcfa999 --- /dev/null +++ b/accessible/tests/mochitest/relations/test_embeds.xul @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + Mozilla Bug 707654 + +

+ +
+    
+ +
+
diff --git a/accessible/tests/mochitest/relations/test_general.html b/accessible/tests/mochitest/relations/test_general.html index 71bf6f8600d..d487f349ade 100644 --- a/accessible/tests/mochitest/relations/test_general.html +++ b/accessible/tests/mochitest/relations/test_general.html @@ -126,19 +126,6 @@ testRelation("legend", RELATION_LABEL_FOR, "fieldset"); testRelation("fieldset", RELATION_LABELLED_BY, "legend"); - // 'embeds' relation for root accessible - var docAcc = null; - var parentOfDocAcc = null; - var parentDocAcc = getAccessible(document); - do { - docAcc = parentDocAcc; - parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]); - parentDocAcc = getAccessible(parentOfDocAcc.document, - [nsIAccessible]); - } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW) - - testRelation(parentDocAcc, RELATION_EMBEDS, docAcc); - // finish test SimpleTest.finish(); } diff --git a/accessible/tests/mochitest/relations/test_general.xul b/accessible/tests/mochitest/relations/test_general.xul index ca288d1bf6c..23bf7276b28 100644 --- a/accessible/tests/mochitest/relations/test_general.xul +++ b/accessible/tests/mochitest/relations/test_general.xul @@ -103,19 +103,6 @@ // 'default button' relation testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit"); - // 'embeds' relation for root accessible - var docAcc = null; - var parentOfDocAcc = null; - var parentDocAcc = getAccessible(document); - do { - docAcc = parentDocAcc; - parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]); - parentDocAcc = getAccessible(parentOfDocAcc.document, - [nsIAccessible]); - } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW) - - testRelation(parentDocAcc, RELATION_EMBEDS, docAcc); - // 'labelled by'/'label for' relation for xul:goupbox and xul:label of // xul:caption var groupboxAcc = getAccessible("groupbox"); diff --git a/browser/app/Makefile.in b/browser/app/Makefile.in index 7e4aeec1ef1..d16f45622ca 100644 --- a/browser/app/Makefile.in +++ b/browser/app/Makefile.in @@ -107,11 +107,6 @@ ifdef _MSC_VER WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup endif -ifeq ($(OS_ARCH),WINNT) -OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool) -OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32) -endif - ifeq ($(OS_ARCH),WINNT) RCINCLUDE = splash.rc ifndef GNU_CC diff --git a/browser/config/mozconfigs/linux32/release b/browser/config/mozconfigs/linux32/release index d26d29f57e9..b4188d60ffc 100644 --- a/browser/config/mozconfigs/linux32/release +++ b/browser/config/mozconfigs/linux32/release @@ -18,3 +18,6 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4" diff --git a/browser/config/mozconfigs/linux64/release b/browser/config/mozconfigs/linux64/release index d26d29f57e9..b4188d60ffc 100644 --- a/browser/config/mozconfigs/linux64/release +++ b/browser/config/mozconfigs/linux64/release @@ -18,3 +18,6 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4" diff --git a/browser/config/mozconfigs/macosx-universal/release b/browser/config/mozconfigs/macosx-universal/release index 2d1c94f9a5f..8913864c0f1 100644 --- a/browser/config/mozconfigs/macosx-universal/release +++ b/browser/config/mozconfigs/macosx-universal/release @@ -14,3 +14,6 @@ export MOZ_TELEMETRY_REPORTING=1 # Treat warnings as errors in directories with FAIL_ON_WARNINGS. ac_add_options --enable-warnings-as-errors + +# Enable parallel compiling +mk_add_options MOZ_MAKE_FLAGS="-j4" diff --git a/browser/config/version.txt b/browser/config/version.txt index 2d216daf369..d4157f8df48 100644 --- a/browser/config/version.txt +++ b/browser/config/version.txt @@ -1 +1 @@ -12.0a1 +13.0a1 diff --git a/build/unix/build-toolchain/glibc-deterministic.patch b/build/unix/build-toolchain/glibc-deterministic.patch index 01f5295e914..016fbf1ea72 100644 --- a/build/unix/build-toolchain/glibc-deterministic.patch +++ b/build/unix/build-toolchain/glibc-deterministic.patch @@ -22,6 +22,22 @@ diff -ru a/csu/Makefile b/csu/Makefile *) ;; \ esac; \ files="$(all-Banner-files)"; \ +diff -ru a/Makerules b/Makerules +--- a/Makerules 2011-01-17 23:34:07.000000000 -0500 ++++ b/Makerules 2012-01-30 08:47:56.565068903 -0500 +@@ -992,9 +992,9 @@ + echo ' Use the shared library, but some functions are only in';\ + echo ' the static library, so try that secondarily. */';\ + cat $<; \ +- echo 'GROUP ( $(slibdir)/libc.so$(libc.so-version)' \ +- '$(libdir)/$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ +- ' AS_NEEDED (' $(slibdir)/$(rtld-installed-name) ') )' \ ++ echo 'GROUP ( libc.so$(libc.so-version)' \ ++ '$(patsubst %,$(libtype.oS),$(libprefix)$(libc-name))'\ ++ ' AS_NEEDED (' $(rtld-installed-name) ') )' \ + ) > $@.new + mv -f $@.new $@ + diff -ru a/nscd/nscd_stat.c b/nscd/nscd_stat.c --- a/nscd/nscd_stat.c 2011-01-17 23:34:07.000000000 -0500 +++ b/nscd/nscd_stat.c 2012-01-23 15:54:45.231607606 -0500 diff --git a/config/milestone.txt b/config/milestone.txt index 62564253ccb..f780391e9db 100644 --- a/config/milestone.txt +++ b/config/milestone.txt @@ -10,4 +10,4 @@ # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -12.0a1 +13.0a1 diff --git a/ipc/app/Makefile.in b/ipc/app/Makefile.in index 85fd62f4997..e5bf9fac733 100644 --- a/ipc/app/Makefile.in +++ b/ipc/app/Makefile.in @@ -96,11 +96,6 @@ ifdef _MSC_VER WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup endif -ifeq ($(OS_ARCH),WINNT) -OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool) -OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32) -endif - include $(topsrcdir)/config/rules.mk ifeq ($(OS_ARCH),WINNT) diff --git a/js/src/config/milestone.txt b/js/src/config/milestone.txt index 62564253ccb..f780391e9db 100644 --- a/js/src/config/milestone.txt +++ b/js/src/config/milestone.txt @@ -10,4 +10,4 @@ # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -12.0a1 +13.0a1 diff --git a/layout/base/crashtests/722137.html b/layout/base/crashtests/722137.html new file mode 100644 index 00000000000..7dae47f1de7 --- /dev/null +++ b/layout/base/crashtests/722137.html @@ -0,0 +1,18 @@ + + + + + + + +‪𐡱 + diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 47436de3527..5e4c0403799 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -347,3 +347,4 @@ load 691118-1.html load 695861.html load 698335.html load 707098.html +load 722137.html diff --git a/layout/base/nsBidiPresUtils.cpp b/layout/base/nsBidiPresUtils.cpp index baa343d5466..1b2339895e4 100644 --- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -819,12 +819,10 @@ nsBidiPresUtils::ResolveParagraph(nsBlockFrame* aBlockFrame, RemoveBidiContinuation(aBpd, frame, frameIndex, newIndex, lineOffset); } - } else if (runLength == fragmentLength && - frame->GetNextSibling()) { + } else if (runLength == fragmentLength) { /* - * If the directional run ends at the end of the frame, and this is - * not the containing frame's last child, make sure that the next - * frame is a non-fluid continuation + * If the directional run ends at the end of the frame, make sure + * that any continuation is non-fluid */ nsIFrame* next = frame->GetNextInFlow(); if (next) { diff --git a/mobile/android/base/GeckoInputConnection.java b/mobile/android/base/GeckoInputConnection.java index 54ed53ae508..3cb40da239f 100644 --- a/mobile/android/base/GeckoInputConnection.java +++ b/mobile/android/base/GeckoInputConnection.java @@ -206,8 +206,16 @@ public class GeckoInputConnection extract.selectionEnd = b; extract.startOffset = 0; - extract.text = content.toString(); + try { + extract.text = content.toString(); + } catch (IndexOutOfBoundsException iob) { + Log.d(LOGTAG, + "IndexOutOfBoundsException thrown from getExtractedText(). start: " + + Selection.getSelectionStart(content) + + " end: " + Selection.getSelectionEnd(content)); + return null; + } return extract; } diff --git a/mobile/android/base/gfx/FloatSize.java b/mobile/android/base/gfx/FloatSize.java index 1824b27edda..07dfc9fe845 100644 --- a/mobile/android/base/gfx/FloatSize.java +++ b/mobile/android/base/gfx/FloatSize.java @@ -61,6 +61,10 @@ public class FloatSize { @Override public String toString() { return "(" + width + "," + height + ")"; } + public boolean isPositive() { + return (width > 0 && height > 0); + } + public boolean fuzzyEquals(FloatSize size) { return (FloatUtils.fuzzyEquals(size.width, width) && FloatUtils.fuzzyEquals(size.height, height)); diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index f5e0ee52b52..cec9e83988d 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -429,8 +429,14 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL DisplayMetrics metrics = new DisplayMetrics(); GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - if (!force && metrics.widthPixels == mScreenSize.width && - metrics.heightPixels == mScreenSize.height) { + // Return immediately if the screen size hasn't changed or the viewport + // size is zero (which indicates that the rendering surface hasn't been + // allocated yet). + boolean screenSizeChanged = (metrics.widthPixels != mScreenSize.width || + metrics.heightPixels != mScreenSize.height); + boolean viewportSizeValid = (getLayerController() != null && + getLayerController().getViewportSize().isPositive()); + if (!(force || (screenSizeChanged && viewportSizeValid))) { return; } diff --git a/mobile/android/base/gfx/MultiTileLayer.java b/mobile/android/base/gfx/MultiTileLayer.java index 10fb5f112a7..5d156f9cfad 100644 --- a/mobile/android/base/gfx/MultiTileLayer.java +++ b/mobile/android/base/gfx/MultiTileLayer.java @@ -71,6 +71,11 @@ public class MultiTileLayer extends Layer { 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; + public MultiTileLayer(CairoImage image, IntSize tileSize) { super(); @@ -102,7 +107,7 @@ public class MultiTileLayer extends Layer { * Invalidates the backing buffer. Data will not be uploaded from an invalid * backing buffer. This method is only valid inside a transaction. */ - public void invalidateBuffer() { + protected void invalidateBuffer() { if (!inTransaction()) { throw new RuntimeException("invalidateBuffer() is only valid inside a transaction"); } @@ -181,17 +186,16 @@ public class MultiTileLayer extends Layer { // entire width, regardless of the dirty-rect's width, and so // can override existing data. Point origin = getOffsetOrigin(); - Rect validRect = tile.getValidTextureArea(); - validRect.offset(tileOrigin.x - origin.x, tileOrigin.y - origin.y); - Region validRegion = new Region(validRect); + 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.isComplex()) { - validRect.set(validRegion.getBounds()); - validRect.offset(origin.x - tileOrigin.x, origin.y - tileOrigin.y); + if (!validRegion.isEmpty() && !validRegion.isComplex()) { + validRegion.translate(origin.x - tileOrigin.x, origin.y - tileOrigin.y); + tile.getValidTextureArea().set(validRegion.getBounds()); } } else { // Update tile metrics @@ -286,7 +290,6 @@ public class MultiTileLayer extends Layer { // valid by monitoring reflows on the browser element, or // something along these lines. LinkedList invalidTiles = new LinkedList(); - Rect bounds = mValidRegion.getBounds(); for (ListIterator i = mTiles.listIterator(); i.hasNext();) { SubTile tile = i.next(); @@ -301,7 +304,7 @@ public class MultiTileLayer extends Layer { // First bracketed clause: Invalidate off-screen, off-buffer tiles // Second: Invalidate visible tiles at the wrong resolution that have updates - if ((!Rect.intersects(bounds, tilespaceTileBounds) && + if ((!opRegion.op(tilespaceTileBounds, mValidRegion, Region.Op.INTERSECT) && !Rect.intersects(tilespaceViewport, tilespaceTileBounds)) || (!FloatUtils.fuzzyEquals(tile.getResolution(), getResolution()) && opRegion.op(tilespaceTileBounds, updateRegion, Region.Op.INTERSECT))) { @@ -415,9 +418,8 @@ public class MultiTileLayer extends Layer { @Override public void setOrigin(Point origin) { - Point oldOrigin = getOrigin(); - - if (!origin.equals(oldOrigin)) { + if (mOrigin == null || !origin.equals(mOrigin)) { + mOrigin = origin; super.setOrigin(origin); invalidateBuffer(); } @@ -425,9 +427,8 @@ public class MultiTileLayer extends Layer { @Override public void setResolution(float resolution) { - float oldResolution = getResolution(); - - if (!FloatUtils.fuzzyEquals(resolution, oldResolution)) { + if (!FloatUtils.fuzzyEquals(resolution, mResolution)) { + mResolution = resolution; super.setResolution(resolution); invalidateBuffer(); } @@ -441,7 +442,7 @@ public class MultiTileLayer extends Layer { * Invalidates all sub-tiles. This should be called if the source backing * this layer has changed. This method is only valid inside a transaction. */ - public void invalidateTiles() { + protected void invalidateTiles() { if (!inTransaction()) { throw new RuntimeException("invalidateTiles() is only valid inside a transaction"); } diff --git a/mobile/android/confvars.sh b/mobile/android/confvars.sh index 2f0426955ae..febe50a0d0d 100644 --- a/mobile/android/confvars.sh +++ b/mobile/android/confvars.sh @@ -38,7 +38,7 @@ MOZ_APP_BASENAME=Fennec MOZ_APP_VENDOR=Mozilla -MOZ_APP_VERSION=12.0a1 +MOZ_APP_VERSION=13.0a1 MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official diff --git a/mobile/xul/confvars.sh b/mobile/xul/confvars.sh index 65064e1f788..53f1f3b3da2 100644 --- a/mobile/xul/confvars.sh +++ b/mobile/xul/confvars.sh @@ -38,7 +38,7 @@ MOZ_APP_BASENAME=Fennec MOZ_APP_VENDOR=Mozilla -MOZ_APP_VERSION=12.0a1 +MOZ_APP_VERSION=13.0a1 MOZ_BRANDING_DIRECTORY=mobile/xul/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/xul/branding/official diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 8462f3822b9..066d3a7b7fa 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -2733,7 +2733,9 @@ pref("font.name.monospace.he", "Droid Sans Mono"); pref("font.name.serif.ja", "Droid Serif"); pref("font.name.sans-serif.ja", "Droid Sans Japanese"); -pref("font.name.monospace.ja", "Droid Sans Mono"); +pref("font.name.monospace.ja", "MotoyaLMaru"); +pref("font.name-list.sans-serif.ja", "MotoyaLMaru, MotoyaLCedar, Droid Sans Japanese"); +pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar"); pref("font.name.serif.ko", "Droid Serif"); pref("font.name.sans-serif.ko", "Droid Sans"); @@ -2750,38 +2752,44 @@ pref("font.name.monospace.tr", "Droid Sans Mono"); pref("font.name.serif.x-baltic", "Droid Serif"); pref("font.name.sans-serif.x-baltic", "Droid Sans"); pref("font.name.monospace.x-baltic", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-baltic", "Roboto, Droid Sans"); pref("font.name.serif.x-central-euro", "Droid Serif"); pref("font.name.sans-serif.x-central-euro", "Droid Sans"); pref("font.name.monospace.x-central-euro", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-central-euro", "Roboto, Droid Sans"); pref("font.name.serif.x-cyrillic", "Droid Serif"); pref("font.name.sans-serif.x-cyrillic", "Droid Sans"); pref("font.name.monospace.x-cyrillic", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-cyrillic", "Roboto, Droid Sans"); pref("font.name.serif.x-unicode", "Droid Serif"); pref("font.name.sans-serif.x-unicode", "Droid Sans"); pref("font.name.monospace.x-unicode", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-unicode", "Roboto, Droid Sans"); pref("font.name.serif.x-user-def", "Droid Serif"); pref("font.name.sans-serif.x-user-def", "Droid Sans"); pref("font.name.monospace.x-user-def", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-user-def", "Roboto, Droid Sans"); pref("font.name.serif.x-western", "Droid Serif"); pref("font.name.sans-serif.x-western", "Droid Sans"); pref("font.name.monospace.x-western", "Droid Sans Mono"); +pref("font.name-list.sans-serif.x-western", "Roboto, Droid Sans"); pref("font.name.serif.zh-CN", "Droid Serif"); pref("font.name.sans-serif.zh-CN", "Droid Sans"); pref("font.name.monospace.zh-CN", "Droid Sans Mono"); -// ming_uni.ttf (HKSCS-2001) -// http://www.info.gov.hk/digital21/eng/hkscs/download/uime.exe pref("font.name.serif.zh-HK", "Droid Serif"); pref("font.name.sans-serif.zh-HK", "Droid Sans"); pref("font.name.monospace.zh-HK", "Droid Sans Mono"); -// zh-TW +pref("font.name.serif.zh-TW", "Droid Serif"); +pref("font.name.sans-serif.zh-TW", "Droid Sans"); +pref("font.name.monospace.zh-TW", "Droid Sans Mono"); pref("font.default.ar", "sans-serif"); pref("font.size.variable.ar", 16); diff --git a/security/manager/ssl/src/nsNSSIOLayer.cpp b/security/manager/ssl/src/nsNSSIOLayer.cpp index 15149230d58..5eb665f6173 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.cpp +++ b/security/manager/ssl/src/nsNSSIOLayer.cpp @@ -154,6 +154,8 @@ nsNSSSocketInfo::nsNSSSocketInfo() : mMutex("nsNSSSocketInfo::nsNSSSocketInfo"), mFd(nsnull), mCertVerificationState(before_cert_verification), + mCertVerificationStarted(0), + mCertVerificationEnded(0), mSecurityState(nsIWebProgressListener::STATE_IS_INSECURE), mSubRequestsHighSecurity(0), mSubRequestsLowSecurity(0), @@ -960,6 +962,7 @@ nsNSSSocketInfo::SetCertVerificationWaiting() NS_ASSERTION(mCertVerificationState != waiting_for_cert_verification, "Invalid state transition to waiting_for_cert_verification"); mCertVerificationState = waiting_for_cert_verification; + mCertVerificationStarted = PR_IntervalNow(); } void @@ -969,6 +972,8 @@ nsNSSSocketInfo::SetCertVerificationResult(PRErrorCode errorCode, NS_ASSERTION(mCertVerificationState == waiting_for_cert_verification, "Invalid state transition to cert_verification_finished"); + mCertVerificationEnded = PR_IntervalNow(); + if (errorCode != 0) { SetCanceled(errorCode, errorMessageType); } else if (mFd) { @@ -1025,11 +1030,36 @@ void nsNSSSocketInfo::SetAllowTLSIntoleranceTimeout(bool aAllow) bool nsNSSSocketInfo::HandshakeTimeout() { + if (mCertVerificationState == waiting_for_cert_verification) { + // Do not do a TLS interlerance timeout during cert verification because: + // + // * If we would have timed out, but cert verification is still ongoing, + // then the handshake probably already completed, and it is probably the + // certificate validation (OCSP responder or similar) that is timing + // out. + // * If certificate validation AND the handshake is slow, then that is a + // good indication that the network is bad, and so the problem probably + // isn't the server being TLS intolerant. + // * When we timeout, we return non-zero flags from PR_Poll, which will + // cause the application to try to read from and/or write to the socket, + // possibly in a loop. But, it is likely that the socket is blocked on + // cert authentication, so those read and/or write calls would result in + // PR_WOULD_BLOCK_ERROR, causing the application to spin. + return false; + } + if (!mHandshakeInProgress || !mAllowTLSIntoleranceTimeout) return false; - return ((PRIntervalTime)(PR_IntervalNow() - mHandshakeStartTime) - > PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS)); + PRIntervalTime now = PR_IntervalNow(); + PRIntervalTime certVerificationTime = + mCertVerificationEnded - mCertVerificationStarted; + PRIntervalTime totalTime = now - mHandshakeStartTime; + PRIntervalTime totalTimeExceptCertVerificationTime = + totalTime - certVerificationTime; + + return totalTimeExceptCertVerificationTime > + PR_SecondsToInterval(HANDSHAKE_TIMEOUT_SECONDS); } void nsSSLIOLayerHelpers::Cleanup() @@ -2082,6 +2112,7 @@ nsSSLIOLayerPoll(PRFileDesc * fd, PRInt16 in_flags, PRInt16 *out_flags) : "[%p] poll SSL socket using lower %d\n", fd, (int) in_flags)); + // See comments in HandshakeTimeout before moving and/or changing this block if (socketInfo->HandshakeTimeout()) { NS_ASSERTION(in_flags & PR_POLL_EXCEPT, "caller did not poll for EXCEPT (handshake timeout)"); diff --git a/security/manager/ssl/src/nsNSSIOLayer.h b/security/manager/ssl/src/nsNSSIOLayer.h index 2ee94c08b55..2e20f324816 100644 --- a/security/manager/ssl/src/nsNSSIOLayer.h +++ b/security/manager/ssl/src/nsNSSIOLayer.h @@ -184,6 +184,8 @@ protected: nsCOMPtr mCallbacks; PRFileDesc* mFd; CertVerificationState mCertVerificationState; + PRIntervalTime mCertVerificationStarted; + PRIntervalTime mCertVerificationEnded; PRUint32 mSecurityState; PRInt32 mSubRequestsHighSecurity; PRInt32 mSubRequestsLowSecurity; diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c index f9e456584f6..6d0d305886b 100644 --- a/security/nss/lib/ssl/sslsock.c +++ b/security/nss/lib/ssl/sslsock.c @@ -1955,7 +1955,21 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_flags, PRInt16 *p_out_flags) } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) && (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */ new_flags |= PR_POLL_WRITE; /* also select on write. */ - } + } + + if (ss->version >= SSL_LIBRARY_VERSION_3_0 && + ss->ssl3.hs.restartTarget != NULL) { + /* Read and write will block until the asynchronous callback completes + * (e.g. until SSL_AuthCertificateComplete is called), so don't tell + * the caller to poll the socket unless there is pending write data. + */ + if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) { + new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT); + } else { + new_flags = 0; + } + } + if (new_flags && (fd->lower->methods->poll != NULL)) { PRInt16 lower_out_flags = 0; PRInt16 lower_new_flags; diff --git a/security/patches/README b/security/patches/README index 8f1272e1972..9500a39f09e 100644 --- a/security/patches/README +++ b/security/patches/README @@ -4,5 +4,6 @@ on top of the NSS release. bug-542832-ssl-restart-4.patch and bug-542832-ssl-restart-tstclnt-4.patch were added so that we could test the new PSM SSL threading code (bug 674147) and SPDY (bug 528288). bug-717906-lowhash was added to fix an issue with recent -Mozilla builds on fedora. These patches will be removed when the NSS 3.13.2 +Mozilla builds on fedora. bug-710176-ssl-restart-7-poll-v5.patch were added +to fix a bug 710176. These patches will be removed when the NSS 3.13.2 release that includes them is imported into mozilla-central. diff --git a/security/patches/bug-710176-ssl-restart-7-poll-v5.patch b/security/patches/bug-710176-ssl-restart-7-poll-v5.patch new file mode 100644 index 00000000000..7d5f3f69e5e --- /dev/null +++ b/security/patches/bug-710176-ssl-restart-7-poll-v5.patch @@ -0,0 +1,40 @@ +# HG changeset patch +# Parent 4560e2c22b83f85f9238b9094de7a190042676df +# User Brian Smith + +diff --git a/security/nss/lib/ssl/sslsock.c b/security/nss/lib/ssl/sslsock.c +--- a/security/nss/lib/ssl/sslsock.c ++++ b/security/nss/lib/ssl/sslsock.c +@@ -1950,17 +1950,31 @@ ssl_Poll(PRFileDesc *fd, PRInt16 how_fla + } + } + } else if ((new_flags & PR_POLL_READ) && (SSL_DataPending(fd) > 0)) { + *p_out_flags = PR_POLL_READ; /* it's ready already. */ + return new_flags; + } else if ((ss->lastWriteBlocked) && (how_flags & PR_POLL_READ) && + (ss->pendingBuf.len != 0)) { /* write data waiting to be sent */ + new_flags |= PR_POLL_WRITE; /* also select on write. */ +- } ++ } ++ ++ if (ss->version >= SSL_LIBRARY_VERSION_3_0 && ++ ss->ssl3.hs.restartTarget != NULL) { ++ /* Read and write will block until the asynchronous callback completes ++ * (e.g. until SSL_AuthCertificateComplete is called), so don't tell ++ * the caller to poll the socket unless there is pending write data. ++ */ ++ if (ss->lastWriteBlocked && ss->pendingBuf.len != 0) { ++ new_flags &= (PR_POLL_WRITE | PR_POLL_EXCEPT); ++ } else { ++ new_flags = 0; ++ } ++ } ++ + if (new_flags && (fd->lower->methods->poll != NULL)) { + PRInt16 lower_out_flags = 0; + PRInt16 lower_new_flags; + lower_new_flags = fd->lower->methods->poll(fd->lower, new_flags, + &lower_out_flags); + if ((lower_new_flags & lower_out_flags) && (how_flags != new_flags)) { + PRInt16 out_flags = lower_out_flags & ~PR_POLL_RW; + if (lower_out_flags & PR_POLL_READ) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 0467ffb61d5..1300a935a26 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "base/histogram.h" +#include "base/pickle.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsCOMPtr.h" @@ -47,6 +48,8 @@ #include "jsapi.h" #include "nsStringGlue.h" #include "nsITelemetry.h" +#include "nsIFile.h" +#include "nsILocalFile.h" #include "Telemetry.h" #include "nsTHashtable.h" #include "nsHashKeys.h" @@ -54,6 +57,7 @@ #include "nsXULAppAPI.h" #include "nsThreadUtils.h" #include "mozilla/Mutex.h" +#include "mozilla/FileUtils.h" namespace { @@ -227,11 +231,9 @@ enum reflectStatus { }; enum reflectStatus -ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) +ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h, + const Histogram::SampleSet &ss) { - Histogram::SampleSet ss; - h->SnapshotSample(&ss); - // We don't want to reflect corrupt histograms. if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) { return REFLECT_CORRUPT; @@ -260,6 +262,14 @@ ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) return REFLECT_OK; } +enum reflectStatus +ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) +{ + Histogram::SampleSet ss; + h->SnapshotSample(&ss); + return ReflectHistogramAndSamples(cx, obj, h, ss); +} + JSBool JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp) { @@ -677,6 +687,333 @@ TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *re return WrapAndReturnHistogram(h, cx, ret); } +class TelemetrySessionData : public nsITelemetrySessionData +{ + NS_DECL_ISUPPORTS + NS_DECL_NSITELEMETRYSESSIONDATA + +public: + static nsresult LoadFromDisk(nsIFile *, TelemetrySessionData **ptr); + static nsresult SaveToDisk(nsIFile *, const nsACString &uuid); + + TelemetrySessionData(const char *uuid); + ~TelemetrySessionData(); + +private: + struct EnumeratorArgs { + JSContext *cx; + JSObject *snapshots; + }; + typedef nsBaseHashtableET EntryType; + typedef nsTHashtable SessionMapType; + static PLDHashOperator ReflectSamples(EntryType *entry, void *arg); + SessionMapType mSampleSetMap; + nsCString mUUID; + + bool DeserializeHistogramData(Pickle &pickle, void **iter); + static bool SerializeHistogramData(Pickle &pickle); + + // The file format version. Should be incremented whenever we change + // how individual SampleSets are stored in the file. + static const unsigned int sVersion = 1; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetrySessionData, nsITelemetrySessionData) + +TelemetrySessionData::TelemetrySessionData(const char *uuid) + : mUUID(uuid) +{ + mSampleSetMap.Init(); +} + +TelemetrySessionData::~TelemetrySessionData() +{ + mSampleSetMap.Clear(); +} + +NS_IMETHODIMP +TelemetrySessionData::GetUuid(nsACString &uuid) +{ + uuid = mUUID; + return NS_OK; +} + +PLDHashOperator +TelemetrySessionData::ReflectSamples(EntryType *entry, void *arg) +{ + struct EnumeratorArgs *args = static_cast(arg); + // This has the undesirable effect of creating a histogram for the + // current session with the given ID. But there's no good way to + // compute the ranges and buckets from scratch. + Histogram *h = nsnull; + nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h); + if (NS_FAILED(rv)) { + return PL_DHASH_STOP; + } + + // Don't reflect histograms with no data associated with them. + if (entry->mData.sum() == 0) { + return PL_DHASH_NEXT; + } + + JSObject *snapshot = JS_NewObject(args->cx, NULL, NULL, NULL); + if (!(snapshot + && ReflectHistogramAndSamples(args->cx, snapshot, h, entry->mData) + && JS_DefineProperty(args->cx, args->snapshots, + h->histogram_name().c_str(), + OBJECT_TO_JSVAL(snapshot), NULL, NULL, + JSPROP_ENUMERATE))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +TelemetrySessionData::GetSnapshots(JSContext *cx, jsval *ret) +{ + JSObject *snapshots = JS_NewObject(cx, NULL, NULL, NULL); + if (!snapshots) { + return NS_ERROR_FAILURE; + } + + struct EnumeratorArgs args = { cx, snapshots }; + PRUint32 count = mSampleSetMap.EnumerateEntries(ReflectSamples, + static_cast(&args)); + if (count != mSampleSetMap.Count()) { + return NS_ERROR_FAILURE; + } + + *ret = OBJECT_TO_JSVAL(snapshots); + return NS_OK; +} + +bool +TelemetrySessionData::DeserializeHistogramData(Pickle &pickle, void **iter) +{ + PRUint32 count = 0; + if (!pickle.ReadUInt32(iter, &count)) { + return false; + } + + for (size_t i = 0; i < count; ++i) { + int stored_length; + const char *name; + if (!pickle.ReadData(iter, &name, &stored_length)) { + return false; + } + + Telemetry::ID id; + nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id); + if (NS_FAILED(rv)) { + // We serialized a non-static histogram. Just drop its data on + // the floor. If we can't deserialize the data, though, we're in + // trouble. + Histogram::SampleSet ss; + if (!ss.Deserialize(iter, pickle)) { + return false; + } + } + + EntryType *entry = mSampleSetMap.GetEntry(id); + if (!entry) { + entry = mSampleSetMap.PutEntry(id); + if (NS_UNLIKELY(!entry)) { + return false; + } + if (!entry->mData.Deserialize(iter, pickle)) { + return false; + } + } + } + + return true; +} + +nsresult +TelemetrySessionData::LoadFromDisk(nsIFile *file, TelemetrySessionData **ptr) +{ + *ptr = nsnull; + nsresult rv; + nsCOMPtr f(do_QueryInterface(file, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + AutoFDClose fd; + rv = f->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + PRInt32 size = PR_Available(fd); + if (size == -1) { + return NS_ERROR_FAILURE; + } + + nsAutoArrayPtr data(new char[size]); + PRInt32 amount = PR_Read(fd, data, size); + if (amount != size) { + return NS_ERROR_FAILURE; + } + + Pickle pickle(data, size); + void *iter = NULL; + + unsigned int storedVersion; + if (!(pickle.ReadUInt32(&iter, &storedVersion) + && storedVersion == sVersion)) { + return NS_ERROR_FAILURE; + } + + const char *uuid; + int uuidLength; + if (!pickle.ReadData(&iter, &uuid, &uuidLength)) { + return NS_ERROR_FAILURE; + } + + nsAutoPtr sessionData(new TelemetrySessionData(uuid)); + if (!sessionData->DeserializeHistogramData(pickle, &iter)) { + return NS_ERROR_FAILURE; + } + + *ptr = sessionData.forget(); + return NS_OK; +} + +bool +TelemetrySessionData::SerializeHistogramData(Pickle &pickle) +{ + StatisticsRecorder::Histograms hs; + StatisticsRecorder::GetHistograms(&hs); + + if (!pickle.WriteUInt32(hs.size())) { + return false; + } + + for (StatisticsRecorder::Histograms::const_iterator it = hs.begin(); + it != hs.end(); + ++it) { + const Histogram *h = *it; + const char *name = h->histogram_name().c_str(); + + Histogram::SampleSet ss; + h->SnapshotSample(&ss); + + if (!(pickle.WriteData(name, strlen(name)+1) + && ss.Serialize(&pickle))) { + return false; + } + } + + return true; +} + +nsresult +TelemetrySessionData::SaveToDisk(nsIFile *file, const nsACString &uuid) +{ + nsresult rv; + nsCOMPtr f(do_QueryInterface(file, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + AutoFDClose fd; + rv = f->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); + if (NS_FAILED(rv)) { + return rv; + } + + Pickle pickle; + if (!pickle.WriteUInt32(sVersion)) { + return NS_ERROR_FAILURE; + } + + // Include the trailing NULL for the UUID to make reading easier. + const char *data; + size_t length = uuid.GetData(&data); + if (!(pickle.WriteData(data, length+1) + && SerializeHistogramData(pickle))) { + return NS_ERROR_FAILURE; + } + + PRInt32 amount = PR_Write(fd, static_cast(pickle.data()), + pickle.size()); + if (amount != pickle.size()) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +class SaveHistogramEvent : public nsRunnable +{ +public: + SaveHistogramEvent(nsIFile *file, const nsACString &uuid, + nsITelemetrySaveSessionDataCallback *callback) + : mFile(file), mUUID(uuid), mCallback(callback) + {} + + NS_IMETHOD Run() + { + nsresult rv = TelemetrySessionData::SaveToDisk(mFile, mUUID); + mCallback->Handle(!!NS_SUCCEEDED(rv)); + return rv; + } + +private: + nsCOMPtr mFile; + nsCString mUUID; + nsCOMPtr mCallback; +}; + +NS_IMETHODIMP +TelemetryImpl::SaveHistograms(nsIFile *file, const nsACString &uuid, + nsITelemetrySaveSessionDataCallback *callback, + bool isSynchronous) +{ + nsCOMPtr event = new SaveHistogramEvent(file, uuid, callback); + if (isSynchronous) { + return event ? event->Run() : NS_ERROR_FAILURE; + } else { + return NS_DispatchToCurrentThread(event); + } +} + +class LoadHistogramEvent : public nsRunnable +{ +public: + LoadHistogramEvent(nsIFile *file, + nsITelemetryLoadSessionDataCallback *callback) + : mFile(file), mCallback(callback) + {} + + NS_IMETHOD Run() + { + TelemetrySessionData *sessionData = nsnull; + nsresult rv = TelemetrySessionData::LoadFromDisk(mFile, &sessionData); + if (NS_FAILED(rv)) { + mCallback->Handle(nsnull); + } else { + nsCOMPtr data(sessionData); + mCallback->Handle(data); + } + return rv; + } + +private: + nsCOMPtr mFile; + nsCOMPtr mCallback; +}; + +NS_IMETHODIMP +TelemetryImpl::LoadHistograms(nsIFile *file, + nsITelemetryLoadSessionDataCallback *callback) +{ + nsCOMPtr event = new LoadHistogramEvent(file, callback); + return NS_DispatchToCurrentThread(event); +} + NS_IMETHODIMP TelemetryImpl::GetCanRecord(bool *ret) { *ret = mCanRecord; diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 5aa510fb82e..7a6c2a85f90 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -42,6 +42,7 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); +Cu.import("resource://gre/modules/ctypes.jsm"); // When modifying the payload in incompatible ways, please bump this version number const PAYLOAD_VERSION = 1; @@ -179,6 +180,10 @@ TelemetryPing.prototype = { _histograms: {}, _initialized: false, _prevValues: {}, + // Generate a unique id once per session so the server can cope with + // duplicate submissions. + _uuid: generateUUID(), + _prevSession: null, /** * Returns a set of histograms that can be converted into JSON @@ -187,8 +192,7 @@ TelemetryPing.prototype = { * histogram_type: <0 for exponential, 1 for linear>, bucketX:countX, ....} ...} * where bucket[XY], count[XY] are positive integers. */ - getHistograms: function getHistograms() { - let hls = Telemetry.histogramSnapshots; + getHistograms: function getHistograms(hls) { let info = Telemetry.registeredHistograms; let ret = {}; @@ -397,24 +401,48 @@ TelemetryPing.prototype = { send: function send(reason, server) { // populate histograms one last time this.gatherMemory(); + let data = this.getSessionPayloadAndSlug(reason); + + // Don't record a successful ping for previous session data. + this.doPing(server, data.slug, data.payload, !data.previous); + this._prevSession = null; + + // We were sending off data from before; now send the actual data + // we've collected this session. + if (data.previous) { + data = this.getSessionPayloadAndSlug(reason); + this.doPing(server, data.slug, data.payload, true); + } + }, + + getSessionPayloadAndSlug: function getSessionPayloadAndSlug(reason) { + // Use a deterministic url for testing. + let isTestPing = (reason == "test-ping"); + let havePreviousSession = !!this._prevSession; + let slug = (isTestPing + ? reason + : (havePreviousSession + ? this._prevSession.uuid + : this._uuid)); let payloadObj = { ver: PAYLOAD_VERSION, - info: this.getMetadata(reason), - simpleMeasurements: getSimpleMeasurements(), - histograms: this.getHistograms(), - slowSQL: Telemetry.slowSQL + // Send a different reason string for previous session data. + info: this.getMetadata(havePreviousSession ? "saved-session" : reason), }; + if (havePreviousSession) { + payloadObj.histograms = this.getHistograms(this._prevSession.snapshots); + } + else { + payloadObj.simpleMeasurements = getSimpleMeasurements(); + payloadObj.histograms = this.getHistograms(Telemetry.histogramSnapshots); + payloadObj.slowSQL = Telemetry.slowSQL; + } + return { previous: !!havePreviousSession, slug: slug, payload: JSON.stringify(payloadObj) }; + }, - let isTestPing = (reason == "test-ping"); - // Generate a unique id once per session so the server can cope with duplicate submissions. - // Use a deterministic url for testing. - if (!this._path) - this._path = "/submit/telemetry/" + (isTestPing ? reason : generateUUID()); - - let hping = Telemetry.getHistogramById("TELEMETRY_PING"); - let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - - let url = server + this._path; + doPing: function doPing(server, slug, payload, recordSuccess) { + let submitPath = "/submit/telemetry/" + slug; + let url = server + submitPath; let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Ci.nsIXMLHttpRequest); request.mozBackgroundRequest = true; @@ -423,6 +451,7 @@ TelemetryPing.prototype = { request.setRequestHeader("Content-Type", "application/json"); let startTime = new Date(); + let file = this.savedHistogramsFile(); function finishRequest(channel) { let success = false; @@ -430,15 +459,23 @@ TelemetryPing.prototype = { success = channel.QueryInterface(Ci.nsIHttpChannel).requestSucceeded; } catch(e) { } - hsuccess.add(success); - hping.add(new Date() - startTime); - if (isTestPing) + if (recordSuccess) { + let hping = Telemetry.getHistogramById("TELEMETRY_PING"); + let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); + + hsuccess.add(success); + hping.add(new Date() - startTime); + } + if (success && file.exists()) { + file.remove(true); + } + if (slug == "test-ping") Services.obs.notifyObservers(null, "telemetry-test-xhr-complete", null); } request.addEventListener("error", function(aEvent) finishRequest(request.channel), false); request.addEventListener("load", function(aEvent) finishRequest(request.channel), false); - request.send(JSON.stringify(payloadObj)); + request.send(payload); }, attachObservers: function attachObservers() { @@ -459,6 +496,25 @@ TelemetryPing.prototype = { } }, + savedHistogramsFile: function savedHistogramsFile() { + let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsILocalFile); + let profileFile = profileDirectory.clone(); + + // There's a bunch of binary data in the file, so we need to be + // sensitive to multiple machine types. Use ctypes to get some + // discriminating information. + let size = ctypes.voidptr_t.size; + // Hack to figure out endianness. + let uint32_array_t = ctypes.uint32_t.array(1); + let array = uint32_array_t([0xdeadbeef]); + let uint8_array_t = ctypes.uint8_t.array(4); + let array_as_bytes = ctypes.cast(array, uint8_array_t); + let endian = (array_as_bytes[0] === 0xde) ? "big" : "little" + let name = "sessionHistograms.dat." + size + endian; + profileFile.append(name); + return profileFile; + }, + /** * Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry. */ @@ -479,6 +535,7 @@ TelemetryPing.prototype = { Services.obs.addObserver(this, "private-browsing", false); Services.obs.addObserver(this, "profile-before-change", false); Services.obs.addObserver(this, "sessionstore-windows-restored", false); + Services.obs.addObserver(this, "quit-application-granted", false); // Delay full telemetry initialization to give the browser time to // run various late initializers. Otherwise our gathered memory @@ -492,6 +549,12 @@ TelemetryPing.prototype = { delete self._timer } this._timer.initWithCallback(timerCallback, TELEMETRY_DELAY, Ci.nsITimer.TYPE_ONE_SHOT); + + // Load data from the previous session. + let loadCallback = function(data) { + self._prevSession = data; + } + Telemetry.loadHistograms(this.savedHistogramsFile(), loadCallback); }, /** @@ -502,6 +565,7 @@ TelemetryPing.prototype = { Services.obs.removeObserver(this, "sessionstore-windows-restored"); Services.obs.removeObserver(this, "profile-before-change"); Services.obs.removeObserver(this, "private-browsing"); + Services.obs.removeObserver(this, "quit-application-granted"); }, /** @@ -567,6 +631,11 @@ TelemetryPing.prototype = { } this.send(aTopic == "idle" ? "idle-daily" : aTopic, server); break; + case "quit-application-granted": + Telemetry.saveHistograms(this.savedHistogramsFile(), + this._uuid, function (success) success, + /*isSynchronous=*/true); + break; } }, diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index 5e82bf126f0..b001b48ed8d 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -38,8 +38,39 @@ * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" +#include "nsIFile.idl" -[scriptable, uuid(db854295-478d-4de9-8211-d73ed7d81cd0)] +[scriptable, uuid(c177b6b0-5ef1-44f5-bc67-6bcf7d2518e5)] +interface nsITelemetrySessionData : nsISupports +{ + /** + * The UUID of our previous session. + */ + readonly attribute ACString uuid; + + /** + * An object containing a snapshot from all registered histograms that had + * data recorded in the previous session. + * { name1: data1, name2: data2, .... } + * where the individual dataN are as nsITelemetry.histogramSnapshots. + */ + [implicit_jscontext] + readonly attribute jsval snapshots; +}; + +[scriptable, function, uuid(aff36c9d-7e4c-41ab-a9b6-53773bbca0cd)] +interface nsITelemetryLoadSessionDataCallback : nsISupports +{ + void handle(in nsITelemetrySessionData data); +}; + +[scriptable, function, uuid(40065f26-afd2-4417-93de-c1de9adb1548)] +interface nsITelemetrySaveSessionDataCallback : nsISupports +{ + void handle(in bool success); +}; + +[scriptable, uuid(22fc825e-288f-457e-80d5-5bb35f06d37e)] interface nsITelemetry : nsISupports { /** @@ -127,6 +158,34 @@ interface nsITelemetry : nsISupports [implicit_jscontext] jsval getHistogramById(in ACString id); + /** + * Save persistent histograms to the given file. + * + * @param file - filename for saving + * @param uuid - UUID of this session + * @param callback - function to be caled when file writing is complete + * @param isSynchronous - whether the save is done synchronously. Defaults + * to asynchronous saving. + */ + void saveHistograms(in nsIFile file, in ACString uuid, + in nsITelemetrySaveSessionDataCallback callback, + [optional] in boolean isSynchronous); + + /* Reconstruct an nsITelemetryDataSession object containing histogram + * information from the given file; the file must have been produced + * via saveHistograms. The file is read asynchronously. + * + * This method does not modify the histogram information being + * collected in the current session. + * + * The reconstructed object is then passed to the given callback. + * + * @param file - the file to load histogram information from + * @param callback - function to process histogram information + */ + void loadHistograms(in nsIFile file, + in nsITelemetryLoadSessionDataCallback callback); + /** * Set this to false to disable gathering of telemetry statistics. */ diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index e0711d7cedd..56dc92afbd8 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -105,6 +105,45 @@ function test_privateMode() { do_check_neq(uneval(orig), uneval(h.snapshot())); } +function generateUUID() { + let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); + // strip {} + return str.substring(1, str.length - 1); +} + +// Check that we do sane things when saving to disk. +function test_loadSave() +{ + let dirService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + let tmpDir = dirService.get("TmpD", Ci.nsILocalFile); + let tmpFile = tmpDir.clone(); + tmpFile.append("saved-histograms.dat"); + if (tmpFile.exists()) { + tmpFile.remove(true); + } + + let saveFinished = false; + let loadFinished = false; + let uuid = generateUUID(); + let loadCallback = function(data) { + do_check_true(data != null); + do_check_eq(uuid, data.uuid); + loadFinished = true; + do_test_finished(); + }; + let saveCallback = function(success) { + do_check_true(success); + Telemetry.loadHistograms(tmpFile, loadCallback); + saveFinished = true; + }; + do_test_pending(); + Telemetry.saveHistograms(tmpFile, uuid, saveCallback); + do_register_cleanup(function () do_check_true(saveFinished)); + do_register_cleanup(function () do_check_true(loadFinished)); + do_register_cleanup(function () tmpFile.exists() && tmpFile.remove(true)); +} + function run_test() { let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR] @@ -121,4 +160,6 @@ function run_test() test_getHistogramById(); test_getSlowSQL(); test_privateMode(); + test_loadSave(); + } diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm index 76c015a74fe..3bb7004eb51 100644 --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm @@ -1062,17 +1062,14 @@ NS_METHOD nsCocoaWindow::PlaceBehind(nsTopLevelWidgetZPlacement aPlacement, return NS_OK; } -// Note bug 278777, we need to update state when the window is unminimized -// from the dock by users. NS_METHOD nsCocoaWindow::SetSizeMode(PRInt32 aMode) { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; - PRInt32 previousMode; - nsBaseWidget::GetSizeMode(&previousMode); - - nsresult rv = nsBaseWidget::SetSizeMode(aMode); - NS_ENSURE_SUCCESS(rv, rv); + // mSizeMode will be updated in DispatchSizeModeEvent, which will be called + // from a delegate method that handles the state change during one of the + // calls below. + nsSizeMode previousMode = mSizeMode; if (aMode == nsSizeMode_Normal) { if ([mWindow isMiniaturized]) @@ -1419,8 +1416,13 @@ nsCocoaWindow::ReportMoveEvent() void nsCocoaWindow::DispatchSizeModeEvent() { + nsSizeMode newMode = GetWindowSizeMode(mWindow); + if (mSizeMode == newMode) + return; + + mSizeMode = newMode; nsSizeModeEvent event(true, NS_SIZEMODE, this); - event.mSizeMode = GetWindowSizeMode(mWindow); + event.mSizeMode = mSizeMode; event.time = PR_IntervalNow(); nsEventStatus status = nsEventStatus_eIgnore; diff --git a/widget/tests/Makefile.in b/widget/tests/Makefile.in index d6f0a437244..4e2d6cd2594 100644 --- a/widget/tests/Makefile.in +++ b/widget/tests/Makefile.in @@ -87,6 +87,8 @@ _CHROME_FILES = test_bug343416.xul \ window_composition_text_querycontent.xul \ test_input_events_on_deactive_window.xul \ test_position_on_resize.xul \ + empty_window.xul \ + test_sizemode_events.xul \ $(NULL) # test_bug413277.html mac-only based on 604789, 605178 @@ -109,7 +111,6 @@ _CHROME_FILES += native_menus_window.xul \ bug586713_window.xul \ test_key_event_counts.xul \ test_bug596600.xul \ - window_bug596600.xul \ test_bug673301.xul \ $(NULL) endif diff --git a/widget/tests/window_bug596600.xul b/widget/tests/empty_window.xul similarity index 74% rename from widget/tests/window_bug596600.xul rename to widget/tests/empty_window.xul index 28e96f68e00..f0e01761d2f 100644 --- a/widget/tests/window_bug596600.xul +++ b/widget/tests/empty_window.xul @@ -1,4 +1,4 @@ - diff --git a/widget/tests/test_bug596600.xul b/widget/tests/test_bug596600.xul index 82c378d287d..9db8b15efa2 100644 --- a/widget/tests/test_bug596600.xul +++ b/widget/tests/test_bug596600.xul @@ -36,9 +36,9 @@ function moveMouseTo(x, y, andThen) { } function openWindows() { - gLeftWindow = open('window_bug596600.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200'); + gLeftWindow = open('empty_window.xul', '_blank', 'chrome,screenX=50,screenY=50,width=200,height=200'); SimpleTest.waitForFocus(function () { - gRightWindow = open('window_bug596600.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200'); + gRightWindow = open('empty_window.xul', '', 'chrome,screenX=300,screenY=50,width=200,height=200'); SimpleTest.waitForFocus(attachIFrameToRightWindow, gRightWindow); }, gLeftWindow); } diff --git a/widget/tests/test_sizemode_events.xul b/widget/tests/test_sizemode_events.xul new file mode 100644 index 00000000000..578d6f6fc62 --- /dev/null +++ b/widget/tests/test_sizemode_events.xul @@ -0,0 +1,107 @@ + + + + + + Test for bug 715867 + + + diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 06b93f893e5..26a09604ac3 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -145,21 +145,38 @@ static PRInt64 GetResident() static void XMappingIter(PRInt64& Vsize, PRInt64& Resident) { + Vsize = -1; + Resident = -1; int mapfd = open("/proc/self/xmap", O_RDONLY); struct stat st; - prxmap_t *prmapp; + prxmap_t *prmapp = NULL; if (mapfd >= 0) { if (!fstat(mapfd, &st)) { int nmap = st.st_size / sizeof(prxmap_t); - prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t)); - int n = read(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t)); - if (n > 0) { - Vsize = 0; - Resident = 0; - for (int i = 0; i < n / sizeof(prxmap_t); i++) { - Vsize += prmapp[i].pr_size; - Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize; + while (1) { + // stat(2) on /proc//xmap returns an incorrect value, + // prior to the release of Solaris 11. + // Here is a workaround for it. + nmap *= 2; + prmapp = (prxmap_t*)malloc((nmap + 1) * sizeof(prxmap_t)); + if (!prmapp) { + // out of memory + break; } + int n = pread(mapfd, prmapp, (nmap + 1) * sizeof(prxmap_t), 0); + if (n < 0) { + break; + } + if (nmap >= n / sizeof (prxmap_t)) { + Vsize = 0; + Resident = 0; + for (int i = 0; i < n / sizeof (prxmap_t); i++) { + Vsize += prmapp[i].pr_size; + Resident += prmapp[i].pr_rss * prmapp[i].pr_pagesize; + } + break; + } + free(prmapp); } free(prmapp); } @@ -169,16 +186,14 @@ static void XMappingIter(PRInt64& Vsize, PRInt64& Resident) static PRInt64 GetVsize() { - PRInt64 Vsize = -1; - PRInt64 Resident = -1; + PRInt64 Vsize, Resident; XMappingIter(Vsize, Resident); return Vsize; } static PRInt64 GetResident() { - PRInt64 Vsize = -1; - PRInt64 Resident = -1; + PRInt64 Vsize, Resident; XMappingIter(Vsize, Resident); return Resident; } diff --git a/xpcom/components/Module.h b/xpcom/components/Module.h index e18a02c1911..4501daa756f 100644 --- a/xpcom/components/Module.h +++ b/xpcom/components/Module.h @@ -53,7 +53,7 @@ namespace mozilla { */ struct Module { - static const unsigned int kVersion = 12; + static const unsigned int kVersion = 13; struct CIDEntry;