author | Matt Brubeck <mbrubeck@mozilla.com> |
Tue, 27 Dec 2011 11:04:58 -0800 | |
changeset 83400 | 7e28cce342a6deb1d18e6382bd76baf61ae955ae |
parent 83371 | a8be91348c312ba8b8f62d1039e995e8479b7424 (current diff) |
parent 83399 | e2f392f454f64d605b7c43ba6f562948eee11650 (diff) |
child 83401 | 8749089face43d24d256823e5089931eebfe1d47 |
child 83419 | 994d41f8225738de17b062746686a88fd0289d75 |
child 105450 | 0cfb7c3645ee2bcac477c4382395753b7d7f49f9 |
push id | 21757 |
push user | mbrubeck@mozilla.com |
push date | Tue, 27 Dec 2011 19:06:17 +0000 |
treeherder | mozilla-central@7e28cce342a6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 12.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
config/rules.mk | file | annotate | diff | comparison | revisions | |
js/src/config/rules.mk | file | annotate | diff | comparison | revisions | |
layout/reftests/svg/sizing/dynamic--inline-resize-cb-height.xhtml | file | annotate | diff | comparison | revisions | |
layout/reftests/svg/sizing/dynamic--inline-resize-cb-width.xhtml | file | annotate | diff | comparison | revisions |
--- a/browser/base/content/test/browser_save_video.js +++ b/browser/base/content/test/browser_save_video.js @@ -1,13 +1,13 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); /** * TestCase for bug 564387 * <https://bugzilla.mozilla.org/show_bug.cgi?id=564387> */ function test() { waitForExplicitFinish(); var fileName; @@ -49,17 +49,17 @@ function test() { MockFilePicker.filterIndex = 1; // kSaveAsType_URL }; mockTransferCallback = onTransferComplete; mockTransferRegisterer.register(); registerCleanupFunction(function () { mockTransferRegisterer.unregister(); - MockFilePicker.reset(); + MockFilePicker.cleanup(); destDir.remove(true); }); // Select "Save Video As" option from context menu var saveVideoCommand = document.getElementById("context-savevideo"); saveVideoCommand.doCommand(); event.target.hidePopup();
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -9,17 +9,16 @@ ; #filter substitution #ifdef XP_MACOSX ; Mac bundle stuff @APPNAME@/Contents/Info.plist @APPNAME@/Contents/PkgInfo -@APPNAME@/Contents/Plug-Ins/ @APPNAME@/Contents/Resources/ #endif [@AB_CD@] @BINPATH@/chrome/@AB_CD@@JAREXT@ @BINPATH@/chrome/@AB_CD@.manifest @BINPATH@/@PREF_DIR@/firefox-l10n.js @BINPATH@/searchplugins/*
--- a/config/rules.mk +++ b/config/rules.mk @@ -368,16 +368,23 @@ ifdef DEFFILE OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE)) EXTRA_DEPS += $(DEFFILE) endif ifdef MAPFILE OS_LDFLAGS += -MAP:$(MAPFILE) endif +else #!GNU_CC + +ifdef DEFFILE +OS_LDFLAGS += $(call normalizepath,$(DEFFILE)) +EXTRA_DEPS += $(DEFFILE) +endif + endif # !GNU_CC endif # WINNT ifeq ($(SOLARIS_SUNPRO_CXX),1) ifeq (86,$(findstring 86,$(OS_TEST))) OS_LDFLAGS += -M $(topsrcdir)/config/solaris_ia32.map endif # x86
--- a/content/html/content/test/test_bug500885.html +++ b/content/html/content/test/test_bug500885.html @@ -13,17 +13,17 @@ https://bugzilla.mozilla.org/show_bug.cg <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500885">Mozilla Bug 500885</a> <div> <input id="file" type="file" /> </div> <script type="text/javascript"> var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); MockFilePicker.returnValue = MockFilePicker.returnOK; function test() { // SpecialPowers.DOMWindowUtils doesn't appear to fire mouseEvents correctly var wu = SpecialPowers.getDOMWindowUtils(window); try { var domActivateEvents; @@ -42,17 +42,20 @@ function test() { is(domActivateEvents, 1, "click on text field should fire 1 DOMActivate event"); domActivateEvents = 0; wu.sendMouseEvent("mousedown", rect.right - 5, rect.top + 5, 0, 1, 0); wu.sendMouseEvent("mouseup", rect.right - 5, rect.top + 5, 0, 1, 0); is(domActivateEvents, 1, "click on button should fire 1 DOMActivate event"); } finally { - SimpleTest.executeSoon(SimpleTest.finish); + SimpleTest.executeSoon(function() { + MockFilePicker.cleanup(); + SimpleTest.finish(); + }); } } SimpleTest.waitForExplicitFinish(); SimpleTest.waitForFocus(test); </script> </body>
--- a/content/html/content/test/test_bug592802.html +++ b/content/html/content/test/test_bug592802.html @@ -21,33 +21,34 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script type="application/javascript"> /** Test for Bug 592802 **/ SimpleTest.waitForExplicitFinish(); var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); var testData = [ /* visibility | display | multiple */ [ "", "", false ], [ "hidden", "", false ], [ "", "none", false ], [ "", "", true ], [ "hidden", "", true ], [ "", "none", true ], ]; var testCounter = 0; var testNb = testData.length; function finished() { + MockFilePicker.cleanup(); SimpleTest.finish(); } SimpleTest.waitForFocus(function() { // mockFilePicker will simulate a cancel for the first time the file picker will be shown. MockFilePicker.returnValue = MockFilePicker.returnCancel; var b2 = document.getElementById('b2');
--- a/content/svg/content/src/nsSVGElement.cpp +++ b/content/svg/content/src/nsSVGElement.cpp @@ -1107,16 +1107,34 @@ nsSVGElement::UpdateContentStyleRule() MappedAttrParser mappedAttrParser(doc->CSSLoader(), doc->GetDocumentURI(), GetBaseURI(), NodePrincipal()); for (PRUint32 i = 0; i < attrCount; ++i) { const nsAttrName* attrName = mAttrsAndChildren.AttrNameAt(i); if (!attrName->IsAtom() || !IsAttributeMapped(attrName->Atom())) continue; + if (Tag() == nsGkAtoms::svg) { + // Special case: we don't want <svg> 'width'/'height' mapped into style + // if the attribute value isn't a valid <length> according to SVG (which + // only supports a subset of the CSS <length> values). We don't enforce + // this by checking the attribute value in nsSVGSVGElement:: + // IsAttributeMapped since we don't want that method to depend on the + // value of the attribute that is being checked. Rather we just prevent + // the actual mapping here, as necessary. + if (attrName->Atom() == nsGkAtoms::width && + !GetAnimatedLength(nsGkAtoms::width)->HasBaseVal()) { + continue; + } + if (attrName->Atom() == nsGkAtoms::height && + !GetAnimatedLength(nsGkAtoms::height)->HasBaseVal()) { + continue; + } + } + nsAutoString value; mAttrsAndChildren.AttrAt(i)->ToString(value); mappedAttrParser.ParseMappedAttrValue(attrName->Atom(), value); } mContentStyleRule = mappedAttrParser.CreateStyleRule(); } static void @@ -1309,16 +1327,30 @@ nsSVGElement::DidAnimateLength(PRUint8 a if (frame) { LengthAttributesInfo info = GetLengthInfo(); frame->AttributeChanged(kNameSpaceID_None, *info.mLengthInfo[aAttrEnum].mName, nsIDOMMutationEvent::MODIFICATION); } } +nsSVGLength2* +nsSVGElement::GetAnimatedLength(const nsIAtom *aAttrName) +{ + LengthAttributesInfo lengthInfo = GetLengthInfo(); + + for (PRUint32 i = 0; i < lengthInfo.mLengthCount; i++) { + if (aAttrName == *lengthInfo.mLengthInfo[i].mName) { + return &lengthInfo.mLengths[i]; + } + } + NS_ABORT_IF_FALSE(false, "no matching length found"); + return nsnull; +} + void nsSVGElement::GetAnimatedLengthValues(float *aFirst, ...) { LengthAttributesInfo info = GetLengthInfo(); NS_ASSERTION(info.mLengthCount > 0, "GetAnimatedLengthValues on element with no length attribs");
--- a/content/svg/content/src/nsSVGElement.h +++ b/content/svg/content/src/nsSVGElement.h @@ -190,16 +190,17 @@ public: virtual void DidAnimatePreserveAspectRatio(); virtual void DidAnimateNumberList(PRUint8 aAttrEnum); virtual void DidAnimateLengthList(PRUint8 aAttrEnum); virtual void DidAnimatePointList(); virtual void DidAnimatePathSegList(); virtual void DidAnimateTransformList(); virtual void DidAnimateString(PRUint8 aAttrEnum); + nsSVGLength2* GetAnimatedLength(const nsIAtom *aAttrName); void GetAnimatedLengthValues(float *aFirst, ...); void GetAnimatedNumberValues(float *aFirst, ...); void GetAnimatedIntegerValues(PRInt32 *aFirst, ...); SVGAnimatedNumberList* GetAnimatedNumberList(PRUint8 aAttrEnum); SVGAnimatedNumberList* GetAnimatedNumberList(nsIAtom *aAttrName); void GetAnimatedLengthListValues(SVGUserUnitList *aFirst, ...); SVGAnimatedLengthList* GetAnimatedLengthList(PRUint8 aAttrEnum); virtual SVGAnimatedPointList* GetAnimatedPointList() {
--- a/content/svg/content/src/nsSVGLength2.h +++ b/content/svg/content/src/nsSVGLength2.h @@ -95,16 +95,19 @@ public: float GetAnimValInSpecifiedUnits() const { return mAnimVal; } float GetBaseValInSpecifiedUnits() const { return mBaseVal; } float GetBaseValue(nsSVGSVGElement* aCtx) const { return mBaseVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } float GetAnimValue(nsSVGSVGElement* aCtx) const { return mAnimVal / GetUnitScaleFactor(aCtx, mSpecifiedUnitType); } + bool HasBaseVal() const { + return mIsBaseSet; + } // Returns true if the animated value of this length has been explicitly // set (either by animation, or by taking on the base value which has been // explicitly set by markup or a DOM call), false otherwise. // If this returns false, the animated value is still valid, that is, // useable, and represents the default base value of the attribute. bool IsExplicitlySet() const { return mIsAnimated || mIsBaseSet; }
--- a/content/svg/content/src/nsSVGSVGElement.cpp +++ b/content/svg/content/src/nsSVGSVGElement.cpp @@ -878,16 +878,30 @@ nsSVGSVGElement::GetTimedDocumentRoot() } //---------------------------------------------------------------------- // nsIContent methods NS_IMETHODIMP_(bool) nsSVGSVGElement::IsAttributeMapped(const nsIAtom* name) const { + // We want to map the 'width' and 'height' attributes into style for + // outer-<svg>, except when the attributes aren't set (since their default + // values of '100%' can cause unexpected and undesirable behaviour for SVG + // inline in HTML). We rely on nsSVGElement::UpdateContentStyleRule() to + // prevent mapping of the default values into style (it only maps attributes + // that are set). We also rely on a check in nsSVGElement:: + // UpdateContentStyleRule() to prevent us mapping the attributes when they're + // given a <length> value that is not currently recognized by the SVG + // specification. + + if (!IsInner() && (name == nsGkAtoms::width || name == nsGkAtoms::height)) { + return true; + } + static const MappedAttributeEntry* const map[] = { sColorMap, sFEFloodMap, sFillStrokeMap, sFiltersMap, sFontSpecificationMap, sGradientStopMap, sGraphicsMap,
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -74,16 +74,19 @@ #include "Exceptions.h" #include "File.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" #include "Worker.h" #include "WorkerFeature.h" #include "WorkerScope.h" +#ifdef ANDROID +#include <android/log.h> +#endif #include "WorkerInlines.h" #if 0 // Define to run GC more often. #define EXTRA_GC #endif using mozilla::MutexAutoLock; @@ -1144,17 +1147,21 @@ public: logged = true; } else { NS_WARNING("Failed to log script error!"); } } if (!logged) { - fputs(NS_ConvertUTF16toUTF8(aMessage).get(), stderr); + NS_ConvertUTF16toUTF8 msg(aMessage); +#ifdef ANDROID + __android_log_print(ANDROID_LOG_INFO, "Gecko", msg.get()); +#endif + fputs(msg.get(), stderr); fflush(stderr); } return true; } }; class TimerRunnable : public WorkerRunnable
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -56,16 +56,19 @@ #include "ListenerManager.h" #include "Location.h" #include "Navigator.h" #include "Principal.h" #include "ScriptLoader.h" #include "Worker.h" #include "WorkerPrivate.h" #include "XMLHttpRequest.h" +#ifdef ANDROID +#include <android/log.h> +#endif #include "WorkerInlines.h" #define PROPERTY_FLAGS \ JSPROP_ENUMERATE | JSPROP_SHARED #define FUNCTION_FLAGS \ JSPROP_ENUMERATE @@ -512,16 +515,19 @@ private: return false; } JSAutoByteString buffer(aCx, str); if (!buffer) { return false; } +#ifdef ANDROID + __android_log_print(ANDROID_LOG_INFO, "Gecko", buffer.ptr()); +#endif fputs(buffer.ptr(), stderr); fflush(stderr); } return true; } static JSBool
--- a/gfx/skia/Makefile.in +++ b/gfx/skia/Makefile.in @@ -297,53 +297,66 @@ CPPSRCS = \ $(NULL) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) EXPORTS_skia += \ include/ports/SkTypeface_mac.h \ $(NULL) CPPSRCS += \ SkFontHost_mac_coretext.cpp \ - SkBitmapProcState_opts_SSE2.cpp \ - SkBlitRow_opts_SSE2.cpp \ - SkUtils_opts_SSE2.cpp \ - opts_check_SSE2.cpp \ SkTime_Unix.cpp \ $(NULL) endif ifeq (android,$(MOZ_WIDGET_TOOLKIT)) CPPSRCS += \ - SkBitmapProcState_opts_arm.cpp \ - SkBlitRow_opts_arm.cpp \ SkFontHost_FreeType.cpp \ SkFontHost_android.cpp \ SkFontHost_gamma.cpp \ - SkUtils_opts_none.cpp \ SkMMapStream.cpp \ SkTime_Unix.cpp \ $(NULL) DEFINES += -DSK_BUILD_FOR_ANDROID_NDK OS_CXXFLAGS += $(CAIRO_FT_CFLAGS) endif ifeq (windows,$(MOZ_WIDGET_TOOLKIT)) EXPORTS_skia += \ include/config/sk_stdint.h \ include/ports/SkTypeface_win.h \ $(NULL) CPPSRCS += \ SkFontHost_win.cpp \ SkTime_win.cpp \ + $(NULL) +endif + +ifneq (,$(INTEL_ARCHITECTURE)) +CPPSRCS += \ SkBitmapProcState_opts_SSE2.cpp \ SkBlitRow_opts_SSE2.cpp \ SkUtils_opts_SSE2.cpp \ opts_check_SSE2.cpp \ $(NULL) +else +ifeq ($(CPU_ARCH)_$(GNU_CC),arm_1) +CPPSRCS += \ + SkBitmapProcState_opts_arm.cpp \ + SkBlitRow_opts_arm.cpp \ + opts_check_arm.cpp \ + $(NULL) +SSRCS += memset.arm.S +else +CPPSRCS += \ + SkBitmapProcState_opts_none.cpp \ + SkBlitRow_opts_none.cpp \ + SkUtils_opts_none.cpp \ + $(NULL) +endif endif include $(topsrcdir)/config/rules.mk ifneq (,$(INTEL_ARCHITECTURE)) ifdef GNU_CC SkBitmapProcState_opts_SSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2 SkBlitRow_opts_SSE2.$(OBJ_SUFFIX): CXXFLAGS+=-msse2
--- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -368,16 +368,23 @@ ifdef DEFFILE OS_LDFLAGS += -DEF:$(call normalizepath,$(DEFFILE)) EXTRA_DEPS += $(DEFFILE) endif ifdef MAPFILE OS_LDFLAGS += -MAP:$(MAPFILE) endif +else #!GNU_CC + +ifdef DEFFILE +OS_LDFLAGS += $(call normalizepath,$(DEFFILE)) +EXTRA_DEPS += $(DEFFILE) +endif + endif # !GNU_CC endif # WINNT ifeq ($(SOLARIS_SUNPRO_CXX),1) ifeq (86,$(findstring 86,$(OS_TEST))) OS_LDFLAGS += -M $(topsrcdir)/config/solaris_ia32.map endif # x86
--- a/layout/forms/test/test_bug36619.html +++ b/layout/forms/test/test_bug36619.html @@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script type="application/javascript"> /** Test for Bug 36619 **/ SimpleTest.waitForExplicitFinish(); var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); // enable popups the first time SpecialPowers.pushPrefEnv({'set': [ ["dom.disable_open_during_load", true], ["privacy.popups.showBrowserMessage", false] ]}, function() { SimpleTest.waitForFocus(function() { @@ -40,17 +40,17 @@ SpecialPowers.pushPrefEnv({'set': [ SimpleTest.executeSoon(function() { ok(MockFilePicker.shown, "File picker show method should have been called"); MockFilePicker.reset(); // Tests that a click on 'a' doesn't call the show method, because it is hidden. document.getElementById("a").click(); SimpleTest.executeSoon(function() { ok(!MockFilePicker.shown, "File picker show method should not have been called"); - MockFilePicker.reset(); + MockFilePicker.cleanup(); SimpleTest.finish(); }); }); }); }); </script>
--- a/layout/forms/test/test_bug377624.html +++ b/layout/forms/test/test_bug377624.html @@ -28,17 +28,17 @@ https://bugzilla.mozilla.org/show_bug.cg <pre id="test"> <script type="application/javascript"> /** Test for Bug 377624 **/ SimpleTest.waitForExplicitFinish(); var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); var testData = [["a", MockFilePicker.filterImages, 1], ["b", MockFilePicker.filterAudio, 1], ["c", MockFilePicker.filterVideo, 1], ["d", 0, 0], ["e", 0, 0], ["f", 0, 0], ["g", 0, 0], @@ -83,17 +83,17 @@ function runTests() { "appendFilters should have been called once"); is(filters[0], MockFilePicker.filterAll + testData[currentTest][1], "Correct filters should have been added"); is(filterIndex, testData[currentTest][2], "File picker should show the correct filter index"); if (++currentTest == testData.length) { - MockFilePicker.reset(); + MockFilePicker.cleanup(); SimpleTest.finish(); } else { launchNextTest(); } }); }; launchNextTest();
--- a/layout/forms/test/test_bug536567.html +++ b/layout/forms/test/test_bug536567.html @@ -19,17 +19,17 @@ https://bugzilla.mozilla.org/show_bug.cg /** Test for Bug 536567 **/ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; const Cm = Components.manager; var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); var ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties); var prefSvc = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); var obSvc = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); if ("@mozilla.org/privatebrowsing;1" in Cc) { var pbSvc = Cc["@mozilla.org/privatebrowsing;1"].getService(Ci.nsIPrivateBrowsingService); } @@ -129,17 +129,17 @@ function runTest() { } function endTest() { prefSvc.clearUserPref("browser.privatebrowsing.keep_current_session"); for(var i = 0; i < dirs.length - 1; i++) { dirs[i].remove(true); } - MockFilePicker.reset(); + MockFilePicker.cleanup(); SimpleTest.finish(); } MockFilePicker.showCallback = function(filepicker) { var test = tests[testIndex]; var returned = -1; for (var i = 0; i < dirs.length; i++) { if (dirs[i].path == MockFilePicker.displayDirectory.path) {
--- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -15,16 +15,17 @@ nestegg_track_codec_data nestegg_track_codec_data_count nestegg_track_codec_id nestegg_track_count nestegg_track_seek nestegg_track_type nestegg_track_video_params nestegg_tstamp_scale #ifndef MOZ_NATIVE_LIBVPX +vpx_codec_control_ vpx_codec_dec_init_ver vpx_codec_decode vpx_codec_destroy vpx_codec_get_frame vpx_codec_peek_stream_info vpx_codec_vp8_dx #endif #endif
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/531200-1-ref.html @@ -0,0 +1,38 @@ +<html class="reftest-print"> +<head> +<title>push rowspan on to next page if it can't be splitted</title> +<style> +img { + width: 10px; + height: 30px; +} +</style> +</head> +<body> + <div style="height: 110px"></div> + <table cellpadding="0" cellspacing="0" border="0"> + <tr> + <td></td> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + <tr style="page-break-after:always"> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + <tr> + <td rowspan="2"> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + <td rowspan="2"></td> + </tr> + <tr> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/531200-1.html @@ -0,0 +1,38 @@ +<html class="reftest-print"> +<head> +<title>push rowspan on to next page if it can't be splitted</title> +<style> +img { + width: 10px; + height: 30px; +} +</style> +</head> +<body> + <div style="height: 110px"></div> + <table cellpadding="0" cellspacing="0" border="0"> + <tr> + <td></td> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + <tr> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + <tr> + <td rowspan="2"> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + <td rowspan="2"></td> + </tr> + <tr> + <td> + <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF/wAAAAAAQaMSAwAAABJJREFUeNpiYBgFo2AwAIAAAwACigABtnCV2AAAAABJRU5ErkJggg=="> + </td> + </tr> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/711359-1-ref.html @@ -0,0 +1,13 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<body> + <table style="border-collapse: collapse;" border="1"> + <tbody id="reference"> + <tr><td> </td></tr> + <tr><td> </td></tr> + <tr><td> </td></tr> + </tbody> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/711359-1.html @@ -0,0 +1,32 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> + <script> + var lastLang = ""; + function LangSelect(aLang) { + var tbody = document.getElementById("l10ntbody"); + var child = tbody.firstChild; + while (child) { + if (child.nodeType == Node.ELEMENT_NODE) { + if (aLang == "*" || aLang == child.getAttribute("language")) + child.removeAttribute("style"); + else + child.setAttribute("style", "display: none"); + } + child = child.nextSibling; + } + } +</script> +</head> + +<body onload="LangSelect('cs');"> + <table style="border-collapse: collapse;" border="1"> + <tbody id="l10ntbody"> + <tr style="display: none;" language="cs"><td> </td></tr> + <tr style="display: none;" language="cs"><td> </td></tr> + <tr style="display: none;" language="cs"><td> </td></tr> + </tbody> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/712849-1-ref.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html><head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <title>testcase</title> +<style> +.tt { + border-collapse: collapse; +} +.nix { + display: none; +} +.ref { + border: solid 3px darkred; +} + +td { + width: 20px; + height: 20px; +} + +</style> +</head> +<body> + <table class="tt"> + <tr id="1"><td > </td></tr> + <tr id="2" class="nix"><td > </td></tr> + <tr id="3" class="ref"><td > </td></tr> + <tr><td> </td></tr> + </table> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/bugs/712849-1.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<html><head> +<meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <title>testcase</title> +<style> +.tt { + border-collapse: collapse; +} +.nix { + display: none; +} +.ref { + border: solid 3px darkred; +} + +td { + width: 20px; + height: 20px; +} + +</style> +<script> + +function test () +{ + document.getElementById ("2").className = "nix"; + document.getElementById ("3").className = "ref"; +} +</script> +</head> +<body onload="test()"> + <table class="tt"> + <tr id="1"><td > </td></tr> + <tr id="2"><td > </td></tr> + <tr id="3"><td > </td></tr> + <tr><td> </td></tr> + </table> +</body> +</html>
--- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1440,16 +1440,17 @@ random-if(d2d) == 523468-1.html 523468-1 == 528038-1c.html 528038-1-ref.html == 528038-1d.html 528038-1-ref.html == 528038-1e.html 528038-1-ref.html == 528038-1f.html 528038-1-ref.html == 528038-2.html 528038-2-ref.html == 528096-1.html 528096-1-ref.html == 530686-1.html 530686-1-ref.html == 531098-1.html 531098-1-ref.html +== 531200-1.html 531200-1-ref.html == 531371-1.html 531371-1-ref.html == 534526-1a.html 534526-1-ref.html == 534526-1b.html 534526-1-ref.html == 534804-1.html 534804-1-ref.html == 534808-1.html 534808-1-ref.html == 534808-2.html 534808-2-ref.html fails-if(/Mac\x20OS\x20X\x2010\.7/.test(http.oscpu)) == 534919-1.html 534919-1-ref.html # Bug 705044 random == 536061.html 536061-ref.html # fixedpoint division in blur code makes this fail @@ -1672,8 +1673,10 @@ fails-if(layersGPUAccelerated&&cocoaWidg == 670467-2.html 670467-2-ref.html != 691087-1.html 691087-1-ref.html == 691571-1.html 691571-1-ref.html == 696307-1.html 696307-1-ref.html == 696739-1.html 696739-1-ref.html needs-focus == 703186-1.html 703186-1-ref.html needs-focus == 703186-2.html 703186-2-ref.html needs-focus != 703186-1.html 703186-2.html +== 711359-1.html 711359-1-ref.html +== 712849-1.html 712849-1-ref.html
deleted file mode 100644 --- a/layout/reftests/svg/sizing/dynamic--inline-resize-cb-height.xhtml +++ /dev/null @@ -1,52 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait"> - <head> - - <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=294086 --> - - <title>Test: resize of container block height</title> - - <!-- - This testcase checks that SVG embedded inline with a percentage height is - updated correctly when its containing block is resized. - --> - - <style type="text/css"> - -html, body, div { - padding: 0; - border: 0; - margin: 0; - width: 100%; - height: 100%; /* inline style override on the div below */ - background: white; - overflow: hidden; -} - - </style> - <script type="text/javascript"> - -function resize_div() -{ - var XHTML_NS = 'http://www.w3.org/1999/xhtml'; - document.getElementsByTagNameNS(XHTML_NS, 'div').item(0).style.height = '100%'; - document.documentElement.removeAttribute('class'); -} - -document.addEventListener("MozReftestInvalidate", resize_div, false); - - </script> - </head> - <body> - - <div style="height:50%;"> - <svg xmlns="http://www.w3.org/2000/svg" width="5000" height="100%"> - <rect width="100%" height="100%" fill="blue"/> - </svg> - </div> - - </body> -</html>
deleted file mode 100644 --- a/layout/reftests/svg/sizing/dynamic--inline-resize-cb-width.xhtml +++ /dev/null @@ -1,52 +0,0 @@ -<!-- - Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ ---> -<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait"> - <head> - - <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=294086 --> - - <title>Test: resize of container block width</title> - - <!-- - This testcase checks that SVG embedded inline with a percentage width is - updated correctly when its containing block is resized. - --> - - <style type="text/css"> - -html, body, div { - padding: 0; - border: 0; - margin: 0; - width: 100%; /* inline style override on the div below */ - height: 100%; - background: white; - overflow: hidden; -} - - </style> - <script type="text/javascript"> - -function resize_div() -{ - var XHTML_NS = 'http://www.w3.org/1999/xhtml'; - document.getElementsByTagNameNS(XHTML_NS, 'div').item(0).style.width = '100%'; - document.documentElement.removeAttribute('class'); -} - -document.addEventListener("MozReftestInvalidate", resize_div, false); - - </script> - </head> - <body> - - <div style="width:50%;"> - <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="5000"> - <rect width="100%" height="100%" fill="blue"/> - </svg> - </div> - - </body> -</html>
--- a/layout/reftests/svg/sizing/reftest.list +++ b/layout/reftests/svg/sizing/reftest.list @@ -298,17 +298,13 @@ random-if(Android) == object--auto-auto- # Assorted tests to check that dynamic changes work correctly # # Here we have an assortment of different tests to check that updates occur # correctly when changes are made that should result in a change in the size # or position of the SVG. == dynamic--inline-css-height.xhtml pass.svg == dynamic--inline-css-width.xhtml pass.svg -# These two don't have a whole lot of point anymore now that the meaning -# of percentages has changed. -== dynamic--inline-resize-cb-height.xhtml standalone-sanity-height-150px.svg -== dynamic--inline-resize-cb-width.xhtml standalone-sanity-width-300px.svg skip == dynamic--inline-resize-window-height.xhtml pass.svg # XXX breaks the reftest run as the window height somehow is not restored skip == dynamic--inline-resize-window-width.xhtml pass.svg # Fails way too much fails random-if(Android) == dynamic--object-svg-unloaded.xhtml pass.svg # == dynamic--object--auto-auto--pct-px.html
--- a/layout/style/viewsource.css +++ b/layout/style/viewsource.css @@ -16,16 +16,17 @@ * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Blake Ross <BlakeR1234@aol.com> + * Geoff Lankow <geoff@darktrojan.net> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -42,26 +43,40 @@ background-color: white; color: black; direction: ltr; } #viewsource { font-family: -moz-fixed; font-weight: normal; white-space: pre; + counter-reset: line; } #viewsource.wrap { white-space: pre-wrap; word-wrap: break-word; } pre { font: inherit; color: inherit; white-space: inherit; - margin: 0; + margin: 0 0 0 5ch; +} +pre[id]:before, +span[id]:before { + content: counter(line) " "; + counter-increment: line; + -moz-user-select: none; + display: inline-block; + width: 5ch; + margin: 0 0 0 -5ch; + text-align: right; + color: #ccc; + font-weight: normal; + font-style: normal; } .start-tag { color: purple; font-weight: bold; } .end-tag { color: purple; font-weight: bold;
--- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -54,17 +54,17 @@ load 384637-1.svg load 384728-1.svg load 385246-1.svg load 385246-2.svg load 385552-1.svg load 385552-2.svg load 385840-1.svg load 385852-1.svg load 386475-1.xhtml -load 386566-1.svg +asserts(1) load 386566-1.svg # Bug 713626 load 386690-1.svg load 387290-1.svg load 402408-1.svg load 404677-1.xhtml load 409565-1.xhtml load 409573-1.svg load 420697-1.svg load 420697-2.svg
--- a/layout/tables/nsCellMap.cpp +++ b/layout/tables/nsCellMap.cpp @@ -1855,18 +1855,21 @@ nsCellMap::ExpandWithRows(nsTableCellMap if (cellFrame) { AppendCell(aMap, cellFrame, rowX, false, aRgFirstRowIndex, aDamageArea, &colIndex); } cFrame = cFrame->GetNextSibling(); } newRowIndex++; } - SetDamageArea(0, aRgFirstRowIndex + startRowIndex, aMap.GetColCount(), - 1 + endRowIndex - startRowIndex, aDamageArea); + // mark all following rows damaged, they might contain a previously set + // damage area which we can not shift. + PRInt32 firstDamagedRow = aRgFirstRowIndex + startRowIndex; + SetDamageArea(0, firstDamagedRow, aMap.GetColCount(), + aMap.GetRowCount() - firstDamagedRow, aDamageArea); } void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, nsTArray<nsTableCellFrame*>& aCellFrames, PRInt32 aRowIndex, PRInt32 aColIndex, PRInt32 aRowSpan, // same for all cells bool aRowSpanIsZero, @@ -2023,18 +2026,21 @@ void nsCellMap::ShrinkWithoutRows(nsTabl } mRows.RemoveElementAt(rowX); // Decrement our row and next available index counts. mContentRowCount--; } aMap.RemoveColsAtEnd(); - SetDamageArea(0, aRgFirstRowIndex + aStartRowIndex, aMap.GetColCount(), 0, - aDamageArea); + // mark all following rows damaged, they might contain a previously set + // damage area which we can not shift. + PRInt32 firstDamagedRow = aRgFirstRowIndex + aStartRowIndex; + SetDamageArea(0, firstDamagedRow, aMap.GetColCount(), + aMap.GetRowCount() - firstDamagedRow, aDamageArea); } PRInt32 nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, bool& aIsZeroColSpan) const { aIsZeroColSpan = false; PRInt32 colSpan = aCellFrameToAdd.GetColSpan(); if (0 == colSpan) {
--- a/layout/tables/nsTableRowFrame.cpp +++ b/layout/tables/nsTableRowFrame.cpp @@ -201,30 +201,24 @@ nsTableRowFrame::DidSetStyleContext(nsSt } NS_IMETHODIMP nsTableRowFrame::AppendFrames(ChildListID aListID, nsFrameList& aFrameList) { NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); - // Append the frames - // XXXbz why do we append here first, then append to table, while - // for InsertFrames we do it in the other order? Bug 507419 covers this. const nsFrameList::Slice& newCells = mFrames.AppendFrames(nsnull, aFrameList); // Add the new cell frames to the table nsTableFrame *tableFrame = nsTableFrame::GetTableFrame(this); for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) { - nsTableCellFrame *cellFrame = do_QueryFrame(e.get()); - NS_ASSERTION(cellFrame, "Unexpected frame"); - if (cellFrame) { - // Add the cell to the cell map - tableFrame->AppendCell(*cellFrame, GetRowIndex()); - } + nsIFrame *childFrame = e.get(); + NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); + tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex()); } PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_HAS_DIRTY_CHILDREN); tableFrame->SetGeometryDirty(); return NS_OK; } @@ -233,42 +227,36 @@ nsTableRowFrame::AppendFrames(ChildListI NS_IMETHODIMP nsTableRowFrame::InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame, nsFrameList& aFrameList) { NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, "inserting after sibling frame with different parent"); + //Insert Frames in the frame list + const nsFrameList::Slice& newCells = mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList); // Get the table frame nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this); - // gather the new frames (only those which are cells) into an array - // XXXbz there shouldn't be any other ones here... can we just put - // them all in the array and not do all this QI nonsense? nsIAtom* cellFrameType = (tableFrame->IsBorderCollapse()) ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame; nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType); nsTArray<nsTableCellFrame*> cellChildren; - for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { - nsTableCellFrame *cellFrame = do_QueryFrame(e.get()); - NS_ASSERTION(cellFrame, "Unexpected frame"); - if (cellFrame) { - cellChildren.AppendElement(cellFrame); - } + for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) { + nsIFrame *childFrame = e.get(); + NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure"); + cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame)); } // insert the cells into the cell map PRInt32 colIndex = -1; if (prevCellFrame) { prevCellFrame->GetColIndex(colIndex); } tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex); - - // Insert the frames in the frame list - mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList); PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_HAS_DIRTY_CHILDREN); tableFrame->SetGeometryDirty(); return NS_OK; }
--- a/layout/tables/nsTableRowGroupFrame.cpp +++ b/layout/tables/nsTableRowGroupFrame.cpp @@ -950,27 +950,29 @@ nsTableRowGroupFrame::SplitSpanningCells NS_ASSERTION(aSpanningRowBottom >= 0, "Can't split negative heights"); aFirstTruncatedRow = nsnull; aDesiredHeight = 0; bool borderCollapse = static_cast<nsTableFrame*>(aTable.GetFirstInFlow())->IsBorderCollapse(); PRInt32 lastRowIndex = aLastRow.GetRowIndex(); bool wasLast = false; + bool haveRowSpan = false; // Iterate the rows between aFirstRow and aLastRow for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) { wasLast = (row == &aLastRow); PRInt32 rowIndex = row->GetRowIndex(); nsPoint rowPos = row->GetPosition(); // Iterate the cells looking for those that have rowspan > 1 for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) { PRInt32 rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell); // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow // were reflowed correctly during the unconstrained height reflow. if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) { + haveRowSpan = true; nsReflowStatus status; // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow // aAvailHeight is the space between the row group start and the end of the page nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y; NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?"); bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage; nsRect rowRect = row->GetRect(); @@ -1016,16 +1018,19 @@ nsTableRowGroupFrame::SplitSpanningCells cell->GetColIndex(colIndex); aContRow->InsertCellFrame(contCell, colIndex); } } } } } } + if (!haveRowSpan) { + aDesiredHeight = aLastRow.GetRect().YMost(); + } } // Remove the next-in-flow of the row, its cells and their cell blocks. This // is necessary in case the row doesn't need a continuation later on or needs // a continuation which doesn't have the same number of cells that now exist. void nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext, nsTableRowFrame* aRow)
--- a/memory/mozutils/mozutils.def.in +++ b/memory/mozutils/mozutils.def.in @@ -1,9 +1,9 @@ -; ***** BEGIN LICENSE BLOCK ***** +; ***** BEGIN LICENSE BLOCK ***** ; Version: MPL 1.1/GPL 2.0/LGPL 2.1 ; ; The contents of this file are subject to the Mozilla Public License Version ; 1.1 (the "License"); you may not use this file except in compliance with ; the License. You may obtain a copy of the License at ; http://www.mozilla.org/MPL/ ; ; Software distributed under the License is distributed on an "AS IS" basis,
--- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -63,20 +63,23 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.EditorInfo; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.Button; import android.widget.EditText; +import android.widget.ExpandableListView; import android.widget.ImageButton; import android.widget.RelativeLayout; import android.widget.ListView; +import java.util.Map; + import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.db.BrowserDB; import org.json.JSONArray; import org.json.JSONObject; public class AwesomeBar extends Activity implements GeckoEventListener { private static final String LOGTAG = "GeckoAwesomeBar"; @@ -381,78 +384,101 @@ public class AwesomeBar extends Activity @Override public void onDestroy() { super.onDestroy(); mAwesomeTabs.destroy(); GeckoAppShell.unregisterGeckoEventListener("SearchEngines:Data", this); } - private Cursor mContextMenuCursor = null; + private Object mContextMenuSubject = null; @Override public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, view, menuInfo); + ListView list = (ListView) view; + Object selectedItem = null; + String title = ""; - AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; - ListView list = (ListView) view; - Object selecteditem = list.getItemAtPosition(info.position); + if (view == (ListView)findViewById(R.id.history_list)) { + ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo; + ExpandableListView exList = (ExpandableListView)list; + int childPosition = exList.getPackedPositionChild(info.packedPosition); + int groupPosition = exList.getPackedPositionGroup(info.packedPosition); + selectedItem = exList.getExpandableListAdapter().getChild(groupPosition, childPosition); - if (!(selecteditem instanceof Cursor)) { - mContextMenuCursor = null; + Map<String, Object> map = (Map<String, Object>)selectedItem; + title = (String)map.get(URLColumns.TITLE); + } else { + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + selectedItem = list.getItemAtPosition(info.position); + + Cursor cursor = (Cursor)selectedItem; + title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE)); + } + + if (selectedItem == null || !((selectedItem instanceof Cursor) || (selectedItem instanceof Map))) { + mContextMenuSubject = null; return; } - mContextMenuCursor = (Cursor) selecteditem; + mContextMenuSubject = selectedItem; MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.awesomebar_contextmenu, menu); - String title = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.TITLE)); menu.setHeaderTitle(title); } @Override public boolean onContextItemSelected(MenuItem item) { - if (mContextMenuCursor == null) + if (mContextMenuSubject == null) return false; - AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo(); + String url = ""; + byte[] b = null; + String title = ""; + if (mContextMenuSubject instanceof Cursor) { + Cursor cursor = (Cursor)mContextMenuSubject; + url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)); + b = (byte[]) cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON)); + title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE)); + } else if (mContextMenuSubject instanceof Map) { + Map<String, Object> map = (Map<String, Object>)mContextMenuSubject; + url = (String)map.get(URLColumns.URL); + b = (byte[]) map.get(URLColumns.FAVICON); + title = (String)map.get(URLColumns.TITLE); + } else { + return false; + } + + mContextMenuSubject = null; switch (item.getItemId()) { case R.id.open_new_tab: { - String url = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.URL)); GeckoApp.mAppContext.loadUrl(url, AwesomeBar.Type.ADD); break; } case R.id.add_to_launcher: { - String url = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.URL)); - byte[] b = (byte[]) mContextMenuCursor.getBlob(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.FAVICON)); - String title = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.TITLE)); - Bitmap bitmap = null; if (b != null) bitmap = BitmapFactory.decodeByteArray(b, 0, b.length); GeckoAppShell.createShortcut(title, url, bitmap, ""); break; } case R.id.share: { - String url = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.URL)); - String title = mContextMenuCursor.getString(mContextMenuCursor.getColumnIndexOrThrow(URLColumns.TITLE)); GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title); break; } default: { - mContextMenuCursor = null; return super.onContextItemSelected(item); } } - mContextMenuCursor = null; return true; } public static class AwesomeBarEditText extends EditText { OnKeyPreImeListener mOnKeyPreImeListener; public interface OnKeyPreImeListener { public boolean onKeyPreIme(View v, int keyCode, KeyEvent event);
--- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -137,17 +137,17 @@ public class GeckoPreferences } final private int DIALOG_CREATE_MASTER_PASSWORD = 0; final private int DIALOG_REMOVE_MASTER_PASSWORD = 1; @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String prefName = preference.getKey(); - if (prefName.equals("privacy.masterpassword.enabled")) { + if (prefName != null && prefName.equals("privacy.masterpassword.enabled")) { showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD); return false; } setPreference(prefName, newValue); if (preference instanceof ListPreference) { // We need to find the entry for the new value int newIndex = ((ListPreference)preference).findIndexOfValue((String) newValue);
--- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,17 +136,17 @@ public class Tabs implements GeckoEventL /** Close tab and then select the default next tab */ public void closeTab(Tab tab) { closeTab(tab, getNextTab(tab)); } /** Close tab and then select nextTab */ public void closeTab(Tab tab, Tab nextTab) { - if (tab == null) + if (tab == null || nextTab == null) return; GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Select", String.valueOf(nextTab.getId()))); GeckoAppShell.sendEventToGecko(new GeckoEvent("Tab:Close", String.valueOf(tab.getId()))); } /** Return the tab that will be selected by default after this one is closed */ public Tab getNextTab(Tab tab) {
--- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -954,16 +954,17 @@ public class PanZoomController Log.d(LOGTAG, "onScaleBegin in " + mState); if (mState == PanZoomState.ANIMATED_ZOOM) return false; mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); GeckoApp.mAppContext.hidePluginViews(); + GeckoApp.mAppContext.mAutoCompletePopup.hide(); cancelTouch(); return true; } @Override public void onScaleEnd(ScaleGestureDetector detector) { Log.d(LOGTAG, "onScaleEnd in " + mState); @@ -1070,16 +1071,17 @@ public class PanZoomController GeckoEvent e = new GeckoEvent("Gesture:DoubleTap", ret.toString()); GeckoAppShell.sendEventToGecko(e); return true; } private boolean animatedZoomTo(RectF zoomToRect) { GeckoApp.mAppContext.hidePluginViews(); + GeckoApp.mAppContext.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; final float startZoom = mController.getZoomFactor(); final PointF startPoint = mController.getOrigin(); RectF viewport = mController.getViewport(); float newHeight = zoomToRect.width() * viewport.height() / viewport.width();
--- a/mobile/android/chrome/content/about.xhtml +++ b/mobile/android/chrome/content/about.xhtml @@ -79,21 +79,22 @@ <ul id="aboutLinks"> <div class="top-border"></div> <li><a id="faqURL">&aboutPage.faq.label;</a></li> <li><a id="supportURL">&aboutPage.support.label;</a></li> <li><a id="privacyURL">&aboutPage.privacyPolicy.label;</a></li> <li><a href="about:rights">&aboutPage.rights.label;</a></li> <li><a id="releaseNotesURL">&aboutPage.relNotes.label;</a></li> <li><a id="creditsURL">&aboutPage.credits.label;</a></li> + <li><a href="about:license">&aboutPage.license.label;</a></li> <div class="bottom-border"></div> </ul> <div id="aboutDetails"> - <p id="license"><b><a href="about:license">&aboutPage.licenseLink;</a>&aboutPage.licenseLinkSuffix;</b> &logoTrademark;</p> + <p>&logoTrademark;</p> </div> <script type="application/javascript;version=1.8"><![CDATA[ let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils, Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); // Include the build date if this is an "a#" (nightly or aurora) build
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -3336,28 +3336,28 @@ var PluginHelper = { (overlay.scrollHeight - 5 > pluginRect.height); return overflows; } }; var PermissionsHelper = { - _permissonTypes: ["password", "geo", "popup", "indexedDB", + _permissonTypes: ["password", "geolocation", "popup", "indexedDB", "offline-app", "desktop-notification"], _permissionStrings: { "password": { label: "password.rememberPassword", allowed: "password.remember", denied: "password.never" }, - "geo": { + "geolocation": { label: "geolocation.shareLocation", - allowed: "geolocation.alwaysShare", - denied: "geolocation.neverShare" + allowed: "geolocation.alwaysAllow", + denied: "geolocation.neverAllow" }, "popup": { label: "blockPopups.label", allowed: "popupButtonAlwaysAllow2", denied: "popupButtonNeverWarn2" }, "indexedDB": { label: "offlineApps.storeOfflineData", @@ -3439,17 +3439,17 @@ var PermissionsHelper = { } }, /** * Gets the permission value stored for a specified permission type. * * @param aType * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "indexedDB", "popup", "image" + * e.g. "geolocation", "indexedDB", "popup" * * @return A permission value defined in nsIPermissionManager. */ getPermission: function getPermission(aURI, aType) { // Password saving isn't a nsIPermissionManager permission type, so handle // it seperately. if (aType == "password") { // By default, login saving is enabled, so if it is disabled, the @@ -3460,28 +3460,28 @@ var PermissionsHelper = { // Check to see if the user ever actually saved a login if (Services.logins.countLogins(aURI.prePath, "", "")) return Services.perms.ALLOW_ACTION; return Services.perms.UNKNOWN_ACTION; } // Geolocation consumers use testExactPermission - if (aType == "geo") + if (aType == "geolocation") return Services.perms.testExactPermission(aURI, aType); return Services.perms.testPermission(aURI, aType); }, /** * Clears a user-set permission value for the site given a permission type. * * @param aType * The permission type string stored in permission manager. - * e.g. "cookie", "geo", "indexedDB", "popup", "image" + * e.g. "geolocation", "indexedDB", "popup" */ clearPermission: function clearPermission(aURI, aType) { // Password saving isn't a nsIPermissionManager permission type, so handle // it seperately. if (aType == "password") { // Get rid of exisiting stored logings let logins = Services.logins.findLogins({}, aURI.prePath, "", ""); for (let i = 0; i < logins.length; i++) { @@ -3492,16 +3492,18 @@ var PermissionsHelper = { } else { Services.perms.remove(aURI.host, aType); } } } var MasterPassword = { pref: "privacy.masterpassword.enabled", + _tokenName: "", + get _secModuleDB() { delete this._secModuleDB; return this._secModuleDB = Cc["@mozilla.org/security/pkcs11moduledb;1"].getService(Ci.nsIPKCS11ModuleDB); }, get _pk11DB() { delete this._pk11DB; return this._pk11DB = Cc["@mozilla.org/security/pk11tokendb;1"].getService(Ci.nsIPK11TokenDB);
--- a/mobile/android/locales/en-US/chrome/about.dtd +++ b/mobile/android/locales/en-US/chrome/about.dtd @@ -1,18 +1,12 @@ <!ENTITY aboutPage.title "About &brandShortName;"> +<!ENTITY aboutPage.checkForUpdates.link "Check for Updates »"> +<!ENTITY aboutPage.checkForUpdates.checking "Looking for updates…"> +<!ENTITY aboutPage.checkForUpdates.none "No updates available"> +<!ENTITY aboutPage.checkForUpdates.found "Update available"> <!ENTITY aboutPage.faq.label "FAQ"> <!ENTITY aboutPage.support.label "Support"> <!ENTITY aboutPage.privacyPolicy.label "Privacy Policy"> <!ENTITY aboutPage.rights.label "Know Your Rights"> <!ENTITY aboutPage.relNotes.label "Release Notes"> <!ENTITY aboutPage.credits.label "Credits"> -<!ENTITY aboutPage.checkForUpdates.link "Check for Updates »"> -<!ENTITY aboutPage.checkForUpdates.checking "Looking for updates…"> -<!ENTITY aboutPage.checkForUpdates.none "No updates available"> -<!ENTITY aboutPage.checkForUpdates.found "Update available"> - - -<!-- LOCALIZATION NOTE: - These strings are concatenated in order. Unneeded strings may be left blank. ---> -<!ENTITY aboutPage.licenseLink "Licensing information"> -<!ENTITY aboutPage.licenseLinkSuffix "."> +<!ENTITY aboutPage.license.label "Licensing Information">
--- a/mobile/android/themes/core/aboutPage.css +++ b/mobile/android/themes/core/aboutPage.css @@ -98,17 +98,17 @@ body { } #aboutLinks { margin: 0 -10px 15px -10px; padding: 0; } #aboutLinks > li { - line-height: 3; + line-height: 2.6; border-top: 1px solid white; border-bottom: 1px solid #C1C7CC; } #aboutLinks > li > a { padding-left: 25px; display: block; }
--- a/parser/htmlparser/tests/reftest/bug482921-1-ref.html +++ b/parser/htmlparser/tests/reftest/bug482921-1-ref.html @@ -1,24 +1,27 @@ -<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><span class="doctype"><!DOCTYPE html></span> -<span><<span class="start-tag">html</span>></span> -<span><<span class="start-tag">head</span>></span> -<span><<span class="start-tag">title</span>></span><span>Title</span><span></<span class="end-tag">title</span>></span> -<span><<span class="start-tag">script</span>></span> -var lt = "<"; -<!-- -var s = "<script>foo</script>"; ---> -<span class="end-tag"></script></span><span></span><span class="comment"><!-- Not quite optimal highlight there. --></span> -<span><<span class="start-tag">style</span>></span> -/* </foo> */ -<span></<span class="end-tag">style</span>></span> -<span></<span class="end-tag">head</span>></span> -<span><<span class="start-tag">body</span>></span> -<span><<span class="start-tag">p</span>></span><span>Entity: <span class="entity"><span>&</span>amp; </span></span><span></<span class="end-tag">p</span>></span> -<span><<span class="start-tag">iframe</span>></span><img><span></<span class="end-tag">iframe</span>></span> -<span><<span class="start-tag">noscript</span>></span><p>Not para</p><span></<span class="end-tag">noscript</span>></span> -<span><<span class="start-tag">svg</span>></span> -<span><<span class="start-tag">title</span>></span><span></span><span class="cdata"><![CDATA[bar]]></span><span></span><span></<span class="end-tag">title</span>></span> -<span><<span class="start-tag">script</span>></span><span></span><span class="comment"><!-- this is a comment --></span><span></span><span></<span class="end-tag">script</span>></span> -<span></<span class="end-tag">svg</span>></span> -<span></<span class="end-tag">body</span>></span> -<span></<span class="end-tag">html</span>></span> +<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="doctype"><!DOCTYPE html></span> +<span id></span><span><<span class="start-tag">html</span>></span> +<span id></span><span><<span class="start-tag">head</span>></span> +<span id></span><span><<span class="start-tag">title</span>></span><span>Title</span><span></<span class="end-tag">title</span>></span> +<span id></span><span><<span class="start-tag">script</span>></span> +<span id></span>var lt = "<"; +<span id></span><!-- +<span id></span>var s = "<script>foo</script>"; +<span id></span>--> +<span id></span><span class="end-tag"></script></span><span></span><span class="comment"><!-- Not quite optimal highlight there. --></span> +<span id></span><span><<span class="start-tag">style</span>></span> +<span id></span>/* </foo> */ +<span id></span><span></<span class="end-tag">style</span>></span> +<span id></span><span></<span class="end-tag">head</span>></span> +<span id></span><span><<span class="start-tag">body</span>></span> +<span id></span><span><<span class="start-tag">p</span>></span><span>Entity: <span class="entity"><span>&</span>amp; </span></span><span></<span class="end-tag">p</span>></span> +<span id></span><span><<span class="start-tag">iframe</span>></span><img><span></<span class="end-tag">iframe</span>></span> +<span id></span><span><<span class="start-tag">noscript</span>></span><p>Not para</p><span></<span class="end-tag">noscript</span>></span> +<span id></span><span><<span class="start-tag">svg</span>></span> +<span id></span><span><<span class="start-tag">title</span>></span><span></span><span class="cdata"><![CDATA[bar]]></span><span></span><span></<span class="end-tag">title</span>></span> +<span id></span><span><<span class="start-tag">script</span>></span><span></span><span class="comment"><!-- this is a comment --></span><span></span><span></<span class="end-tag">script</span>></span> +<span id></span><span></<span class="end-tag">svg</span>></span> +<span id></span><span></<span class="end-tag">body</span>></span> +<span id></span><span></<span class="end-tag">html</span>></span> +<span id></span> +</pre> +<!-- View source CSS matches the <pre id> and <span id> elements and produces line numbers. -->
--- a/parser/htmlparser/tests/reftest/bug482921-2-ref.html +++ b/parser/htmlparser/tests/reftest/bug482921-2-ref.html @@ -1,25 +1,28 @@ -<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><span class="pi"><?xml version="1.0" encoding="utf-8"?></span> -<span class="pi"><?foo bar?></span> -<span><<span class="start-tag">html</span>></span> -<span><<span class="start-tag">head</span>></span> -<span><<span class="start-tag">title</span>></span><span>Title</span><span></<span class="end-tag">title</span>></span> -<span><<span class="start-tag">script</span>></span> -var s = "<span><<span class="start-tag">script</span>></span><span>foo</span><span></<span class="end-tag">script</span>></span>"; -<span class="comment"><!-- -var s = "<script>foo</script>"; ---></span> -<span></<span class="end-tag">script</span>></span><span></span> -<span><<span class="start-tag">style</span>></span> -/* <span><<span class="start-tag">foo</span><span>/</span>></span> */ -<span></<span class="end-tag">style</span>></span> -<span></<span class="end-tag">head</span>></span> -<span><<span class="start-tag">body</span>></span> -<span><<span class="start-tag">p</span>></span><span>Entity: <span class="entity"><span>&</span>amp; </span></span><span></<span class="end-tag">p</span>></span> -<span><<span class="start-tag">iframe</span>></span><span></span><span><<span class="start-tag">img</span>></span><span></<span class="end-tag">iframe</span>></span> -<span><<span class="start-tag">noscript</span>></span><span><<span class="start-tag">p</span>></span><span>Not para</span><span></<span class="end-tag">p</span>></span><span></<span class="end-tag">noscript</span>></span> -<span><<span class="start-tag">svg</span>></span> -<span><<span class="start-tag">title</span>></span><span></span><span class="cdata"><![CDATA[bar]]></span><span></span><span></<span class="end-tag">title</span>></span> -<span><<span class="start-tag">script</span>></span><span></span><span class="comment"><!-- this is a comment --></span><span></span><span></<span class="end-tag">script</span>></span> -<span></<span class="end-tag">svg</span>></span> -<span></<span class="end-tag">body</span>></span> -<span></<span class="end-tag">html</span>></span> +<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="pi"><?xml version="1.0" encoding="utf-8"?></span> +<span id></span><span class="pi"><?foo bar?></span> +<span id></span><span><<span class="start-tag">html</span>></span> +<span id></span><span><<span class="start-tag">head</span>></span> +<span id></span><span><<span class="start-tag">title</span>></span><span>Title</span><span></<span class="end-tag">title</span>></span> +<span id></span><span><<span class="start-tag">script</span>></span> +<span id></span>var s = "<span><<span class="start-tag">script</span>></span><span>foo</span><span></<span class="end-tag">script</span>></span>"; +<span id></span><span class="comment"><!-- +<span id></span>var s = "<script>foo</script>"; +<span id></span>--></span> +<span id></span><span></<span class="end-tag">script</span>></span><span></span> +<span id></span><span><<span class="start-tag">style</span>></span> +<span id></span>/* <span><<span class="start-tag">foo</span><span>/</span>></span> */ +<span id></span><span></<span class="end-tag">style</span>></span> +<span id></span><span></<span class="end-tag">head</span>></span> +<span id></span><span><<span class="start-tag">body</span>></span> +<span id></span><span><<span class="start-tag">p</span>></span><span>Entity: <span class="entity"><span>&</span>amp; </span></span><span></<span class="end-tag">p</span>></span> +<span id></span><span><<span class="start-tag">iframe</span>></span><span></span><span><<span class="start-tag">img</span>></span><span></<span class="end-tag">iframe</span>></span> +<span id></span><span><<span class="start-tag">noscript</span>></span><span><<span class="start-tag">p</span>></span><span>Not para</span><span></<span class="end-tag">p</span>></span><span></<span class="end-tag">noscript</span>></span> +<span id></span><span><<span class="start-tag">svg</span>></span> +<span id></span><span><<span class="start-tag">title</span>></span><span></span><span class="cdata"><![CDATA[bar]]></span><span></span><span></<span class="end-tag">title</span>></span> +<span id></span><span><<span class="start-tag">script</span>></span><span></span><span class="comment"><!-- this is a comment --></span><span></span><span></<span class="end-tag">script</span>></span> +<span id></span><span></<span class="end-tag">svg</span>></span> +<span id></span><span></<span class="end-tag">body</span>></span> +<span id></span><span></<span class="end-tag">html</span>></span> +<span id></span> +</pre> +<!-- View source CSS matches the <pre id> and <span id> elements and produces line numbers. -->
--- a/parser/htmlparser/tests/reftest/bug535530-2-ref.html +++ b/parser/htmlparser/tests/reftest/bug535530-2-ref.html @@ -1,13 +1,17 @@ -<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><span class="doctype"><!DOCTYPE html></span> -XX<span class="error">&</span>XX -XX<span class="error">&</span>nXX -XX<span class="error">&</span>noXX -XX<span class="error entity">&not</span>XX -XX<span class="error entity">&noti</span>XX -XX<span class="error entity">&notin</span>XX -XX<span class="error">&</span>;XX -XX<span class="error">&</span>n;XX -XX<span class="error">&</span>no;XX -XX<span class="entity">&not;</span>XX -XX<span class="error entity">&noti</span>;XX -XX<span class="entity">&notin;</span>XX +<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="doctype"><!DOCTYPE html></span> +<span id></span>XX<span class="error">&</span>XX +<span id></span>XX<span class="error">&</span>nXX +<span id></span>XX<span class="error">&</span>noXX +<span id></span>XX<span class="error entity">&not</span>XX +<span id></span>XX<span class="error entity">&noti</span>XX +<span id></span>XX<span class="error entity">&notin</span>XX +<span id></span>XX<span class="error">&</span>;XX +<span id></span>XX<span class="error">&</span>n;XX +<span id></span>XX<span class="error">&</span>no;XX +<span id></span>XX<span class="entity">&not;</span>XX +<span id></span>XX<span class="error entity">&noti</span>;XX +<span id></span>XX<span class="entity">&notin;</span>XX +<span id></span> +<span id></span> +</pre> +<!-- View source CSS matches the <pre id> and <span id> elements and produces line numbers. -->
--- a/parser/htmlparser/tests/reftest/bug704667-1-ref.html +++ b/parser/htmlparser/tests/reftest/bug704667-1-ref.html @@ -1,1 +1,4 @@ -<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><span class="error comment"><!--></span> <span class="error comment"><!X></span> +<!DOCTYPE html><html><head><title></title><link rel="stylesheet" type="text/css" href="resource://gre-resources/viewsource.css"></head><body id="viewsource" class="wrap"><pre id><span class="error comment"><!--></span> <span class="error comment"><!X></span> +<span id></span> +</pre> +<!-- View source CSS matches the <pre id> and <span id> elements and produces line numbers. -->
--- a/testing/mochitest/MockFilePicker.jsm +++ b/testing/mochitest/MockFilePicker.jsm @@ -1,49 +1,61 @@ /* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1 + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * - * The Original Code is Mochitest Reusable Mock File Picker. + * The Original Code is Reusable Mock File Picker. * * The Initial Developer of the Original Code is * Geoff Lankow <geoff@darktrojan.net>. * Portions created by the Initial Developer are Copyright (C) 2011 * the Initial Developer. All Rights Reserved. * * Contributor(s): * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * * ***** END LICENSE BLOCK ***** */ var EXPORTED_SYMBOLS = ["MockFilePicker"]; const Cc = Components.classes; const Ci = Components.interfaces; const Cm = Components.manager; const Cu = Components.utils; const CONTRACT_ID = "@mozilla.org/filepicker;1"; -const CLASS_ID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID(); Cu.import("resource://gre/modules/FileUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); - -var MockFilePickerFactory = { +var oldClassID, oldFactory; +var newClassID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID(); +var newFactory = { createInstance: function(aOuter, aIID) { if (aOuter) throw Components.results.NS_ERROR_NO_AGGREGATION; return new MockFilePickerInstance().QueryInterface(aIID); }, lockFactory: function(aLock) { throw Components.results.NS_ERROR_NOT_IMPLEMENTED; }, @@ -61,28 +73,44 @@ var MockFilePicker = { filterImages: Ci.nsIFilePicker.filterImages, filterXML: Ci.nsIFilePicker.filterXML, filterXUL: Ci.nsIFilePicker.filterXUL, filterApps: Ci.nsIFilePicker.filterApps, filterAllowURLs: Ci.nsIFilePicker.filterAllowURLs, filterAudio: Ci.nsIFilePicker.filterAudio, filterVideo: Ci.nsIFilePicker.filterVideo, + init: function() { + this.reset(); + if (!registrar.isCIDRegistered(newClassID)) { + oldClassID = registrar.contractIDToCID(CONTRACT_ID); + oldFactory = Cm.getClassObject(Cc[CONTRACT_ID], Ci.nsIFactory); + registrar.unregisterFactory(oldClassID, oldFactory); + registrar.registerFactory(newClassID, "", CONTRACT_ID, newFactory); + } + }, + reset: function() { this.appendFilterCallback = null; this.appendFiltersCallback = null; this.displayDirectory = null; this.filterIndex = 0; this.mode = null; this.returnFiles = []; this.returnValue = null; this.showCallback = null; this.shown = false; - if (!registrar.isCIDRegistered(CLASS_ID)) - registrar.registerFactory(CLASS_ID, "", CONTRACT_ID, MockFilePickerFactory); + }, + + cleanup: function() { + this.reset(); + if (oldFactory) { + registrar.unregisterFactory(newClassID, newFactory); + registrar.registerFactory(oldClassID, "", CONTRACT_ID, oldFactory); + } }, useAnyFile: function() { var file = FileUtils.getFile("TmpD", ["testfile"]); file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0644); this.returnFiles = [file]; } }; @@ -126,13 +154,16 @@ MockFilePickerInstance.prototype = { getNext: function() { return MockFilePicker.returnFiles[this.index++]; } }; }, show: function() { MockFilePicker.displayDirectory = this.displayDirectory; MockFilePicker.shown = true; - if (typeof MockFilePicker.showCallback == "function") - MockFilePicker.showCallback(this); + if (typeof MockFilePicker.showCallback == "function") { + var returnValue = MockFilePicker.showCallback(this); + if (typeof returnValue != "undefined") + return returnValue; + } return MockFilePicker.returnValue; } };
--- a/testing/mozbase/README +++ b/testing/mozbase/README @@ -1,7 +1,10 @@ -This is the git repo for the mozbase suite of python utilities. +This is the git repo for the Mozilla mozbase suite of python utilities. -Learn more about mozbase here: https://wiki.mozilla.org/Auto-tools/Projects/MozBase +Learn more about mozbase here: +https://wiki.mozilla.org/Auto-tools/Projects/MozBase -Bugs live at https://bugzilla.mozilla.org/buglist.cgi?resolution=---&component=Mozbase&product=Testing and https://bugzilla.mozilla.org/buglist.cgi?resolution=---&status_whiteboard_type=allwordssubstr&query_format=advanced&status_whiteboard=mozbase +Bugs live at +https://bugzilla.mozilla.org/buglist.cgi?resolution=---&component=Mozbase&product=Testing and https://bugzilla.mozilla.org/buglist.cgi?resolution=---&status_whiteboard_type=allwordssubstr&query_format=advanced&status_whiteboard=mozbase -To file a bug, go to https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=Mozbase +To file a bug, go to +https://bugzilla.mozilla.org/enter_bug.cgi?product=Testing&component=Mozbase
new file mode 100644 --- /dev/null +++ b/testing/mozbase/docs.manifest @@ -0,0 +1,9 @@ +#!/usr/bin/env document-it +# documentation manifest for the Mozbase repo +# To generate HTML from this markdown, use document_it: +# http://pypi.python.org/pypi/document_it + +mozinfo/README.md en/Mozinfo +mozprocess/README.md en/Mozprocess +mozprofile/README.md en/Mozprofile +mozrunner/README.md en/Mozrunner
--- a/testing/mozbase/manifestdestiny/README.md +++ b/testing/mozbase/manifestdestiny/README.md @@ -5,21 +5,21 @@ Universal manifests for Mozilla test har What ManifestDestiny gives you: * manifests are ordered lists of tests * tests may have an arbitrary number of key, value pairs * the parser returns an ordered list of test data structures, which are just dicts with some keys. For example, a test with no user-specified metadata looks like this: - [{'path': - '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js', - 'name': 'testToolbar/testBackForwardButtons.js', 'here': - '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests', - 'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}] + [{'path': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests/testToolbar/testBackForwardButtons.js', + 'name': 'testToolbar/testBackForwardButtons.js', 'here': + '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests', + 'manifest': '/home/jhammel/mozmill/src/ManifestDestiny/manifestdestiny/tests',}] The keys displayed here (path, name, here, and manifest) are reserved keys for ManifestDestiny and any consuming APIs. You can add additional key, value metadata to each test. # Why have test manifests?
--- a/testing/mozbase/mozinstall/mozinstall/mozinstall.py +++ b/testing/mozbase/mozinstall/mozinstall/mozinstall.py @@ -165,22 +165,28 @@ def _install_dmg(src, dest): for data in proc.communicate()[0].split(): if data.find("/Volumes/") != -1: appDir = data break for appFile in os.listdir(appDir): if appFile.endswith(".app"): appName = appFile break - subprocess.call("cp -r " + os.path.join(appDir, appName) + " " + dest, + + dest = os.path.join(dest, appName) + assert not os.path.isfile(dest) + if not os.path.isdir(dest): + os.makedirs(dest) + subprocess.call("cp -r " + + os.path.join(appDir,appName, "*") + " " + dest, shell=True) finally: subprocess.call("hdiutil detach " + appDir + " -quiet", shell=True) - return os.path.join(dest, appName) + return dest def _install_exe(src, dest): # possibly gets around UAC in vista (still need to run as administrator) os.environ['__compat_layer'] = "RunAsInvoker" cmd = [src, "/S", "/D=" + os.path.realpath(dest)] subprocess.call(cmd) return dest
--- a/testing/mozbase/mozprocess/mozprocess/processhandler.py +++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py @@ -41,16 +41,17 @@ import logging import mozinfo import os import select import signal import subprocess import sys import threading import time +import traceback from Queue import Queue from datetime import datetime, timedelta __all__ = ['ProcessHandlerMixin', 'ProcessHandler'] if mozinfo.isWin: import ctypes, ctypes.wintypes, msvcrt from ctypes import sizeof, addressof, c_ulong, byref, POINTER, WinError @@ -106,17 +107,20 @@ class ProcessHandlerMixin(object): universal_newlines, startupinfo, creationflags) except OSError, e: print >> sys.stderr, args raise def __del__(self, _maxint=sys.maxint): if mozinfo.isWin: if self._handle: - self._internal_poll(_deadstate=_maxint) + if hasattr(self, '_internal_poll'): + self._internal_poll(_deadstate=_maxint) + else: + self.poll(_deadstate=sys.maxint) if self._handle or self._job or self._io_port: self._cleanup() else: subprocess.Popen.__del__(self) def kill(self): self.returncode = 0 if mozinfo.isWin: @@ -252,23 +256,25 @@ class ProcessHandlerMixin(object): # because it handles errors more gracefully than event or condition. self._process_events = Queue() # Spin up our thread for managing the IO Completion Port self._procmgrthread = threading.Thread(target = self._procmgr) except: print >> sys.stderr, """Exception trying to use job objects; falling back to not using job objects for managing child processes""" + tb = traceback.format_exc() + print >> sys.stderr, tb # Ensure no dangling handles left behind self._cleanup_job_io_port() else: self._job = None winprocess.ResumeThread(int(ht)) - if self._procmgrthread: + if getattr(self, '_procmgrthread', None): self._procmgrthread.start() ht.Close() for i in (p2cread, c2pwrite, errwrite): if i is not None: i.Close() # Windows Process Manager - watches the IO Completion Port and @@ -368,17 +374,23 @@ falling back to not using job objects fo # First, check to see if the process is still running if self._handle: self.returncode = winprocess.GetExitCodeProcess(self._handle) else: # Dude, the process is like totally dead! return self.returncode - if self._job and self._procmgrthread.is_alive(): + # Python 2.5 uses isAlive versus is_alive use the proper one + threadalive = False + if hasattr(self._procmgrthread, 'is_alive'): + threadalive = self._procmgrthread.is_alive() + else: + threadalive = self._procmgrthread.isAlive() + if self._job and threadalive: # Then we are managing with IO Completion Ports # wait on a signal so we know when we have seen the last # process come through. # We use queues to synchronize between the thread and this # function because events just didn't have robust enough error # handling on pre-2.7 versions try: # timeout is the max amount of time the procmgr thread will wait for @@ -424,31 +436,31 @@ falling back to not using job objects fo self._cleanup() return self.returncode def _cleanup_job_io_port(self): """ Do the job and IO port cleanup separately because there are cases where we want to clean these without killing _handle (i.e. if we fail to create the job object in the first place) """ - if self._job and self._job != winprocess.INVALID_HANDLE_VALUE: + if getattr(self, '_job') and self._job != winprocess.INVALID_HANDLE_VALUE: self._job.Close() self._job = None else: # If windows already freed our handle just set it to none # (saw this intermittently while testing) self._job = None - if self._io_port and self._io_port != winprocess.INVALID_HANDLE_VALUE: + if getattr(self, '_io_port', None) and self._io_port != winprocess.INVALID_HANDLE_VALUE: self._io_port.Close() self._io_port = None else: self._io_port = None - if self._procmgrthread: + if getattr(self, '_procmgrthread', None): self._procmgrthread = None def _cleanup(self): self._cleanup_job_io_port() if self._thread and self._thread != winprocess.INVALID_HANDLE_VALUE: self._thread.Close() self._thread = None else:
--- a/testing/mozbase/mozprocess/mozprocess/winprocess.py +++ b/testing/mozbase/mozprocess/mozprocess/winprocess.py @@ -250,17 +250,17 @@ GetQueuedCompletionStatus = GetQueuedCom # Note that the completion key is just a number, not a pointer. CreateIoCompletionPortProto = WINFUNCTYPE(HANDLE, # Return Type HANDLE, # File Handle HANDLE, # Existing Completion Port c_ulong, # Completion Key DWORD # Number of Threads ) CreateIoCompletionPortFlags = ((1, "FileHandle", INVALID_HANDLE_VALUE), - (1, "ExistingCompletionPort", None), + (1, "ExistingCompletionPort", 0), (1, "CompletionKey", c_ulong(0)), (1, "NumberOfConcurrentThreads", 0)) CreateIoCompletionPort = CreateIoCompletionPortProto(("CreateIoCompletionPort", windll.kernel32), CreateIoCompletionPortFlags) CreateIoCompletionPort.errcheck = ErrCheckHandle # SetInformationJobObject
--- a/testing/mozbase/mozprocess/setup.py +++ b/testing/mozbase/mozprocess/setup.py @@ -51,18 +51,18 @@ except (OSError, IOError): setup(name='mozprocess', version=version, description="Mozilla-authored process handling", long_description=description, classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers keywords='', author='Mozilla Automation and Testing Team', - author_email='mozmill-dev@googlegroups.com', - url='http://github.com/mozautomation/mozmill', + author_email='tools@lists.mozilla.com', + url='https://github.com/mozilla/mozbase/tree/master/mozprocess', license='MPL', packages=find_packages(exclude=['ez_setup', 'examples', 'tests']), include_package_data=True, zip_safe=False, install_requires=['mozinfo'], entry_points=""" # -*- Entry points: -*- """,
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/Makefile @@ -0,0 +1,57 @@ +# +# proclaunch tests Makefile +# +UNAME := $(shell uname -s) +ifeq ($(UNAME), MINGW32_NT-6.1) +WIN32 = 1 +endif +ifeq ($(UNAME), MINGW32_NT-5.1) +WIN32 = 1 +endif + +ifeq ($(WIN32), 1) +CC = cl +LINK = link +CFLAGS = //Od //I "iniparser" //D "WIN32" //D "_WIN32" //D "_DEBUG" //D "_CONSOLE" //D "_UNICODE" //D "UNICODE" //Gm //EHsc //RTC1 //MDd //W3 //nologo //c //ZI //TC +LFLAGS = //OUT:"proclaunch.exe" //INCREMENTAL //LIBPATH:"iniparser\\" //NOLOGO //DEBUG //SUBSYSTEM:CONSOLE //DYNAMICBASE //NXCOMPAT //MACHINE:X86 //ERRORREPORT:PROMPT iniparser.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib +RM = rm -f + +default: all +all: iniparser proclaunch + +iniparser: + $(MAKE) -C iniparser + +proclaunch.obj: proclaunch.c + $(CC) $(CFLAGS) proclaunch.c + +proclaunch: proclaunch.obj + $(LINK) $(LFLAGS) proclaunch.obj + +else +CC = gcc +ifeq ($(UNAME), Linux) +CFLAGS = -g -v -Iiniparser +else +CFLAGS = -g -v -arch i386 -Iiniparser +endif + +LFLAGS = -L.. -liniparser +AR = ar +ARFLAGS = rcv +RM = rm -f + + +default: all + +all: libiniparser.a proclaunch + +libiniparser.a: + $(MAKE) -C iniparser + +proclaunch: proclaunch.c + $(CC) $(CFLAGS) -o proclaunch proclaunch.c -Iiniparser -Liniparser -liniparser + +clean veryclean: + $(RM) proclaunch +endif
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/AUTHORS @@ -0,0 +1,6 @@ +Author: Nicolas Devillard <ndevilla@free.fr> + +This tiny library has received countless contributions and I have +not kept track of all the people who contributed. Let them be thanked +for their ideas, code, suggestions, corrections, enhancements! +
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/INSTALL @@ -0,0 +1,15 @@ + +iniParser installation instructions +----------------------------------- + +- Modify the Makefile to suit your environment. +- Type 'make' to make the library. +- Type 'make check' to make the test program. +- Type 'test/iniexample' to launch the test program. +- Type 'test/parse' to launch torture tests. + + + +Enjoy! +N. Devillard +Wed Mar 2 21:14:17 CET 2011
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2000-2011 by Nicolas Devillard. +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/Makefile @@ -0,0 +1,118 @@ +# +# iniparser Makefile +# +UNAME := $(shell uname -s) +ifeq ($(UNAME), MINGW32_NT-6.1) +WIN32 = 1 +endif +ifeq ($(UNAME), MINGW32_NT-5.1) +WIN32 = 1 +endif + +ifeq ($(UNAME), Linux) + # Compiler settings + CC = gcc + # Ar settings to build the library + AR = ar + ARFLAGS = rcv + SHLD = ${CC} ${CFLAGS} + CFLAGS = -O2 -fPIC -Wall -ansi -pedantic + LDSHFLAGS = -shared -Wl,-Bsymbolic -Wl,-rpath -Wl,/usr/lib -Wl,-rpath,/usr/lib + LDFLAGS = -Wl,-rpath -Wl,/usr/lib -Wl,-rpath,/usr/lib +endif + +ifeq ($(UNAME), Darwin) + # Compiler settings + CC = gcc + # Ar settings to build the library + AR = ar + ARFLAGS = rcv + #SHLD = ${CC} ${CFLAGS} + SHLD = libtool + CFLAGS = -v -arch i386 -isysroot /Developer/SDKs/MacOSX10.6.sdk -fPIC -Wall -ansi -pedantic + LDFLAGS = -arch_only i386 +endif + +ifeq ($(WIN32), 1) + CC = cl + CFLAGS = //Od //D "_WIN32" //D "WIN32" //D "_CONSOLE" //D "_CRT_SECURE_NO_WARNINGS" //D "_UNICODE" //D "UNICODE" //Gm //EHsc //RTC1 //MDd //W3 //nologo //c //ZI //TC + LDFLAGS = //OUT:"iniparser.lib" //NOLOGO + LINK = lib +endif + +ifeq ($(WIN32), 1) +SUFFIXES = .obj .c .h .lib + +COMPILE.c=$(CC) $(CFLAGS) -c + +#.c.obj: +# @(echo "compiling $< ...") +# @($(COMPILE.c) $@ $<) + +all: iniparser.obj dictionary.obj iniparser.lib + +SRCS = iniparser.c \ + dictionary.c +OBJS = $(SRCS:.c=.obj) + +iniparser.obj: dictionary.obj + @($(CC) $(CFLAGS) iniparser.c) + +dictionary.obj: + @($(CC) $(CFLAGS) dictionary.c) + +iniparser.lib: dictionary.obj iniparser.obj + @(echo "linking $(OBJS)") + @($(LINK) $(LDFLAGS) $(OBJS)) + +else +# Set RANLIB to ranlib on systems that require it (Sun OS < 4, Mac OSX) +# RANLIB = ranlib +RANLIB = true + +RM = rm -f + +# Implicit rules + +SUFFIXES = .o .c .h .a .so .sl + +COMPILE.c=$(CC) $(CFLAGS) -c +.c.o: + @(echo "compiling $< ...") + @($(COMPILE.c) -o $@ $<) + + +SRCS = iniparser.c \ + dictionary.c + +OBJS = $(SRCS:.c=.o) + + +default: libiniparser.a libiniparser.so + +libiniparser.a: $(OBJS) + @($(AR) $(ARFLAGS) libiniparser.a $(OBJS)) + @($(RANLIB) libiniparser.a) + +ifeq ($(UNAME), Linux) +libiniparser.so: $(OBJS) + @$(SHLD) $(LDSHFLAGS) -o $@.0 $(OBJS) $(LDFLAGS) +else +libiniparser.so: $(OBJS) + @$(SHLD) -o $@.0 $(LDFLAGS) $(OBJS) +endif +endif + +clean: + $(RM) $(OBJS) + +veryclean: + $(RM) $(OBJS) libiniparser.a libiniparser.so* + rm -rf ./html ; mkdir html + cd test ; $(MAKE) veryclean + +docs: + @(cd doc ; $(MAKE)) + +check: + @(cd test ; $(MAKE))
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/README @@ -0,0 +1,12 @@ + +Welcome to iniParser -- version 3.0 +released 02 Mar 2011 + +This modules offers parsing of ini files from the C level. +See a complete documentation in HTML format, from this directory +open the file html/index.html with any HTML-capable browser. + +Enjoy! + +N.Devillard +Wed Mar 2 21:46:14 CET 2011
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/dictionary.c @@ -0,0 +1,407 @@ +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.c + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.27 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.c,v 1.27 2007-11-23 21:39:18 ndevilla Exp $ + $Revision: 1.27 $ +*/ +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ +#include "dictionary.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#endif + +/** Maximum value size for integers and doubles. */ +#define MAXVALSZ 1024 + +/** Minimal allocated number of entries in a dictionary */ +#define DICTMINSZ 128 + +/** Invalid key token */ +#define DICT_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private functions + ---------------------------------------------------------------------------*/ + +/* Doubles the allocated size associated to a pointer */ +/* 'size' is the current allocated size. */ +static void * mem_double(void * ptr, int size) +{ + void * newptr ; + + newptr = calloc(2*size, 1); + if (newptr==NULL) { + return NULL ; + } + memcpy(newptr, ptr, size); + free(ptr); + return newptr ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Duplicate a string + @param s String to duplicate + @return Pointer to a newly allocated string, to be freed with free() + + This is a replacement for strdup(). This implementation is provided + for systems that do not have it. + */ +/*--------------------------------------------------------------------------*/ +static char * xstrdup(char * s) +{ + char * t ; + if (!s) + return NULL ; + t = malloc(strlen(s)+1) ; + if (t) { + strcpy(t,s); + } + return t ; +} + +/*--------------------------------------------------------------------------- + Function codes + ---------------------------------------------------------------------------*/ +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(char * key) +{ + int len ; + unsigned hash ; + int i ; + + len = strlen(key); + for (hash=0, i=0 ; i<len ; i++) { + hash += (unsigned)key[i] ; + hash += (hash<<10); + hash ^= (hash>>6) ; + } + hash += (hash <<3); + hash ^= (hash >>11); + hash += (hash <<15); + return hash ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size) +{ + dictionary * d ; + + /* If no size was specified, allocate space for DICTMINSZ */ + if (size<DICTMINSZ) size=DICTMINSZ ; + + if (!(d = (dictionary *)calloc(1, sizeof(dictionary)))) { + return NULL; + } + d->size = size ; + d->val = (char **)calloc(size, sizeof(char*)); + d->key = (char **)calloc(size, sizeof(char*)); + d->hash = (unsigned int *)calloc(size, sizeof(unsigned)); + return d ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * d) +{ + int i ; + + if (d==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]!=NULL) + free(d->key[i]); + if (d->val[i]!=NULL) + free(d->val[i]); + } + free(d->val); + free(d->key); + free(d->hash); + free(d); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, char * key, char * def) +{ + unsigned hash ; + int i ; + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + return d->val[i] ; + } + } + } + return def ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * d, char * key, char * val) +{ + int i ; + unsigned hash ; + + if (d==NULL || key==NULL) return -1 ; + + /* Compute hash for this key */ + hash = dictionary_hash(key) ; + /* Find if value is already in dictionary */ + if (d->n>0) { + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (hash==d->hash[i]) { /* Same hash value */ + if (!strcmp(key, d->key[i])) { /* Same key */ + /* Found a value: modify and return */ + if (d->val[i]!=NULL) + free(d->val[i]); + d->val[i] = val ? xstrdup(val) : NULL ; + /* Value has been modified: return */ + return 0 ; + } + } + } + } + /* Add a new value */ + /* See if dictionary needs to grow */ + if (d->n==d->size) { + + /* Reached maximum size: reallocate dictionary */ + d->val = (char **)mem_double(d->val, d->size * sizeof(char*)) ; + d->key = (char **)mem_double(d->key, d->size * sizeof(char*)) ; + d->hash = (unsigned int *)mem_double(d->hash, d->size * sizeof(unsigned)) ; + if ((d->val==NULL) || (d->key==NULL) || (d->hash==NULL)) { + /* Cannot grow dictionary */ + return -1 ; + } + /* Double size */ + d->size *= 2 ; + } + + /* Insert key in the first empty slot */ + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) { + /* Add key here */ + break ; + } + } + /* Copy key */ + d->key[i] = xstrdup(key); + d->val[i] = val ? xstrdup(val) : NULL ; + d->hash[i] = hash; + d->n ++ ; + return 0 ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, char * key) +{ + unsigned hash ; + int i ; + + if (key == NULL) { + return; + } + + hash = dictionary_hash(key); + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + /* Compare hash */ + if (hash==d->hash[i]) { + /* Compare string, to avoid hash collisions */ + if (!strcmp(key, d->key[i])) { + /* Found key */ + break ; + } + } + } + if (i>=d->size) + /* Key not found */ + return ; + + free(d->key[i]); + d->key[i] = NULL ; + if (d->val[i]!=NULL) { + free(d->val[i]); + d->val[i] = NULL ; + } + d->hash[i] = 0 ; + d->n -- ; + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out) +{ + int i ; + + if (d==NULL || out==NULL) return ; + if (d->n<1) { + fprintf(out, "empty dictionary\n"); + return ; + } + for (i=0 ; i<d->size ; i++) { + if (d->key[i]) { + fprintf(out, "%20s\t[%s]\n", + d->key[i], + d->val[i] ? d->val[i] : "UNDEF"); + } + } + return ; +} + + +/* Test code */ +#ifdef TESTDIC +#define NVALS 20000 +int main(int argc, char *argv[]) +{ + dictionary * d ; + char * val ; + int i ; + char cval[90] ; + + /* Allocate dictionary */ + printf("allocating...\n"); + d = dictionary_new(0); + + /* Set values in dictionary */ + printf("setting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + dictionary_set(d, cval, "salut"); + } + printf("getting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + val = dictionary_get(d, cval, DICT_INVALID_KEY); + if (val==DICT_INVALID_KEY) { + printf("cannot get value for key [%s]\n", cval); + } + } + printf("unsetting %d values...\n", NVALS); + for (i=0 ; i<NVALS ; i++) { + sprintf(cval, "%04d", i); + dictionary_unset(d, cval); + } + if (d->n != 0) { + printf("error deleting values\n"); + } + printf("deallocating...\n"); + dictionary_del(d); + return 0 ; +} +#endif +/* vim: set ts=4 et sw=4 tw=75 */
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/dictionary.h @@ -0,0 +1,176 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file dictionary.h + @author N. Devillard + @date Sep 2007 + @version $Revision: 1.12 $ + @brief Implements a dictionary for string variables. + + This module implements a simple dictionary object, i.e. a list + of string/string associations. This object is useful to store e.g. + informations retrieved from a configuration file (ini files). +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: dictionary.h,v 1.12 2007-11-23 21:37:00 ndevilla Exp $ + $Author: ndevilla $ + $Date: 2007-11-23 21:37:00 $ + $Revision: 1.12 $ +*/ + +#ifndef _DICTIONARY_H_ +#define _DICTIONARY_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifndef _WIN32 +#include <unistd.h> +#endif + +/*--------------------------------------------------------------------------- + New types + ---------------------------------------------------------------------------*/ + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dictionary object + + This object contains a list of string/string associations. Each + association is identified by a unique string key. Looking up values + in the dictionary is speeded up by the use of a (hopefully collision-free) + hash function. + */ +/*-------------------------------------------------------------------------*/ +typedef struct _dictionary_ { + int n ; /** Number of entries in dictionary */ + int size ; /** Storage size */ + char ** val ; /** List of string values */ + char ** key ; /** List of string keys */ + unsigned * hash ; /** List of hash values for keys */ +} dictionary ; + + +/*--------------------------------------------------------------------------- + Function prototypes + ---------------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------------*/ +/** + @brief Compute the hash key for a string. + @param key Character string to use for key. + @return 1 unsigned int on at least 32 bits. + + This hash function has been taken from an Article in Dr Dobbs Journal. + This is normally a collision-free function, distributing keys evenly. + The key is stored anyway in the struct so that collision can be avoided + by comparing the key itself in last resort. + */ +/*--------------------------------------------------------------------------*/ +unsigned dictionary_hash(char * key); + +/*-------------------------------------------------------------------------*/ +/** + @brief Create a new dictionary object. + @param size Optional initial size of the dictionary. + @return 1 newly allocated dictionary objet. + + This function allocates a new dictionary object of given size and returns + it. If you do not know in advance (roughly) the number of entries in the + dictionary, give size=0. + */ +/*--------------------------------------------------------------------------*/ +dictionary * dictionary_new(int size); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a dictionary object + @param d dictionary object to deallocate. + @return void + + Deallocate a dictionary object and all memory associated to it. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_del(dictionary * vd); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get a value from a dictionary. + @param d dictionary object to search. + @param key Key to look for in the dictionary. + @param def Default value to return if key not found. + @return 1 pointer to internally allocated character string. + + This function locates a key in a dictionary and returns a pointer to its + value, or the passed 'def' pointer if no such key can be found in + dictionary. The returned character pointer points to data internal to the + dictionary object, you should not try to free it or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * dictionary_get(dictionary * d, char * key, char * def); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set a value in a dictionary. + @param d dictionary object to modify. + @param key Key to modify or add. + @param val Value to add. + @return int 0 if Ok, anything else otherwise + + If the given key is found in the dictionary, the associated value is + replaced by the provided one. If the key cannot be found in the + dictionary, it is added to it. + + It is Ok to provide a NULL value for val, but NULL values for the dictionary + or the key are considered as errors: the function will return immediately + in such a case. + + Notice that if you dictionary_set a variable to NULL, a call to + dictionary_get will return a NULL value: the variable will be found, and + its value (NULL) is returned. In other words, setting the variable + content to NULL is equivalent to deleting the variable from the + dictionary. It is not possible (in this implementation) to have a key in + the dictionary without value. + + This function returns non-zero in case of failure. + */ +/*--------------------------------------------------------------------------*/ +int dictionary_set(dictionary * vd, char * key, char * val); + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete a key in a dictionary + @param d dictionary object to modify. + @param key Key to remove. + @return void + + This function deletes a key in a dictionary. Nothing is done if the + key cannot be found. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_unset(dictionary * d, char * key); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump + @param f Opened file pointer. + @return void + + Dumps a dictionary onto an opened file pointer. Key pairs are printed out + as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as + output file pointers. + */ +/*--------------------------------------------------------------------------*/ +void dictionary_dump(dictionary * d, FILE * out); + +#endif
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/iniparser.c @@ -0,0 +1,648 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.c + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ +/* + $Id: iniparser.c,v 2.19 2011-03-02 20:15:13 ndevilla Exp $ + $Revision: 2.19 $ + $Date: 2011-03-02 20:15:13 $ +*/ +/*---------------------------- Includes ------------------------------------*/ +#include <ctype.h> +#include "iniparser.h" + +/*---------------------------- Defines -------------------------------------*/ +#define ASCIILINESZ (1024) +#define INI_INVALID_KEY ((char*)-1) + +/*--------------------------------------------------------------------------- + Private to this module + ---------------------------------------------------------------------------*/ +/** + * This enum stores the status for each parsed line (internal use only). + */ +typedef enum _line_status_ { + LINE_UNPROCESSED, + LINE_ERROR, + LINE_EMPTY, + LINE_COMMENT, + LINE_SECTION, + LINE_VALUE +} line_status ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Convert a string to lowercase. + @param s String to convert. + @return ptr to statically allocated string. + + This function returns a pointer to a statically allocated string + containing a lowercased version of the input string. Do not free + or modify the returned string! Since the returned string is statically + allocated, it will be modified at each function call (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strlwc(char * s) +{ + static char l[ASCIILINESZ+1]; + int i ; + + if (s==NULL) return NULL ; + memset(l, 0, ASCIILINESZ+1); + i=0 ; + while (s[i] && i<ASCIILINESZ) { + l[i] = (char)tolower((int)s[i]); + i++ ; + } + l[ASCIILINESZ]=(char)0; + return l ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Remove blanks at the beginning and the end of a string. + @param s String to parse. + @return ptr to statically allocated string. + + This function returns a pointer to a statically allocated string, + which is identical to the input string, except that all blank + characters at the end and the beg. of the string have been removed. + Do not free or modify the returned string! Since the returned string + is statically allocated, it will be modified at each function call + (not re-entrant). + */ +/*--------------------------------------------------------------------------*/ +static char * strstrip(char * s) +{ + static char l[ASCIILINESZ+1]; + char * last ; + + if (s==NULL) return NULL ; + + while (isspace((int)*s) && *s) s++; + memset(l, 0, ASCIILINESZ+1); + strcpy(l, s); + last = l + strlen(l); + while (last > l) { + if (!isspace((int)*(last-1))) + break ; + last -- ; + } + *last = (char)0; + return (char*)l ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getnsec(dictionary * d) +{ + int i ; + int nsec ; + + if (d==NULL) return -1 ; + nsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + nsec ++ ; + } + } + return nsec ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getsecname(dictionary * d, int n) +{ + int i ; + int foundsec ; + + if (d==NULL || n<0) return NULL ; + foundsec=0 ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (strchr(d->key[i], ':')==NULL) { + foundsec++ ; + if (foundsec>n) + break ; + } + } + if (foundsec<=n) { + return NULL ; + } + return d->key[i] ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f) +{ + int i ; + + if (d==NULL || f==NULL) return ; + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + if (d->val[i]!=NULL) { + fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]); + } else { + fprintf(f, "[%s]=UNDEF\n", d->key[i]); + } + } + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump_ini(dictionary * d, FILE * f) +{ + int i, j ; + char keym[ASCIILINESZ+1]; + int nsec ; + char * secname ; + int seclen ; + + if (d==NULL || f==NULL) return ; + + nsec = iniparser_getnsec(d); + if (nsec<1) { + /* No section in file: dump all keys as they are */ + for (i=0 ; i<d->size ; i++) { + if (d->key[i]==NULL) + continue ; + fprintf(f, "%s = %s\n", d->key[i], d->val[i]); + } + return ; + } + for (i=0 ; i<nsec ; i++) { + secname = iniparser_getsecname(d, i) ; + seclen = (int)strlen(secname); + fprintf(f, "\n[%s]\n", secname); + sprintf(keym, "%s:", secname); + for (j=0 ; j<d->size ; j++) { + if (d->key[j]==NULL) + continue ; + if (!strncmp(d->key[j], keym, seclen+1)) { + fprintf(f, + "%-30s = %s\n", + d->key[j]+seclen+1, + d->val[j] ? d->val[j] : ""); + } + } + } + fprintf(f, "\n"); + return ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, char * key, char * def) +{ + char * lc_key ; + char * sval ; + + if (d==NULL || key==NULL) + return def ; + + lc_key = strlwc(key); + sval = dictionary_get(d, lc_key, def); + return sval ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + "42" -> 42 + "042" -> 34 (octal -> decimal) + "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, char * key, int notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return (int)strtol(str, NULL, 0); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, char * key, double notfound) +{ + char * str ; + + str = iniparser_getstring(d, key, INI_INVALID_KEY); + if (str==INI_INVALID_KEY) return notfound ; + return atof(str); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, char * key, int notfound) +{ + char * c ; + int ret ; + + c = iniparser_getstring(d, key, INI_INVALID_KEY); + if (c==INI_INVALID_KEY) return notfound ; + if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') { + ret = 1 ; + } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') { + ret = 0 ; + } else { + ret = notfound ; + } + return ret; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry( + dictionary * ini, + char * entry +) +{ + int found=0 ; + if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) { + found = 1 ; + } + return found ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, char * entry, char * val) +{ + return dictionary_set(ini, strlwc(entry), val) ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, char * entry) +{ + dictionary_unset(ini, strlwc(entry)); +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Load a single line from an INI file + @param input_line Input line, may be concatenated multi-line input + @param section Output space to store section + @param key Output space to store key + @param value Output space to store value + @return line_status value + */ +/*--------------------------------------------------------------------------*/ +static line_status iniparser_line( + char * input_line, + char * section, + char * key, + char * value) +{ + line_status sta ; + char line[ASCIILINESZ+1]; + int len ; + + strcpy(line, strstrip(input_line)); + len = (int)strlen(line); + + sta = LINE_UNPROCESSED ; + if (len<1) { + /* Empty line */ + sta = LINE_EMPTY ; + } else if (line[0]=='#' || line[0]==';') { + /* Comment line */ + sta = LINE_COMMENT ; + } else if (line[0]=='[' && line[len-1]==']') { + /* Section name */ + sscanf(line, "[%[^]]", section); + strcpy(section, strstrip(section)); + strcpy(section, strlwc(section)); + sta = LINE_SECTION ; + } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2 + || sscanf (line, "%[^=] = '%[^\']'", key, value) == 2 + || sscanf (line, "%[^=] = %[^;#]", key, value) == 2) { + /* Usual key=value, with or without comments */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + strcpy(value, strstrip(value)); + /* + * sscanf cannot handle '' or "" as empty values + * this is done here + */ + if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) { + value[0]=0 ; + } + sta = LINE_VALUE ; + } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2 + || sscanf(line, "%[^=] %[=]", key, value) == 2) { + /* + * Special cases: + * key= + * key=; + * key=# + */ + strcpy(key, strstrip(key)); + strcpy(key, strlwc(key)); + value[0]=0 ; + sta = LINE_VALUE ; + } else { + /* Generate syntax error */ + sta = LINE_ERROR ; + } + return sta ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(char * ininame) +{ + FILE * in ; + + char line [ASCIILINESZ+1] ; + char section [ASCIILINESZ+1] ; + char key [ASCIILINESZ+1] ; + char tmp [ASCIILINESZ+1] ; + char val [ASCIILINESZ+1] ; + + int last=0 ; + int len ; + int lineno=0 ; + int errs=0; + + dictionary * dict ; + + if ((in=fopen(ininame, "r"))==NULL) { + fprintf(stderr, "iniparser: cannot open %s\n", ininame); + return NULL ; + } + + dict = dictionary_new(0) ; + if (!dict) { + fclose(in); + return NULL ; + } + + memset(line, 0, ASCIILINESZ); + memset(section, 0, ASCIILINESZ); + memset(key, 0, ASCIILINESZ); + memset(val, 0, ASCIILINESZ); + last=0 ; + + while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) { + lineno++ ; + len = (int)strlen(line)-1; + if (len==0) + continue; + /* Safety check against buffer overflows */ + if (line[len]!='\n') { + fprintf(stderr, + "iniparser: input line too long in %s (%d)\n", + ininame, + lineno); + dictionary_del(dict); + fclose(in); + return NULL ; + } + /* Get rid of \n and spaces at end of line */ + while ((len>=0) && + ((line[len]=='\n') || (isspace(line[len])))) { + line[len]=0 ; + len-- ; + } + /* Detect multi-line */ + if (line[len]=='\\') { + /* Multi-line value */ + last=len ; + continue ; + } else { + last=0 ; + } + switch (iniparser_line(line, section, key, val)) { + case LINE_EMPTY: + case LINE_COMMENT: + break ; + + case LINE_SECTION: + errs = dictionary_set(dict, section, NULL); + break ; + + case LINE_VALUE: + sprintf(tmp, "%s:%s", section, key); + errs = dictionary_set(dict, tmp, val) ; + break ; + + case LINE_ERROR: + fprintf(stderr, "iniparser: syntax error in %s (%d):\n", + ininame, + lineno); + fprintf(stderr, "-> %s\n", line); + errs++ ; + break; + + default: + break ; + } + memset(line, 0, ASCIILINESZ); + last=0; + if (errs<0) { + fprintf(stderr, "iniparser: memory allocation failure\n"); + break ; + } + } + if (errs) { + dictionary_del(dict); + dict = NULL ; + } + fclose(in); + return dict ; +} + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d) +{ + dictionary_del(d); +} + +/* vim: set ts=4 et sw=4 tw=75 */
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/iniparser/iniparser.h @@ -0,0 +1,273 @@ + +/*-------------------------------------------------------------------------*/ +/** + @file iniparser.h + @author N. Devillard + @date Sep 2007 + @version 3.0 + @brief Parser for ini files. +*/ +/*--------------------------------------------------------------------------*/ + +/* + $Id: iniparser.h,v 1.26 2011-03-02 20:15:13 ndevilla Exp $ + $Revision: 1.26 $ +*/ + +#ifndef _INIPARSER_H_ +#define _INIPARSER_H_ + +/*--------------------------------------------------------------------------- + Includes + ---------------------------------------------------------------------------*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * The following #include is necessary on many Unixes but not Linux. + * It is not needed for Windows platforms. + * Uncomment it if needed. + */ +/* #include <unistd.h> */ + +#include "dictionary.h" + +/*-------------------------------------------------------------------------*/ +/** + @brief Get number of sections in a dictionary + @param d Dictionary to examine + @return int Number of sections found in dictionary + + This function returns the number of sections found in a dictionary. + The test to recognize sections is done on the string stored in the + dictionary: a section name is given as "section" whereas a key is + stored as "section:key", thus the test looks for entries that do not + contain a colon. + + This clearly fails in the case a section name contains a colon, but + this should simply be avoided. + + This function returns -1 in case of error. + */ +/*--------------------------------------------------------------------------*/ + +int iniparser_getnsec(dictionary * d); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Get name for section n in a dictionary. + @param d Dictionary to examine + @param n Section number (from 0 to nsec-1). + @return Pointer to char string + + This function locates the n-th section in a dictionary and returns + its name as a pointer to a string statically allocated inside the + dictionary. Do not free or modify the returned string! + + This function returns NULL in case of error. + */ +/*--------------------------------------------------------------------------*/ + +char * iniparser_getsecname(dictionary * d, int n); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Save a dictionary to a loadable ini file + @param d Dictionary to dump + @param f Opened file pointer to dump to + @return void + + This function dumps a given dictionary into a loadable ini file. + It is Ok to specify @c stderr or @c stdout as output files. + */ +/*--------------------------------------------------------------------------*/ + +void iniparser_dump_ini(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Dump a dictionary to an opened file pointer. + @param d Dictionary to dump. + @param f Opened file pointer to dump to. + @return void + + This function prints out the contents of a dictionary, one element by + line, onto the provided file pointer. It is OK to specify @c stderr + or @c stdout as output files. This function is meant for debugging + purposes mostly. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_dump(dictionary * d, FILE * f); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key + @param d Dictionary to search + @param key Key string to look for + @param def Default value to return if key not found. + @return pointer to statically allocated character string + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the pointer passed as 'def' is returned. + The returned char pointer is pointing to a string allocated in + the dictionary, do not free or modify it. + */ +/*--------------------------------------------------------------------------*/ +char * iniparser_getstring(dictionary * d, char * key, char * def); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to an int + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + Supported values for integers include the usual C notation + so decimal, octal (starting with 0) and hexadecimal (starting with 0x) + are supported. Examples: + + - "42" -> 42 + - "042" -> 34 (octal -> decimal) + - "0x42" -> 66 (hexa -> decimal) + + Warning: the conversion may overflow in various ways. Conversion is + totally outsourced to strtol(), see the associated man page for overflow + handling. + + Credits: Thanks to A. Becker for suggesting strtol() + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getint(dictionary * d, char * key, int notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a double + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return double + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + */ +/*--------------------------------------------------------------------------*/ +double iniparser_getdouble(dictionary * d, char * key, double notfound); + +/*-------------------------------------------------------------------------*/ +/** + @brief Get the string associated to a key, convert to a boolean + @param d Dictionary to search + @param key Key string to look for + @param notfound Value to return in case of error + @return integer + + This function queries a dictionary for a key. A key as read from an + ini file is given as "section:key". If the key cannot be found, + the notfound value is returned. + + A true boolean is found if one of the following is matched: + + - A string starting with 'y' + - A string starting with 'Y' + - A string starting with 't' + - A string starting with 'T' + - A string starting with '1' + + A false boolean is found if one of the following is matched: + + - A string starting with 'n' + - A string starting with 'N' + - A string starting with 'f' + - A string starting with 'F' + - A string starting with '0' + + The notfound value returned if no boolean is identified, does not + necessarily have to be 0 or 1. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_getboolean(dictionary * d, char * key, int notfound); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Set an entry in a dictionary. + @param ini Dictionary to modify. + @param entry Entry to modify (entry name) + @param val New value to associate to the entry. + @return int 0 if Ok, -1 otherwise. + + If the given entry can be found in the dictionary, it is modified to + contain the provided value. If it cannot be found, -1 is returned. + It is Ok to set val to NULL. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_set(dictionary * ini, char * entry, char * val); + + +/*-------------------------------------------------------------------------*/ +/** + @brief Delete an entry in a dictionary + @param ini Dictionary to modify + @param entry Entry to delete (entry name) + @return void + + If the given entry can be found, it is deleted from the dictionary. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_unset(dictionary * ini, char * entry); + +/*-------------------------------------------------------------------------*/ +/** + @brief Finds out if a given entry exists in a dictionary + @param ini Dictionary to search + @param entry Name of the entry to look for + @return integer 1 if entry exists, 0 otherwise + + Finds out if a given entry exists in the dictionary. Since sections + are stored as keys with NULL associated values, this is the only way + of querying for the presence of sections in a dictionary. + */ +/*--------------------------------------------------------------------------*/ +int iniparser_find_entry(dictionary * ini, char * entry) ; + +/*-------------------------------------------------------------------------*/ +/** + @brief Parse an ini file and return an allocated dictionary object + @param ininame Name of the ini file to read. + @return Pointer to newly allocated dictionary + + This is the parser for ini files. This function is called, providing + the name of the file to be read. It returns a dictionary object that + should not be accessed directly, but through accessor functions + instead. + + The returned dictionary must be freed using iniparser_freedict(). + */ +/*--------------------------------------------------------------------------*/ +dictionary * iniparser_load(char * ininame); + +/*-------------------------------------------------------------------------*/ +/** + @brief Free all memory associated to an ini dictionary + @param d Dictionary to free + @return void + + Free all memory associated to an ini dictionary. + It is mandatory to call this function before the dictionary object + gets out of the current context. + */ +/*--------------------------------------------------------------------------*/ +void iniparser_freedict(dictionary * d); + +#endif
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/manifest.ini @@ -0,0 +1,2 @@ +[mozprocess1.py] +[mozprocess2.py] \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/mozprocess1.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Clint Talbert <ctalbert@mozilla.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import os +import subprocess +import sys +import unittest +from time import sleep + +from mozprocess import processhandler + +here = os.path.dirname(os.path.abspath(__file__)) + +def make_proclaunch(aDir): + """ + Makes the proclaunch executable. + Params: + aDir - the directory in which to issue the make commands + Returns: + the path to the proclaunch executable that is generated + """ + p = subprocess.call(["make"], cwd=aDir) + if sys.platform == "win32": + exepath = os.path.join(aDir, "proclaunch.exe") + else: + exepath = os.path.join(aDir, "proclaunch") + return exepath + +def check_for_process(processName): + """ + Use to determine if process of the given name is still running. + + Returns: + detected -- True if process is detected to exist, False otherwise + output -- if process exists, stdout of the process, '' otherwise + """ + output = '' + if sys.platform == "win32": + # On windows we use tasklist + p1 = subprocess.Popen(["tasklist"], stdout=subprocess.PIPE) + output = p1.communicate()[0] + detected = False + for line in output: + if processName in line: + detected = True + break + else: + p1 = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", processName], stdin=p1.stdout, stdout=subprocess.PIPE) + p1.stdout.close() + output = p2.communicate()[0] + detected = False + for line in output: + if "grep %s" % processName in line: + continue + elif processName in line: + detected = True + break + + return detected, output + + +class ProcTest1(unittest.TestCase): + + def __init__(self, *args, **kwargs): + + # Ideally, I'd use setUpClass but that only exists in 2.7. + # So, we'll do this make step now. + self.proclaunch = make_proclaunch(here) + unittest.TestCase.__init__(self, *args, **kwargs) + + def test_process_normal_finish(self): + """Process is started, runs to completion while we wait for it""" + + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], + cwd=here) + p.run() + p.waitForFinish() + + detected, output = check_for_process(self.proclaunch) + self.determine_status(detected, + output, + p.proc.returncode, + p.didTimeout) + + def test_process_waittimeout(self): + """ Process is started, runs but we time out waiting on it + to complete + """ + p = processhandler.ProcessHandler([self.proclaunch, "process_waittimeout.ini"], + cwd=here) + p.run() + p.waitForFinish(timeout=10) + + detected, output = check_for_process(self.proclaunch) + self.determine_status(detected, + output, + p.proc.returncode, + p.didTimeout, + False, + ['returncode', 'didtimeout']) + + def test_process_kill(self): + """ Process is started, we kill it + """ + p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"], + cwd=here) + p.run() + p.kill() + + detected, output = check_for_process(self.proclaunch) + self.determine_status(detected, + output, + p.proc.returncode, + p.didTimeout) + + def determine_status(self, + detected=False, + output='', + returncode=0, + didtimeout=False, + isalive=False, + expectedfail=[]): + """ + Use to determine if the situation has failed. + Parameters: + detected -- value from check_for_process to determine if the process is detected + output -- string of data from detected process, can be '' + returncode -- return code from process, defaults to 0 + didtimeout -- True if process timed out, defaults to False + isalive -- Use True to indicate we pass if the process exists; however, by default + the test will pass if the process does not exist (isalive == False) + expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail + """ + if 'returncode' in expectedfail: + self.assertTrue(returncode, "Detected an expected non-zero return code") + else: + self.assertTrue(returncode == 0, "Detected non-zero return code of: %d" % returncode) + + if 'didtimeout' in expectedfail: + self.assertTrue(didtimeout, "Process timed out as expected") + else: + self.assertTrue(not didtimeout, "Detected that process timed out") + + if detected: + self.assertTrue(isalive, "Detected process is still running, process output: %s" % output) + else: + self.assertTrue(not isalive, "Process ended") + +if __name__ == '__main__': + unittest.main()
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/mozprocess2.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python + +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Clint Talbert <ctalbert@mozilla.com> +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +import os +import subprocess +import sys +import unittest +from time import sleep + +from mozprocess import processhandler + +here = os.path.dirname(os.path.abspath(__file__)) + +# This tests specifically the case reported in bug 671316 +# TODO: Because of the way mutt works we can't just load a utils.py in here. +# so, for all process handler tests, copy these two +# utility functions to to the top of your source. + +def make_proclaunch(aDir): + """ + Makes the proclaunch executable. + Params: + aDir - the directory in which to issue the make commands + Returns: + the path to the proclaunch executable that is generated + """ + p = subprocess.call(["make"], cwd=aDir) + if sys.platform == "win32": + exepath = os.path.join(aDir, "proclaunch.exe") + else: + exepath = os.path.join(aDir, "proclaunch") + return exepath + +def check_for_process(processName): + """ + Use to determine if process is still running. + + Returns: + detected -- True if process is detected to exist, False otherwise + output -- if process exists, stdout of the process, '' otherwise + """ + output = '' + if sys.platform == "win32": + # On windows we use tasklist + p1 = subprocess.Popen(["tasklist"], stdout=subprocess.PIPE) + output = p1.communicate()[0] + detected = False + for line in output: + if processName in line: + detected = True + break + else: + p1 = subprocess.Popen(["ps", "-A"], stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", processName], stdin=p1.stdout, stdout=subprocess.PIPE) + p1.stdout.close() + output = p2.communicate()[0] + detected = False + for line in output: + if "grep %s" % processName in line: + continue + elif processName in line: + detected = True + break + + return detected, output + +class ProcTest2(unittest.TestCase): + + def __init__(self, *args, **kwargs): + + # Ideally, I'd use setUpClass but that only exists in 2.7. + # So, we'll do this make step now. + self.proclaunch = make_proclaunch(here) + unittest.TestCase.__init__(self, *args, **kwargs) + + def test_process_waittimeout(self): + """ Process is started, runs to completion before our wait times out + """ + p = processhandler.ProcessHandler([self.proclaunch, + "process_waittimeout_10s.ini"], + cwd=here) + p.run() + p.waitForFinish(timeout=30) + + detected, output = check_for_process(self.proclaunch) + self.determine_status(detected, + output, + p.proc.returncode, + p.didTimeout) + + def test_process_waitnotimeout(self): + """ Process is started runs to completion while we wait indefinitely + """ + + p = processhandler.ProcessHandler([self.proclaunch, + "process_waittimeout_10s.ini"], + cwd=here) + p.run() + p.waitForFinish() + + detected, output = check_for_process(self.proclaunch) + self.determine_status(detected, + output, + p.proc.returncode, + p.didTimeout) + + def determine_status(self, + detected=False, + output = '', + returncode = 0, + didtimeout = False, + isalive=False, + expectedfail=[]): + """ + Use to determine if the situation has failed. + Parameters: + detected -- value from check_for_process to determine if the process is detected + output -- string of data from detected process, can be '' + returncode -- return code from process, defaults to 0 + didtimeout -- True if process timed out, defaults to False + isalive -- Use True to indicate we pass if the process exists; however, by default + the test will pass if the process does not exist (isalive == False) + expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail + """ + if 'returncode' in expectedfail: + self.assertTrue(returncode, "Detected an expected non-zero return code") + else: + self.assertTrue(returncode == 0, "Detected non-zero return code of: %d" % returncode) + + if 'didtimeout' in expectedfail: + self.assertTrue(didtimeout, "Process timed out as expected") + else: + self.assertTrue(not didtimeout, "Detected that process timed out") + + if detected: + self.assertTrue(isalive, "Detected process is still running, process output: %s" % output) + else: + self.assertTrue(not isalive, "Process ended") + +if __name__ == '__main__': + unittest.main()
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/process_normal_finish.ini @@ -0,0 +1,11 @@ +[main] +children=c1,c2 +maxtime=60 + +[c1] +children=2 +maxtime=60 + +[c2] +children=0 +maxtime=30
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/process_waittimeout.ini @@ -0,0 +1,11 @@ +[main] +children=c1,c2 +maxtime=300 + +[c1] +children=2 +maxtime=300 + +[c2] +children=3 +maxtime=300
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/process_waittimeout_10s.ini @@ -0,0 +1,8 @@ +[main] +children=c1 +maxtime=10 + +[c1] +children=2 +maxtime=5 +
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprocess/tests/proclaunch.c @@ -0,0 +1,189 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Clint Talbert <ctalbert@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include <stdio.h> +#include <stdlib.h> +#include "iniparser.h" + +#ifdef _WIN32 +#include <windows.h> +#include <tchar.h> + +extern int iniparser_getint(dictionary *d, char *key, int notfound); +extern char *iniparser_getstring(dictionary *d, char *key, char *def); + +// This is the windows launcher function +int launchWindows(int children, int maxtime) { + _TCHAR cmdline[50]; + STARTUPINFO startup; + PROCESS_INFORMATION procinfo; + BOOL rv = 0; + + _stprintf(cmdline, _T("proclaunch.exe %d %d"), children, maxtime); + ZeroMemory(&startup, sizeof(STARTUPINFO)); + startup.cb = sizeof(STARTUPINFO); + + ZeroMemory(&procinfo, sizeof(PROCESS_INFORMATION)); + + printf("Launching process!\n"); + rv = CreateProcess(NULL, + cmdline, + NULL, + NULL, + FALSE, + 0, + NULL, + NULL, + &startup, + &procinfo); + + if (!rv) { + DWORD dw = GetLastError(); + printf("error: %d\n", dw); + } + CloseHandle(procinfo.hProcess); + CloseHandle(procinfo.hThread); + return 0; +} +#endif + +int main(int argc, char **argv) { + int children = 0; + int maxtime = 0; + int passedtime = 0; + dictionary *dict = NULL; + + // Command line handling + if (argc == 1 || (0 == strcmp(argv[1], "-h")) || (0 == strcmp(argv[1], "--help"))) { + printf("ProcLauncher takes an ini file. Specify the ini file as the only\n"); + printf("parameter of the command line:\n"); + printf("proclauncher my.ini\n\n"); + printf("The ini file has the form:\n"); + printf("[main]\n"); + printf("children=child1,child2 ; These comma separated values are sections\n"); + printf("maxtime=60 ; Max time this process lives\n"); + printf("[child1] ; Here is a child section\n"); + printf("children=3 ; You can have grandchildren: this spawns 3 of them for child1\n"); + printf("maxtime=30 ; Max time, note it's in seconds. If this time\n"); + printf(" ; is > main:maxtime then the child process will be\n"); + printf(" ; killed when the parent exits. Also, grandchildren\n"); + printf("[child2] ; inherit this maxtime and can't change it.\n"); + printf("maxtime=25 ; You can call these sections whatever you want\n"); + printf("children=0 ; as long as you reference them in a children attribute\n"); + printf("....\n"); + return 0; + } else if (argc == 2) { + // This is ini file mode: + // proclauncher <inifile> + dict = iniparser_load(argv[1]); + + } else if (argc == 3) { + // Then we've been called in child process launching mode: + // proclauncher <children> <maxtime> + children = atoi(argv[1]); + maxtime = atoi(argv[2]); + } + + if (dict) { + /* Dict operation */ + char *childlist = iniparser_getstring(dict, "main:children", NULL); + maxtime = iniparser_getint(dict, (char*)"main:maxtime", 10);; + if (childlist) { + int c = 0, m = 10; + char childkey[50], maxkey[50]; + char cmd[25]; + char *token = strtok(childlist, ","); + + while (token) { + // Reset defaults + memset(childkey, 0, 50); + memset(maxkey, 0, 50); + memset(cmd, 0, 25); + c = 0; + m = 10; + + sprintf(childkey, "%s:children", token); + sprintf(maxkey, "%s:maxtime", token); + c = iniparser_getint(dict, childkey, 0); + m = iniparser_getint(dict, maxkey, 10); + + // Launch the child process + #ifdef _WIN32 + launchWindows(c, m); + #else + sprintf(cmd, "./proclaunch %d %d &", c, m); + system(cmd); + #endif + + // Get the next child entry + token = strtok(NULL, ","); + } + } + iniparser_freedict(dict); + } else { + // Child Process operation - put on your recursive thinking cap + char cmd[25]; + // This is launching grandchildren, there are no great grandchildren, so we + // pass in a 0 for the children to spawn. + #ifdef _WIN32 + while(children > 0) { + launchWindows(0, maxtime); + children--; + } + #else + sprintf(cmd, "./proclaunch %d %d &", 0, maxtime); + printf("Launching child process: %s\n", cmd); + while (children > 0) { + system(cmd); + children--; + } + #endif + } + + /* Now we have launched all the children. Let's wait for max time before returning + This does pseudo busy waiting just to appear active */ + while (passedtime < maxtime) { +#ifdef _WIN32 + Sleep(1000); +#else + sleep(1); +#endif + passedtime++; + } + exit(0); + return 0; +}
--- a/testing/mozbase/mozprofile/README.md +++ b/testing/mozbase/mozprofile/README.md @@ -1,45 +1,106 @@ [Mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile) is a python tool for creating and managing profiles for Mozilla's applications (Firefox, Thunderbird, etc.). In addition to creating profiles, mozprofile can install [addons](https://developer.mozilla.org/en/addons) -and set [preferences](https://developer.mozilla.org/En/A_Brief_Guide_to_Mozilla_Preferences). +and set +[preferences](https://developer.mozilla.org/En/A_Brief_Guide_to_Mozilla_Preferences). Mozprofile can be utilized from the command line or as an API. # Command Line Usage mozprofile may be used to create profiles, set preferences in profiles, or install addons into profiles. The profile to be operated on may be specified with the `--profile` switch. If a profile is not specified, one will be created in a temporary directory which will be echoed to the terminal: - (mozmill)> mozprofile + (mozmill)> mozprofile /tmp/tmp4q1iEU.mozrunner (mozmill)> ls /tmp/tmp4q1iEU.mozrunner user.js To run mozprofile from the command line enter: `mozprofile --help` for a list of options. # API Usage To use mozprofile as an API you can import [mozprofile.profile](https://github.com/mozilla/mozbase/tree/master/mozprofile/mozprofile/profile.py) and/or the -[AddonManager](https://github.com/mozilla/mozbase/tree/master/mozprofile/mozprofile/addons.py). +[AddonManager](https://github.com/mozilla/mozbase/tree/master/mozprofile/mozprofile/addons.py). `mozprofile.profile` features a generic `Profile` class. In addition, subclasses `FirefoxProfile` and `ThundebirdProfile` are available with preset preferences for those applications. +`mozprofile.profile:Profile`: + + def __init__(self, + profile=None, # Path to the profile + addons=None, # String of one or list of addons to install + addon_manifests=None, # Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ + preferences=None, # Dictionary or class of preferences + locations=None, # locations to proxy + proxy=False, # setup a proxy + restore=True # If true remove all installed addons preferences when cleaning up + ): + + def reset(self): + """reset the profile to the beginning state""" + + def set_preferences(self, preferences, filename='user.js'): + """Adds preferences dict to profile preferences""" + + def clean_preferences(self): + """Removed preferences added by mozrunner.""" + + def cleanup(self): + """Cleanup operations for the profile.""" + + +`mozprofile.addons:AddonManager`: + + def __init__(self, profile): + """profile - the path to the profile for which we install addons""" + + def install_addons(self, addons=None, manifests=None): + """ + Installs all types of addons + addons - a list of addon paths to install + manifest - a list of addon manifests to install + """ + + @classmethod + def get_amo_install_path(self, query): + """ + Return the addon xpi install path for the specified AMO query. + See: https://developer.mozilla.org/en/addons.mozilla.org_%28AMO%29_API_Developers%27_Guide/The_generic_AMO_API + for query documentation. + """ + + @classmethod + def addon_details(cls, addon_path): + """ + returns a dictionary of details about the addon + - addon_path : path to the addon directory + Returns: + {'id': u'rainbow@colors.org', # id of the addon + 'version': u'1.4', # version of the addon + 'name': u'Rainbow', # name of the addon + 'unpack': False } # whether to unpack the addon + """ + + def clean_addons(self): + """Cleans up addons in the profile.""" + # Installing Addons Addons may be installed individually or from a manifest. Example: from mozprofile import FirefoxProfile
--- a/testing/mozbase/mozprofile/mozprofile/addons.py +++ b/testing/mozbase/mozprofile/mozprofile/addons.py @@ -130,17 +130,17 @@ class AddonManager(object): def addon_details(cls, addon_path): """ returns a dictionary of details about the addon - addon_path : path to the addon directory Returns: {'id': u'rainbow@colors.org', # id of the addon 'version': u'1.4', # version of the addon 'name': u'Rainbow', # name of the addon - 'unpack': # whether to unpack the addon + 'unpack': False } # whether to unpack the addon """ # TODO: We don't use the unpack variable yet, but we should: bug 662683 details = { 'id': None, 'unpack': False, 'name': None, 'version': None
--- a/testing/mozbase/mozprofile/mozprofile/profile.py +++ b/testing/mozbase/mozprofile/mozprofile/profile.py @@ -51,17 +51,25 @@ try: import simplejson except ImportError: import json as simplejson class Profile(object): """Handles all operations regarding profile. Created new profiles, installs extensions, sets preferences and handles cleanup.""" - def __init__(self, profile=None, addons=None, addon_manifests=None, preferences=None, locations=None, proxy=False, restore=True): + def __init__(self, + profile=None, # Path to the profile + addons=None, # String of one or list of addons to install + addon_manifests=None, # Manifest for addons, see http://ahal.ca/blog/2011/bulk-installing-fx-addons/ + preferences=None, # Dictionary or class of preferences + locations=None, # locations to proxy + proxy=False, # setup a proxy + restore=True # If true remove all installed addons preferences when cleaning up + ): # if true, remove installed addons/prefs afterwards self.restore = restore # Handle profile creation self.create_new = not profile if profile: # Ensure we have a full path to the profile @@ -216,17 +224,17 @@ class Profile(object): except: count += 1 except ImportError: # We can't re-raise an error, so we'll hope the stuff above us will throw pass def cleanup(self): - """Cleanup operations on the profile.""" + """Cleanup operations for the profile.""" if self.restore: if self.create_new: if os.path.exists(self.profile): rmtree(self.profile, onerror=self._cleanup_error) else: self.clean_preferences() self.addon_manager.clean_addons() self.permission_manager.clean_permissions()
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/addonid.py @@ -0,0 +1,146 @@ +#!/usr/bin/env python + +import os +import tempfile +import unittest +import shutil +from mozprofile import addons + +class AddonIDTest(unittest.TestCase): + """ Test finding the addon id in a variety of install.rdf styles """ + + def make_install_rdf(self, filecontents): + path = tempfile.mkdtemp() + f = open(os.path.join(path, "install.rdf"), "w") + f.write(filecontents) + f.close() + return path + + def test_addonID(self): + testlist = self.get_test_list() + for t in testlist: + try: + p = self.make_install_rdf(t) + a = addons.AddonManager(os.path.join(p, "profile")) + addon_id = a.addon_details(p)['id'] + self.assertTrue(addon_id == "winning", "We got the addon id") + finally: + shutil.rmtree(p) + + def get_test_list(self): + """ This just returns a hardcoded list of install.rdf snippets for testing. + When adding snippets for testing, remember that the id we're looking for + is "winning" (no quotes). So, make sure you have that id in your snippet + if you want it to pass. + """ + tests = [ +"""<?xml version="1.0"?> +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + <Description about="urn:mozilla:install-manifest"> + <em:id>winning</em:id> + <em:name>MozMill</em:name> + <em:version>2.0a</em:version> + <em:creator>Adam Christian</em:creator> + <em:description>A testing extension based on the Windmill Testing Framework client source</em:description> + <em:unpack>true</em:unpack> + <em:targetApplication> + <!-- Firefox --> + <Description> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>3.5</em:minVersion> + <em:maxVersion>8.*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <!-- Thunderbird --> + <Description> + <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id> + <em:minVersion>3.0a1pre</em:minVersion> + <em:maxVersion>3.2*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <!-- Sunbird --> + <Description> + <em:id>{718e30fb-e89b-41dd-9da7-e25a45638b28}</em:id> + <em:minVersion>0.6a1</em:minVersion> + <em:maxVersion>1.0pre</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <!-- SeaMonkey --> + <Description> + <em:id>{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}</em:id> + <em:minVersion>2.0a1</em:minVersion> + <em:maxVersion>2.1*</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <!-- Songbird --> + <Description> + <em:id>songbird@songbirdnest.com</em:id> + <em:minVersion>0.3pre</em:minVersion> + <em:maxVersion>1.3.0a</em:maxVersion> + </Description> + </em:targetApplication> + <em:targetApplication> + <Description> + <em:id>toolkit@mozilla.org</em:id> + <em:minVersion>1.9.1</em:minVersion> + <em:maxVersion>2.0*</em:maxVersion> + </Description> + </em:targetApplication> + </Description> +</RDF>""", +"""<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + <Description about="urn:mozilla:install-manifest"> + <em:targetApplication> + <!-- Firefox --> + <Description> + <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id> + <em:minVersion>3.5</em:minVersion> + <em:maxVersion>8.*</em:maxVersion> + </Description> + </em:targetApplication> + <em:id>winning</em:id> + <em:name>MozMill</em:name> + <em:version>2.0a</em:version> + <em:creator>Adam Christian</em:creator> + <em:description>A testing extension based on the Windmill Testing Framework client source</em:description> + <em:unpack>true</em:unpack> + </Description> + </RDF>""", +"""<RDF xmlns="http://www.mozilla.org/2004/em-rdf#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <rdf:Description about="urn:mozilla:install-manifest"> + <id>winning</id> + <name>foo</name> + <version>42</version> + <description>A testing extension based on the Windmill Testing Framework client source</description> + </rdf:Description> +</RDF>""", +"""<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:foobar="http://www.mozilla.org/2004/em-rdf#"> + <Description about="urn:mozilla:install-manifest"> + <foobar:targetApplication> + <!-- Firefox --> + <Description> + <foobar:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</foobar:id> + <foobar:minVersion>3.5</foobar:minVersion> + <foobar:maxVersion>8.*</foobar:maxVersion> + </Description> + </foobar:targetApplication> + <foobar:id>winning</foobar:id> + <foobar:name>MozMill</foobar:name> + <foobar:version>2.0a</foobar:version> + <foobar:creator>Adam Christian</foobar:creator> + <foobar:description>A testing extension based on the Windmill Testing Framework client source</foobar:description> + <foobar:unpack>true</foobar:unpack> + </Description> + </RDF>"""] + return tests + +if __name__ == '__main__': + unittest.main()
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/manifest.ini @@ -0,0 +1,3 @@ +[addonid.py] +[server_locations.py] +[testprofile.py]
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/server_locations.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +import os +import shutil +import tempfile +import unittest +from mozprofile.permissions import PermissionsManager + +class ServerLocationsTest(unittest.TestCase): + """test server locations""" + + locations = """ +# This is the primary location from which tests run. +# +http://mochi.test:8888 primary,privileged + +# a few test locations +http://127.0.0.1:80 privileged +http://127.0.0.1:8888 privileged +https://test:80 privileged +http://mochi.test:8888 privileged +http://example.org:80 privileged +http://test1.example.org:80 privileged + + """ + + def compare_location(self, location, scheme, host, port, options): + self.assertEqual(location.scheme, scheme) + self.assertEqual(location.host, host) + self.assertEqual(location.port, port) + self.assertEqual(location.options, options) + + def test_server_locations(self): + + # make a permissions manager + # needs a pointless temporary directory for now + tempdir = tempfile.mkdtemp() + permissions = PermissionsManager(tempdir) + + # write a permissions file + fd, filename = tempfile.mkstemp() + os.write(fd, self.locations) + os.close(fd) + + # read the locations + locations = permissions.read_locations(filename) + + # ensure that they're what we expect + self.assertEqual(len(locations), 7) + self.compare_location(locations[0], 'http', 'mochi.test', '8888', ['primary', 'privileged']) + self.compare_location(locations[1], 'http', '127.0.0.1', '80', ['privileged']) + self.compare_location(locations[2], 'http', '127.0.0.1', '8888', ['privileged']) + self.compare_location(locations[3], 'https', 'test', '80', ['privileged']) + self.compare_location(locations[4], 'http', 'mochi.test', '8888', ['privileged']) + self.compare_location(locations[5], 'http', 'example.org', '80', ['privileged']) + self.compare_location(locations[6], 'http', 'test1.example.org', '80', ['privileged']) + + # cleanup + del permissions + shutil.rmtree(tempdir) + os.remove(filename) + + +if __name__ == '__main__': + unittest.main()
new file mode 100644 --- /dev/null +++ b/testing/mozbase/mozprofile/tests/testprofile.py @@ -0,0 +1,141 @@ +#!/usr/bin/env python + +import os +import shutil +import subprocess +import tempfile +import unittest +from mozprofile.prefs import Preferences +from mozprofile.profile import Profile + +class ProfileTest(unittest.TestCase): + """test mozprofile""" + + def run_command(self, *args): + """ + runs mozprofile; + returns (stdout, stderr, code) + """ + process = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = process.communicate() + stdout = stdout.strip() + stderr = stderr.strip() + return stdout, stderr, process.returncode + + def compare_generated(self, _prefs, commandline): + """ + writes out to a new profile with mozprofile command line + reads the generated preferences with prefs.py + compares the results + cleans up + """ + profile, stderr, code = self.run_command(*commandline) + prefs_file = os.path.join(profile, 'user.js') + self.assertTrue(os.path.exists(prefs_file)) + read = Preferences.read_prefs(prefs_file) + if isinstance(_prefs, dict): + read = dict(read) + self.assertEqual(_prefs, read) + shutil.rmtree(profile) + + def test_basic_prefs(self): + _prefs = {"browser.startup.homepage": "http://planet.mozilla.org/"} + commandline = ["mozprofile"] + _prefs = _prefs.items() + for pref, value in _prefs: + commandline += ["--pref", "%s:%s" % (pref, value)] + self.compare_generated(_prefs, commandline) + + def test_ordered_prefs(self): + """ensure the prefs stay in the right order""" + _prefs = [("browser.startup.homepage", "http://planet.mozilla.org/"), + ("zoom.minPercent", 30), + ("zoom.maxPercent", 300), + ("webgl.verbose", 'false')] + commandline = ["mozprofile"] + for pref, value in _prefs: + commandline += ["--pref", "%s:%s" % (pref, value)] + _prefs = [(i, Preferences.cast(j)) for i, j in _prefs] + self.compare_generated(_prefs, commandline) + + def test_ini(self): + + # write the .ini file + _ini = """[DEFAULT] +browser.startup.homepage = http://planet.mozilla.org/ + +[foo] +browser.startup.homepage = http://github.com/ +""" + fd, name = tempfile.mkstemp(suffix='.ini') + os.write(fd, _ini) + os.close(fd) + commandline = ["mozprofile", "--preferences", name] + + # test the [DEFAULT] section + _prefs = {'browser.startup.homepage': 'http://planet.mozilla.org/'} + self.compare_generated(_prefs, commandline) + + # test a specific section + _prefs = {'browser.startup.homepage': 'http://github.com/'} + commandline[-1] = commandline[-1] + ':foo' + self.compare_generated(_prefs, commandline) + + # cleanup + os.remove(name) + + def test_magic_markers(self): + """ensure our magic markers are working""" + + profile = Profile() + prefs_file = os.path.join(profile.profile, 'user.js') + + # we shouldn't have any initial preferences + initial_prefs = Preferences.read_prefs(prefs_file) + self.assertFalse(initial_prefs) + initial_prefs = file(prefs_file).read().strip() + self.assertFalse(initial_prefs) + + # add some preferences + prefs1 = [("browser.startup.homepage", "http://planet.mozilla.org/"), + ("zoom.minPercent", 30)] + profile.set_preferences(prefs1) + self.assertEqual(prefs1, Preferences.read_prefs(prefs_file)) + lines = file(prefs_file).read().strip().splitlines() + self.assertTrue('#MozRunner Prefs Start' in lines) + self.assertTrue('#MozRunner Prefs End' in lines) + + # add some more preferences + prefs2 = [("zoom.maxPercent", 300), + ("webgl.verbose", 'false')] + profile.set_preferences(prefs2) + self.assertEqual(prefs1 + prefs2, Preferences.read_prefs(prefs_file)) + lines = file(prefs_file).read().strip().splitlines() + self.assertTrue(lines.count('#MozRunner Prefs Start') == 2) + self.assertTrue(lines.count('#MozRunner Prefs End') == 2) + + # now clean it up + profile.clean_preferences() + final_prefs = Preferences.read_prefs(prefs_file) + self.assertFalse(final_prefs) + lines = file(prefs_file).read().strip().splitlines() + self.assertTrue('#MozRunner Prefs Start' not in lines) + self.assertTrue('#MozRunner Prefs End' not in lines) + + def test_json(self): + _prefs = {"browser.startup.homepage": "http://planet.mozilla.org/"} + json = '{"browser.startup.homepage": "http://planet.mozilla.org/"}' + + # just repr it...could use the json module but we don't need it here + fd, name = tempfile.mkstemp(suffix='.json') + os.write(fd, json) + os.close(fd) + + commandline = ["mozprofile", "--preferences", name] + self.compare_generated(_prefs, commandline) + + +if __name__ == '__main__': + unittest.main()
--- a/testing/mozbase/mozrunner/mozrunner/runner.py +++ b/testing/mozbase/mozrunner/mozrunner/runner.py @@ -39,57 +39,50 @@ # # ***** END LICENSE BLOCK ***** __all__ = ['Runner', 'ThunderbirdRunner', 'FirefoxRunner', 'runners', 'CLI', 'cli', 'package_metadata'] import mozinfo import optparse import os +import platform import sys import ConfigParser from utils import get_metadata_from_egg from utils import findInPath from mozprofile import * from mozprocess.processhandler import ProcessHandler package_metadata = get_metadata_from_egg('mozrunner') -class BinaryLocationException(Exception): - """exception for failure to find the binary""" - - class Runner(object): """Handles all running operations. Finds bins, runs and kills the process.""" - ### data to be filled in by subclasses - profile = Profile # profile class to use by default - names = [] # names of application to look for on PATH - app_name = '' # name of application in windows registry - program_names = [] # names of application in windows program files + profile_class = Profile # profile class to use by default @classmethod - def create(cls, binary=None, cmdargs=None, env=None, kp_kwargs=None, profile_args=None, + def create(cls, binary, cmdargs=None, env=None, kp_kwargs=None, profile_args=None, clean_profile=True, process_class=ProcessHandler): profile = cls.profile_class(**(profile_args or {})) return cls(profile, binary=binary, cmdargs=cmdargs, env=env, kp_kwargs=kp_kwargs, clean_profile=clean_profile, process_class=process_class) - def __init__(self, profile, binary=None, cmdargs=None, env=None, + def __init__(self, profile, binary, cmdargs=None, env=None, kp_kwargs=None, clean_profile=True, process_class=ProcessHandler): self.process_handler = None self.process_class = process_class self.profile = profile self.clean_profile = clean_profile - self.firstrun = False - # find the binary - self.binary = self.__class__.get_binary(binary) + self.binary = binary + if not self.binary: + raise Exception("Binary not specified") if not os.path.exists(self.binary): raise OSError("Binary path does not exist: %s" % self.binary) self.cmdargs = cmdargs or [] _cmdargs = [i for i in self.cmdargs if i != '-foreground'] if len(_cmdargs) != len(self.cmdargs): # foreground should be last; see @@ -114,92 +107,16 @@ class Runner(object): if os.environ.get('LD_LIBRARY_PATH', None): self.env['LD_LIBRARY_PATH'] = '%s:%s' % (os.environ['LD_LIBRARY_PATH'], dirname) else: self.env['LD_LIBRARY_PATH'] = dirname # arguments for ProfessHandler.Process self.kp_kwargs = kp_kwargs or {} - @classmethod - def get_binary(cls, binary=None): - """determine the binary""" - if binary is None: - binary = cls.find_binary() - if binary is None: - raise BinaryLocationException("Your binary could not be located; you will need to set it") - return binary - elif mozinfo.isMac and binary.find('Contents/MacOS/') == -1: - return os.path.join(binary, 'Contents/MacOS/%s-bin' % cls.names[0]) - else: - return binary - - @classmethod - def find_binary(cls): - """Finds the binary for class names if one was not provided.""" - - binary = None - if mozinfo.isUnix: - for name in cls.names: - binary = findInPath(name) - if binary: - return binary - elif mozinfo.isWin: - - # find the default executable from the windows registry - try: - # assumes cls.app_name is defined, as it should be for implementors - import _winreg - app_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, r"Software\Mozilla\Mozilla %s" % cls.app_name) - version, _type = _winreg.QueryValueEx(app_key, "CurrentVersion") - version_key = _winreg.OpenKey(app_key, version + r"\Main") - path, _ = _winreg.QueryValueEx(version_key, "PathToExe") - return path - except: # XXX not sure what type of exception this should be - pass - - # search for the binary in the path - for name in cls.names: - binary = findInPath(name) - if binary: - return binary - - # search for the binary in program files - if sys.platform == 'cygwin': - program_files = os.environ['PROGRAMFILES'] - else: - program_files = os.environ['ProgramFiles'] - - program_files = [program_files] - if "ProgramFiles(x86)" in os.environ: - program_files.append(os.environ["ProgramFiles(x86)"]) - for program_file in program_files: - for program_name in cls.program_names: - path = os.path.join(program_name, program_file, 'firefox.exe') - if os.path.isfile(path): - return path - - elif mozinfo.isMac: - for name in cls.names: - appdir = os.path.join('Applications', name.capitalize()+'.app') - if os.path.isdir(os.path.join(os.path.expanduser('~/'), appdir)): - binary = os.path.join(os.path.expanduser('~/'), appdir, - 'Contents/MacOS/'+name+'-bin') - elif os.path.isdir('/'+appdir): - binary = os.path.join("/"+appdir, 'Contents/MacOS/'+name+'-bin') - - if binary is not None: - if not os.path.isfile(binary): - binary = binary.replace(name+'-bin', 'firefox-bin') - if not os.path.isfile(binary): - binary = None - if binary: - return binary - return binary - @property def command(self): """Returns the command list to run.""" return [self.binary, '-profile', self.profile.profile] def get_repositoryInfo(self): """Read repository information from application.ini and platform.ini.""" @@ -226,31 +143,20 @@ class Runner(object): """Run self.command in the proper environment.""" # ensure you are stopped self.stop() # ensure the profile exists if not self.profile.exists(): self.profile.reset() - self.firstrun = False - - # run once to register any extensions - # see: - # - http://hg.mozilla.org/releases/mozilla-1.9.2/file/915a35e15cde/build/automation.py.in#l702 - # - http://mozilla-xp.com/mozilla.dev.apps.firefox/Rules-for-when-firefox-bin-restarts-it-s-process - # This run just calls through processhandler to popen directly as we - # are not particuarly cared in tracking this process - if not self.firstrun: - firstrun = ProcessHandler.Process(self.command+['-silent', '-foreground'], env=self.env, **self.kp_kwargs) - firstrun.wait() - self.firstrun = True - - # now run for real, this run uses the managed processhandler - self.process_handler = self.process_class(self.command+self.cmdargs, env=self.env, **self.kp_kwargs) + + cmd = self._wrap_command(self.command+self.cmdargs) + # this run uses the managed processhandler + self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs) self.process_handler.run() def wait(self, timeout=None, outputTimeout=None): """Wait for the app to exit.""" if self.process_handler is None: return self.process_handler.waitForFinish(timeout=timeout, outputTimeout=outputTimeout) self.process_handler = None @@ -269,64 +175,61 @@ class Runner(object): """ self.profile.reset() def cleanup(self): self.stop() if self.clean_profile: self.profile.cleanup() + def _wrap_command(self, cmd): + """ + If running on OS X 10.5 or older, wrap |cmd| so that it will + be executed as an i386 binary, in case it's a 32-bit/64-bit universal + binary. + """ + if mozinfo.isMac and hasattr(platform, 'mac_ver') and \ + platform.mac_ver()[0][:4] < '10.6': + return ["arch", "-arch", "i386"] + cmd + return cmd + __del__ = cleanup class FirefoxRunner(Runner): """Specialized Runner subclass for running Firefox.""" - app_name = 'Firefox' profile_class = FirefoxProfile - program_names = ['Mozilla Firefox'] + + def __init__(self, profile, binary=None, **kwargs): - # (platform-dependent) names of binary - if mozinfo.isMac: - names = ['firefox', 'minefield', 'shiretoko'] - elif mozinfo.isUnix: - names = ['firefox', 'mozilla-firefox', 'iceweasel'] - elif mozinfo.isWin: - names =['firefox'] - else: - raise AssertionError("I don't know what platform you're on") + # take the binary from BROWSER_PATH environment variable + if (not binary) and 'BROWSER_PATH' in os.environ: + binary = os.environ['BROWSER_PATH'] - def __init__(self, profile, **kwargs): - Runner.__init__(self, profile, **kwargs) + Runner.__init__(self, profile, binary, **kwargs) # Find application version number appdir = os.path.dirname(os.path.realpath(self.binary)) appini = ConfigParser.RawConfigParser() appini.read(os.path.join(appdir, 'application.ini')) # Version needs to be of the form 3.6 or 4.0b and not the whole string version = appini.get('App', 'Version').rstrip('0123456789pre').rstrip('.') # Disable compatibility check. See: # - http://kb.mozillazine.org/Extensions.checkCompatibility # - https://bugzilla.mozilla.org/show_bug.cgi?id=659048 preference = {'extensions.checkCompatibility.' + version: False, 'extensions.checkCompatibility.nightly': False} self.profile.set_preferences(preference) - @classmethod - def get_binary(cls, binary=None): - if (not binary) and 'BROWSER_PATH' in os.environ: - return os.environ['BROWSER_PATH'] - return Runner.get_binary(binary) class ThunderbirdRunner(Runner): """Specialized Runner subclass for running Thunderbird""" - app_name = 'Thunderbird' profile_class = ThunderbirdProfile - names = ["thunderbird", "shredder"] runners = {'firefox': FirefoxRunner, 'thunderbird': ThunderbirdRunner} class CLI(MozProfileCLI): """Command line interface.""" module = "mozrunner"
--- a/testing/mozbase/mozrunner/setup.py +++ b/testing/mozbase/mozrunner/setup.py @@ -38,17 +38,17 @@ # # ***** END LICENSE BLOCK ***** import os import sys from setuptools import setup, find_packages PACKAGE_NAME = "mozrunner" -PACKAGE_VERSION = "4.1" +PACKAGE_VERSION = "5.1" desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)""" # take description from README here = os.path.dirname(os.path.abspath(__file__)) try: description = file(os.path.join(here, 'README.md')).read() except (OSError, IOError): description = ''
new file mode 100644 --- /dev/null +++ b/testing/mozbase/test-manifest.ini @@ -0,0 +1,5 @@ +# mozbase test manifest, in the format of +# https://github.com/mozilla/mozbase/blob/master/manifestdestiny/README.txt + +[include:mozprocess/tests/manifest.ini] +[include:mozprofile/tests/manifest.ini]
new file mode 100644 --- /dev/null +++ b/testing/mozbase/test.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +""" +run mozbase tests +""" + +import imp +import manifestparser +import os +import sys +import unittest + +here = os.path.dirname(os.path.abspath(__file__)) + +def unittests(path): + """return the unittests in a .py file""" + + path = os.path.abspath(path) + unittests = [] + assert os.path.exists(path) + directory = os.path.dirname(path) + sys.path.insert(0, directory) # insert directory into path for top-level imports + modname = os.path.splitext(os.path.basename(path))[0] + module = imp.load_source(modname, path) + sys.path.pop(0) # remove directory from global path + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(module) + for test in suite: + unittests.append(test) + return unittests + +def main(args=sys.argv[1:]): + + # read the manifest + if args: + manifests = args + else: + manifests = [os.path.join(here, 'test-manifest.ini')] + missing = [] + for manifest in manifests: + # ensure manifests exist + if not os.path.exists(manifest): + missing.append(manifest) + assert not missing, 'manifest%s not found: %s' % ((len(manifests) == 1 and '' or 's'), ', '.join(missing)) + manifest = manifestparser.TestManifest(manifests=manifests) + + # gather the tests + tests = manifest.active_tests() + unittestlist = [] + for test in tests: + unittestlist.extend(unittests(test['path'])) + + # run the tests + suite = unittest.TestSuite(unittestlist) + runner = unittest.TextTestRunner() + results = runner.run(suite) + + # exit according to results + sys.exit((results.failures or results.errors) and 1 or 0) + +if __name__ == '__main__': + main()
--- a/toolkit/content/tests/browser/browser_save_resend_postdata.js +++ b/toolkit/content/tests/browser/browser_save_resend_postdata.js @@ -30,17 +30,17 @@ * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); /** * Test for bug 471962 <https://bugzilla.mozilla.org/show_bug.cgi?id=471962>: * When saving an inner frame as file only, the POST data of the outer page is * sent to the address of the inner page. * * Test for bug 485196 <https://bugzilla.mozilla.org/show_bug.cgi?id=485196>: * Web page generated by POST is retried as GET when Save Frame As used, and the @@ -95,17 +95,17 @@ function test() { MockFilePicker.filterIndex = 1; // kSaveAsType_URL }; mockTransferCallback = onTransferComplete; mockTransferRegisterer.register(); registerCleanupFunction(function () { mockTransferRegisterer.unregister(); - MockFilePicker.reset(); + MockFilePicker.cleanup(); destDir.remove(true); }); var docToSave = innerFrame.contentDocument; // We call internalSave instead of saveDocument to bypass the history // cache. internalSave(docToSave.location.href, docToSave, null, null, docToSave.contentType, false, null, null,
--- a/toolkit/mozapps/downloads/tests/unit/test_privatebrowsing_downloadLastDir.js +++ b/toolkit/mozapps/downloads/tests/unit/test_privatebrowsing_downloadLastDir.js @@ -52,17 +52,17 @@ let context = { getInterface: XPCOMUtils.generateQI([Ci.nsIDOMWindow]) }; let launcher = { source: Services.io.newURI("http://test1.com/file", null, null) }; Cu.import("resource://test/MockFilePicker.jsm"); -MockFilePicker.reset(); +MockFilePicker.init(); MockFilePicker.returnValue = Ci.nsIFilePicker.returnOK; function run_test() { let pb; try { pb = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService); @@ -138,10 +138,10 @@ function run_test() do_check_eq(prefs.getComplexValue("lastDir", Ci.nsILocalFile).path, dir3.path); // gDownloadLastDir should be usable after leaving the private browsing mode do_check_eq(gDownloadLastDir.file.path, dir3.path); // cleanup prefsService.clearUserPref("browser.privatebrowsing.keep_current_session"); [dir1, dir2, dir3].forEach(function(dir) dir.remove(true)); - MockFilePicker.reset(); + MockFilePicker.cleanup(); }
--- a/toolkit/mozapps/extensions/test/browser/browser_bug567127.js +++ b/toolkit/mozapps/extensions/test/browser/browser_bug567127.js @@ -1,16 +1,16 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ // Tests bug 567127 - Add install button to the add-ons manager var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); var gManagerWindow; var gSawInstallNotification = false; // This listens for the next opened window and checks it is of the right url. // opencallback is called when the new window is fully loaded // closecallback is called when the window is closed function WindowOpenListener(url, opencallback, closecallback) { @@ -107,17 +107,17 @@ function test() { gManagerWindow = aWindow; run_next_test(); }); } function end_test() { is(gSawInstallNotification, true, "Should have seen addon-install-started notification."); - MockFilePicker.reset(); + MockFilePicker.cleanup(); close_manager(gManagerWindow, function() { finish(); }); } add_test(function() { var filePaths = [
--- a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js +++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js @@ -6,17 +6,17 @@ var gManagerWindow; var gCategoryUtilities; var gProvider; const SETTINGS_ROWS = 8; var MockFilePicker = SpecialPowers.MockFilePicker; -MockFilePicker.reset(); +MockFilePicker.init(); var observer = { lastData: null, observe: function(aSubject, aTopic, aData) { if (aTopic == "addon-options-displayed") { this.lastData = aData; // Test if the binding has applied before the observers are notified. We test the second setting here, // because the code operates on the first setting and we want to check it applies to all. @@ -86,17 +86,17 @@ function end_test() { Services.prefs.clearUserPref("extensions.inlinesettings1.color"); Services.prefs.clearUserPref("extensions.inlinesettings1.file"); Services.prefs.clearUserPref("extensions.inlinesettings1.directory"); Services.prefs.clearUserPref("extensions.inlinesettings3.radioBool"); Services.prefs.clearUserPref("extensions.inlinesettings3.radioInt"); Services.prefs.clearUserPref("extensions.inlinesettings3.radioString"); Services.prefs.clearUserPref("extensions.inlinesettings3.menulist"); - MockFilePicker.reset(); + MockFilePicker.cleanup(); close_manager(gManagerWindow, function() { AddonManager.getAddonByID("inlinesettings1@tests.mozilla.org", function(aAddon) { aAddon.uninstall(); finish(); }); }); }
--- a/tools/profiler/sps/platform-linux.cc +++ b/tools/profiler/sps/platform-linux.cc @@ -29,17 +29,16 @@ #include <fcntl.h> // open #include <unistd.h> // sysconf #ifdef __GLIBC__ #include <execinfo.h> // backtrace, backtrace_symbols #endif // def __GLIBC__ #include <strings.h> // index #include <errno.h> #include <stdarg.h> -#include "v8-support.h" #include "platform.h" #include <string.h> #include <stdio.h> // Real time signals are not supported on android. // This behaves as a standard signal. #define SIGNAL_SAVE_PROFILE 42
--- a/tools/profiler/sps/platform-macos.cc +++ b/tools/profiler/sps/platform-macos.cc @@ -20,17 +20,16 @@ #include <sys/types.h> #include <sys/sysctl.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> #include <errno.h> -#include "v8-support.h" #include "platform.h" // this port is based off of v8 svn revision 9837 // XXX: this is a very stubbed out implementation // that only supports a single Sampler struct SamplerRegistry { static void AddActiveSampler(Sampler *sampler) {
--- a/tools/profiler/sps/platform-win32.cc +++ b/tools/profiler/sps/platform-win32.cc @@ -1,14 +1,13 @@ // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <windows.h> -#include "v8-support.h" #include "platform.h" #include <process.h> class Sampler::PlatformData : public Malloced { public: // Get a handle to the calling thread. This is the thread that we are // going to profile. We need to make a copy of the handle because we are
--- a/tools/profiler/sps/platform.h +++ b/tools/profiler/sps/platform.h @@ -3,40 +3,31 @@ // found in the LICENSE file. #ifdef ANDROID #include <android/log.h> #else #define __android_log_print(a, ...) #endif +#include "mozilla/StdInt.h" #include "mozilla/Util.h" #include "mozilla/unused.h" #include "mozilla/TimeStamp.h" #include "v8-support.h" #include <vector> #define ASSERT(a) MOZ_ASSERT(a) #ifdef ANDROID #define ENABLE_SPS_LEAF_DATA #define LOG(text) __android_log_print(ANDROID_LOG_ERROR, "profiler", "%s", text); #else #define LOG(text) printf("Profiler: %s\n", text) #endif -#ifdef _MSC_VER - typedef __int8 byte; - typedef __int32 int32_t; - typedef unsigned __int32 uint32_t; - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; -#else - #include <stdint.h> - typedef uint8 byte; -#endif -typedef byte* Address; +typedef uint8_t* Address; class MapEntry { public: MapEntry(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName) : mStart(aStart) , mEnd(aEnd) , mOffset(aOffset) , mName(strdup(aName))
--- a/tools/profiler/sps/v8-support.h +++ b/tools/profiler/sps/v8-support.h @@ -46,21 +46,17 @@ #elif defined(_M_IX86) || defined(__i386__) || defined(__i386) #define V8_HOST_ARCH_IA32 1 #elif defined(__ARMEL__) #define V8_HOST_ARCH_ARM 1 #else #warning Please add support for your architecture in chromium_types.h #endif -#ifdef _WIN32 - typedef __int32 Atomic32; -#else - typedef int32_t Atomic32; -#endif +typedef int32_t Atomic32; #if defined(V8_HOST_ARCH_X64) || defined(V8_HOST_ARCH_IA32) || defined(V8_HOST_ARCH_ARM) inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { *ptr = value; } #endif