diff --git a/browser/base/content/browser-context.inc b/browser/base/content/browser-context.inc index a4b7c8645d0..2f9d64304df 100644 --- a/browser/base/content/browser-context.inc +++ b/browser/base/content/browser-context.inc @@ -46,6 +46,10 @@ label="&spellAddToDictionary.label;" accesskey="&spellAddToDictionary.accesskey;" oncommand="InlineSpellCheckerUI.addToDictionary();"/> + CreateBundle(BRAND_PROPERTIES, getter_AddRefs(brandBundle)); NS_ENSURE_SUCCESS(rv, rv); - nsString brandShortName, brandFullName; + nsString brandShortName; brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(), getter_Copies(brandShortName)); - brandBundle->GetStringFromName(NS_LITERAL_STRING("brandFullName").get(), - getter_Copies(brandFullName)); // use brandShortName as the application id. NS_ConvertUTF16toUTF8 id(brandShortName); diff --git a/configure.in b/configure.in index 8fe2b1334c0..aa48cc884d5 100644 --- a/configure.in +++ b/configure.in @@ -336,6 +336,7 @@ if test -n "$gonkdir" ; then fi AC_DEFINE(ANDROID) + AC_DEFINE(HAVE_SYS_UIO_H) CROSS_COMPILE=1 MOZ_CHROME_FILE_FORMAT=omni ZLIB_DIR=yes @@ -1864,20 +1865,15 @@ if test "$GNU_CC"; then _MOZ_RTTI_FLAGS_ON=-frtti _MOZ_RTTI_FLAGS_OFF=-fno-rtti - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Werror=declaration-after-statement - MSVC doesn't like these - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-overlength-strings" - + # Turn on GNU specific features + # -Wall - turn on all warnings + # -pedantic - make compiler warn about non-ANSI stuff, and + # be a little bit stricter + # -Wdeclaration-after-statement - MSVC doesn't like these + # Warnings slamm took out for now (these were giving more noise than help): + # -Wbad-function-cast - warns when casting a function to a new return type + # -Wshadow - removed because it generates more noise than help --pete + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith -Wdeclaration-after-statement" if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -1893,26 +1889,12 @@ if test "$GNU_CC"; then dnl Turn pedantic on but disable the warnings for long long _PEDANTIC=1 + if test -z "$INTEL_CC"; then + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W" + fi + _DEFINES_CFLAGS='-include $(DEPTH)/mozilla-config.h -DMOZILLA_CLIENT' _USE_CPP_INCLUDE_FLAG=1 - - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_cc_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_C - _SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_cc_has_wtype_limits="yes", - ac_cc_has_wtype_limits="no") - CFLAGS="$_SAVE_CFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_cc_has_wtype_limits" = "yes"; then - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits" - fi elif test "$SOLARIS_SUNPRO_CC"; then DSO_CFLAGS='' if test "$CPU_ARCH" = "sparc"; then @@ -1940,22 +1922,8 @@ fi if test "$GNU_CXX"; then # FIXME: Let us build with strict aliasing. bug 414641. CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-strict-aliasing" - - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Woverloaded-virtual - ??? - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # -Wctor-dtor-privacy - ??? - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-overlength-strings -Wno-ctor-dtor-privacy" - + # Turn on GNU specific features + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -2056,24 +2024,6 @@ if test "$GNU_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type" fi - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_has_wtype_limits="yes", - ac_has_wtype_limits="no") - CXXFLAGS="$_SAVE_CXXFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_has_wtype_limits" = "yes"; then - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits" - fi - else _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_MOZILLA_CONFIG_H_ $(ACDEFINES)' fi diff --git a/content/base/src/nsGenericElement.cpp b/content/base/src/nsGenericElement.cpp index 6f9e76ce17a..db47f0f858c 100644 --- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -2129,7 +2129,8 @@ nsGenericElement::GetBoundingClientRect(nsIDOMClientRect** aResult) } nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(frame, - nsLayoutUtils::GetContainingBlockForClientRect(frame)); + nsLayoutUtils::GetContainingBlockForClientRect(frame), + nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); rect->SetLayoutRect(r); return NS_OK; } @@ -2157,7 +2158,8 @@ nsGenericElement::GetClientRects(nsIDOMClientRectList** aResult) nsLayoutUtils::RectListBuilder builder(rectList); nsLayoutUtils::GetAllInFlowRects(frame, - nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder); + nsLayoutUtils::GetContainingBlockForClientRect(frame), &builder, + nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); if (NS_FAILED(builder.mRV)) return builder.mRV; *aResult = rectList.forget().get(); diff --git a/content/xslt/src/base/txExpandedNameMap.h b/content/xslt/src/base/txExpandedNameMap.h index 03d784aa0d9..fa1d5cfd46a 100644 --- a/content/xslt/src/base/txExpandedNameMap.h +++ b/content/xslt/src/base/txExpandedNameMap.h @@ -102,7 +102,8 @@ protected: const txExpandedName key() { - NS_ASSERTION(mCurrentPos < mMap.mItems.Length(), + NS_ASSERTION(mCurrentPos >= 0 && + mCurrentPos < mMap.mItems.Length(), "invalid position in txExpandedNameMap::iterator"); return txExpandedName(mMap.mItems[mCurrentPos].mNamespaceID, mMap.mItems[mCurrentPos].mLocalName); @@ -111,7 +112,8 @@ protected: protected: void* itemValue() { - NS_ASSERTION(mCurrentPos < mMap.mItems.Length(), + NS_ASSERTION(mCurrentPos >= 0 && + mCurrentPos < mMap.mItems.Length(), "invalid position in txExpandedNameMap::iterator"); return mMap.mItems[mCurrentPos].mValue; } diff --git a/docshell/test/browser/browser_bug435325.js b/docshell/test/browser/browser_bug435325.js index e01246886d2..36326468697 100644 --- a/docshell/test/browser/browser_bug435325.js +++ b/docshell/test/browser/browser_bug435325.js @@ -12,8 +12,8 @@ function test() { // Go offline and disable the cache, then try to load the test URL. Services.io.offline = true; - gPrefService.setBoolPref("browser.cache.disk.enable", false); - gPrefService.setBoolPref("browser.cache.memory.enable", false); + Services.prefs.setBoolPref("browser.cache.disk.enable", false); + Services.prefs.setBoolPref("browser.cache.memory.enable", false); content.location = "http://example.com/"; } @@ -42,8 +42,8 @@ function checkPage() { } registerCleanupFunction(function() { - gPrefService.setBoolPref("browser.cache.disk.enable", true); - gPrefService.setBoolPref("browser.cache.memory.enable", true); + Services.prefs.setBoolPref("browser.cache.disk.enable", true); + Services.prefs.setBoolPref("browser.cache.memory.enable", true); Services.io.offline = false; gBrowser.removeCurrentTab(); }); diff --git a/dom/base/nsDOMMemoryReporter.cpp b/dom/base/nsDOMMemoryReporter.cpp index 61c8187ac43..b9909d3529a 100644 --- a/dom/base/nsDOMMemoryReporter.cpp +++ b/dom/base/nsDOMMemoryReporter.cpp @@ -230,3 +230,12 @@ nsDOMMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, return NS_OK; } +NS_IMETHODIMP +nsDOMMemoryMultiReporter::GetExplicitNonHeap(PRInt64* aAmount) +{ + // This reporter only measures heap memory. + *aAmount = 0; + return NS_OK; +} + + diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 6464d8da2f2..99ef44573d8 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -129,6 +129,8 @@ static PRLogModuleInfo* gJSDiagnostics; // a page) and doing the actual GC. #define NS_GC_DELAY 4000 // ms +#define NS_SHRINK_GC_BUFFERS_DELAY 4000 // ms + // The amount of time we wait from the first request to GC to actually // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms @@ -142,6 +144,7 @@ static PRLogModuleInfo* gJSDiagnostics; // if you add statics here, add them to the list in nsJSRuntime::Startup static nsITimer *sGCTimer; +static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sCCTimer; static bool sGCHasRun; @@ -1098,8 +1101,9 @@ nsJSContext::~nsJSContext() void nsJSContext::DestroyJSContext() { - if (!mContext) + if (!mContext) { return; + } // Clear our entry in the JSContext, bugzilla bug 66413 ::JS_SetContextPrivate(mContext, nsnull); @@ -1108,14 +1112,14 @@ nsJSContext::DestroyJSContext() Preferences::UnregisterCallback(JSOptionChangedCallback, js_options_dot_str, this); - bool do_gc = mGCOnDestruction && !sGCTimer; - + if (mGCOnDestruction) { + PokeGC(); + } + // Let xpconnect destroy the JSContext when it thinks the time is right. nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { - xpc->ReleaseJSContext(mContext, !do_gc); - } else if (do_gc) { - ::JS_DestroyContext(mContext); + xpc->ReleaseJSContext(mContext, true); } else { ::JS_DestroyContextNoGC(mContext); } @@ -3224,6 +3228,7 @@ nsJSContext::GarbageCollectNow(bool shrinkingGC) SAMPLE_LABEL("GC", "GarbageCollectNow"); KillGCTimer(); + KillShrinkGCBuffersTimer(); // Reset sPendingLoadCount in case the timer that fired was a // timer we scheduled due to a normal GC timer firing while @@ -3239,6 +3244,18 @@ nsJSContext::GarbageCollectNow(bool shrinkingGC) } } +//static +void +nsJSContext::ShrinkGCBuffersNow() +{ + NS_TIME_FUNCTION_MIN(1.0); + SAMPLE_LABEL("GC", "ShrinkGCBuffersNow"); + + KillShrinkGCBuffersTimer(); + + JS_ShrinkGCBuffers(nsJSRuntime::sRuntime); +} + //Static void nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener) @@ -3296,6 +3313,14 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) nsJSContext::GarbageCollectNow(); } +void +ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure) +{ + NS_RELEASE(sShrinkGCBuffersTimer); + + nsJSContext::ShrinkGCBuffersNow(); +} + // static void CCTimerFired(nsITimer *aTimer, void *aClosure) @@ -3359,6 +3384,26 @@ nsJSContext::PokeGC() first = false; } +// static +void +nsJSContext::PokeShrinkGCBuffers() +{ + if (sShrinkGCBuffersTimer) { + return; + } + + CallCreateInstance("@mozilla.org/timer;1", &sShrinkGCBuffersTimer); + + if (!sShrinkGCBuffersTimer) { + // Failed to create timer (probably because we're in XPCOM shutdown) + return; + } + + sShrinkGCBuffersTimer->InitWithFuncCallback(ShrinkGCBuffersTimerFired, nsnull, + NS_SHRINK_GC_BUFFERS_DELAY, + nsITimer::TYPE_ONE_SHOT); +} + // static void nsJSContext::MaybePokeCC() @@ -3400,6 +3445,17 @@ nsJSContext::KillGCTimer() } } +//static +void +nsJSContext::KillShrinkGCBuffersTimer() +{ + if (sShrinkGCBuffersTimer) { + sShrinkGCBuffersTimer->Cancel(); + + NS_RELEASE(sShrinkGCBuffersTimer); + } +} + //static void nsJSContext::KillCCTimer() @@ -3465,10 +3521,11 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) } } - // If we didn't end up scheduling a GC, and there are unused - // chunks waiting to expire, make sure we will GC again soon. - if (!sGCTimer && JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS) > 0) { - nsJSContext::PokeGC(); + // If we didn't end up scheduling a GC, make sure that we release GC buffers + // soon after canceling previous shrinking attempt + nsJSContext::KillShrinkGCBuffersTimer(); + if (!sGCTimer) { + nsJSContext::PokeShrinkGCBuffers(); } } @@ -3808,6 +3865,7 @@ void nsJSRuntime::Shutdown() { nsJSContext::KillGCTimer(); + nsJSContext::KillShrinkGCBuffersTimer(); nsJSContext::KillCCTimer(); NS_IF_RELEASE(gNameSpaceManager); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 78ff3def233..a45f2448dc9 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -183,11 +183,15 @@ public: static void LoadEnd(); static void GarbageCollectNow(bool shrinkingGC = false); + static void ShrinkGCBuffersNow(); static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull); static void PokeGC(); static void KillGCTimer(); + static void PokeShrinkGCBuffers(); + static void KillShrinkGCBuffersTimer(); + static void PokeCC(); static void MaybePokeCC(); static void KillCCTimer(); diff --git a/dom/tests/mochitest/general/Makefile.in b/dom/tests/mochitest/general/Makefile.in index a25019cc09c..0b359ce6577 100644 --- a/dom/tests/mochitest/general/Makefile.in +++ b/dom/tests/mochitest/general/Makefile.in @@ -58,6 +58,7 @@ _TEST_FILES = \ test_bug631440.html \ test_bug653364.html \ test_bug629535.html \ + test_clientRects.html \ test_consoleAPI.html \ test_domWindowUtils.html \ test_domWindowUtils_scrollXY.html \ diff --git a/dom/tests/mochitest/general/test_clientRects.html b/dom/tests/mochitest/general/test_clientRects.html new file mode 100644 index 00000000000..de4a8bdfbb8 --- /dev/null +++ b/dom/tests/mochitest/general/test_clientRects.html @@ -0,0 +1,125 @@ + + + + Tests for getClientRects/getBoundingClientRect + + + + + + + + +
+ +
+
+
+
+
+
+ + + +
+
+
+ + + +
+
+ + +
+ +
+
+
+ +
+
+
+
+
+ +
+ + + + + + + + + + +

+ + + + diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 40a0466e55f..87d30b4c13b 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -186,17 +186,14 @@ public: NS_LITERAL_CSTRING(")/"); } - NS_IMETHOD - CollectReports(nsIMemoryMultiReporterCallback* aCallback, - nsISupports* aClosure) + nsresult + CollectForRuntime(bool aIsQuick, void* aData) { AssertIsOnMainThread(); - IterateData data; - if (mWorkerPrivate) { bool disabled; - if (!mWorkerPrivate->BlockAndCollectRuntimeStats(&data, &disabled)) { + if (!mWorkerPrivate->BlockAndCollectRuntimeStats(aIsQuick, aData, &disabled)) { return NS_ERROR_FAILURE; } @@ -217,12 +214,35 @@ public: mWorkerPrivate = nsnull; } } + return NS_OK; + } + + NS_IMETHOD + CollectReports(nsIMemoryMultiReporterCallback* aCallback, + nsISupports* aClosure) + { + AssertIsOnMainThread(); + + IterateData data; + nsresult rv = CollectForRuntime(/* isQuick = */false, &data); + if (NS_FAILED(rv)) { + return rv; + } // Always report, even if we're disabled, so that we at least get an entry // in about::memory. ReportJSRuntimeStats(data, mPathPrefix, aCallback, aClosure); + return NS_OK; } + + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *aAmount) + { + AssertIsOnMainThread(); + + return CollectForRuntime(/* isQuick = */true, aAmount); + } }; NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerMemoryReporter, nsIMemoryMultiReporter) @@ -1378,16 +1398,17 @@ class CollectRuntimeStatsRunnable : public WorkerControlRunnable Mutex mMutex; CondVar mCondVar; volatile bool mDone; - IterateData* mData; + bool mIsQuick; + void* mData; bool* mSucceeded; public: - CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, IterateData* aData, - bool* aSucceeded) + CollectRuntimeStatsRunnable(WorkerPrivate* aWorkerPrivate, bool aIsQuick, + void* aData, bool* aSucceeded) : WorkerControlRunnable(aWorkerPrivate, WorkerThread, UnchangedBusyCount), mMutex("CollectRuntimeStatsRunnable::mMutex"), mCondVar(mMutex, "CollectRuntimeStatsRunnable::mCondVar"), mDone(false), - mData(aData), mSucceeded(aSucceeded) + mIsQuick(aIsQuick), mData(aData), mSucceeded(aSucceeded) { } bool @@ -1429,7 +1450,9 @@ public: { JSAutoSuspendRequest asr(aCx); - *mSucceeded = CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData); + *mSucceeded = mIsQuick ? + mozilla::xpconnect::memory::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), mData) : + mozilla::xpconnect::memory::CollectCompartmentStatsForRuntime(JS_GetRuntime(aCx), mData); { MutexAutoLock lock(mMutex); @@ -2569,7 +2592,7 @@ WorkerPrivate::ScheduleDeletion(bool aWasPending) } bool -WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData, bool* aDisabled) +WorkerPrivate::BlockAndCollectRuntimeStats(bool aIsQuick, void* aData, bool* aDisabled) { AssertIsOnMainThread(); NS_ASSERTION(aData, "Null data!"); @@ -2589,7 +2612,7 @@ WorkerPrivate::BlockAndCollectRuntimeStats(IterateData* aData, bool* aDisabled) bool succeeded; nsRefPtr runnable = - new CollectRuntimeStatsRunnable(this, aData, &succeeded); + new CollectRuntimeStatsRunnable(this, aIsQuick, aData, &succeeded); if (!runnable->Dispatch(nsnull)) { NS_WARNING("Failed to dispatch runnable!"); succeeded = false; diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index ac4742311e9..4f1caea3760 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -661,8 +661,7 @@ public: ScheduleDeletion(bool aWasPending); bool - BlockAndCollectRuntimeStats(mozilla::xpconnect::memory::IterateData* aData, - bool* aDisabled); + BlockAndCollectRuntimeStats(bool isQuick, void* aData, bool* aDisabled); bool DisableMemoryReporter(); diff --git a/editor/txtsvc/public/nsIInlineSpellChecker.idl b/editor/txtsvc/public/nsIInlineSpellChecker.idl index b34f6f57d54..2baa96110e9 100644 --- a/editor/txtsvc/public/nsIInlineSpellChecker.idl +++ b/editor/txtsvc/public/nsIInlineSpellChecker.idl @@ -43,7 +43,7 @@ interface nsISelection; interface nsIEditor; interface nsIEditorSpellCheck; -[scriptable, uuid(f456dda1-965d-470c-8c55-e51b38e45212)] +[scriptable, uuid(df635540-d073-47b8-8678-18776130691d)] interface nsIInlineSpellChecker : nsISupports { @@ -68,6 +68,7 @@ interface nsIInlineSpellChecker : nsISupports nsIDOMRange getMisspelledWord(in nsIDOMNode aNode, in long aOffset); void replaceWord(in nsIDOMNode aNode, in long aOffset, in AString aNewword); void addWordToDictionary(in AString aWord); + void removeWordFromDictionary(in AString aWord); void ignoreWord(in AString aWord); void ignoreWords([array, size_is(aCount)] in wstring aWordsToIgnore, in unsigned long aCount); diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 6a2631b6901..91069a7c391 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -869,6 +869,24 @@ mozInlineSpellChecker::AddWordToDictionary(const nsAString &word) return ScheduleSpellCheck(status); } +// mozInlineSpellChecker::RemoveWordFromDictionary + +NS_IMETHODIMP +mozInlineSpellChecker::RemoveWordFromDictionary(const nsAString &word) +{ + NS_ENSURE_TRUE(mSpellCheck, NS_ERROR_NOT_INITIALIZED); + + nsAutoString wordstr(word); + nsresult rv = mSpellCheck->RemoveWordFromDictionary(wordstr.get()); + NS_ENSURE_SUCCESS(rv, rv); + + mozInlineSpellStatus status(this); + nsCOMPtr range = do_QueryInterface(NULL); // Check everything + rv = status.InitForRange(range); + NS_ENSURE_SUCCESS(rv, rv); + return ScheduleSpellCheck(status); +} + // mozInlineSpellChecker::IgnoreWord NS_IMETHODIMP diff --git a/gfx/ycbcr/QuellGccWarnings.patch b/gfx/ycbcr/QuellGccWarnings.patch deleted file mode 100644 index 82482205df8..00000000000 --- a/gfx/ycbcr/QuellGccWarnings.patch +++ /dev/null @@ -1,40 +0,0 @@ -diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp ---- a/gfx/ycbcr/yuv_convert.cpp -+++ b/gfx/ycbcr/yuv_convert.cpp -@@ -337,16 +337,17 @@ NS_GFX_(void) ScaleYCbCrToRGB32(const ui - source_dx_uv >> kFractionBits); - } - } - else { - ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr, - dest_pixel, width, source_dx); - } - #else -+ (void)source_dx_uv; - ScaleYUVToRGB32Row(y_ptr, u_ptr, v_ptr, - dest_pixel, width, source_dx); - #endif - } - } - // MMX used for FastConvertYUVToRGB32Row and FilterRows requires emms. - if (has_mmx) - EMMS(); -diff --git a/gfx/ycbcr/yuv_row.h b/gfx/ycbcr/yuv_row.h ---- a/gfx/ycbcr/yuv_row.h -+++ b/gfx/ycbcr/yuv_row.h -@@ -129,14 +129,14 @@ extern SIMD_ALIGNED(int16 kCoefficientsR - #if defined(ARCH_CPU_X86) && !defined(ARCH_CPU_X86_64) - #if defined(_MSC_VER) - #define EMMS() __asm emms - #pragma warning(disable: 4799) - #else - #define EMMS() asm("emms") - #endif - #else --#define EMMS() -+#define EMMS() ((void)0) - #endif - - } // extern "C" - - #endif // MEDIA_BASE_YUV_ROW_H_ diff --git a/gfx/ycbcr/README b/gfx/ycbcr/README index a951bc83a84..95ea0fdb270 100644 --- a/gfx/ycbcr/README +++ b/gfx/ycbcr/README @@ -25,5 +25,3 @@ convert.patch contains the following changes: win64.patch: SSE2 optimization for Microsoft Visual C++ x64 version TypeFromSize.patch: Bug 656185 - Add a method to detect YUVType from plane sizes. - -QuellGccWarnings.patch: Bug 711895 - Avoid some GCC compilation warnings. diff --git a/gfx/ycbcr/update.sh b/gfx/ycbcr/update.sh index 3a38fe81a3d..9b0c07ab54f 100644 --- a/gfx/ycbcr/update.sh +++ b/gfx/ycbcr/update.sh @@ -9,4 +9,3 @@ cp $1/media/base/yuv_row_posix.cc yuv_row_c.cpp patch -p3 isLive()) + while (cur < end && !cur->isLive()) ++cur; } @@ -201,7 +201,8 @@ class HashTable : private AllocPolicy void popFront() { JS_ASSERT(!empty()); - while (++cur != end && !cur->isLive()); + while (++cur < end && !cur->isLive()) + continue; } }; @@ -342,14 +343,14 @@ class HashTable : private AllocPolicy Entry *newTable = (Entry *)alloc.malloc_(capacity * sizeof(Entry)); if (!newTable) return NULL; - for (Entry *e = newTable, *end = e + capacity; e != end; ++e) + for (Entry *e = newTable, *end = e + capacity; e < end; ++e) new(e) Entry(); return newTable; } static void destroyTable(AllocPolicy &alloc, Entry *oldTable, uint32_t capacity) { - for (Entry *e = oldTable, *end = e + capacity; e != end; ++e) + for (Entry *e = oldTable, *end = e + capacity; e < end; ++e) e->~Entry(); alloc.free_(oldTable); } @@ -565,7 +566,7 @@ class HashTable : private AllocPolicy table = newTable; /* Copy only live entries, leaving removed ones behind. */ - for (Entry *src = oldTable, *end = src + oldCap; src != end; ++src) { + for (Entry *src = oldTable, *end = src + oldCap; src < end; ++src) { if (src->isLive()) { src->unsetCollision(); findFreeEntry(src->getKeyHash()) = Move(*src); @@ -607,7 +608,7 @@ class HashTable : private AllocPolicy memset(table, 0, sizeof(*table) * capacity()); } else { uint32_t tableCapacity = capacity(); - for (Entry *e = table, *end = table + tableCapacity; e != end; ++e) + for (Entry *e = table, *end = table + tableCapacity; e < end; ++e) *e = Move(Entry()); } removedCount = 0; diff --git a/js/src/configure.in b/js/src/configure.in index 13661c92c44..12467ef87c5 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1806,20 +1806,14 @@ if test "$GNU_CC"; then _MOZ_RTTI_FLAGS_ON=-frtti _MOZ_RTTI_FLAGS_OFF=-fno-rtti - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Wdeclaration-after-statement - MSVC doesn't like these - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -Wpointer-arith -Wdeclaration-after-statement -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wno-overlength-strings" - + # Turn on GNU specific features + # -Wall - turn on all warnings + # -pedantic - make compiler warn about non-ANSI stuff, and + # be a little bit stricter + # Warnings slamm took out for now (these were giving more noise than help): + # -Wbad-function-cast - warns when casting a function to a new return type + # -Wshadow - removed because it generates more noise than help --pete + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wall -W -Wno-unused -Wpointer-arith" if test -z "$INTEL_CC" -a -z "$CLANG_CC"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -1835,26 +1829,12 @@ if test "$GNU_CC"; then dnl Turn pedantic on but disable the warnings for long long _PEDANTIC=1 + if test -z "$INTEL_CC"; then + _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -W" + fi + _DEFINES_CFLAGS='-include $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT' _USE_CPP_INCLUDE_FLAG=1 - - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_cc_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_C - _SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_cc_has_wtype_limits="yes", - ac_cc_has_wtype_limits="no") - CFLAGS="$_SAVE_CFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_cc_has_wtype_limits" = "yes"; then - _WARNINGS_CFLAGS="${_WARNINGS_CFLAGS} -Wtype-limits" - fi elif test "$SOLARIS_SUNPRO_CC"; then DSO_CFLAGS='' if test "$CPU_ARCH" = "sparc"; then @@ -1880,21 +1860,8 @@ else fi if test "$GNU_CXX"; then - # Turn on GNU-specific warnings: - # -Wall - turn on a lot of warnings - # -pedantic - this is turned on below - # -Wpointer-arith - enabled with -pedantic, but good to have even if not - # -Woverloaded-virtual - ??? - # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wempty-body" - - # Turn off the following warnings that -Wall/-pedantic turn on: - # -Woverlength-strings - we exceed the minimum maximum length all the time - # -Wctor-dtor-privacy - ??? - # - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-overlength-strings -Wno-ctor-dtor-privacy" - + # Turn on GNU specific features + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor" if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then # Don't use -Wcast-align with ICC or clang case "$CPU_ARCH" in @@ -1995,24 +1962,6 @@ if test "$GNU_CXX"; then _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=return-type" fi - AC_CACHE_CHECK(whether the compiler supports -Wtype-limits, - ac_has_wtype_limits, - [ - AC_LANG_SAVE - AC_LANG_CPLUSPLUS - _SAVE_CXXFLAGS="$CXXFLAGS" - CXXFLAGS="$CXXFLAGS -Wtype-limits" - AC_TRY_COMPILE([], - [return(0);], - ac_has_wtype_limits="yes", - ac_has_wtype_limits="no") - CXXFLAGS="$_SAVE_CXXFLAGS" - AC_LANG_RESTORE - ]) - if test "$ac_has_wtype_limits" = "yes"; then - _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wtype-limits" - fi - else _DEFINES_CXXFLAGS='-DMOZILLA_CLIENT -D_JS_CONFDEFS_H_ $(ACDEFINES)' fi diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index f30d3f4696b..5afb88e20fb 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1863,23 +1863,29 @@ EmitIndexOp(JSContext *cx, JSOp op, uintN index, BytecodeEmitter *bce, JSOp *psu JS_END_MACRO static bool -EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) +EmitAtomOp(JSContext *cx, JSAtom *atom, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) { JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM); - if (op == JSOP_GETPROP && - pn->pn_atom == cx->runtime->atomState.lengthAtom) { + if (op == JSOP_GETPROP && atom == cx->runtime->atomState.lengthAtom) { /* Specialize length accesses for the interpreter. */ op = JSOP_LENGTH; } jsatomid index; - if (!bce->makeAtomIndex(pn->pn_atom, &index)) + if (!bce->makeAtomIndex(atom, &index)) return false; return EmitIndexOp(cx, op, index, bce, psuffix); } +static bool +EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *psuffix = NULL) +{ + JS_ASSERT(pn->pn_atom != NULL); + return EmitAtomOp(cx, pn->pn_atom, op, bce, psuffix); +} + static JSBool EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce) { @@ -5478,16 +5484,16 @@ EmitXMLTag(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } static bool -EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, XMLProcessingInstruction &pi) { JS_ASSERT(!bce->inStrictMode()); jsatomid index; - if (!bce->makeAtomIndex(pn->pn_pidata, &index)) + if (!bce->makeAtomIndex(pi.data(), &index)) return false; if (!EmitIndexOp(cx, JSOP_QNAMEPART, index, bce)) return false; - if (!EmitAtomOp(cx, pn, JSOP_XMLPI, bce)) + if (!EmitAtomOp(cx, pi.target(), JSOP_XMLPI, bce)) return false; return true; } @@ -6766,16 +6772,16 @@ EmitSyntheticStatements(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrd } static bool -EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) +EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ConditionalExpression &conditional) { /* Emit the condition, then branch if false to the else part. */ - if (!EmitTree(cx, bce, pn->pn_kid1)) + if (!EmitTree(cx, bce, &conditional.condition())) return false; ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_COND); if (noteIndex < 0) return false; ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFEQ, 0); - if (beq < 0 || !EmitTree(cx, bce, pn->pn_kid2)) + if (beq < 0 || !EmitTree(cx, bce, &conditional.thenExpression())) return false; /* Jump around else, fixup the branch, emit else, fixup jump. */ @@ -6796,7 +6802,7 @@ EmitConditionalExpression(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) */ JS_ASSERT(bce->stackDepth > 0); bce->stackDepth--; - if (!EmitTree(cx, bce, pn->pn_kid3)) + if (!EmitTree(cx, bce, &conditional.elseExpression())) return false; CHECK_AND_SET_JUMP_OFFSET_AT(cx, bce, jmp); return SetSrcNoteOffset(cx, bce, noteIndex, 0, jmp - beq); @@ -7221,8 +7227,8 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; break; - case PNK_HOOK: - ok = EmitConditionalExpression(cx, bce, pn); + case PNK_CONDITIONAL: + ok = EmitConditionalExpression(cx, bce, pn->asConditionalExpression()); break; case PNK_OR: @@ -7441,7 +7447,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_DEFSHARP: { JS_ASSERT(bce->hasSharps()); - int sharpnum = pn->pn_num; + jsint sharpnum = pn->asDefSharpExpression().number(); pn = pn->pn_kid; if (pn->isKind(PNK_RB)) { ok = EmitArray(cx, bce, pn, sharpnum); @@ -7460,13 +7466,14 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!EmitTree(cx, bce, pn)) return JS_FALSE; - EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, (jsatomid) sharpnum); + EMIT_UINT16PAIR_IMM_OP(JSOP_DEFSHARP, bce->sharpSlotBase, jsatomid(sharpnum)); break; } case PNK_USESHARP: JS_ASSERT(bce->hasSharps()); - EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase, (jsatomid) pn->pn_num); + EMIT_UINT16PAIR_IMM_OP(JSOP_USESHARP, bce->sharpSlotBase, + jsatomid(pn->asUseSharpExpression().number())); break; #endif /* JS_HAS_SHARP_VARS */ @@ -7594,7 +7601,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) break; case PNK_XMLPI: - if (!EmitXMLProcessingInstruction(cx, bce, pn)) + if (!EmitXMLProcessingInstruction(cx, bce, pn->asXMLProcessingInstruction())) return false; break; #endif /* JS_HAS_XML_SUPPORT */ diff --git a/js/src/frontend/FoldConstants.cpp b/js/src/frontend/FoldConstants.cpp index 46e84891470..3d621febdf0 100644 --- a/js/src/frontend/FoldConstants.cpp +++ b/js/src/frontend/FoldConstants.cpp @@ -273,11 +273,13 @@ FoldXMLConstants(JSContext *cx, ParseNode *pn, TreeContext *tc) return JS_FALSE; break; - case PNK_XMLPI: - str = js_MakeXMLPIString(cx, pn2->pn_pitarget, pn2->pn_pidata); + case PNK_XMLPI: { + XMLProcessingInstruction &pi = pn2->asXMLProcessingInstruction(); + str = js_MakeXMLPIString(cx, pi.target(), pi.data()); if (!str) return JS_FALSE; break; + } cantfold: default: @@ -556,7 +558,7 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) break; /* FALL THROUGH */ - case PNK_HOOK: + case PNK_CONDITIONAL: /* Reduce 'if (C) T; else E' into T for true C, E for false. */ switch (pn1->getKind()) { case PNK_NUMBER: @@ -591,8 +593,8 @@ js::FoldConstants(JSContext *cx, ParseNode *pn, TreeContext *tc, bool inCond) * False condition and no else, or an empty then-statement was * moved up over pn. Either way, make pn an empty block (not an * empty statement, which does not decompile, even when labeled). - * NB: pn must be a PNK_IF as PNK_HOOK can never have a null kid - * or an empty statement for a child. + * NB: pn must be a PNK_IF as PNK_CONDITIONAL can never have a null + * kid or an empty statement for a child. */ pn->setKind(PNK_STATEMENTLIST); pn->setArity(PN_LIST); diff --git a/js/src/frontend/ParseNode.cpp b/js/src/frontend/ParseNode.cpp index 74256ed0792..50acf52a7e5 100644 --- a/js/src/frontend/ParseNode.cpp +++ b/js/src/frontend/ParseNode.cpp @@ -535,7 +535,7 @@ CloneParseTree(ParseNode *opn, TreeContext *tc) case PN_UNARY: NULLCHECK(pn->pn_kid = CloneParseTree(opn->pn_kid, tc)); - pn->pn_num = opn->pn_num; + pn->pn_u.unary.num = opn->pn_u.unary.num; pn->pn_hidden = opn->pn_hidden; break; diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h index 29d8bbe969c..e0dbe6a29b1 100644 --- a/js/src/frontend/ParseNode.h +++ b/js/src/frontend/ParseNode.h @@ -41,6 +41,8 @@ #ifndef ParseNode_h__ #define ParseNode_h__ +#include "mozilla/Attributes.h" + #include "jsscript.h" #include "frontend/ParseMaps.h" @@ -61,7 +63,7 @@ namespace js { enum ParseNodeKind { PNK_SEMI, PNK_COMMA, - PNK_HOOK, + PNK_CONDITIONAL, PNK_COLON, PNK_OR, PNK_AND, @@ -313,7 +315,8 @@ enum ParseNodeKind { * PNK_MULASSIGN, * PNK_DIVASSIGN, * PNK_MODASSIGN - * PNK_HOOK ternary pn_kid1: cond, pn_kid2: then, pn_kid3: else + * PNK_CONDITIONAL ternary (cond ? trueExpr : falseExpr) + * pn_kid1: cond, pn_kid2: then, pn_kid3: else * PNK_OR binary pn_left: first in || chain, pn_right: rest of chain * PNK_AND binary pn_left: first in && chain, pn_right: rest of chain * PNK_BITOR binary pn_left: left-assoc | expr, pn_right: ^ expr @@ -485,6 +488,10 @@ struct Definition; class LoopControlStatement; class BreakStatement; class ContinueStatement; +class XMLProcessingInstruction; +class ConditionalExpression; +class DefSharpExpression; +class UseSharpExpression; struct ParseNode { private: @@ -495,6 +502,8 @@ struct ParseNode { pn_used : 1, /* name node is on a use-chain */ pn_defn : 1; /* this node is a Definition */ + void operator=(const ParseNode &other) MOZ_DELETE; + public: ParseNode(ParseNodeKind kind, JSOp op, ParseNodeArity arity) : pn_type(kind), pn_op(op), pn_arity(arity), pn_parens(0), pn_used(0), pn_defn(0), @@ -590,7 +599,7 @@ struct ParseNode { } binary; struct { /* one kid if unary */ ParseNode *kid; - jsint num; /* -1 or sharp variable number */ + jsint num; /* sharp variable number or unused */ JSBool hidden; /* hidden genexp-induced JSOP_YIELD or directive prologue member (as pn_prologue) */ @@ -617,15 +626,20 @@ struct ParseNode { AtomDefnMapPtr defnMap; ParseNode *tree; /* sub-tree containing name uses */ } nameset; - struct { /* PN_NULLARY variant for E4X XML PI */ - PropertyName *target; /* target in */ - JSAtom *data; /* data (or null) in */ - } xmlpi; jsdouble dval; /* aligned numeric literal value */ class { friend class LoopControlStatement; PropertyName *label; /* target of break/continue statement */ } loopControl; + class { /* E4X XML PI */ + friend class XMLProcessingInstruction; + PropertyName *target; /* non-empty */ + JSAtom *data; /* may be empty, never null */ + } xmlpi; + class { + friend class UseSharpExpression; + jsint number; /* #number# */ + } usesharp; } pn_u; #define pn_funbox pn_u.name.funbox @@ -646,7 +660,6 @@ struct ParseNode { #define pn_pval pn_u.binary.pval #define pn_iflags pn_u.binary.iflags #define pn_kid pn_u.unary.kid -#define pn_num pn_u.unary.num #define pn_hidden pn_u.unary.hidden #define pn_prologue pn_u.unary.hidden #define pn_atom pn_u.name.atom @@ -656,8 +669,6 @@ struct ParseNode { #define pn_names pn_u.nameset.defnMap #define pn_tree pn_u.nameset.tree #define pn_dval pn_u.dval -#define pn_pitarget pn_u.xmlpi.target -#define pn_pidata pn_u.xmlpi.data protected: void init(TokenKind type, JSOp op, ParseNodeArity arity) { @@ -924,6 +935,12 @@ struct ParseNode { /* Casting operations. */ inline BreakStatement &asBreakStatement(); inline ContinueStatement &asContinueStatement(); +#if JS_HAS_XML_SUPPORT + inline XMLProcessingInstruction &asXMLProcessingInstruction(); +#endif + inline ConditionalExpression &asConditionalExpression(); + inline DefSharpExpression &asDefSharpExpression(); + inline UseSharpExpression &asUseSharpExpression(); }; struct NullaryNode : public ParseNode { @@ -1059,6 +1076,145 @@ ParseNode::asContinueStatement() return *static_cast(this); } +class DebuggerStatement : public ParseNode { + public: + DebuggerStatement(const TokenPos &pos) + : ParseNode(PNK_DEBUGGER, JSOP_NOP, PN_NULLARY, pos) + { } +}; + +#if JS_HAS_XML_SUPPORT +class XMLProcessingInstruction : public ParseNode { + public: + XMLProcessingInstruction(PropertyName *target, JSAtom *data, const TokenPos &pos) + : ParseNode(PNK_XMLPI, JSOP_NOP, PN_NULLARY, pos) + { + pn_u.xmlpi.target = target; + pn_u.xmlpi.data = data; + } + + PropertyName *target() const { + return pn_u.xmlpi.target; + } + + JSAtom *data() const { + return pn_u.xmlpi.data; + } +}; + +inline XMLProcessingInstruction & +ParseNode::asXMLProcessingInstruction() +{ + JS_ASSERT(isKind(PNK_XMLPI)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_NULLARY); + return *static_cast(this); +} +#endif + +class ConditionalExpression : public ParseNode { + public: + ConditionalExpression(ParseNode *condition, ParseNode *thenExpr, ParseNode *elseExpr) + : ParseNode(PNK_CONDITIONAL, JSOP_NOP, PN_TERNARY, + TokenPos::make(condition->pn_pos.begin, elseExpr->pn_pos.end)) + { + JS_ASSERT(condition); + JS_ASSERT(thenExpr); + JS_ASSERT(elseExpr); + pn_u.ternary.kid1 = condition; + pn_u.ternary.kid2 = thenExpr; + pn_u.ternary.kid3 = elseExpr; + } + + ParseNode &condition() const { + return *pn_u.ternary.kid1; + } + + ParseNode &thenExpression() const { + return *pn_u.ternary.kid2; + } + + ParseNode &elseExpression() const { + return *pn_u.ternary.kid3; + } +}; + +inline ConditionalExpression & +ParseNode::asConditionalExpression() +{ + JS_ASSERT(isKind(PNK_CONDITIONAL)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_TERNARY); + return *static_cast(this); +} + +class DefSharpExpression : public ParseNode { + public: + DefSharpExpression(uint16_t number, ParseNode *expr, + const TokenPtr &begin, const TokenPtr &end) + : ParseNode(PNK_DEFSHARP, JSOP_NOP, PN_UNARY, TokenPos::make(begin, end)) + { + pn_u.unary.num = number; + pn_u.unary.kid = expr; + } + + jsint number() const { + return pn_u.unary.num; + } + + ParseNode &expression() const { + return *pn_u.unary.kid; + } +}; + +inline DefSharpExpression & +ParseNode::asDefSharpExpression() +{ + JS_ASSERT(isKind(PNK_DEFSHARP)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_UNARY); + return *static_cast(this); +} + +class UseSharpExpression : public ParseNode { + public: + UseSharpExpression(uint16_t number, const TokenPos &pos) + : ParseNode(PNK_USESHARP, JSOP_NOP, PN_NULLARY, pos) + { + pn_u.usesharp.number = number; + } + + jsint number() const { + return pn_u.usesharp.number; + } +}; + +inline UseSharpExpression & +ParseNode::asUseSharpExpression() +{ + JS_ASSERT(isKind(PNK_USESHARP)); + JS_ASSERT(isOp(JSOP_NOP)); + JS_ASSERT(pn_arity == PN_NULLARY); + return *static_cast(this); +} + +class ThisLiteral : public ParseNode { + public: + ThisLiteral(const TokenPos &pos) : ParseNode(PNK_THIS, JSOP_THIS, PN_NULLARY, pos) { } +}; + +class NullLiteral : public ParseNode { + public: + NullLiteral(const TokenPos &pos) : ParseNode(PNK_NULL, JSOP_NULL, PN_NULLARY, pos) { } +}; + +class BooleanLiteral : public ParseNode { + public: + BooleanLiteral(bool b, const TokenPos &pos) + : ParseNode(b ? PNK_TRUE : PNK_FALSE, b ? JSOP_TRUE : JSOP_FALSE, PN_NULLARY, pos) + { } +}; + ParseNode * CloneLeftHandSide(ParseNode *opn, TreeContext *tc); diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index c87cae9e40c..784767543be 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -2931,7 +2931,6 @@ Parser::letBlock(LetContext letContext) if (!semi) return NULL; - semi->pn_num = -1; semi->pn_kid = pnlet; letContext = LetExpresion; @@ -4226,7 +4225,7 @@ Parser::statement() return pn; case TOK_DEBUGGER: - pn = NullaryNode::create(PNK_DEBUGGER, tc); + pn = tc->parser->new_(tokenStream.currentToken().pos); if (!pn) return NULL; tc->flags |= TCF_FUN_HEAVYWEIGHT; @@ -4643,37 +4642,30 @@ Parser::orExpr1() JS_ALWAYS_INLINE ParseNode * Parser::condExpr1() { - ParseNode *pn = orExpr1(); - if (pn && tokenStream.isCurrentTokenType(TOK_HOOK)) { - ParseNode *pn1 = pn; - pn = TernaryNode::create(PNK_HOOK, tc); - if (!pn) - return NULL; + ParseNode *condition = orExpr1(); + if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK)) + return condition; - /* - * Always accept the 'in' operator in the middle clause of a ternary, - * where it's unambiguous, even if we might be parsing the init of a - * for statement. - */ - uintN oldflags = tc->flags; - tc->flags &= ~TCF_IN_FOR_INIT; - ParseNode *pn2 = assignExpr(); - tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + /* + * Always accept the 'in' operator in the middle clause of a ternary, + * where it's unambiguous, even if we might be parsing the init of a + * for statement. + */ + uintN oldflags = tc->flags; + tc->flags &= ~TCF_IN_FOR_INIT; + ParseNode *thenExpr = assignExpr(); + tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); + if (!thenExpr) + return NULL; - if (!pn2) - return NULL; - MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); - ParseNode *pn3 = assignExpr(); - if (!pn3) - return NULL; - pn->pn_pos.begin = pn1->pn_pos.begin; - pn->pn_pos.end = pn3->pn_pos.end; - pn->pn_kid1 = pn1; - pn->pn_kid2 = pn2; - pn->pn_kid3 = pn3; - tokenStream.getToken(); /* need to read one token past the end */ - } - return pn; + MUST_MATCH_TOKEN(TOK_COLON, JSMSG_COLON_IN_COND); + + ParseNode *elseExpr = assignExpr(); + if (!elseExpr) + return NULL; + + tokenStream.getToken(); /* read one token past the end */ + return new_(condition, thenExpr, elseExpr); } bool @@ -6363,13 +6355,10 @@ Parser::xmlElementContent(ParseNode *pn) pn2->pn_xflags &= ~PNX_XMLROOT; pn->pn_xflags |= pn2->pn_xflags; } else if (tt == TOK_XMLPI) { - pn2 = NullaryNode::create(PNK_XMLPI, tc); + const Token &tok = tokenStream.currentToken(); + pn2 = new_(tok.xmlPITarget(), tok.xmlPIData(), tok.pos); if (!pn2) return false; - const Token &tok = tokenStream.currentToken(); - pn2->setOp(tok.t_op); - pn2->pn_pitarget = tok.xmlPITarget(); - pn2->pn_pidata = tok.xmlPIData(); } else { JS_ASSERT(tt == TOK_XMLCDATA || tt == TOK_XMLCOMMENT); pn2 = atomNode(tt == TOK_XMLCDATA ? PNK_XMLCDATA : PNK_XMLCOMMENT, @@ -6576,16 +6565,6 @@ Parser::parseXMLText(JSObject *chain, bool allowList) #endif /* JS_HAS_XMLSUPPORT */ -static ParseNode * -PrimaryExprNode(ParseNodeKind kind, JSOp op, TreeContext *tc) -{ - ParseNode *pn = NullaryNode::create(kind, tc); - if (!pn) - return NULL; - pn->setOp(op); - return pn; -} - ParseNode * Parser::primaryExpr(TokenKind tt, JSBool afterDot) { @@ -6954,40 +6933,45 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) #endif #if JS_HAS_SHARP_VARS - case TOK_DEFSHARP: - pn = UnaryNode::create(PNK_DEFSHARP, tc); - if (!pn) + case TOK_DEFSHARP: { + if (!tc->ensureSharpSlots()) return NULL; - pn->pn_num = tokenStream.currentToken().sharpNumber(); + const Token &tok = tokenStream.currentToken(); + TokenPtr begin = tok.pos.begin; + uint16_t number = tok.sharpNumber(); + tt = tokenStream.getToken(TSF_OPERAND); - pn->pn_kid = primaryExpr(tt, JS_FALSE); - if (!pn->pn_kid) + ParseNode *expr = primaryExpr(tt, false); + if (!expr) return NULL; - if (pn->pn_kid->isKind(PNK_USESHARP) || - pn->pn_kid->isKind(PNK_DEFSHARP) || - pn->pn_kid->isKind(PNK_STRING) || - pn->pn_kid->isKind(PNK_NUMBER) || - pn->pn_kid->isKind(PNK_TRUE) || - pn->pn_kid->isKind(PNK_FALSE) || - pn->pn_kid->isKind(PNK_NULL) || - pn->pn_kid->isKind(PNK_THIS)) + if (expr->isKind(PNK_USESHARP) || + expr->isKind(PNK_DEFSHARP) || + expr->isKind(PNK_STRING) || + expr->isKind(PNK_NUMBER) || + expr->isKind(PNK_TRUE) || + expr->isKind(PNK_FALSE) || + expr->isKind(PNK_NULL) || + expr->isKind(PNK_THIS)) { - reportErrorNumber(pn->pn_kid, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); + reportErrorNumber(expr, JSREPORT_ERROR, JSMSG_BAD_SHARP_VAR_DEF); return NULL; } - if (!tc->ensureSharpSlots()) - return NULL; - break; - - case TOK_USESHARP: - /* Check for forward/dangling references at runtime, to allow eval. */ - pn = NullaryNode::create(PNK_USESHARP, tc); + pn = new_(number, expr, begin, tokenStream.currentToken().pos.end); if (!pn) return NULL; + break; + } + + case TOK_USESHARP: { if (!tc->ensureSharpSlots()) return NULL; - pn->pn_num = tokenStream.currentToken().sharpNumber(); + /* Check for forward/dangling references at runtime, to allow eval. */ + const Token &tok = tokenStream.currentToken(); + pn = new_(tok.sharpNumber(), tok.pos); + if (!pn) + return NULL; break; + } #endif /* JS_HAS_SHARP_VARS */ case TOK_LP: @@ -7053,14 +7037,14 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) break; #if JS_HAS_XML_SUPPORT - case TOK_XMLPI: + case TOK_XMLPI: { JS_ASSERT(!tc->inStrictMode()); - pn = NullaryNode::create(PNK_XMLPI, tc); + const Token &tok = tokenStream.currentToken(); + pn = new_(tok.xmlPITarget(), tok.xmlPIData(), tok.pos); if (!pn) return NULL; - pn->pn_pitarget = tokenStream.currentToken().xmlPITarget(); - pn->pn_pidata = tokenStream.currentToken().xmlPIData(); break; + } #endif case TOK_NAME: @@ -7223,13 +7207,13 @@ Parser::primaryExpr(TokenKind tt, JSBool afterDot) break; case TOK_TRUE: - return PrimaryExprNode(PNK_TRUE, JSOP_TRUE, tc); + return new_(true, tokenStream.currentToken().pos); case TOK_FALSE: - return PrimaryExprNode(PNK_FALSE, JSOP_FALSE, tc); + return new_(false, tokenStream.currentToken().pos); case TOK_THIS: - return PrimaryExprNode(PNK_THIS, JSOP_THIS, tc); + return new_(tokenStream.currentToken().pos); case TOK_NULL: - return PrimaryExprNode(PNK_NULL, JSOP_NULL, tc); + return new_(tokenStream.currentToken().pos); case TOK_ERROR: /* The scanner or one of its subroutines reported the error. */ diff --git a/js/src/frontend/TokenStream.h b/js/src/frontend/TokenStream.h index 0490804e7c3..274a4a18f8f 100644 --- a/js/src/frontend/TokenStream.h +++ b/js/src/frontend/TokenStream.h @@ -330,8 +330,8 @@ struct Token { private: friend struct Token; struct { /* pair for XML PI */ - JSAtom *data; /* auxiliary atom table entry */ - PropertyName *target; /* main atom table entry */ + PropertyName *target; /* non-empty */ + JSAtom *data; /* maybe empty, never null */ } xmlpi; uint16_t sharpNumber; /* sharp variable number: #1# or #1= */ jsdouble number; /* floating point number */ @@ -359,6 +359,9 @@ struct Token { } void setProcessingInstruction(PropertyName *target, JSAtom *data) { + JS_ASSERT(target); + JS_ASSERT(data); + JS_ASSERT(!target->empty()); u.xmlpi.target = target; u.xmlpi.data = data; } diff --git a/js/src/gc/Barrier.h b/js/src/gc/Barrier.h index ef6e663f55f..1503bcf9306 100644 --- a/js/src/gc/Barrier.h +++ b/js/src/gc/Barrier.h @@ -426,6 +426,7 @@ class ReadBarriered T *value; public: + ReadBarriered() : value(NULL) {} ReadBarriered(T *value) : value(value) {} T *get() const { @@ -437,6 +438,9 @@ class ReadBarriered operator T*() const { return get(); } + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + T *unsafeGet() { return value; } void set(T *v) { value = v; } diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index b63297c1f82..2ec819b66de 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -404,24 +404,11 @@ DoGetElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *hole, Value * return true; } -template -static void -AssertGreaterThanZero(IndexType index) -{ - JS_ASSERT(index >= 0); -} - -template<> -void -AssertGreaterThanZero(jsuint index) -{ -} - template static JSBool GetElement(JSContext *cx, JSObject *obj, IndexType index, JSBool *hole, Value *vp) { - AssertGreaterThanZero(index); + JS_ASSERT(index >= 0); if (obj->isDenseArray() && index < obj->getDenseArrayInitializedLength() && !(*vp = obj->getDenseArrayElement(uint32_t(index))).isMagic(JS_ARRAY_HOLE)) { *hole = JS_FALSE; diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index cc10655ab5c..42e77995db3 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -104,7 +104,6 @@ ThreadData::ThreadData(JSRuntime *rt) #ifdef JS_THREADSAFE requestDepth(0), #endif - waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), execAlloc(NULL), bumpAlloc(NULL), @@ -152,17 +151,22 @@ ThreadData::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, * The computedSize is 0 because sizeof(DtoaState) isn't available here and * it's not worth making it available. */ - *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0); + if (normal) + *normal = mallocSizeOf(dtoaState, /* sizeof(DtoaState) */0); - *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); + if (temporary) + *temporary = tempLifoAlloc.sizeOfExcludingThis(mallocSizeOf); - size_t method = 0, regexp = 0, unused = 0; - if (execAlloc) - execAlloc->sizeOfCode(&method, ®exp, &unused); - JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ - *regexpCode = regexp + unused; + if (regexpCode) { + size_t method = 0, regexp = 0, unused = 0; + if (execAlloc) + execAlloc->sizeOfCode(&method, ®exp, &unused); + JS_ASSERT(method == 0); /* this execAlloc is only used for regexp code */ + *regexpCode = regexp + unused; + } - *stackCommitted = stackSpace.sizeOfCommitted(); + if (stackCommitted) + *stackCommitted = stackSpace.sizeOfCommitted(); } #endif @@ -253,7 +257,8 @@ JSThread::sizeOfIncludingThis(JSMallocSizeOfFun mallocSizeOf, size_t *normal, si size_t *regexpCode, size_t *stackCommitted) { data.sizeOfExcludingThis(mallocSizeOf, normal, temporary, regexpCode, stackCommitted); - *normal += mallocSizeOf(this, sizeof(JSThread)); + if (normal) + *normal += mallocSizeOf(this, sizeof(JSThread)); } JSThread * @@ -1593,13 +1598,13 @@ JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx) * Retry when we are done with the background sweeping and have stopped * all the allocations and released the empty GC chunks. */ - { + ShrinkGCBuffers(this); #ifdef JS_THREADSAFE + { AutoLockGC lock(this); gcHelperThread.waitBackgroundSweepOrAllocEnd(); -#endif - gcChunkPool.expire(this, true); } +#endif if (!p) p = OffTheBooks::malloc_(nbytes); else if (p == reinterpret_cast(1)) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 7a878701a9e..59d60ce4506 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -140,12 +140,6 @@ struct ThreadData { /* Keeper of the contiguous stack used by all contexts in this thread. */ StackSpace stackSpace; - /* - * Flag indicating that we are waiving any soft limits on the GC heap - * because we want allocations to be infallible (except when we hit OOM). - */ - bool waiveGCQuota; - /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 1a58096577e..ab450aeec64 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -125,12 +125,18 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj return obj; } -JS_PUBLIC_API(void) +JS_FRIEND_API(void) JS_ShrinkingGC(JSContext *cx) { js_GC(cx, NULL, GC_SHRINK, gcstats::PUBLIC_API); } +JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt) +{ + ShrinkGCBuffers(rt); +} + JS_FRIEND_API(JSPrincipals *) JS_GetCompartmentPrincipals(JSCompartment *compartment) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 5a382c87d98..b9e2fd8c14f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -73,6 +73,9 @@ JS_ObjectCountDynamicSlots(JSObject *obj); extern JS_FRIEND_API(void) JS_ShrinkingGC(JSContext *cx); +extern JS_FRIEND_API(void) +JS_ShrinkGCBuffers(JSRuntime *rt); + extern JS_FRIEND_API(size_t) JS_GetE4XObjectsCreated(JSContext *cx); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 5a1e2e7cdea..3c9196f8e39 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -517,6 +517,22 @@ ChunkPool::expire(JSRuntime *rt, bool releaseAll) return freeList; } +static void +FreeChunkList(Chunk *chunkListHead) +{ + while (Chunk *chunk = chunkListHead) { + JS_ASSERT(!chunk->info.numArenasFreeCommitted); + chunkListHead = chunk->info.next; + FreeChunk(chunk); + } +} + +void +ChunkPool::expireAndFree(JSRuntime *rt, bool releaseAll) +{ + FreeChunkList(expire(rt, releaseAll)); +} + JS_FRIEND_API(int64_t) ChunkPool::countCleanDecommittedArenas(JSRuntime *rt) { @@ -553,16 +569,6 @@ Chunk::release(JSRuntime *rt, Chunk *chunk) FreeChunk(chunk); } -static void -FreeChunkList(Chunk *chunkListHead) -{ - while (Chunk *chunk = chunkListHead) { - JS_ASSERT(!chunk->info.numArenasFreeCommitted); - chunkListHead = chunk->info.next; - FreeChunk(chunk); - } -} - inline void Chunk::prepareToBeFreed(JSRuntime *rt) { @@ -923,11 +929,12 @@ InFreeList(ArenaHeader *aheader, uintptr_t addr) } /* - * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets - * thingKind accordingly. Otherwise returns the reason for rejection. + * Tests whether w is a (possibly dead) GC thing. Returns CGCT_VALID and + * details about the thing if so. On failure, returns the reason for rejection. */ inline ConservativeGCTest -MarkIfGCThingWord(JSTracer *trc, jsuword w) +IsAddressableGCThing(JSRuntime *rt, jsuword w, + gc::AllocKind *thingKindPtr, ArenaHeader **arenaHeader, void **thing) { /* * We assume that the compiler never uses sub-word alignment to store @@ -953,7 +960,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) Chunk *chunk = Chunk::fromAddress(addr); - if (!trc->runtime->gcChunkSet.has(chunk)) + if (!rt->gcChunkSet.has(chunk)) return CGCT_NOTCHUNK; /* @@ -974,7 +981,7 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) if (!aheader->allocated()) return CGCT_FREEARENA; - JSCompartment *curComp = trc->runtime->gcCurrentCompartment; + JSCompartment *curComp = rt->gcCurrentCompartment; if (curComp && curComp != aheader->compartment) return CGCT_OTHERCOMPARTMENT; @@ -988,20 +995,41 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) uintptr_t shift = (offset - minOffset) % Arena::thingSize(thingKind); addr -= shift; + if (thing) + *thing = reinterpret_cast(addr); + if (arenaHeader) + *arenaHeader = aheader; + if (thingKindPtr) + *thingKindPtr = thingKind; + return CGCT_VALID; +} + +/* + * Returns CGCT_VALID and mark it if the w can be a live GC thing and sets + * thingKind accordingly. Otherwise returns the reason for rejection. + */ +inline ConservativeGCTest +MarkIfGCThingWord(JSTracer *trc, jsuword w) +{ + void *thing; + ArenaHeader *aheader; + AllocKind thingKind; + ConservativeGCTest status = IsAddressableGCThing(trc->runtime, w, &thingKind, &aheader, &thing); + if (status != CGCT_VALID) + return status; + /* * Check if the thing is free. We must use the list of free spans as at * this point we no longer have the mark bits from the previous GC run and * we must account for newly allocated things. */ - if (InFreeList(aheader, addr)) + if (InFreeList(aheader, uintptr_t(thing))) return CGCT_NOTLIVE; - void *thing = reinterpret_cast(addr); - #ifdef DEBUG const char pattern[] = "machine_stack %lx"; - char nameBuf[sizeof(pattern) - 3 + sizeof(addr) * 2]; - JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) addr); + char nameBuf[sizeof(pattern) - 3 + sizeof(thing) * 2]; + JS_snprintf(nameBuf, sizeof(nameBuf), "machine_stack %lx", (unsigned long) thing); JS_SET_TRACING_NAME(trc, nameBuf); #endif MarkKind(trc, thing, MapAllocToTraceKind(thingKind)); @@ -1011,10 +1039,11 @@ MarkIfGCThingWord(JSTracer *trc, jsuword w) GCMarker *marker = static_cast(trc); if (marker->conservativeDumpFileName) marker->conservativeRoots.append(thing); - if (shift) + if (jsuword(thing) != w) marker->conservativeStats.unaligned++; } #endif + return CGCT_VALID; } @@ -1154,6 +1183,12 @@ RecordNativeStackTopForGC(JSContext *cx) } /* namespace js */ +bool +js_IsAddressableGCThing(JSRuntime *rt, jsuword w, gc::AllocKind *thingKind, void **thing) +{ + return js::IsAddressableGCThing(rt, w, thingKind, NULL, thing) == CGCT_VALID; +} + #ifdef DEBUG static void CheckLeakedRoots(JSRuntime *rt); @@ -1182,7 +1217,7 @@ js_FinishGC(JSRuntime *rt) * Finish the pool after the background thread stops in case it was doing * the background sweeping. */ - FreeChunkList(rt->gcChunkPool.expire(rt, true)); + rt->gcChunkPool.expireAndFree(rt, true); #ifdef DEBUG if (!rt->gcRootsHash.empty()) @@ -1342,7 +1377,7 @@ JSRuntime::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) void JSRuntime::reduceGCTriggerBytes(uint32_t amount) { JS_ASSERT(amount > 0); - JS_ASSERT(gcTriggerBytes >= amount); + JS_ASSERT(gcTriggerBytes - amount >= 0); if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; @@ -1361,7 +1396,7 @@ JSCompartment::setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind) void JSCompartment::reduceGCTriggerBytes(uint32_t amount) { JS_ASSERT(amount > 0); - JS_ASSERT(gcTriggerBytes >= amount); + JS_ASSERT(gcTriggerBytes - amount >= 0); if (gcTriggerBytes - amount < GC_ALLOCATION_THRESHOLD * GC_HEAP_GROWTH_FACTOR) return; gcTriggerBytes -= amount; @@ -1641,12 +1676,6 @@ RunLastDitchGC(JSContext *cx) #endif } -inline bool -IsGCAllowed(JSContext *cx) -{ - return !JS_THREAD_DATA(cx)->waiveGCQuota; -} - /* static */ void * ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) { @@ -1658,7 +1687,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = !!rt->gcIsNeeded; for (;;) { - if (JS_UNLIKELY(runGC) && IsGCAllowed(cx)) { + if (JS_UNLIKELY(runGC)) { RunLastDitchGC(cx); /* Report OOM of the GC failed to free enough memory. */ @@ -1679,14 +1708,11 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) return thing; /* - * We failed to allocate. Run the GC if we can unless we have done it - * already. Otherwise report OOM but first schedule a new GC soon. + * We failed to allocate. Run the GC if we haven't done it already. + * Otherwise report OOM. */ - if (runGC || !IsGCAllowed(cx)) { - AutoLockGC lock(rt); - TriggerGC(rt, gcstats::REFILL); + if (runGC) break; - } runGC = true; } @@ -2397,28 +2423,53 @@ GCHelperThread::threadLoop() } bool -GCHelperThread::prepareForBackgroundSweep(JSContext *cx) +GCHelperThread::prepareForBackgroundSweep() { - JS_ASSERT(cx->runtime == rt); JS_ASSERT(state == IDLE); size_t maxArenaLists = MAX_BACKGROUND_FINALIZE_KINDS * rt->compartments.length(); - if (!finalizeVector.reserve(maxArenaLists)) - return false; - context = cx; - return true; + return finalizeVector.reserve(maxArenaLists); } /* Must be called with the GC lock taken. */ -inline void -GCHelperThread::startBackgroundSweep(bool shouldShrink) +void +GCHelperThread::startBackgroundSweep(JSContext *cx, bool shouldShrink) { /* The caller takes the GC lock. */ JS_ASSERT(state == IDLE); + JS_ASSERT(cx); + JS_ASSERT(!finalizationContext); + finalizationContext = cx; shrinkFlag = shouldShrink; state = SWEEPING; PR_NotifyCondVar(wakeup); } +/* Must be called with the GC lock taken. */ +void +GCHelperThread::startBackgroundShrink() +{ + switch (state) { + case IDLE: + JS_ASSERT(!finalizationContext); + shrinkFlag = true; + state = SWEEPING; + PR_NotifyCondVar(wakeup); + break; + case SWEEPING: + shrinkFlag = true; + break; + case ALLOCATING: + case CANCEL_ALLOCATION: + /* + * If we have started background allocation there is nothing to + * shrink. + */ + break; + case SHUTDOWN: + JS_NOT_REACHED("No shrink on shutdown"); + } +} + /* Must be called with the GC lock taken. */ void GCHelperThread::waitBackgroundSweepEnd() @@ -2470,9 +2521,8 @@ GCHelperThread::replenishAndFreeLater(void *ptr) void GCHelperThread::doSweep() { - JS_ASSERT(context); - - { + if (JSContext *cx = finalizationContext) { + finalizationContext = NULL; AutoUnlockGC unlock(rt); /* @@ -2480,11 +2530,9 @@ GCHelperThread::doSweep() * finalizeObjects. */ for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i) - ArenaLists::backgroundFinalize(context, *i); + ArenaLists::backgroundFinalize(cx, *i); finalizeVector.resize(0); - context = NULL; - if (freeCursor) { void **array = freeCursorEnd - FREE_ARRAY_LENGTH; freeElementsAndArray(array, freeCursor); @@ -2499,7 +2547,18 @@ GCHelperThread::doSweep() freeVector.resize(0); } - ExpireChunksAndArenas(rt, shouldShrink()); + bool shrinking = shrinkFlag; + ExpireChunksAndArenas(rt, shrinking); + + /* + * The main thread may have called ShrinkGCBuffers while + * ExpireChunksAndArenas(rt, false) was running, so we recheck the flag + * afterwards. + */ + if (!shrinking && shrinkFlag) { + shrinkFlag = false; + ExpireChunksAndArenas(rt, true); + } } #endif /* JS_THREADSAFE */ @@ -2982,7 +3041,7 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) JS_ASSERT(!cx->gcBackgroundFree); rt->gcHelperThread.waitBackgroundSweepOrAllocEnd(); if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { - if (rt->gcHelperThread.prepareForBackgroundSweep(cx)) + if (rt->gcHelperThread.prepareForBackgroundSweep()) cx->gcBackgroundFree = &rt->gcHelperThread; } #endif @@ -2993,12 +3052,10 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) js_PurgeThreads_PostGlobalSweep(cx); #ifdef JS_THREADSAFE - if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { + if (cx->gcBackgroundFree) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); cx->gcBackgroundFree = NULL; - rt->gcHelperThread.startBackgroundSweep(gckind == GC_SHRINK); - } else { - JS_ASSERT(!cx->gcBackgroundFree); + rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK); } #endif @@ -3076,6 +3133,18 @@ js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, gcstats::Re namespace js { +void +ShrinkGCBuffers(JSRuntime *rt) +{ + AutoLockGC lock(rt); + JS_ASSERT(!rt->gcRunning); +#ifndef JS_THREADSAFE + ExpireChunksAndArenas(rt, true); +#else + rt->gcHelperThread.startBackgroundShrink(); +#endif +} + class AutoCopyFreeListToArenas { JSRuntime *rt; @@ -3154,6 +3223,28 @@ struct IterateCellCallbackOp void operator()(Cell *cell) { (*callback)(cx, data, cell, traceKind, thingSize); } }; +void +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback) +{ + CHECK_REQUEST(cx); + + JSRuntime *rt = cx->runtime; + JS_ASSERT(!rt->gcRunning); + + AutoLockGC lock(rt); + AutoGCSession gcsession(cx); +#ifdef JS_THREADSAFE + rt->gcHelperThread.waitBackgroundSweepEnd(); +#endif + AutoUnlockGC unlock(rt); + + AutoCopyFreeListToArenas copy(rt); + for (CompartmentsIter c(rt); !c.done(); c.next()) { + (*compartmentCallback)(cx, data, c); + } +} + void IterateCompartmentsArenasCells(JSContext *cx, void *data, IterateCompartmentCallback compartmentCallback, @@ -3279,19 +3370,17 @@ void RunDebugGC(JSContext *cx) { #ifdef JS_GC_ZEAL - if (IsGCAllowed(cx)) { - JSRuntime *rt = cx->runtime; + JSRuntime *rt = cx->runtime; - /* - * If rt->gcDebugCompartmentGC is true, only GC the current - * compartment. But don't GC the atoms compartment. - */ - rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; - if (rt->gcTriggerCompartment == rt->atomsCompartment) - rt->gcTriggerCompartment = NULL; + /* + * If rt->gcDebugCompartmentGC is true, only GC the current + * compartment. But don't GC the atoms compartment. + */ + rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL; + if (rt->gcTriggerCompartment == rt->atomsCompartment) + rt->gcTriggerCompartment = NULL; - RunLastDitchGC(cx); - } + RunLastDitchGC(cx); #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d4bfa3a09aa..c548730d8a5 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,6 +808,9 @@ class ChunkPool { */ Chunk *expire(JSRuntime *rt, bool releaseAll); + /* Must be called with the GC lock taken. */ + void expireAndFree(JSRuntime *rt, bool releaseAll); + /* Must be called either during the GC or with the GC lock taken. */ JS_FRIEND_API(int64_t) countCleanDecommittedArenas(JSRuntime *rt); }; @@ -1360,6 +1363,9 @@ js_GCThingIsMarked(void *thing, uintN color); extern void js_TraceStackFrame(JSTracer *trc, js::StackFrame *fp); +extern bool +js_IsAddressableGCThing(JSRuntime *rt, jsuword w, js::gc::AllocKind *thingKind, void **thing); + namespace js { extern JS_REQUIRES_STACK void @@ -1382,6 +1388,9 @@ TriggerCompartmentGC(JSCompartment *comp, js::gcstats::Reason reason); extern void MaybeGC(JSContext *cx); +extern void +ShrinkGCBuffers(JSRuntime *rt); + } /* namespace js */ /* @@ -1453,7 +1462,7 @@ class GCHelperThread { PRCondVar *done; volatile State state; - JSContext *context; + JSContext *finalizationContext; bool shrinkFlag; Vector freeVector; @@ -1489,6 +1498,8 @@ class GCHelperThread { wakeup(NULL), done(NULL), state(IDLE), + finalizationContext(NULL), + shrinkFlag(false), freeCursor(NULL), freeCursorEnd(NULL), backgroundAllocation(true) @@ -1498,7 +1509,10 @@ class GCHelperThread { void finish(); /* Must be called with the GC lock taken. */ - inline void startBackgroundSweep(bool shouldShrink); + void startBackgroundSweep(JSContext *cx, bool shouldShrink); + + /* Must be called with the GC lock taken. */ + void startBackgroundShrink(); /* Must be called with the GC lock taken. */ void waitBackgroundSweepEnd(); @@ -1543,7 +1557,7 @@ class GCHelperThread { } /* Must be called with the GC lock taken. */ - bool prepareForBackgroundSweep(JSContext *cx); + bool prepareForBackgroundSweep(); }; #endif /* JS_THREADSAFE */ @@ -1772,6 +1786,12 @@ typedef void (*IterateArenaCallback)(JSContext *cx, void *data, gc::Arena *arena typedef void (*IterateCellCallback)(JSContext *cx, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize); +/* + * This function calls |compartmentCallback| on every compartment. + */ +extern JS_FRIEND_API(void) +IterateCompartments(JSContext *cx, void *data, + IterateCompartmentCallback compartmentCallback); /* * This function calls |compartmentCallback| on every compartment, * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 083bc6530ce..4e3e52b3b31 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2617,7 +2617,7 @@ struct types::ObjectTableKey struct types::ObjectTableEntry { - TypeObject *object; + ReadBarriered object; Type *types; }; @@ -5837,9 +5837,6 @@ JSObject::getNewType(JSContext *cx, JSFunction *fun) if (type->newScript && type->newScript->fun != fun) type->clearNewScript(cx); - if (cx->compartment->needsBarrier()) - TypeObject::readBarrier(type); - return type; } @@ -5905,9 +5902,6 @@ JSCompartment::getLazyType(JSContext *cx, JSObject *proto) TypeObject *type = *p; JS_ASSERT(type->lazy()); - if (cx->compartment->needsBarrier()) - TypeObject::readBarrier(type); - return type; } diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index d03e04e4625..bc685b84a43 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -898,7 +898,7 @@ struct TypeObjectEntry static inline HashNumber hash(JSObject *base); static inline bool match(TypeObject *key, JSObject *lookup); }; -typedef HashSet TypeObjectSet; +typedef HashSet, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet; /* * Call to mark a script's arguments as having been created, recompile any @@ -1120,14 +1120,14 @@ class TypeScript }; struct ArrayTableKey; -typedef HashMap ArrayTypeTable; +typedef HashMap,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable; struct ObjectTableKey; struct ObjectTableEntry; typedef HashMap ObjectTypeTable; struct AllocationSiteKey; -typedef HashMap AllocationSiteTable; +typedef HashMap,AllocationSiteKey,SystemAllocPolicy> AllocationSiteTable; /* Type information for a compartment. */ struct TypeCompartment diff --git a/js/src/jsinferinlines.h b/js/src/jsinferinlines.h index e76bfc31895..16574dacdbc 100644 --- a/js/src/jsinferinlines.h +++ b/js/src/jsinferinlines.h @@ -1279,9 +1279,8 @@ TypeObject::readBarrier(TypeObject *type) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = type->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier"); + if (comp->needsBarrier()) + MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier"); #endif } diff --git a/js/src/jsprobes.cpp b/js/src/jsprobes.cpp index c8a66fcadc9..2527bbd3b6f 100644 --- a/js/src/jsprobes.cpp +++ b/js/src/jsprobes.cpp @@ -55,6 +55,8 @@ #include "jsscript.h" #include "jsstr.h" +#include "methodjit/Compiler.h" + #include "jsobjinlines.h" #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) @@ -115,15 +117,114 @@ Probes::JITGranularityRequested() } #ifdef JS_METHODJIT +/* + * Flatten the tree of inlined frames into a series of native code regions, one + * for each contiguous section of native code that belongs to a single + * ActiveFrame. (Note that some of these regions may be zero-length, for + * example if two ActiveFrames end at the same place.) + */ +typedef mjit::Compiler::ActiveFrame ActiveFrame; + +bool +Probes::JITWatcher::CollectNativeRegions(RegionVector ®ions, + JSRuntime *rt, + mjit::JITScript *jit, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames) +{ + regions.resize(jit->nInlineFrames * 2 + 2); + + mjit::JSActiveFrame **stack = + rt->array_new(jit->nInlineFrames+2); + if (!stack) + return false; + uint32_t depth = 0; + uint32_t ip = 0; + + stack[depth++] = NULL; + stack[depth++] = outerFrame; + regions[0].frame = outerFrame; + regions[0].script = outerFrame->script; + regions[0].pc = outerFrame->script->code; + regions[0].enter = true; + ip++; + + for (uint32_t i = 0; i <= jit->nInlineFrames; i++) { + mjit::JSActiveFrame *frame = (i < jit->nInlineFrames) ? inlineFrames[i] : outerFrame; + + // Not a down frame; pop the current frame, then pop until we reach + // this frame's parent, recording subframe ends as we go + while (stack[depth-1] != frame->parent) { + depth--; + JS_ASSERT(depth > 0); + // Pop up from regions[ip-1].frame to top of the stack: start a + // region in the destination frame and close off the source + // (origin) frame at the end of its script + mjit::JSActiveFrame *src = regions[ip-1].frame; + mjit::JSActiveFrame *dst = stack[depth-1]; + JS_ASSERT_IF(!dst, i == jit->nInlineFrames); + regions[ip].frame = dst; + regions[ip].script = dst ? dst->script : NULL; + regions[ip].pc = src->parentPC + 1; + regions[ip-1].endpc = src->script->code + src->script->length; + regions[ip].enter = false; + ip++; + } + + if (i < jit->nInlineFrames) { + // Push a frame (enter an inlined function). Start a region at the + // beginning of the new frame's script, and end the previous region + // at parentPC. + stack[depth++] = frame; + + regions[ip].frame = frame; + regions[ip].script = frame->script; + regions[ip].pc = frame->script->code; + regions[ip-1].endpc = frame->parentPC; + regions[ip].enter = true; + ip++; + } + } + + // Final region is always zero-length and not particularly useful + ip--; + regions.popBack(); + + mjit::JSActiveFrame *prev = NULL; + for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) { + mjit::JSActiveFrame *frame = iter->frame; + if (iter->enter) { + // Pushing down a frame, so region starts at the beginning of the + // (destination) frame + iter->mainOffset = frame->mainCodeStart; + iter->stubOffset = frame->stubCodeStart; + } else { + // Popping up a level, so region starts at the end of the (source) frame + iter->mainOffset = prev->mainCodeEnd; + iter->stubOffset = prev->stubCodeEnd; + } + prev = frame; + } + + JS_ASSERT(ip == 2 * jit->nInlineFrames + 1); + rt->array_delete(stack); + + // All of the stub code comes immediately after the main code + for (NativeRegion *iter = regions.begin(); iter != regions.end(); ++iter) + iter->stubOffset += outerFrame->mainCodeEnd; + + return true; +} + void Probes::registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - js::mjit::Compiler_ActiveFrame **inlineFrames, + js::mjit::JSActiveFrame *outerFrame, + js::mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize) { for (JITWatcher **p = jitWatchers.begin(); p != jitWatchers.end(); ++p) - (*p)->registerMJITCode(cx, jscr, script, fun, + (*p)->registerMJITCode(cx, jscr, outerFrame, inlineFrames, mainCodeAddress, mainCodeSize, stubCodeAddress, stubCodeSize); diff --git a/js/src/jsprobes.h b/js/src/jsprobes.h index ec2f484034c..903312275ad 100644 --- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -52,7 +52,7 @@ namespace js { namespace mjit { struct NativeAddressInfo; -struct Compiler_ActiveFrame; +struct JSActiveFrame; } namespace Probes { @@ -230,12 +230,31 @@ enum JITReportGranularity { */ class JITWatcher { public: + struct NativeRegion { + mjit::JSActiveFrame *frame; + JSScript *script; + size_t inlinedOffset; + jsbytecode *pc; + jsbytecode *endpc; + uintptr_t mainOffset; + uintptr_t stubOffset; + bool enter; + }; + + typedef Vector RegionVector; + virtual JITReportGranularity granularityRequested() = 0; #ifdef JS_METHODJIT + static bool CollectNativeRegions(RegionVector ®ions, + JSRuntime *rt, + mjit::JITScript *jit, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames); + virtual void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - mjit::Compiler_ActiveFrame** inlineFrames, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize) = 0; @@ -282,8 +301,8 @@ JITGranularityRequested(); */ void registerMJITCode(JSContext *cx, js::mjit::JITScript *jscr, - JSScript *script, JSFunction *fun, - mjit::Compiler_ActiveFrame** inlineFrames, + mjit::JSActiveFrame *outerFrame, + mjit::JSActiveFrame **inlineFrames, void *mainCodeAddress, size_t mainCodeSize, void *stubCodeAddress, size_t stubCodeSize); diff --git a/js/src/jsreflect.cpp b/js/src/jsreflect.cpp index def4ab44aeb..da0edbac1c9 100644 --- a/js/src/jsreflect.cpp +++ b/js/src/jsreflect.cpp @@ -619,8 +619,6 @@ class NodeBuilder bool xmlComment(Value text, TokenPos *pos, Value *dst); - bool xmlPI(Value target, TokenPos *pos, Value *dst); - bool xmlPI(Value target, Value content, TokenPos *pos, Value *dst); }; @@ -1570,12 +1568,6 @@ NodeBuilder::xmlComment(Value text, TokenPos *pos, Value *dst) return newNode(AST_XMLCOMMENT, pos, "contents", text, dst); } -bool -NodeBuilder::xmlPI(Value target, TokenPos *pos, Value *dst) -{ - return xmlPI(target, NullValue(), pos, dst); -} - bool NodeBuilder::xmlPI(Value target, Value contents, TokenPos *pos, Value *dst) { @@ -2410,7 +2402,7 @@ ASTSerializer::expression(ParseNode *pn, Value *dst) builder.sequenceExpression(exprs, &pn->pn_pos, dst); } - case PNK_HOOK: + case PNK_CONDITIONAL: { Value test, cons, alt; @@ -2630,13 +2622,16 @@ ASTSerializer::expression(ParseNode *pn, Value *dst) case PNK_DEFSHARP: { + DefSharpExpression &defsharp = pn->asDefSharpExpression(); Value expr; - return expression(pn->pn_kid, &expr) && - builder.graphExpression(pn->pn_num, expr, &pn->pn_pos, dst); + return expression(&defsharp.expression(), &expr) && + builder.graphExpression(defsharp.number(), expr, &defsharp.pn_pos, dst); } - case PNK_USESHARP: - return builder.graphIndexExpression(pn->pn_num, &pn->pn_pos, dst); + case PNK_USESHARP: { + UseSharpExpression &expr = pn->asUseSharpExpression(); + return builder.graphIndexExpression(expr.number(), &expr.pn_pos, dst); + } case PNK_ARRAYCOMP: /* NB: it's no longer the case that pn_count could be 2. */ @@ -2795,14 +2790,13 @@ ASTSerializer::xml(ParseNode *pn, Value *dst) case PNK_XMLCOMMENT: return builder.xmlComment(atomContents(pn->pn_atom), &pn->pn_pos, dst); - case PNK_XMLPI: - if (!pn->pn_pidata) - return builder.xmlPI(atomContents(pn->pn_pitarget), &pn->pn_pos, dst); - else - return builder.xmlPI(atomContents(pn->pn_pitarget), - atomContents(pn->pn_pidata), - &pn->pn_pos, - dst); + case PNK_XMLPI: { + XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction(); + return builder.xmlPI(atomContents(pi.target()), + atomContents(pi.data()), + &pi.pn_pos, + dst); + } #endif default: diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index c5d32ae07d6..77bc003a26d 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1275,14 +1275,8 @@ BaseShape::getUnowned(JSContext *cx, const BaseShape &base) BaseShapeSet::AddPtr p = table.lookupForAdd(&base); - if (p) { - UnownedBaseShape *base = *p; - - if (cx->compartment->needsBarrier()) - BaseShape::readBarrier(base); - - return base; - } + if (p) + return *p; BaseShape *nbase_ = js_NewGCBaseShape(cx); if (!nbase_) @@ -1382,14 +1376,8 @@ EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObje InitialShapeSet::AddPtr p = table.lookupForAdd(lookup); - if (p) { - Shape *shape = p->shape; - - if (cx->compartment->needsBarrier()) - Shape::readBarrier(shape); - - return shape; - } + if (p) + return p->shape; BaseShape base(clasp, parent, objectFlags); UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base); diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 14a53cd723a..63239c8c9ad 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -493,7 +493,7 @@ struct BaseShapeEntry static inline HashNumber hash(const BaseShape *base); static inline bool match(UnownedBaseShape *key, const BaseShape *lookup); }; -typedef HashSet BaseShapeSet; +typedef HashSet, BaseShapeEntry, SystemAllocPolicy> BaseShapeSet; struct Shape : public js::gc::Cell { @@ -975,7 +975,7 @@ struct InitialShapeEntry * certain classes (e.g. String, RegExp) which may add certain baked-in * properties. */ - js::Shape *shape; + ReadBarriered shape; /* * Matching prototype for the entry. The shape of an object determines its diff --git a/js/src/jsscopeinlines.h b/js/src/jsscopeinlines.h index c6ffc2136d0..fe8e3a6b774 100644 --- a/js/src/jsscopeinlines.h +++ b/js/src/jsscopeinlines.h @@ -362,9 +362,8 @@ Shape::readBarrier(const Shape *shape) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = shape->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier"); + if (comp->needsBarrier()) + MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier"); #endif } @@ -391,9 +390,8 @@ BaseShape::readBarrier(BaseShape *base) { #ifdef JSGC_INCREMENTAL JSCompartment *comp = base->compartment(); - JS_ASSERT(comp->needsBarrier()); - - MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier"); + if (comp->needsBarrier()) + MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier"); #endif } diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 7267622d802..a0a0b25511b 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -1687,7 +1687,9 @@ class TypedArrayTemplate { JS_ASSERT(tarray); + JS_ASSERT(0 <= begin); JS_ASSERT(begin <= getLength(tarray)); + JS_ASSERT(0 <= end); JS_ASSERT(end <= getLength(tarray)); JSObject *bufobj = getBuffer(tarray); diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index 75b0605e90e..a063021fb48 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1621,11 +1621,12 @@ ParseNodeToXML(Parser *parser, ParseNode *pn, goto skip_child; xml_class = JSXML_CLASS_COMMENT; } else if (pn->isKind(PNK_XMLPI)) { + XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction(); if (IS_XML(str)) { Value v = StringValue(str); JSAutoByteString bytes; if (js_ValueToPrintable(cx, v, &bytes)) { - ReportCompileErrorNumber(cx, &parser->tokenStream, pn, + ReportCompileErrorNumber(cx, &parser->tokenStream, &pi, JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr()); } goto fail; @@ -1634,11 +1635,11 @@ ParseNodeToXML(Parser *parser, ParseNode *pn, if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) goto skip_child; - qn = ParseNodeToQName(parser, pn, inScopeNSes, JS_FALSE); + qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE); if (!qn) goto fail; - str = pn->pn_pidata ? pn->pn_pidata : cx->runtime->emptyString; + str = pi.data(); xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION; } else { /* CDATA section content, or element text. */ diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 63e04a6f30c..2e098dde216 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -486,8 +486,10 @@ void mjit::Compiler::popActiveFrame() { JS_ASSERT(a->parent); + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); this->PC = a->parentPC; - this->a = a->parent; + this->a = (ActiveFrame *) a->parent; this->script = a->script; this->analysis = this->script->analysis(); @@ -551,9 +553,14 @@ mjit::Compiler::performCompilation(JITScript **jitp) #undef CHECK_STATUS +mjit::JSActiveFrame::JSActiveFrame() + : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX) +{ +} + mjit::Compiler::ActiveFrame::ActiveFrame(JSContext *cx) - : parent(NULL), parentPC(NULL), script(NULL), jumpMap(NULL), - inlineIndex(UINT32_MAX), varTypes(NULL), needReturnValue(false), + : jumpMap(NULL), + varTypes(NULL), needReturnValue(false), syncReturnValue(false), returnValueDouble(false), returnSet(false), returnEntry(NULL), returnJumps(NULL), exitState(NULL) {} @@ -922,6 +929,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) return Compile_Abort; } + a->mainCodeEnd = masm.size(); + a->stubCodeEnd = stubcc.size(); + for (size_t i = 0; i < branchPatches.length(); i++) { Label label = labelOf(branchPatches[i].pc, branchPatches[i].inlineIndex); branchPatches[i].jump.linkTo(label, &masm); @@ -1385,8 +1395,9 @@ mjit::Compiler::finishThisUp(JITScript **jitp) JSC::ExecutableAllocator::makeExecutable(result, masm.size() + stubcc.size()); JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size()); - Probes::registerMJITCode(cx, jit, script, script->function() ? script->function() : NULL, - (mjit::Compiler_ActiveFrame**) inlineFrames.begin(), + Probes::registerMJITCode(cx, jit, + a, + (JSActiveFrame**) inlineFrames.begin(), result, masm.size(), result + masm.size(), stubcc.size()); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index 889235c2fa9..635815b8216 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -62,6 +62,27 @@ struct InvariantCodePatch { InvariantCodePatch() : hasPatch(false) {} }; +struct JSActiveFrame { + JSActiveFrame *parent; + jsbytecode *parentPC; + JSScript *script; + + /* + * Index into inlineFrames or OUTER_FRAME, matches this frame's index in + * the cross script SSA. + */ + uint32_t inlineIndex; + + /* JIT code generation tracking state */ + size_t mainCodeStart; + size_t stubCodeStart; + size_t mainCodeEnd; + size_t stubCodeEnd; + size_t inlinePCOffset; + + JSActiveFrame(); +}; + class Compiler : public BaseCompiler { friend class StubCompiler; @@ -355,26 +376,12 @@ class Compiler : public BaseCompiler */ public: - struct ActiveFrame { - ActiveFrame *parent; - jsbytecode *parentPC; - JSScript *script; + struct ActiveFrame : public JSActiveFrame { Label *jumpMap; - /* - * Index into inlineFrames or OUTER_FRAME, matches this frame's index - * in the cross script SSA. - */ - uint32_t inlineIndex; - /* Current types for non-escaping vars in the script. */ VarType *varTypes; - /* JIT code generation tracking state */ - size_t mainCodeStart; - size_t stubCodeStart; - size_t inlinePCOffset; - /* State for managing return from inlined frames. */ bool needReturnValue; /* Return value will be used. */ bool syncReturnValue; /* Return value should be fully synced. */ @@ -470,7 +477,7 @@ private: return PC; ActiveFrame *scan = a; while (scan && scan->parent != outer) - scan = scan->parent; + scan = static_cast(scan->parent); return scan->parentPC; } @@ -491,7 +498,7 @@ private: while (na->parent) { if (na->exitState) return true; - na = na->parent; + na = static_cast(na->parent); } return false; } diff --git a/js/src/methodjit/FastBuiltins.cpp b/js/src/methodjit/FastBuiltins.cpp index 60339e35692..c794b2b6c2c 100644 --- a/js/src/methodjit/FastBuiltins.cpp +++ b/js/src/methodjit/FastBuiltins.cpp @@ -909,6 +909,7 @@ mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) return compileRound(arg, Round); } if (native == js_math_sqrt && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && (argType == JSVAL_TYPE_INT32 || argType == JSVAL_TYPE_DOUBLE)) { return compileMathSqrt(arg); } @@ -949,6 +950,7 @@ mjit::Compiler::inlineNativeFunction(uint32_t argc, bool callingNew) JSValueType arg2Type = arg2->isTypeKnown() ? arg2->getKnownType() : JSVAL_TYPE_UNKNOWN; if (native == js_math_pow && type == JSVAL_TYPE_DOUBLE && + masm.supportsFloatingPointSqrt() && (arg1Type == JSVAL_TYPE_DOUBLE || arg1Type == JSVAL_TYPE_INT32) && arg2Type == JSVAL_TYPE_DOUBLE && arg2->isConstant()) { diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 0962d6fbb6f..d66b6d5bc7d 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -140,27 +140,23 @@ static const size_t STUB_CALLS_FOR_OP_COUNT = 255; static uint32_t StubCallsForOp[STUB_CALLS_FOR_OP_COUNT]; #endif +// Called from JaegerTrampoline only extern "C" void JS_FASTCALL PushActiveVMFrame(VMFrame &f) { + f.oldregs = &f.cx->stack.regs(); + f.cx->stack.repointRegs(&f.regs); f.entryfp->script()->compartment()->jaegerCompartment()->pushActiveFrame(&f); f.entryfp->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void*, JaegerTrampolineReturn)); f.regs.clearInlined(); } +// Called from JaegerTrampolineReturn, JaegerThrowpoline, JaegerInterpoline extern "C" void JS_FASTCALL PopActiveVMFrame(VMFrame &f) { f.entryfp->script()->compartment()->jaegerCompartment()->popActiveFrame(); -} - -extern "C" void JS_FASTCALL -SetVMFrameRegs(VMFrame &f) -{ - f.oldregs = &f.cx->stack.regs(); - - /* Restored on exit from EnterMethodJIT. */ - f.cx->stack.repointRegs(&f.regs); + f.cx->stack.repointRegs(f.oldregs); } #if defined(__APPLE__) || (defined(XP_WIN) && !defined(JS_CPU_X64)) || defined(XP_OS2) @@ -328,8 +324,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ "pushq %rdx" "\n" "movq %rsp, %rdi" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movq %rsp, %rdi" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" /* Jump into the JIT'd code. */ @@ -514,8 +508,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Jump into the JIT'd code. */ "movl %esp, %ecx" "\n" - "call " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" - "movl %esp, %ecx" "\n" "call " SYMBOL_STRING_VMFRAME(PushActiveVMFrame) "\n" "movl 28(%esp), %ebp" "\n" /* load fp for JIT code */ @@ -730,8 +722,6 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n" /* Preserve 'fp' (r1) in r10 (JSFrameReg). */ " mov r10, r1" "\n" -" mov r0, sp" "\n" -" blx " SYMBOL_STRING_VMFRAME(SetVMFrameRegs) "\n" " mov r0, sp" "\n" " blx " SYMBOL_STRING_VMFRAME(PushActiveVMFrame)"\n" @@ -877,8 +867,6 @@ extern "C" { /* Jump into into the JIT'd code. */ mov ecx, esp; - call SetVMFrameRegs; - mov ecx, esp; call PushActiveVMFrame; mov ebp, [esp + 28]; /* load fp for JIT code */ @@ -1055,7 +1043,6 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi #endif JS_ASSERT(cx->fp() == fp); - FrameRegs &oldRegs = cx->regs(); JSBool ok; { @@ -1069,9 +1056,6 @@ mjit::EnterMethodJIT(JSContext *cx, StackFrame *fp, void *code, Value *stackLimi JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms()); #endif - /* Undo repointRegs in SetVMFrameRegs. */ - cx->stack.repointRegs(&oldRegs); - JaegerStatus status = cx->compartment->jaegerCompartment()->lastUnfinished(); if (status) { if (partial) { diff --git a/js/src/methodjit/TrampolineMasmX64.asm b/js/src/methodjit/TrampolineMasmX64.asm index 0320b9cc25b..57fc6b97529 100644 --- a/js/src/methodjit/TrampolineMasmX64.asm +++ b/js/src/methodjit/TrampolineMasmX64.asm @@ -37,7 +37,6 @@ extern js_InternalThrow:PROC -extern SetVMFrameRegs:PROC extern PushActiveVMFrame:PROC extern PopActiveVMFrame:PROC extern js_InternalInterpret:PROC @@ -95,8 +94,6 @@ JaegerTrampoline PROC FRAME push r8 mov rcx, rsp sub rsp, 20h - call SetVMFrameRegs - lea rcx, [rsp+20h] call PushActiveVMFrame add rsp, 20h diff --git a/js/src/methodjit/TrampolineMingwX64.s b/js/src/methodjit/TrampolineMingwX64.s index ea956403f6d..197d0b1c366 100644 --- a/js/src/methodjit/TrampolineMingwX64.s +++ b/js/src/methodjit/TrampolineMingwX64.s @@ -37,7 +37,6 @@ .extern js_InternalThrow -.extern SetVMFrameRegs .extern PushActiveVMFrame .extern PopActiveVMFrame .extern js_InternalInterpret @@ -101,8 +100,6 @@ JaegerTrampoline: push r8 mov rcx, rsp sub rsp, 0x20 - call SetVMFrameRegs - lea rcx, [rsp+0x20] call PushActiveVMFrame add rsp, 0x20 diff --git a/js/src/methodjit/TrampolineSUNWX64.s b/js/src/methodjit/TrampolineSUNWX64.s index e03e462a984..3787e396bc5 100644 --- a/js/src/methodjit/TrampolineSUNWX64.s +++ b/js/src/methodjit/TrampolineSUNWX64.s @@ -79,8 +79,6 @@ JaegerTrampoline: /* Set cx->regs and set the active frame. Save rdx and align frame in one. */ pushq %rdx movq %rsp, %rdi - call SetVMFrameRegs - movq %rsp, %rdi call PushActiveVMFrame /* Jump into into the JIT'd code. */ diff --git a/js/src/methodjit/TrampolineSUNWX86.s b/js/src/methodjit/TrampolineSUNWX86.s index 4c54f13459f..146525b1250 100644 --- a/js/src/methodjit/TrampolineSUNWX86.s +++ b/js/src/methodjit/TrampolineSUNWX86.s @@ -66,7 +66,6 @@ JaegerTrampoline: /* Jump into the JIT'd code. */ /* No fastcall for sunstudio. */ pushl %esp - call SetVMFrameRegs call PushActiveVMFrame popl %edx diff --git a/js/src/methodjit/TrampolineSparc.s b/js/src/methodjit/TrampolineSparc.s index 1b15369ab12..fb79b5a2abf 100644 --- a/js/src/methodjit/TrampolineSparc.s +++ b/js/src/methodjit/TrampolineSparc.s @@ -51,8 +51,6 @@ JaegerTrampoline: st %i1, [%fp - 24] ! entryFp st %i1, [%fp - 20] ! entryncode st %g0, [%fp - 16] ! stubRejoin - call SetVMFrameRegs - mov %sp, %o0 call PushActiveVMFrame mov %sp, %o0 ld [%fp - 36], %l0 ! fp diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 3ac3e6f5608..eac36194893 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1259,6 +1259,15 @@ CompartmentMemoryCallback(JSContext *cx, void *vdata, JSCompartment *compartment JS::SizeOfCompartmentShapeTable(compartment, JsMallocSizeOf); } +void +ExplicitNonHeapCompartmentCallback(JSContext *cx, void *data, JSCompartment *compartment) +{ + size_t *n = static_cast(data); +#ifdef JS_METHODJIT + *n += JS::SizeOfCompartmentMjitCode(compartment); +#endif +} + void ChunkCallback(JSContext *cx, void *vdata, js::gc::Chunk *chunk) { @@ -1541,8 +1550,9 @@ CompartmentStats::CompartmentStats(JSContext *cx, JSCompartment *c) } JSBool -CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) +CollectCompartmentStatsForRuntime(JSRuntime *rt, void *vdata) { + IterateData *data = (IterateData *)vdata; JSContext *cx = JS_NewContext(rt, 0); if (!cx) { NS_ERROR("couldn't create context for memory tracing"); @@ -1688,6 +1698,60 @@ CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data) return true; } +JSBool +GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data) +{ + PRInt64 *amount = (PRInt64 *)data; + + JSContext *cx = JS_NewContext(rt, 0); + if (!cx) { + NS_ERROR("couldn't create context for memory tracing"); + return NS_ERROR_ABORT; + } + + // explicit//gc-heap/* + *amount = PRInt64(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * + js::gc::ChunkSize; + + { + JSAutoRequest ar(cx); + + // explicit//mjit-code + size_t n = 0; + js::IterateCompartments(cx, &n, ExplicitNonHeapCompartmentCallback); + *amount += n; + + { + #ifndef JS_THREADSAFE + #error "This code assumes JS_THREADSAFE is defined" + #endif + + // Need the GC lock to call JS_ContextIteratorUnlocked() and to + // access rt->threads. + js::AutoLockGC lock(rt); + + // explicit/runtime/threads/regexp-code + // explicit/runtime/threads/stack-committed + for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) { + JSThread *thread = r.front().value; + size_t regexpCode, stackCommitted; + thread->sizeOfIncludingThis(JsMallocSizeOf, + NULL, + NULL, + ®expCode, + &stackCommitted); + + *amount += regexpCode; + *amount += stackCommitted; + } + } + } + + JS_DestroyContextNoGC(cx); + + return true; +} + #define SLOP_BYTES_STRING \ " The measurement includes slop bytes caused by the heap allocator rounding up request sizes." @@ -1958,7 +2022,7 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, "Memory on the garbage-collected JavaScript heap, within chunks with at " "least one allocated GC thing, that could be holding useful data but " "currently isn't. Memory here is mutually exclusive with memory reported" - "under gc-heap-decommitted.", + "under 'explicit/js/gc-heap-decommitted'.", callback, closure); ReportGCHeapBytes(pathPrefix + @@ -1967,7 +2031,7 @@ ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, "Memory on the garbage-collected JavaScript heap taken by completely empty " "chunks, that soon will be released unless claimed for new allocations. " "Memory here is mutually exclusive with memory reported under " - "gc-heap-decommitted.", + "'explicit/js/gc-heap-decommitted'.", callback, closure); ReportGCHeapBytes(pathPrefix + @@ -2105,6 +2169,17 @@ public: return NS_OK; } + + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *n) + { + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime(); + + if (!GetExplicitNonHeapForRuntime(rt, n)) + return NS_ERROR_FAILURE; + + return NS_OK; + } }; NS_IMPL_THREADSAFE_ISUPPORTS1(XPConnectJSCompartmentsMultiReporter diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index 10316b72ec9..f8c66bf5810 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -311,7 +311,9 @@ struct IterateData }; JSBool -CollectCompartmentStatsForRuntime(JSRuntime *rt, IterateData *data); +CollectCompartmentStatsForRuntime(JSRuntime *rt, void *data); +JSBool +GetExplicitNonHeapForRuntime(JSRuntime *rt, void *data); void ReportJSRuntimeStats(const IterateData &data, const nsACString &pathPrefix, diff --git a/layout/base/nsDisplayList.cpp b/layout/base/nsDisplayList.cpp index 50d8f6afa1b..a3eadf257b2 100644 --- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -460,7 +460,7 @@ GetDisplayPortBounds(nsDisplayListBuilder* aBuilder, nsDisplayItem* aItem) } const nsRect* displayport = aBuilder->GetDisplayPort(); - nsRect result = nsLayoutUtils::TransformRectToBoundsInAncestor( + nsRect result = nsLayoutUtils::TransformAncestorRectToFrame( frame, nsRect(0, 0, displayport->width, displayport->height), aBuilder->ReferenceFrame()); diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index 20a6f4f77da..4dfba589e63 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -194,6 +194,8 @@ static const char sPrintOptionsContractID[] = "@mozilla.org/gfx/printset //switch to page layout #include "nsGfxCIID.h" +#include "nsObserverService.h" + #include "mozilla/dom/Element.h" using namespace mozilla; @@ -509,6 +511,18 @@ public: nsCOMPtr mTop; }; +class nsDocumentShownDispatcher : public nsRunnable +{ +public: + nsDocumentShownDispatcher(nsIDocument *aDocument) + : mDocument(aDocument) {} + + NS_IMETHOD Run(); + +private: + nsCOMPtr mDocument; +}; + //------------------------------------------------------------------ // DocumentViewerImpl @@ -2039,6 +2053,10 @@ DocumentViewerImpl::Show(void) } } + // Notify observers that a new page has been shown. (But not right now; + // running JS at this time is not safe.) + NS_DispatchToMainThread(new nsDocumentShownDispatcher(mDocument)); + return NS_OK; } @@ -4371,3 +4389,17 @@ DocumentViewerImpl::SetPrintPreviewPresentation(nsIViewManager* aViewManager, mPresContext = aPresContext; mPresShell = aPresShell; } + +// Fires the "document-shown" event so that interested parties (right now, the +// mobile browser) are aware of it. +NS_IMETHODIMP +nsDocumentShownDispatcher::Run() +{ + nsCOMPtr observerService = + mozilla::services::GetObserverService(); + if (observerService) { + observerService->NotifyObservers(mDocument, "document-shown", NULL); + } + return NS_OK; +} + diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 2ac11bea1d7..6c54f6a11be 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -975,7 +975,7 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aF * out how to convert back to aFrame's coordinates and must use the CTM. */ if (transformFound) - return InvertTransformsToRoot(aFrame, widgetToView); + return TransformRootPointToFrame(aFrame, widgetToView); /* Otherwise, all coordinate systems are translations of one another, * so we can just subtract out the different. @@ -1117,68 +1117,62 @@ nsLayoutUtils::MatrixTransformPoint(const nsPoint &aPoint, NSFloatPixelsToAppUnits(float(image.y), aFactor)); } -static gfxPoint -InvertTransformsToAncestor(nsIFrame *aFrame, - const gfxPoint &aPoint, - nsIFrame *aStopAtAncestor = nsnull) +static gfx3DMatrix +GetTransformToAncestor(nsIFrame *aFrame, nsIFrame *aAncestor) { - NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?"); - - /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform - * the point. - */ - nsIFrame *parent = nsnull; - gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent); - gfxPoint result = aPoint; - - if (parent && parent != aStopAtAncestor) { - result = InvertTransformsToAncestor(parent, aPoint, aStopAtAncestor); + nsIFrame* parent; + gfx3DMatrix ctm = aFrame->GetTransformMatrix(aAncestor, &parent); + while (parent && parent != aAncestor) { + ctm = ctm * parent->GetTransformMatrix(aAncestor, &parent); } + return ctm; +} - result = ctm.Inverse().ProjectPoint(result); - return result; +static gfxPoint +TransformGfxPointFromAncestor(nsIFrame *aFrame, + const gfxPoint &aPoint, + nsIFrame *aAncestor) +{ + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.Inverse().ProjectPoint(aPoint); } static gfxRect -InvertGfxRectToAncestor(nsIFrame *aFrame, - const gfxRect &aRect, - nsIFrame *aStopAtAncestor = nsnull) +TransformGfxRectFromAncestor(nsIFrame *aFrame, + const gfxRect &aRect, + nsIFrame *aAncestor) { - NS_PRECONDITION(aFrame, "Why are you inverting transforms when there is no frame?"); + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.Inverse().ProjectRectBounds(aRect); +} - /* To invert everything to the root, we'll get the CTM, invert it, and use it to transform - * the point. - */ - nsIFrame *parent = nsnull; - gfx3DMatrix ctm = aFrame->GetTransformMatrix(&parent); - gfxRect result = aRect; - - if (parent && parent != aStopAtAncestor) { - result = InvertGfxRectToAncestor(parent, aRect, aStopAtAncestor); - } - - result = ctm.Inverse().ProjectRectBounds(result); - return result; +static gfxRect +TransformGfxRectToAncestor(nsIFrame *aFrame, + const gfxRect &aRect, + nsIFrame *aAncestor) +{ + gfx3DMatrix ctm = GetTransformToAncestor(aFrame, aAncestor); + return ctm.ProjectRectBounds(aRect); } nsPoint -nsLayoutUtils::InvertTransformsToRoot(nsIFrame *aFrame, - const nsPoint &aPoint) +nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame, + const nsPoint &aPoint) { float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor), NSAppUnitsToFloatPixels(aPoint.y, factor)); - result = InvertTransformsToAncestor(aFrame, result); + result = TransformGfxPointFromAncestor(aFrame, result, nsnull); return nsPoint(NSFloatPixelsToAppUnits(float(result.x), factor), NSFloatPixelsToAppUnits(float(result.y), factor)); } nsRect -nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, - const nsRect &aRect, - nsIFrame* aStopAtAncestor) +nsLayoutUtils::TransformAncestorRectToFrame(nsIFrame* aFrame, + const nsRect &aRect, + nsIFrame* aAncestor) { float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor), @@ -1186,7 +1180,7 @@ nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, NSAppUnitsToFloatPixels(aRect.width, factor), NSAppUnitsToFloatPixels(aRect.height, factor)); - result = InvertGfxRectToAncestor(aFrame, result, aStopAtAncestor); + result = TransformGfxRectFromAncestor(aFrame, result, aAncestor); return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), NSFloatPixelsToAppUnits(float(result.y), factor), @@ -1194,6 +1188,25 @@ nsLayoutUtils::TransformRectToBoundsInAncestor(nsIFrame* aFrame, NSFloatPixelsToAppUnits(float(result.height), factor)); } +nsRect +nsLayoutUtils::TransformFrameRectToAncestor(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor) +{ + float factor = aFrame->PresContext()->AppUnitsPerDevPixel(); + gfxRect result(NSAppUnitsToFloatPixels(aRect.x, factor), + NSAppUnitsToFloatPixels(aRect.y, factor), + NSAppUnitsToFloatPixels(aRect.width, factor), + NSAppUnitsToFloatPixels(aRect.height, factor)); + + result = TransformGfxRectToAncestor(aFrame, result, aAncestor); + + return nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), + NSFloatPixelsToAppUnits(float(result.y), factor), + NSFloatPixelsToAppUnits(float(result.width), factor), + NSFloatPixelsToAppUnits(float(result.height), factor)); +} + static nsIntPoint GetWidgetOffset(nsIWidget* aWidget, nsIWidget*& aRootWidget) { nsIntPoint offset(0, 0); nsIWidget* parent = aWidget->GetParent(); @@ -1834,27 +1847,35 @@ nsLayoutUtils::GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback) } struct BoxToBorderRect : public nsLayoutUtils::BoxCallback { - nsIFrame* mRelativeTo; + nsIFrame* mRelativeTo; nsLayoutUtils::RectCallback* mCallback; + PRUint32 mFlags; - BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback) - : mRelativeTo(aRelativeTo), mCallback(aCallback) {} + BoxToBorderRect(nsIFrame* aRelativeTo, nsLayoutUtils::RectCallback* aCallback, + PRUint32 aFlags) + : mRelativeTo(aRelativeTo), mCallback(aCallback), mFlags(aFlags) {} virtual void AddBox(nsIFrame* aFrame) { nsRect r; nsIFrame* outer = nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(aFrame, &r); - if (outer) { - mCallback->AddRect(r + outer->GetOffsetTo(mRelativeTo)); - } else - mCallback->AddRect(nsRect(aFrame->GetOffsetTo(mRelativeTo), aFrame->GetSize())); + if (!outer) { + outer = aFrame; + r = nsRect(nsPoint(0, 0), aFrame->GetSize()); + } + if (mFlags & nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS) { + r = nsLayoutUtils::TransformFrameRectToAncestor(outer, r, mRelativeTo); + } else { + r += outer->GetOffsetTo(mRelativeTo); + } + mCallback->AddRect(r); } }; void nsLayoutUtils::GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, - RectCallback* aCallback) + RectCallback* aCallback, PRUint32 aFlags) { - BoxToBorderRect converter(aRelativeTo, aCallback); + BoxToBorderRect converter(aRelativeTo, aCallback, aFlags); GetAllInFlowBoxes(aFrame, &converter); } @@ -1880,19 +1901,14 @@ void nsLayoutUtils::RectListBuilder::AddRect(const nsRect& aRect) { nsIFrame* nsLayoutUtils::GetContainingBlockForClientRect(nsIFrame* aFrame) { - // get the nearest enclosing SVG foreign object frame or the root frame - while (aFrame->GetParent() && - !aFrame->IsFrameOfType(nsIFrame::eSVGForeignObject)) { - aFrame = aFrame->GetParent(); - } - - return aFrame; + return aFrame->PresContext()->PresShell()->GetRootFrame(); } nsRect -nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo) { +nsLayoutUtils::GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, + PRUint32 aFlags) { RectAccumulator accumulator; - GetAllInFlowRects(aFrame, aRelativeTo, &accumulator); + GetAllInFlowRects(aFrame, aRelativeTo, &accumulator, aFlags); return accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect : accumulator.mResultRect; } diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index fc53096a714..3b355bf397d 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -515,11 +515,21 @@ public: bool aShouldIgnoreSuppression = false, bool aIgnoreRootScrollFrame = false); - + /** + * Transform aRect relative to aAncestor down to the coordinate system of + * aFrame. Computes the bounding-box of the true quadrilateral. + */ + static nsRect TransformAncestorRectToFrame(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor); - static nsRect TransformRectToBoundsInAncestor(nsIFrame* aFrame, - const nsRect& aRect, - nsIFrame* aStopAtAncestor); + /** + * Transform aRect relative to aFrame up to the coordinate system of + * aAncestor. Computes the bounding-box of the true quadrilateral. + */ + static nsRect TransformFrameRectToAncestor(nsIFrame* aFrame, + const nsRect& aRect, + nsIFrame* aAncestor); /** * Given a point in the global coordinate space, returns that point expressed @@ -530,8 +540,8 @@ public: * @param aPoint The point, in the global space, to get in the frame-local space. * @return aPoint, expressed in aFrame's canonical coordinate space. */ - static nsPoint InvertTransformsToRoot(nsIFrame* aFrame, - const nsPoint &aPt); + static nsPoint TransformRootPointToFrame(nsIFrame* aFrame, + const nsPoint &aPt); /** * Helper function that, given a rectangle and a matrix, returns the smallest @@ -694,8 +704,8 @@ public: }; struct RectAccumulator : public RectCallback { - nsRect mResultRect; - nsRect mFirstRect; + nsRect mResultRect; + nsRect mFirstRect; bool mSeenFirstRect; RectAccumulator(); @@ -713,6 +723,9 @@ public: static nsIFrame* GetContainingBlockForClientRect(nsIFrame* aFrame); + enum { + RECTS_ACCOUNT_FOR_TRANSFORMS = 0x01 + }; /** * Collect all CSS border-boxes associated with aFrame and its * continuations, "drilling down" through outer table frames and @@ -721,15 +734,22 @@ public: * into account) and passed to the callback in frame-tree order. * If aFrame is null, no boxes are returned. * For SVG frames, returns one rectangle, the bounding box. + * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting + * the boxes into aRelativeTo coordinates, transforms (including CSS + * and SVG transforms) are taken into account. */ static void GetAllInFlowRects(nsIFrame* aFrame, nsIFrame* aRelativeTo, - RectCallback* aCallback); + RectCallback* aCallback, PRUint32 aFlags = 0); /** * Computes the union of all rects returned by GetAllInFlowRects. If * the union is empty, returns the first rect. + * If aFlags includes RECTS_ACCOUNT_FOR_TRANSFORMS, then when converting + * the boxes into aRelativeTo coordinates, transforms (including CSS + * and SVG transforms) are taken into account. */ - static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo); + static nsRect GetAllInFlowRectsUnion(nsIFrame* aFrame, nsIFrame* aRelativeTo, + PRUint32 aFlags = 0); enum { EXCLUDE_BLUR_SHADOWS = 0x01 diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 03fa8bddaa3..49f5fb123a0 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -722,6 +722,13 @@ PresShell::MemoryReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, return NS_OK; } +NS_IMETHODIMP +PresShell::MemoryReporter::GetExplicitNonHeap(PRInt64 *aAmount) { + // This reporter doesn't do any KIND_NONHEAP measurements. + *aAmount = 0; + return NS_OK; +} + class nsAutoCauseReflowNotifier { public: diff --git a/layout/base/tests/test_bug677878.html b/layout/base/tests/test_bug677878.html index db34829d29a..f972546c3ba 100644 --- a/layout/base/tests/test_bug677878.html +++ b/layout/base/tests/test_bug677878.html @@ -36,7 +36,9 @@ runtests(); function runtests() { function doClick() { document.getElementById("test2").addEventListener("mousedown", testFinish, true); - synthesizeMouseAtCenter(document.getElementById("test2"), { type: "mousedown" }) + // Don't target the center because the center could actually be outside the + // viewport. + synthesizeMouse(document.getElementById("test2"), 10, 10, { type: "mousedown" }) } setTimeout(doClick, 300); } diff --git a/layout/forms/nsComboboxControlFrame.cpp b/layout/forms/nsComboboxControlFrame.cpp index f74f1e789fc..b232bab9366 100644 --- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -550,8 +550,8 @@ nsComboboxControlFrame::GetCSSTransformTranslation() bool is3DTransform = false; gfxMatrix transform; while (frame) { - nsIFrame* parent = nsnull; - gfx3DMatrix ctm = frame->GetTransformMatrix(&parent); + nsIFrame* parent; + gfx3DMatrix ctm = frame->GetTransformMatrix(nsnull, &parent); gfxMatrix matrix; if (ctm.Is2D(&matrix)) { transform = transform * matrix; diff --git a/layout/generic/TextOverflow.cpp b/layout/generic/TextOverflow.cpp index 599923a3324..cb7bdcc7e1e 100644 --- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -531,8 +531,8 @@ TextOverflow::ExamineLineFrames(nsLineBox* aLine, guessRight == (mRight.mActive && mRight.IsNeeded())) { break; } else { - guessLeft = mLeft.IsNeeded(); - guessRight = mRight.IsNeeded(); + guessLeft = mLeft.mActive && mLeft.IsNeeded(); + guessRight = mRight.mActive && mRight.IsNeeded(); mLeft.Reset(); mRight.Reset(); aFramesToHide->Clear(); diff --git a/layout/generic/crashtests/700031.xhtml b/layout/generic/crashtests/700031.xhtml new file mode 100644 index 00000000000..70f924279ef --- /dev/null +++ b/layout/generic/crashtests/700031.xhtml @@ -0,0 +1,9 @@ + + +
+ +abcdef + +
+ + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index cd3cd607931..3e0b16c5817 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -382,3 +382,5 @@ load 683702-1.xhtml load 688996-1.html load 688996-2.html load 683712.html +load text-overflow-bug713610.html +load 700031.xhtml diff --git a/layout/generic/crashtests/text-overflow-bug713610.html b/layout/generic/crashtests/text-overflow-bug713610.html new file mode 100644 index 00000000000..145295364a1 --- /dev/null +++ b/layout/generic/crashtests/text-overflow-bug713610.html @@ -0,0 +1,6 @@ + + + +
+ + diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index b5f78934dd3..ae69c9064ba 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4541,7 +4541,8 @@ nsIFrame::InvalidateInternal(const nsRect& aDamageRect, nscoord aX, nscoord aY, } gfx3DMatrix -nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) +nsIFrame::GetTransformMatrix(nsIFrame* aStopAtAncestor, + nsIFrame** aOutAncestor) { NS_PRECONDITION(aOutAncestor, "Need a place to put the ancestor!"); @@ -4553,7 +4554,8 @@ nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) /* Compute the delta to the parent, which we need because we are converting * coordinates to our parent. */ - NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), "Cannot transform the viewport frame!"); + NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this), + "Cannot transform the viewport frame!"); PRInt32 scaleFactor = PresContext()->AppUnitsPerDevPixel(); gfx3DMatrix result = @@ -4582,7 +4584,7 @@ nsIFrame::GetTransformMatrix(nsIFrame **aOutAncestor) return gfx3DMatrix(); /* Keep iterating while the frame can't possibly be transformed. */ - while (!(*aOutAncestor)->IsTransformed()) { + while (!(*aOutAncestor)->IsTransformed() && *aOutAncestor != aStopAtAncestor) { /* If no parent, stop iterating. Otherwise, update the ancestor. */ nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor); if (!parent) diff --git a/layout/generic/nsHTMLReflowMetrics.h b/layout/generic/nsHTMLReflowMetrics.h index 21c357b4482..5c54a205b5a 100644 --- a/layout/generic/nsHTMLReflowMetrics.h +++ b/layout/generic/nsHTMLReflowMetrics.h @@ -71,11 +71,11 @@ private: nsRect mRects[2]; public: nsRect& Overflow(size_t aIndex) { - NS_ASSERTION(aIndex < 2, "index out of range"); + NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range"); return mRects[aIndex]; } const nsRect& Overflow(size_t aIndex) const { - NS_ASSERTION(aIndex < 2, "index out of range"); + NS_ASSERTION(0 <= aIndex && aIndex < 2, "index out of range"); return mRects[aIndex]; } diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h index 3dfeecc8710..f44a90f795d 100644 --- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -1932,17 +1932,20 @@ public: virtual nsIAtom* GetType() const = 0; /** - * Returns a transformation matrix that converts points in this frame's coordinate space - * to points in some ancestor frame's coordinate space. The frame decides which ancestor - * it will use as a reference point. If this frame has no ancestor, aOutAncestor will be - * set to null. + * Returns a transformation matrix that converts points in this frame's + * coordinate space to points in some ancestor frame's coordinate space. + * The frame decides which ancestor it will use as a reference point. + * If this frame has no ancestor, aOutAncestor will be set to null. * - * @param aOutAncestor [out] The ancestor frame the frame has chosen. If this frame has no - * ancestor, aOutAncestor will be nsnull. - * @return A gfxMatrix that converts points in this frame's coordinate space into - * points in aOutAncestor's coordinate space. + * @param aStopAtAncestor don't look further than aStopAtAncestor. If null, + * all ancestors (including across documents) will be traversed. + * @param aOutAncestor [out] The ancestor frame the frame has chosen. If + * this frame has no ancestor, *aOutAncestor will be set to null. + * @return A gfxMatrix that converts points in this frame's coordinate space + * into points in aOutAncestor's coordinate space. */ - virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor); + virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor, + nsIFrame **aOutAncestor); /** * Bit-flags to pass to IsFrameOfType() diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 4a8744b7c72..7ee52edb061 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -2337,8 +2337,10 @@ nsTextFrame::GetTrimmedOffsets(const nsTextFragment* aFrag, { NS_ASSERTION(mTextRun, "Need textrun here"); // This should not be used during reflow. We need our TEXT_REFLOW_FLAGS - // to be set correctly. - NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW), + // to be set correctly. If our parent wasn't reflowed due to the frame + // tree being too deep then the return value doesn't matter. + NS_ASSERTION(!(GetStateBits() & NS_FRAME_FIRST_REFLOW) || + (GetParent()->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE), "Can only call this on frames that have been reflowed"); NS_ASSERTION(!(GetStateBits() & NS_FRAME_IN_REFLOW), "Can only call this on frames that are not being reflowed"); diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index f4f8973c5c0..d6c1045205b 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -140,7 +140,12 @@ nsSVGForeignObjectFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) { nsSVGForeignObjectFrameBase::DidSetStyleContext(aOldStyleContext); - UpdateGraphic(); + // No need to invalidate before first reflow - that will happen elsewhere. + // Moreover we haven't been initialised properly yet so we may not have the + // right state bits. + if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { + UpdateGraphic(); + } } NS_IMETHODIMP @@ -278,7 +283,8 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, } gfx3DMatrix -nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame **aOutAncestor) +nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor, + nsIFrame **aOutAncestor) { NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!"); diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index c73f062ce7f..6f5914f71ce 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -93,7 +93,8 @@ public: /** * Foreign objects can return a transform matrix. */ - virtual gfx3DMatrix GetTransformMatrix(nsIFrame **aOutAncestor); + virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor, + nsIFrame **aOutAncestor); /** * Get the "type" of the frame diff --git a/layout/svg/crashtests/713413-1.svg b/layout/svg/crashtests/713413-1.svg new file mode 100644 index 00000000000..7131202335e --- /dev/null +++ b/layout/svg/crashtests/713413-1.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/layout/svg/crashtests/crashtests.list b/layout/svg/crashtests/crashtests.list index 7b7c6f76dc7..1634d742332 100644 --- a/layout/svg/crashtests/crashtests.list +++ b/layout/svg/crashtests/crashtests.list @@ -121,3 +121,4 @@ load 692203-2.svg load 693424-1.svg load 709920-1.svg load 709920-2.svg +load 713413-1.svg diff --git a/layout/tables/crashtests/710098-1.html b/layout/tables/crashtests/710098-1.html new file mode 100644 index 00000000000..4886f8388a3 --- /dev/null +++ b/layout/tables/crashtests/710098-1.html @@ -0,0 +1,7 @@ + + + + +
+ + diff --git a/layout/tables/crashtests/crashtests.list b/layout/tables/crashtests/crashtests.list index 3fe5f837409..e713b832e10 100644 --- a/layout/tables/crashtests/crashtests.list +++ b/layout/tables/crashtests/crashtests.list @@ -123,3 +123,4 @@ load 695430-1.html load 707622-1.html load 705996-1.html load 705996-2.html +load 710098-1.html diff --git a/layout/tables/nsCellMap.cpp b/layout/tables/nsCellMap.cpp index 9362fb21957..2fe9ce552d9 100644 --- a/layout/tables/nsCellMap.cpp +++ b/layout/tables/nsCellMap.cpp @@ -2182,7 +2182,7 @@ void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, // get the rowspan and colspan from the cell map since the content may have changed bool zeroColSpan; PRUint32 numCols = aMap.GetColCount(); - PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, false); + PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, true); PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan); PRUint32 endRowIndex = aRowIndex + rowSpan - 1; PRUint32 endColIndex = aColIndex + colSpan - 1; diff --git a/mfbt/Assertions.h b/mfbt/Assertions.h index 3fb7f33e1e8..e01194bb7a5 100644 --- a/mfbt/Assertions.h +++ b/mfbt/Assertions.h @@ -38,6 +38,8 @@ * * ***** END LICENSE BLOCK ***** */ +/* Implementations of runtime and static assertion macros for C and C++. */ + #ifndef mozilla_Assertions_h_ #define mozilla_Assertions_h_ diff --git a/mfbt/RangedPtr.h b/mfbt/RangedPtr.h index dd1e76abf97..74ad1c75e45 100644 --- a/mfbt/RangedPtr.h +++ b/mfbt/RangedPtr.h @@ -38,6 +38,11 @@ * * ***** END LICENSE BLOCK ***** */ +/* + * Implements a smart pointer asserted to remain within a range specified at + * construction. + */ + #ifndef mozilla_RangedPtr_h_ #define mozilla_RangedPtr_h_ diff --git a/mfbt/RefPtr.h b/mfbt/RefPtr.h index ebb1a41dc3b..8c3ff94f102 100644 --- a/mfbt/RefPtr.h +++ b/mfbt/RefPtr.h @@ -38,16 +38,14 @@ * * ***** END LICENSE BLOCK ***** */ +/* Helpers for defining and using refcounted objects. */ + #ifndef mozilla_RefPtr_h_ #define mozilla_RefPtr_h_ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" -/** - * Helpers for defining and using refcounted objects. - */ - namespace mozilla { template class RefCounted; diff --git a/mfbt/Util.h b/mfbt/Util.h index cb28c0b5b71..a573b9e2283 100644 --- a/mfbt/Util.h +++ b/mfbt/Util.h @@ -37,6 +37,11 @@ * * ***** END LICENSE BLOCK ***** */ +/* + * Miscellaneous uncategorized functionality. Please add new functionality to + * new headers, or to other appropriate existing headers, not here. + */ + #ifndef mozilla_Util_h_ #define mozilla_Util_h_ diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 3e2db6ee1d0..202d32c4bec 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -615,8 +615,11 @@ abstract public class GeckoApp if (mLastUri == lastHistoryEntry.mUri && mLastTitle == lastHistoryEntry.mTitle) return; - - mLastViewport = mSoftwareLayerClient.getGeckoViewportMetrics().toJSON(); + + ViewportMetrics viewportMetrics = mSoftwareLayerClient.getGeckoViewportMetrics(); + if (viewportMetrics != null) + mLastViewport = viewportMetrics.toJSON(); + mLastUri = lastHistoryEntry.mUri; mLastTitle = lastHistoryEntry.mTitle; Bitmap bitmap = mSoftwareLayerClient.getBitmap(); diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index a13e0bf81c4..ca12bdaed20 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -78,6 +78,7 @@ public class GeckoEvent { public static final int ACTIVITY_START = 17; public static final int BROADCAST = 19; public static final int VIEWPORT = 20; + public static final int EXPOSE = 21; public static final int IME_COMPOSITION_END = 0; public static final int IME_COMPOSITION_BEGIN = 1; diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index d3d29883f27..7c455c54a0a 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -165,10 +165,6 @@ public class ProfileMigrator { // Places URL hit is newer than Android, // allow it to be updated with places date allowUpdate = true; - } else { - Log.i(LOGTAG, "Android history is newer, not adding: " + url - + " date: " + (new Date(date)).toString() - + " android: " + (new Date(androidDate)).toString()); } } @@ -178,7 +174,6 @@ public class ProfileMigrator { if (title != null) { BrowserDB.updateHistoryTitle(mCr, url, title); } - Log.i(LOGTAG, "Adding history: " + url); } } @@ -203,8 +198,6 @@ public class ProfileMigrator { String title = cursor.getString(titleCol); // Convert from us (Places) to ms (Java, Android) long date = cursor.getLong(dateCol) / (long)1000; - Log.i(LOGTAG, "History: " + title + " URL: " + url - + " time: " + (new Date(date)).toString()); addHistory(androidHistory, url, title, date); placesHistory.add(url); cursor.moveToNext(); @@ -231,7 +224,6 @@ public class ProfileMigrator { protected void addBookmark(String url, String title) { if (!BrowserDB.isBookmark(mCr, url)) { - Log.i(LOGTAG, "Adding bookmark: " + url); if (title == null) { title = url; } @@ -254,7 +246,6 @@ public class ProfileMigrator { while (!cursor.isAfterLast()) { String url = cursor.getString(urlCol); String title = cursor.getString(titleCol); - Log.i(LOGTAG, "Bookmark: " + title + " URL: " + url); addBookmark(url, title); cursor.moveToNext(); } @@ -272,12 +263,13 @@ public class ProfileMigrator { protected void addFavicon(String url, String mime, byte[] data) { ByteArrayInputStream byteStream = new ByteArrayInputStream(data); BitmapDrawable image = (BitmapDrawable) Drawable.createFromStream(byteStream, "src"); - try { - BrowserDB.updateFaviconForUrl(mCr, url, image); - Log.i(LOGTAG, "Favicon added: " + mime + " URL: " + url); - } catch (SQLiteException e) { - Log.i(LOGTAG, "Favicon failed: " + mime + " URL: " + url - + " error:" + e.getMessage()); + if (image != null) { + try { + BrowserDB.updateFaviconForUrl(mCr, url, image); + } catch (SQLiteException e) { + Log.i(LOGTAG, "Migrating favicon failed: " + mime + " URL: " + url + + " error:" + e.getMessage()); + } } } @@ -349,6 +341,8 @@ public class ProfileMigrator { dbFile.delete(); dbFileWal.delete(); dbFileShm.delete(); + + Log.i(LOGTAG, "Profile migration finished"); } catch (SQLiteException e) { if (db != null) { db.close(); diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index 55f8f98a261..b01a188472e 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -140,7 +140,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL layerController.setViewportMetrics(mGeckoViewport); } - GeckoAppShell.registerGeckoEventListener("Viewport:Update", this); + GeckoAppShell.registerGeckoEventListener("Viewport:Expose", this); + GeckoAppShell.registerGeckoEventListener("Viewport:UpdateAndDraw", this); GeckoAppShell.registerGeckoEventListener("Viewport:UpdateLater", this); } @@ -340,19 +341,15 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL } public void handleMessage(String event, JSONObject message) { - if ("Viewport:Update".equals(event)) { - beginTransaction(mTileLayer); - try { - updateViewport(message.getString("viewport"), false); - } catch (JSONException e) { - Log.e(LOGTAG, "Unable to update viewport", e); - } finally { - endTransaction(mTileLayer); - } + if ("Viewport:Expose".equals(event)) { + GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.EXPOSE)); + } else if ("Viewport:UpdateAndDraw".equals(event)) { + mUpdateViewportOnEndDraw = true; + + // Redraw everything. + Rect rect = new Rect(0, 0, mBufferSize.width, mBufferSize.height); + GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.DRAW, rect)); } else if ("Viewport:UpdateLater".equals(event)) { - if (!mTileLayer.inTransaction()) { - Log.e(LOGTAG, "Viewport:UpdateLater called while not in transaction. You should be using Viewport:Update instead!"); - } mUpdateViewportOnEndDraw = true; } } diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 6800252b66b..ff4e70545c9 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -264,7 +264,10 @@ public class PanZoomController // the screen orientation changed) so abort it and start a new one to // ensure the viewport doesn't contain out-of-bounds areas case NOTHING: - bounce(); + // Don't do animations here; they're distracting and can cause flashes on page + // transitions. + mController.setViewportMetrics(getValidViewportMetrics()); + mController.notifyLayerClientOfGeometryChange(); break; } } @@ -666,18 +669,18 @@ public class PanZoomController if (flingingX || flingingY) { mX.displace(); mY.displace(); updatePosition(); - } - /* - * If we're still flinging with an appreciable velocity, stop here. The threshold is - * higher in the case of overscroll, so we bounce back eagerly when overscrolling but - * coast smoothly to a stop when not. - */ - float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess())); - PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity()); - float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD; - if (PointUtils.distance(velocityVector) >= threshold) - return; + /* + * If we're still flinging with an appreciable velocity, stop here. The threshold is + * higher in the case of overscroll, so we bounce back eagerly when overscrolling but + * coast smoothly to a stop when not. + */ + float excess = PointUtils.distance(new PointF(mX.getExcess(), mY.getExcess())); + PointF velocityVector = new PointF(mX.getRealVelocity(), mY.getRealVelocity()); + float threshold = (excess >= 1.0f) ? STOPPED_THRESHOLD : FLING_STOPPED_THRESHOLD; + if (PointUtils.distance(velocityVector) >= threshold) + return; + } /* * Perform a bounce-back animation if overscrolled, unless panning is being overridden diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 27a6b639f8d..4bf837da5e8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -155,6 +155,16 @@ var Strings = {}; }); }); +var MetadataProvider = { + getDrawMetadata: function getDrawMetadata() { + return BrowserApp.getDrawMetadata(); + }, + + drawingAllowed: function drawingAllowed() { + return !BrowserApp.selectedTab.suppressDrawing; + } +}; + var BrowserApp = { _tabs: [], _selectedTab: null, @@ -169,7 +179,7 @@ var BrowserApp = { BrowserEventHandler.init(); ViewportHandler.init(); - getBridge().setDrawMetadataProvider(this.getDrawMetadata.bind(this)); + getBridge().setDrawMetadataProvider(MetadataProvider); Services.obs.addObserver(this, "Tab:Add", false); Services.obs.addObserver(this, "Tab:Load", false); @@ -190,6 +200,7 @@ var BrowserApp = { Services.obs.addObserver(this, "Viewport:Change", false); Services.obs.addObserver(this, "AgentMode:Change", false); Services.obs.addObserver(this, "SearchEngines:Get", false); + Services.obs.addObserver(this, "document-shown", false); function showFullScreenWarning() { NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short"); @@ -427,7 +438,6 @@ var BrowserApp = { }, quit: function quit() { - Cu.reportError("got quit quit message"); window.QueryInterface(Ci.nsIDOMChromeWindow).minimize(); window.close(); }, @@ -639,8 +649,65 @@ var BrowserApp = { return; let focused = doc.activeElement; if ((focused instanceof HTMLInputElement && focused.mozIsTextField(false)) || (focused instanceof HTMLTextAreaElement)) { - focused.scrollIntoView(false); - BrowserApp.getTabForBrowser(aBrowser).sendViewportUpdate(); + let tab = BrowserApp.getTabForBrowser(aBrowser); + let win = aBrowser.contentWindow; + + // tell gecko to scroll the field into view. this will scroll any nested scrollable elements + // as well as the browser's content window, and modify the scrollX and scrollY on the content window. + focused.scrollIntoView(true); + + // update userScrollPos so that we don't send a duplicate viewport update by triggering + // our scroll listener + tab.userScrollPos.x = win.scrollX; + tab.userScrollPos.y = win.scrollY; + + // note that: + // 1. because of the way we do zooming using a CSS transform, gecko does not take into + // account the effect of the zoom on the viewport size. + // 2. if the input element is near the bottom/right of the page (less than one viewport + // height/width away from the bottom/right), the scrollIntoView call will make gecko scroll to the + // bottom/right of the page in an attempt to align the input field with the top of the viewport. + // however, since gecko doesn't know about the zoom, what it thinks is the "bottom/right of + // the page" isn't actually the bottom/right of the page at the current zoom level, and we + // need to adjust this further. + // 3. we can't actually adjust this by changing the window scroll position, as gecko already thinks + // we're at the bottom/right, so instead we do it by changing the viewportExcess on the tab and + // moving the browser element. + + let visibleContentWidth = tab._viewport.width / tab._viewport.zoom; + let visibleContentHeight = tab._viewport.height / tab._viewport.zoom; + // get the rect that the focused element occupies relative to what gecko thinks the viewport is, + // and adjust it by viewportExcess to so that it is relative to what the user sees as the viewport. + let focusedRect = focused.getBoundingClientRect(); + focusedRect = { + left: focusedRect.left - tab.viewportExcess.x, + right: focusedRect.right - tab.viewportExcess.x, + top: focusedRect.top - tab.viewportExcess.y, + bottom: focusedRect.bottom - tab.viewportExcess.y + }; + let transformChanged = false; + if (focusedRect.right >= visibleContentWidth && focusedRect.left > 0) { + // the element is too far off the right side, so we need to scroll to the right more + tab.viewportExcess.x += Math.min(focusedRect.left, focusedRect.right - visibleContentWidth); + transformChanged = true; + } else if (focusedRect.left < 0) { + // the element is too far off the left side, so we need to scroll to the left more + tab.viewportExcess.x += focusedRect.left; + transformChanged = true; + } + if (focusedRect.bottom >= visibleContentHeight && focusedRect.top > 0) { + // the element is too far down, so we need to scroll down more + tab.viewportExcess.y += Math.min(focusedRect.top, focusedRect.bottom - visibleContentHeight); + transformChanged = true; + } else if (focusedRect.top < 0) { + // the element is too far up, so we need to scroll up more + tab.viewportExcess.y += focusedRect.top; + transformChanged = true; + } + if (transformChanged) + tab.updateTransform(); + // finally, let java know where we ended up + tab.sendViewportUpdate(); } }, @@ -705,6 +772,20 @@ var BrowserApp = { ViewportHandler.onResize(); } else if (aTopic == "SearchEngines:Get") { this.getSearchEngines(); + } else if (aTopic == "document-shown") { + let tab = this.selectedTab; + if (tab.browser.contentDocument != aSubject) { + return; + } + + ViewportHandler.resetMetadata(tab); + + // Unsuppress drawing unless the page was being thawed from the bfcache (which is an atomic + // operation, so there is no drawing to suppress). + if (tab.suppressDrawing) { + tab.sendExposeEvent(); + tab.suppressDrawing = false; + } } }, @@ -712,7 +793,7 @@ var BrowserApp = { delete this.defaultBrowserWidth; let width = Services.prefs.getIntPref("browser.viewport.desktopWidth"); return this.defaultBrowserWidth = width; - } + } }; var NativeWindow = { @@ -1184,6 +1265,12 @@ Tab.prototype = { this.browser.addEventListener("PluginClickToPlay", this, true); this.browser.addEventListener("pagehide", this, true); + let chromeEventHandler = this.browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .chromeEventHandler; + chromeEventHandler.addEventListener("DOMWindowCreated", this, false); + Services.obs.addObserver(this, "http-on-modify-request", false); if (!aParams.delayLoad) { @@ -1320,20 +1407,22 @@ Tab.prototype = { transformChanged = true; } + if (transformChanged) + this.updateTransform(); + }, + + updateTransform: function() { let hasZoom = (Math.abs(this._viewport.zoom - 1.0) >= 1e-6); + let x = this._viewport.offsetX + Math.round(-this.viewportExcess.x * this._viewport.zoom); + let y = this._viewport.offsetY + Math.round(-this.viewportExcess.y * this._viewport.zoom); - if (transformChanged) { - let x = this._viewport.offsetX + Math.round(-excessX * this._viewport.zoom); - let y = this._viewport.offsetY + Math.round(-excessY * this._viewport.zoom); + let transform = + "translate(" + x + "px, " + + y + "px)"; + if (hasZoom) + transform += " scale(" + this._viewport.zoom + ")"; - let transform = - "translate(" + x + "px, " + - y + "px)"; - if (hasZoom) - transform += " scale(" + this._viewport.zoom + ")"; - - this.browser.style.MozTransform = transform; - } + this.browser.style.MozTransform = transform; }, get viewport() { @@ -1393,8 +1482,7 @@ Tab.prototype = { return; sendMessageToJava({ gecko: { - type: "Viewport:Update", - viewport: JSON.stringify(this.viewport) + type: "Viewport:UpdateAndDraw" } }); }, @@ -1537,6 +1625,18 @@ Tab.prototype = { } break; } + + case "DOMWindowCreated": { + // Conveniently, this call to getBrowserForDocument() will return null if the document is + // not the top-level content document of the browser. + let browser = BrowserApp.getBrowserForDocument(aEvent.originalTarget); + if (!browser) + break; + + let tab = BrowserApp.getTabForBrowser(browser); + tab.suppressDrawing = true; + break; + } } }, @@ -1796,6 +1896,16 @@ Tab.prototype = { } }, + sendExposeEvent: function() { + // Now that the document is actually on the screen, send an expose event. + this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + sendMessageToJava({ + gecko: { + type: "Viewport:Expose" + } + }); + }, + QueryInterface: XPCOMUtils.generateQI([ Ci.nsIWebProgressListener, Ci.nsISHistoryListener, diff --git a/mobile/android/chrome/tests/browser_scrollbar.js b/mobile/android/chrome/tests/browser_scrollbar.js index 008d43c35f9..231b6fde4c6 100644 --- a/mobile/android/chrome/tests/browser_scrollbar.js +++ b/mobile/android/chrome/tests/browser_scrollbar.js @@ -65,7 +65,9 @@ function checkScrollbarsPosition(aX) { let verticalRect = verticalScrollbar.getBoundingClientRect(); let margin = parseInt(verticalScrollbar.getAttribute("end")); - let expectedPosition = window.innerWidth - aX - margin; + let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/); + let translateX = matches ? parseInt(matches[1]) : 0; + let expectedPosition = window.innerWidth - aX - margin + translateX; is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")"); EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); diff --git a/mobile/xul/chrome/tests/browser_scrollbar.js b/mobile/xul/chrome/tests/browser_scrollbar.js index 008d43c35f9..231b6fde4c6 100644 --- a/mobile/xul/chrome/tests/browser_scrollbar.js +++ b/mobile/xul/chrome/tests/browser_scrollbar.js @@ -65,7 +65,9 @@ function checkScrollbarsPosition(aX) { let verticalRect = verticalScrollbar.getBoundingClientRect(); let margin = parseInt(verticalScrollbar.getAttribute("end")); - let expectedPosition = window.innerWidth - aX - margin; + let matches = verticalScrollbar.style.MozTransform.match(/^translate\(([-0-9]+)px/); + let translateX = matches ? parseInt(matches[1]) : 0; + let expectedPosition = window.innerWidth - aX - margin + translateX; is(verticalRect.right, expectedPosition, "The vertical scrollbar should be position to " + expectedPosition + " (got " + verticalRect.right + ")"); EventUtils.synthesizeMouse(browser, width / 2, height * 3 / 4, { type: "mouseup" }); diff --git a/testing/mochitest/tests/SimpleTest/SimpleTest.js b/testing/mochitest/tests/SimpleTest/SimpleTest.js index 2fb1e9408ed..6a933fa0773 100644 --- a/testing/mochitest/tests/SimpleTest/SimpleTest.js +++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js @@ -16,7 +16,11 @@ var SimpleTest = { }; var parentRunner = null; -var isPrimaryTestWindow = !!parent.TestRunner; + +// In normal test runs, the window that has a TestRunner in its parent is +// the primary window. In single test runs, if there is no parent and there +// is no opener then it is the primary window. +var isPrimaryTestWindow = !!parent.TestRunner || (parent == window && !opener); // Finds the TestRunner for this test run and the SpecialPowers object (in // case it is not defined) from a parent/opener window. diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 8a9d90469cd..50be70fb34d 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -236,6 +236,9 @@ Reporter.prototype = { }, treeNameMatches: function(aTreeName) { + // Nb: the '/' must be present, because we have a KIND_OTHER reporter + // called "explicit" which is not part of the "explicit" tree. + aTreeName += "/"; return this._path.slice(0, aTreeName.length) === aTreeName; } }; diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul index cb32c7ff96a..c1cc9ed3a64 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory.xul @@ -36,8 +36,10 @@ while (e.hasMoreElements()) { var r = e.getNext().QueryInterface(Ci.nsIMemoryMultiReporter); // Call collectReports, even though we don't use its results, just to - // test that the multi-reporter doesn't crash or anything. + // test that the multi-reporter doesn't crash or anything. And likewise + // for the |explicitNonHeap| field. r.collectReports(function(){}, null); + dummy += r.explicitNonHeap; mgr.unregisterMultiReporter(r); realMultiReporters.push(r); } @@ -78,8 +80,6 @@ f("", "explicit/b/b", HEAP, 75 * MB), f("", "explicit/b/c/a", HEAP, 70 * MB), f("", "explicit/b/c/b", HEAP, 2 * MB), // omitted - f("", "explicit/c", NONHEAP, 100 * MB), - f("", "explicit/c/d", NONHEAP, 13 * MB), // subsumed by parent f("", "explicit/g", HEAP, 1 * MB), // internal, dup: merge f("", "explicit/g/a", HEAP, 6 * MB), f("", "explicit/g/b", HEAP, 5 * MB), @@ -89,13 +89,16 @@ var fakeMultiReporters = [ { collectReports: function(cbObj, closure) { function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); } + f("explicit/c", NONHEAP, BYTES, 100 * MB), + f("explicit/c/d", NONHEAP, BYTES, 13 * MB), // subsumed by parent f("explicit/c/d", NONHEAP, BYTES, 10 * MB), // dup, subsumed by parent f("explicit/cc", NONHEAP, BYTES, 13 * MB); f("explicit/cc", NONHEAP, BYTES, 10 * MB); // dup f("explicit/d", NONHEAP, BYTES, 499 * KB); // omitted f("explicit/e", NONHEAP, BYTES, 100 * KB); // omitted f("explicit/f/g/h/i", HEAP, BYTES, 20 * MB); - } + }, + explicitNonHeap: (100 + 13 + 10)*MB + (499 + 100)*KB }, { collectReports: function(cbObj, closure) { function f(p, k, u, a) { cbObj.callback("", p, k, u, a, "(desc)", closure); } @@ -104,7 +107,8 @@ f("other2", OTHER, BYTES, 222 * MB); f("perc2", OTHER, PERCENTAGE, 10000); f("perc1", OTHER, PERCENTAGE, 4567); - } + }, + explicitNonHeap: 0 }, { collectReports: function(cbObj, closure) { // The amounts are given in pages, so multiply here by 4kb. @@ -115,7 +119,8 @@ f("map/vsize/a", 19); f("map/swap/b/c", 10); f("map/resident/a", 42); - } + }, + explicitNonHeap: 0 } ]; for (var i = 0; i < fakeReporters.length; i++) { diff --git a/toolkit/components/downloads/test/schema_migration/head_migration.js b/toolkit/components/downloads/test/schema_migration/head_migration.js index 7961beaa6e7..a71fe32cd55 100644 --- a/toolkit/components/downloads/test/schema_migration/head_migration.js +++ b/toolkit/components/downloads/test/schema_migration/head_migration.js @@ -34,12 +34,18 @@ * * ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://gre/modules/Services.jsm"); + const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; do_get_profile(); +do_register_cleanup(function() { + Services.obs.notifyObservers(null, "quit-application", null); +}); + var dirSvc = Cc["@mozilla.org/file/directory_service;1"]. getService(Ci.nsIProperties); diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 609326b5b5d..dce53728b9a 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -57,6 +57,7 @@ const MEM_HISTOGRAMS = { "js-gc-heap": "MEMORY_JS_GC_HEAP", "js-compartments-system": "MEMORY_JS_COMPARTMENTS_SYSTEM", "js-compartments-user": "MEMORY_JS_COMPARTMENTS_USER", + "explicit": "MEMORY_EXPLICIT", "resident": "MEMORY_RESIDENT", "explicit/storage/sqlite": "MEMORY_STORAGE_SQLITE", "explicit/images/content/used/uncompressed": @@ -263,33 +264,36 @@ TelemetryPing.prototype = { while (e.hasMoreElements()) { let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter); let id = MEM_HISTOGRAMS[mr.path]; - if (!id || mr.amount == -1) { + if (!id) { + continue; + } + // mr.amount is expensive to read in some cases, so get it only once. + let amount = mr.amount; + if (amount == -1) { continue; } let val; if (mr.units == Ci.nsIMemoryReporter.UNITS_BYTES) { - val = Math.floor(mr.amount / 1024); + val = Math.floor(amount / 1024); } else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT) { - val = mr.amount; + val = amount; } else if (mr.units == Ci.nsIMemoryReporter.UNITS_COUNT_CUMULATIVE) { // If the reporter gives us a cumulative count, we'll report the // difference in its value between now and our previous ping. - // Read mr.amount just once so our arithmetic is consistent. - let curVal = mr.amount; if (!(mr.path in this._prevValues)) { // If this is the first time we're reading this reporter, store its // current value but don't report it in the telemetry ping, so we // ignore the effect startup had on the reporter. - this._prevValues[mr.path] = curVal; + this._prevValues[mr.path] = amount; continue; } - val = curVal - this._prevValues[mr.path]; - this._prevValues[mr.path] = curVal; + val = amount - this._prevValues[mr.path]; + this._prevValues[mr.path] = amount; } else { NS_ASSERT(false, "Can't handle memory reporter with units " + mr.units); @@ -297,11 +301,6 @@ TelemetryPing.prototype = { } this.addValue(mr.path, id, val); } - // "explicit" is found differently. - let explicit = mgr.explicit; // Get it only once, it's reasonably expensive - if (explicit != -1) { - this.addValue("explicit", "MEMORY_EXPLICIT", Math.floor(explicit / 1024)); - } }, /** diff --git a/toolkit/content/InlineSpellChecker.jsm b/toolkit/content/InlineSpellChecker.jsm index 8942c9427a5..0d25229e681 100644 --- a/toolkit/content/InlineSpellChecker.jsm +++ b/toolkit/content/InlineSpellChecker.jsm @@ -38,9 +38,11 @@ var EXPORTED_SYMBOLS = [ "InlineSpellChecker" ]; var gLanguageBundle; var gRegionBundle; +const MAX_UNDO_STACK_DEPTH = 1; function InlineSpellChecker(aEditor) { this.init(aEditor); + this.mAddedWordStack = []; // We init this here to preserve it between init/uninit calls } InlineSpellChecker.prototype = { @@ -284,8 +286,27 @@ InlineSpellChecker.prototype = { // callback for adding the current misspelling to the user-defined dictionary addToDictionary: function() { + // Prevent the undo stack from growing over the max depth + if (this.mAddedWordStack.length == MAX_UNDO_STACK_DEPTH) + this.mAddedWordStack.shift(); + + this.mAddedWordStack.push(this.mMisspelling); this.mInlineSpellChecker.addWordToDictionary(this.mMisspelling); }, + // callback for removing the last added word to the dictionary LIFO fashion + undoAddToDictionary: function() + { + if (this.mAddedWordStack.length > 0) + { + var word = this.mAddedWordStack.pop(); + this.mInlineSpellChecker.removeWordFromDictionary(word); + } + }, + canUndo : function() + { + // Return true if we have words on the stack + return (this.mAddedWordStack.length > 0); + }, ignoreWord: function() { this.mInlineSpellChecker.ignoreWord(this.mMisspelling); diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index e4e2233f539..6857bb620b0 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -144,6 +144,7 @@ _TEST_FILES += \ test_textbox_emptytext.xul \ test_textbox_number.xul \ test_textbox_search.xul \ + test_textbox_dictionary.xul\ test_toolbar.xul \ xul_selectcontrol.js \ test_popupincontent.xul \ diff --git a/toolkit/content/tests/chrome/test_textbox_dictionary.xul b/toolkit/content/tests/chrome/test_textbox_dictionary.xul new file mode 100644 index 00000000000..53a0bb9e081 --- /dev/null +++ b/toolkit/content/tests/chrome/test_textbox_dictionary.xul @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + diff --git a/toolkit/content/widgets/textbox.xml b/toolkit/content/widgets/textbox.xml index 50d0bfe361a..b2a663dc8cc 100644 --- a/toolkit/content/widgets/textbox.xml +++ b/toolkit/content/widgets/textbox.xml @@ -515,6 +515,8 @@ + @@ -580,6 +582,7 @@ this._setMenuItemVisibility("spell-check-enabled", false); this._setMenuItemVisibility("spell-check-separator", false); this._setMenuItemVisibility("spell-add-to-dictionary", false); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", false); this._setMenuItemVisibility("spell-suggestions-separator", false); this._setMenuItemVisibility("spell-dictionaries", false); return; @@ -593,6 +596,7 @@ var overMisspelling = spellui.overMisspelling; this._setMenuItemVisibility("spell-add-to-dictionary", overMisspelling); + this._setMenuItemVisibility("spell-undo-add-to-dictionary", spellui.canUndo()); this._setMenuItemVisibility("spell-suggestions-separator", overMisspelling); // suggestion list diff --git a/toolkit/locales/en-US/chrome/global/textcontext.dtd b/toolkit/locales/en-US/chrome/global/textcontext.dtd index c870eeca52b..26c66d6f4dd 100644 --- a/toolkit/locales/en-US/chrome/global/textcontext.dtd +++ b/toolkit/locales/en-US/chrome/global/textcontext.dtd @@ -13,6 +13,8 @@ + + diff --git a/toolkit/mozapps/downloads/tests/unit/head_downloads.js b/toolkit/mozapps/downloads/tests/unit/head_downloads.js new file mode 100644 index 00000000000..4f199e5cf3b --- /dev/null +++ b/toolkit/mozapps/downloads/tests/unit/head_downloads.js @@ -0,0 +1,5 @@ +Components.utils.import("resource://gre/modules/Services.jsm"); + +do_register_cleanup(function() { + Services.obs.notifyObservers(null, "quit-application", null); +}); diff --git a/toolkit/mozapps/downloads/tests/unit/xpcshell.ini b/toolkit/mozapps/downloads/tests/unit/xpcshell.ini index 7c79798ffa8..558d009bbf0 100644 --- a/toolkit/mozapps/downloads/tests/unit/xpcshell.ini +++ b/toolkit/mozapps/downloads/tests/unit/xpcshell.ini @@ -1,5 +1,5 @@ [DEFAULT] -head = +head = head_downloads.js tail = [test_DownloadLastDir.js] diff --git a/tools/profiler/Makefile.in b/tools/profiler/Makefile.in index 9b430997d78..4d3976d6348 100644 --- a/tools/profiler/Makefile.in +++ b/tools/profiler/Makefile.in @@ -83,6 +83,7 @@ ifneq (,$(filter Android Linux,$(OS_TARGET))) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-linux.cc \ platform-linux.cc \ TableTicker.cpp \ $(NULL) @@ -92,6 +93,7 @@ ifeq ($(OS_TARGET),Darwin) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-macos.cc \ platform-macos.cc \ TableTicker.cpp \ $(NULL) @@ -102,6 +104,7 @@ ifeq ($(OS_TARGET),WINNT) DEFINES += -DMOZ_ENABLE_PROFILER_SPS CPPSRCS += \ + shared-libraries-win32.cc \ platform-win32.cc \ TableTicker.cpp \ $(NULL) diff --git a/tools/profiler/sps/TableTicker.cpp b/tools/profiler/sps/TableTicker.cpp index ae90763ac34..ee5126da6a3 100644 --- a/tools/profiler/sps/TableTicker.cpp +++ b/tools/profiler/sps/TableTicker.cpp @@ -43,8 +43,17 @@ #include "nsXULAppAPI.h" #include "nsThreadUtils.h" #include "prenv.h" +#include "shared-libraries.h" +#include "mozilla/StringBuilder.h" + +// we eventually want to make this runtime switchable +//#define USE_BACKTRACE +#ifdef USE_BACKTRACE +#include +#endif using std::string; +using namespace mozilla; #ifdef XP_WIN #include @@ -71,6 +80,7 @@ using std::string; #define snprintf _snprintf #endif + mozilla::tls::key pkey_stack; mozilla::tls::key pkey_ticker; // We need to track whether we've been initialized otherwise @@ -113,12 +123,12 @@ public: { } string TagToString(Profile *profile); - void WriteTag(Profile *profile, FILE* stream); private: union { const char* mTagData; float mTagFloat; + Address mTagAddress; }; Address mLeafAddress; char mTagName; @@ -134,6 +144,10 @@ public: , mEntrySize(aEntrySize) { mEntries = new ProfileEntry[mEntrySize]; + mNeedsSharedLibraryInfo = false; +#if defined(ENABLE_SPS_LEAF_DATA) || defined(USE_BACKTRACE) + mNeedsSharedLibraryInfo = true; +#endif } ~Profile() @@ -153,18 +167,17 @@ public: } } - void ToString(string* profile) + void ToString(StringBuilder &profile) { - // Can't be called from signal because - // get_maps calls non reentrant functions. -#ifdef ENABLE_SPS_LEAF_DATA - mMaps = getmaps(getpid()); -#endif + if (mNeedsSharedLibraryInfo) { + // Can't be called from signal because + // getting the shared library information can call non-reentrant functions. + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); + } - *profile = ""; int oldReadPos = mReadPos; while (mReadPos != mWritePos) { - *profile += mEntries[mReadPos].TagToString(this); + profile.Append(mEntries[mReadPos].TagToString(this).c_str()); mReadPos = (mReadPos + 1) % mEntrySize; } mReadPos = oldReadPos; @@ -172,26 +185,25 @@ public: void WriteProfile(FILE* stream) { - // Can't be called from signal because - // get_maps calls non reentrant functions. -#ifdef ENABLE_SPS_LEAF_DATA - mMaps = getmaps(getpid()); -#endif + if (mNeedsSharedLibraryInfo) { + // Can't be called from signal because + // getting the shared library information can call non-reentrant functions. + mSharedLibraryInfo = SharedLibraryInfo::GetInfoForSelf(); + } int oldReadPos = mReadPos; while (mReadPos != mWritePos) { - mEntries[mReadPos].WriteTag(this, stream); + string tag = mEntries[mReadPos].TagToString(this); + fwrite(tag.data(), 1, tag.length(), stream); mReadPos = (mReadPos + 1) % mEntrySize; } mReadPos = oldReadPos; } -#ifdef ENABLE_SPS_LEAF_DATA - MapInfo& getMap() + SharedLibraryInfo& getSharedLibraryInfo() { - return mMaps; + return mSharedLibraryInfo; } -#endif private: // Circular buffer 'Keep One Slot Open' implementation // for simplicity @@ -199,9 +211,8 @@ private: int mWritePos; // points to the next entry we will write to int mReadPos; // points to the next entry we will read to int mEntrySize; -#ifdef ENABLE_SPS_LEAF_DATA - MapInfo mMaps; -#endif + bool mNeedsSharedLibraryInfo; + SharedLibraryInfo mSharedLibraryInfo; }; class SaveProfileTask; @@ -303,6 +314,49 @@ void TableTicker::HandleSaveRequest() NS_DispatchToMainThread(runnable); } +#ifdef USE_BACKTRACE +static +void doBacktrace(Profile &aProfile) +{ + void *array[100]; + int count = backtrace (array, 100); + + bool isSignal = true; +#ifndef __i386__ + // the test doesn't work for 64bit + isSignal = false; +#endif + for (int i = count-1; i >= 0; i--) { + if( isSignal ) { + if( (intptr_t)array[i] == -1 ) { // signal frames have addresses of -1? + isSignal = false; + } + continue; + } + aProfile.addTag(ProfileEntry('l', (const char*)array[i])); + } + aProfile.addTag(ProfileEntry('s', "XRE_Main", 0)); +} +#endif + +static +void doSampleStackTrace(Stack *aStack, Profile &aProfile, TickSample *sample) +{ + // Sample + // 's' tag denotes the start of a sample block + // followed by 0 or more 'c' tags. + for (int i = 0; i < aStack->mStackPointer; i++) { + if (i == 0) { + Address pc = 0; + if (sample) { + pc = sample->pc; + } + aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc)); + } else { + aProfile.addTag(ProfileEntry('c', aStack->mStack[i])); + } + } +} void TableTicker::Tick(TickSample* sample) { @@ -315,20 +369,11 @@ void TableTicker::Tick(TickSample* sample) } mStack->mQueueClearMarker = true; - // Sample - // 's' tag denotes the start of a sample block - // followed by 0 or more 'c' tags. - for (int i = 0; i < mStack->mStackPointer; i++) { - if (i == 0) { - Address pc = 0; - if (sample) { - pc = sample->pc; - } - mProfile.addTag(ProfileEntry('s', mStack->mStack[i], pc)); - } else { - mProfile.addTag(ProfileEntry('c', mStack->mStack[i])); - } - } +#ifdef USE_BACKTRACE + doBacktrace(mProfile); +#else + doSampleStackTrace(mStack, mProfile, sample); +#endif if (!sLastTracerEvent.IsNull()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; @@ -343,6 +388,27 @@ string ProfileEntry::TagToString(Profile *profile) char buff[50]; snprintf(buff, 50, "%-40f", mTagFloat); tag += string(1, mTagName) + string("-") + string(buff) + string("\n"); + } else if (mTagName == 'l') { + bool found = false; + char tagBuff[1024]; + SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); + Address pc = mTagAddress; + // TODO Use binary sort (STL) + for (size_t i = 0; i < shlibInfo.GetSize(); i++) { + SharedLibrary &e = shlibInfo.GetEntry(i); + if (pc > (Address)e.GetStart() && pc < (Address)e.GetEnd()) { + if (e.GetName()) { + found = true; + snprintf(tagBuff, 1024, "l-%s@%p\n", e.GetName(), pc - e.GetStart()); + tag += string(tagBuff); + break; + } + } + } + if (!found) { + snprintf(tagBuff, 1024, "l-???@%p\n", pc); + tag += string(tagBuff); + } } else { tag += string(1, mTagName) + string("-") + string(mTagData) + string("\n"); } @@ -351,11 +417,11 @@ string ProfileEntry::TagToString(Profile *profile) if (mLeafAddress) { bool found = false; char tagBuff[1024]; - MapInfo& maps = profile->getMap(); + SharedLibraryInfo& shlibInfo = profile->getSharedLibraryInfo(); unsigned long pc = (unsigned long)mLeafAddress; // TODO Use binary sort (STL) - for (size_t i = 0; i < maps.GetSize(); i++) { - MapEntry &e = maps.GetEntry(i); + for (size_t i = 0; i < shlibInfo.GetSize(); i++) { + SharedLibrary &e = shlibInfo.GetEntry(i); if (pc > e.GetStart() && pc < e.GetEnd()) { if (e.GetName()) { found = true; @@ -374,33 +440,6 @@ string ProfileEntry::TagToString(Profile *profile) return tag; } -void ProfileEntry::WriteTag(Profile *profile, FILE *stream) -{ - fprintf(stream, "%c-%s\n", mTagName, mTagData); - -#ifdef ENABLE_SPS_LEAF_DATA - if (mLeafAddress) { - bool found = false; - MapInfo& maps = profile->getMap(); - unsigned long pc = (unsigned long)mLeafAddress; - // TODO Use binary sort (STL) - for (size_t i = 0; i < maps.GetSize(); i++) { - MapEntry &e = maps.GetEntry(i); - if (pc > e.GetStart() && pc < e.GetEnd()) { - if (e.GetName()) { - found = true; - fprintf(stream, "l-%s@%li\n", e.GetName(), pc - e.GetStart()); - break; - } - } - } - if (!found) { - fprintf(stream, "l-???@%li\n", pc); - } - } -#endif -} - #define PROFILE_DEFAULT_ENTRY 100000 #define PROFILE_DEFAULT_INTERVAL 10 @@ -454,11 +493,11 @@ char* mozilla_sampler_get_profile() { return NULL; } - string profile; - t->GetProfile()->ToString(&profile); + StringBuilder profile; + t->GetProfile()->ToString(profile); - char *rtn = (char*)malloc( (strlen(profile.c_str())+1) * sizeof(char) ); - strcpy(rtn, profile.c_str()); + char *rtn = (char*)malloc( (profile.Length()+1) * sizeof(char) ); + strcpy(rtn, profile.Buffer()); return rtn; } diff --git a/tools/profiler/sps/platform-linux.cc b/tools/profiler/sps/platform-linux.cc index 8a855ad5269..44fcae99e87 100644 --- a/tools/profiler/sps/platform-linux.cc +++ b/tools/profiler/sps/platform-linux.cc @@ -39,12 +39,7 @@ #include #include -// Real time signals are not supported on android. -// This behaves as a standard signal. -#define SIGNAL_SAVE_PROFILE 42 - -#define PATH_MAX_TOSTRING(x) #x -#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) +#define SIGNAL_SAVE_PROFILE SIGUSR2 #if defined(__GLIBC__) // glibc doesn't implement gettid(2). @@ -55,61 +50,6 @@ pid_t gettid() } #endif -#ifdef ENABLE_SPS_LEAF_DATA -/* a crapy version of getline, because it's not included in bionic */ -static ssize_t getline(char **lineptr, size_t *n, FILE *stream) -{ - char *ret; - if (!*lineptr) { - *lineptr = (char*)malloc(4096); - } - ret = fgets(*lineptr, 4096, stream); - if (!ret) - return 0; - return strlen(*lineptr); -} - -MapInfo getmaps(pid_t pid) -{ - MapInfo info; - char path[PATH_MAX]; - snprintf(path, PATH_MAX, "/proc/%d/maps", pid); - FILE *maps = fopen(path, "r"); - char *line = NULL; - int count = 0; - size_t line_size = 0; - while (maps && getline (&line, &line_size, maps) > 0) { - int ret; - //XXX: needs input sanitizing - unsigned long start; - unsigned long end; - char perm[6] = ""; - unsigned long offset; - char name[PATH_MAX] = ""; - ret = sscanf(line, - "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n", - &start, &end, perm, &offset, name); - if (!strchr(perm, 'x')) { - // Ignore non executable entries - continue; - } - if (ret != 5 && ret != 4) { - LOG("Get maps line failed"); - continue; - } - MapEntry entry(start, end, offset, name); - info.AddMapEntry(entry); - if (count > 10000) { - LOG("Get maps failed"); - break; - } - count++; - } - free(line); - return info; -} -#endif - static Sampler* sActiveSampler = NULL; @@ -193,9 +133,11 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { sActiveSampler->Tick(sample); } +#ifndef XP_MACOSX void tgkill(pid_t tgid, pid_t tid, int signalno) { syscall(SYS_tgkill, tgid, tid, signalno); } +#endif class Sampler::PlatformData : public Malloced { public: @@ -203,16 +145,26 @@ class Sampler::PlatformData : public Malloced { : sampler_(sampler), signal_handler_installed_(false), vm_tgid_(getpid()), +#ifndef XP_MACOSX vm_tid_(gettid()), - signal_sender_launched_(false) { +#endif + signal_sender_launched_(false) +#ifdef XP_MACOSX + , signal_receiver_(pthread_self()) +#endif + { } void SignalSender() { while (sampler_->IsActive()) { sampler_->HandleSaveRequest(); +#ifdef XP_MACOSX + pthread_kill(signal_receiver_, SIGPROF); +#else // Glibc doesn't provide a wrapper for tgkill(2). tgkill(vm_tgid_, vm_tid_, SIGPROF); +#endif // Convert ms to us and subtract 100 us to compensate delays // occuring during signal delivery. @@ -240,6 +192,9 @@ class Sampler::PlatformData : public Malloced { pid_t vm_tid_; bool signal_sender_launched_; pthread_t signal_sender_thread_; +#ifdef XP_MACOSX + pthread_t signal_receiver_; +#endif }; diff --git a/tools/profiler/sps/platform.h b/tools/profiler/sps/platform.h index 092113928f6..87fdf0f54a3 100644 --- a/tools/profiler/sps/platform.h +++ b/tools/profiler/sps/platform.h @@ -24,63 +24,6 @@ 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)) - {} - - MapEntry(const MapEntry& aEntry) - : mStart(aEntry.mStart) - , mEnd(aEntry.mEnd) - , mOffset(aEntry.mOffset) - , mName(strdup(aEntry.mName)) - {} - - ~MapEntry() - { - free(mName); - } - - unsigned long GetStart() { return mStart; } - unsigned long GetEnd() { return mEnd; } - char* GetName() { return mName; } - -private: - unsigned long mStart; - unsigned long mEnd; - unsigned long mOffset; - char *mName; -}; - -class MapInfo { -public: - MapInfo() {} - - void AddMapEntry(MapEntry entry) - { - mEntries.push_back(entry); - } - - MapEntry& GetEntry(size_t i) - { - return mEntries[i]; - } - - size_t GetSize() - { - return mEntries.size(); - } -private: - std::vector mEntries; -}; - -#ifdef ENABLE_SPS_LEAF_DATA -struct MapInfo getmaps(pid_t pid); -#endif // ---------------------------------------------------------------------------- // Mutex // diff --git a/tools/profiler/sps/shared-libraries-linux.cc b/tools/profiler/sps/shared-libraries-linux.cc new file mode 100644 index 00000000000..fb9baa0e2a7 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-linux.cc @@ -0,0 +1,65 @@ +#define PATH_MAX_TOSTRING(x) #x +#define PATH_MAX_STRING(x) PATH_MAX_TOSTRING(x) +#include +#include +#include +#include +#include +#include "platform.h" +#include "shared-libraries.h" + +#ifndef __GLIBC__ +/* a crapy version of getline, because it's not included in bionic */ +static ssize_t getline(char **lineptr, size_t *n, FILE *stream) +{ + char *ret; + if (!*lineptr) { + *lineptr = (char*)malloc(4096); + } + ret = fgets(*lineptr, 4096, stream); + if (!ret) + return 0; + return strlen(*lineptr); +} +#endif + +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + pid_t pid = getpid(); + SharedLibraryInfo info; + char path[PATH_MAX]; + snprintf(path, PATH_MAX, "/proc/%d/maps", pid); + FILE *maps = fopen(path, "r"); + char *line = NULL; + int count = 0; + size_t line_size = 0; + while (maps && getline (&line, &line_size, maps) > 0) { + int ret; + //XXX: needs input sanitizing + unsigned long start; + unsigned long end; + char perm[6] = ""; + unsigned long offset; + char name[PATH_MAX] = ""; + ret = sscanf(line, + "%lx-%lx %6s %lx %*s %*x %" PATH_MAX_STRING(PATH_MAX) "s\n", + &start, &end, perm, &offset, name); + if (!strchr(perm, 'x')) { + // Ignore non executable entries + continue; + } + if (ret != 5 && ret != 4) { + LOG("Get maps line failed"); + continue; + } + SharedLibrary shlib(start, end, offset, name); + info.AddSharedLibrary(shlib); + if (count > 10000) { + LOG("Get maps failed"); + break; + } + count++; + } + free(line); + return info; +} diff --git a/tools/profiler/sps/shared-libraries-macos.cc b/tools/profiler/sps/shared-libraries-macos.cc new file mode 100644 index 00000000000..0b8ae85d864 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-macos.cc @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Girard + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "shared-libraries.h" + +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +// borrowed from Breakpad +// Fallback declarations for TASK_DYLD_INFO and friends, introduced in +// in the Mac OS X 10.6 SDK. +#define TASK_DYLD_INFO 17 +struct task_dyld_info { + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; + }; +typedef struct task_dyld_info task_dyld_info_data_t; +typedef struct task_dyld_info *task_dyld_info_t; +#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) + +#endif + +// Architecture specific abstraction. +#ifdef __i386__ +typedef mach_header platform_mach_header; +typedef segment_command mach_segment_command_type; +#define MACHO_MAGIC_NUMBER MH_MAGIC +#define CMD_SEGMENT LC_SEGMENT +#define seg_size uint32_t +#else +typedef mach_header_64 platform_mach_header; +typedef segment_command_64 mach_segment_command_type; +#define MACHO_MAGIC_NUMBER MH_MAGIC_64 +#define CMD_SEGMENT LC_SEGMENT_64 +#define seg_size uint64_t +#endif + +static +void addSharedLibrary(const platform_mach_header* header, char *name, SharedLibraryInfo &info) { + const struct load_command *cmd = + reinterpret_cast(header + 1); + + seg_size size; + // Find the cmd segment in the macho image. It will contain the offset we care about. + for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { + if (cmd->cmd == CMD_SEGMENT) { + const mach_segment_command_type *seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + size = seg->vmsize; + unsigned long long start = reinterpret_cast(header); + info.AddSharedLibrary(SharedLibrary(start, start+seg->vmsize, seg->vmsize, name)); + return; + } + } + + cmd = reinterpret_cast + (reinterpret_cast(cmd) + cmd->cmdsize); + } +} + +// Use dyld to inspect the macho image information. We can build the SharedLibraryEntry structure +// giving us roughtly the same info as /proc/PID/maps in Linux. +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + SharedLibraryInfo sharedLibraryInfo; + + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(mach_task_self (), TASK_DYLD_INFO, (task_info_t)&task_dyld_info, + &count) != KERN_SUCCESS) { + return sharedLibraryInfo; + } + + struct dyld_all_image_infos* aii = (struct dyld_all_image_infos*)task_dyld_info.all_image_info_addr; + size_t infoCount = aii->infoArrayCount; + + // Iterate through all dyld images (loaded libraries) to get their names + // and offests. + for (size_t i = 0; i < infoCount; ++i) { + const dyld_image_info *info = &aii->infoArray[i]; + + // If the magic number doesn't match then go no further + // since we're not pointing to where we think we are. + if (info->imageLoadAddress->magic != MACHO_MAGIC_NUMBER) { + continue; + } + + const platform_mach_header* header = + reinterpret_cast(info->imageLoadAddress); + + // Add the entry for this image. + addSharedLibrary(header, (char*)info->imageFilePath, sharedLibraryInfo); + + } + return sharedLibraryInfo; +} + diff --git a/tools/profiler/sps/shared-libraries-win32.cc b/tools/profiler/sps/shared-libraries-win32.cc new file mode 100644 index 00000000000..396e679f8b5 --- /dev/null +++ b/tools/profiler/sps/shared-libraries-win32.cc @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jeff Muizelaar + * + * 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 +#include + +#include "shared-libraries.h" + +SharedLibraryInfo SharedLibraryInfo::GetInfoForSelf() +{ + SharedLibraryInfo sharedLibraryInfo; + + /* + HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, GetCurrentProcessId()); + + MODULEENTRY32 module; + Module32First(snap, &module); + do { + // process module + } while (Module32Next(snap, &module)); + */ + + return sharedLibraryInfo; +} + diff --git a/tools/profiler/sps/shared-libraries.h b/tools/profiler/sps/shared-libraries.h new file mode 100644 index 00000000000..6aa2acb79e8 --- /dev/null +++ b/tools/profiler/sps/shared-libraries.h @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 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): + * Jeff Muizelaar + * + * 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 +#include +#include +#include + +class SharedLibrary { +public: + SharedLibrary(unsigned long aStart, unsigned long aEnd, unsigned long aOffset, char *aName) + : mStart(aStart) + , mEnd(aEnd) + , mOffset(aOffset) + , mName(strdup(aName)) + {} + + SharedLibrary(const SharedLibrary& aEntry) + : mStart(aEntry.mStart) + , mEnd(aEntry.mEnd) + , mOffset(aEntry.mOffset) + , mName(strdup(aEntry.mName)) + {} + + SharedLibrary& operator=(const SharedLibrary& aEntry) + { + mStart = aEntry.mStart; + mEnd = aEntry.mEnd; + mOffset = aEntry.mOffset; + mName = strdup(aEntry.mName); + return *this; + } + + ~SharedLibrary() + { + free(mName); + } + + uintptr_t GetStart() { return mStart; } + uintptr_t GetEnd() { return mEnd; } + char* GetName() { return mName; } + +private: + explicit SharedLibrary() {} + + uintptr_t mStart; + uintptr_t mEnd; + uintptr_t mOffset; + char *mName; +}; + +class SharedLibraryInfo { +public: + static SharedLibraryInfo GetInfoForSelf(); + SharedLibraryInfo() {} + + void AddSharedLibrary(SharedLibrary entry) + { + mEntries.push_back(entry); + } + + SharedLibrary& GetEntry(size_t i) + { + return mEntries[i]; + } + + size_t GetSize() + { + return mEntries.size(); + } +private: + std::vector mEntries; +}; diff --git a/widget/src/android/AndroidJavaWrappers.h b/widget/src/android/AndroidJavaWrappers.h index f05cff9874c..377c721ac23 100644 --- a/widget/src/android/AndroidJavaWrappers.h +++ b/widget/src/android/AndroidJavaWrappers.h @@ -532,6 +532,7 @@ public: ACTIVITY_START = 17, BROADCAST = 19, VIEWPORT = 20, + EXPOSE = 21, dummy_java_enum_list_end }; diff --git a/widget/src/android/nsIAndroidBridge.idl b/widget/src/android/nsIAndroidBridge.idl index 7e2d837e52c..5e75d72ff89 100644 --- a/widget/src/android/nsIAndroidBridge.idl +++ b/widget/src/android/nsIAndroidBridge.idl @@ -1,8 +1,14 @@ #include "nsISupports.idl" -[scriptable, function, uuid(9feed1e5-bb90-4663-b70a-e03cb27a9e8b)] +[scriptable, uuid(c60bf6cf-5e31-4bf8-a1f0-a82c061d65a8)] interface nsIAndroidDrawMetadataProvider : nsISupports { AString getDrawMetadata(); + + /* + * Returns true if drawing should be allowed or false if it should be suppressed (during page + * transitions). + */ + boolean drawingAllowed(); }; [scriptable, uuid(7dd8441a-4f38-49b2-bd90-da69d02a96cf)] diff --git a/widget/src/android/nsWindow.cpp b/widget/src/android/nsWindow.cpp index 63461edb20b..1dc1baf463b 100644 --- a/widget/src/android/nsWindow.cpp +++ b/widget/src/android/nsWindow.cpp @@ -978,6 +978,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) break; case AndroidGeckoEvent::DRAW: + case AndroidGeckoEvent::EXPOSE: win->OnDraw(ae); break; @@ -1168,10 +1169,32 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) if (gAndroidBounds.width <= 0 || gAndroidBounds.height <= 0) return; + nsCOMPtr metadataProvider = + AndroidBridge::Bridge()->GetDrawMetadataProvider(); + + /* + * If this is a DRAW event (not an EXPOSE event), check to see whether browser.js wants us to + * draw. This will be false during page transitions, in which case we immediately bail out. + */ + + bool shouldDraw = true; + if (metadataProvider && ae->Type() == AndroidGeckoEvent::DRAW) { + metadataProvider->DrawingAllowed(&shouldDraw); + } + if (!shouldDraw) { + return; + } + AndroidGeckoSoftwareLayerClient &client = AndroidBridge::Bridge()->GetSoftwareLayerClient(); client.BeginDrawing(gAndroidBounds.width, gAndroidBounds.height); + // Redraw the entire tile on an EXPOSE event. Otherwise (on a DRAW event), redraw only the + // portion specified by the event. + nsIntRect rect(0, 0, gAndroidBounds.width, gAndroidBounds.height); + if (ae->Type() == AndroidGeckoEvent::DRAW) + rect = ae->Rect(); + nsAutoString metadata; unsigned char *bits = NULL; if (sHasDirectTexture) { @@ -1204,14 +1227,11 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) // XXX: lock only the dirty rect above and pass it in here DrawTo(targetSurface); } else { - DrawTo(targetSurface, ae->Rect()); + DrawTo(targetSurface, rect); } - { - nsCOMPtr metadataProvider = - AndroidBridge::Bridge()->GetDrawMetadataProvider(); - if (metadataProvider) - metadataProvider->GetDrawMetadata(metadata); + if (metadataProvider) { + metadataProvider->GetDrawMetadata(metadata); } } if (sHasDirectTexture) { @@ -1220,7 +1240,7 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae) client.UnlockBuffer(); } } - client.EndDrawing(ae->Rect(), metadata); + client.EndDrawing(rect, metadata); return; #endif diff --git a/widget/src/gonk/nsAppShell.cpp b/widget/src/gonk/nsAppShell.cpp index 0e57c7ebdb3..ce5229b5501 100644 --- a/widget/src/gonk/nsAppShell.cpp +++ b/widget/src/gonk/nsAppShell.cpp @@ -40,22 +40,25 @@ #define _GNU_SOURCE #include +#include #include #include #include #include #include -#include #include #include +#include #include +#include "nscore.h" +#include "mozilla/FileUtils.h" +#include "mozilla/Services.h" #include "nsAppShell.h" #include "nsGkAtoms.h" #include "nsGUIEvent.h" -#include "nsWindow.h" #include "nsIObserverService.h" -#include "mozilla/Services.h" +#include "nsWindow.h" #include "android/log.h" @@ -79,7 +82,15 @@ #define SYN_MT_REPORT 2 #endif -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#define LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#ifdef VERBOSE_LOG_ENABLED +# define VERBOSE_LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args) +#else +# define VERBOSE_LOG(args...) \ + (void)0 +#endif using namespace mozilla; @@ -119,14 +130,14 @@ PRUint64 timevalToMS(const struct timeval &time) } static void -sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y) +sendMouseEvent(PRUint32 msg, struct timeval& time, int x, int y) { nsMouseEvent event(true, msg, NULL, nsMouseEvent::eReal, nsMouseEvent::eNormal); event.refPoint.x = x; event.refPoint.y = y; - event.time = timevalToMS(*time); + event.time = timevalToMS(time); event.isShift = false; event.isControl = false; event.isMeta = false; @@ -136,7 +147,6 @@ sendMouseEvent(PRUint32 msg, struct timeval *time, int x, int y) event.clickCount = 1; nsWindow::DispatchInputEvent(event); - //LOG("Dispatched type %d at %dx%d", msg, x, y); } static nsEventStatus @@ -172,57 +182,175 @@ sendSpecialKeyEvent(nsIAtom *command, const timeval &time) nsWindow::DispatchInputEvent(event); } +static void +maybeSendKeyEvent(int keyCode, bool pressed, const timeval& time) +{ + switch (keyCode) { + case KEY_BACK: + sendKeyEvent(NS_VK_ESCAPE, pressed, time); + break; + case KEY_MENU: + if (!pressed) + sendSpecialKeyEvent(nsGkAtoms::Menu, time); + break; + case KEY_SEARCH: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::Search, time); + break; + case KEY_HOME: + sendKeyEvent(NS_VK_HOME, pressed, time); + break; + case KEY_POWER: + sendKeyEvent(NS_VK_SLEEP, pressed, time); + break; + case KEY_VOLUMEUP: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeUp, time); + break; + case KEY_VOLUMEDOWN: + if (pressed) + sendSpecialKeyEvent(nsGkAtoms::VolumeDown, time); + break; + default: + VERBOSE_LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", + keyCode, pressed); + } +} + static void maybeSendKeyEvent(const input_event& e) { if (e.type != EV_KEY) { - LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", + VERBOSE_LOG("Got unknown key event type. type 0x%04x code 0x%04x value %d", e.type, e.code, e.value); return; } if (e.value != 0 && e.value != 1) { - LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", + VERBOSE_LOG("Got unknown key event value. type 0x%04x code 0x%04x value %d", e.type, e.code, e.value); return; } bool pressed = e.value == 1; - switch (e.code) { - case KEY_BACK: - sendKeyEvent(NS_VK_ESCAPE, pressed, e.time); - break; - case KEY_MENU: - if (!pressed) - sendSpecialKeyEvent(nsGkAtoms::Menu, e.time); - break; - case KEY_SEARCH: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::Search, e.time); - break; - case KEY_HOME: - sendKeyEvent(NS_VK_HOME, pressed, e.time); - break; - case KEY_POWER: - sendKeyEvent(NS_VK_SLEEP, pressed, e.time); - break; - case KEY_VOLUMEUP: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeUp, e.time); - break; - case KEY_VOLUMEDOWN: - if (pressed) - sendSpecialKeyEvent(nsGkAtoms::VolumeDown, e.time); - break; - default: - LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d", - e.type, e.code, e.value); + maybeSendKeyEvent(e.code, pressed, e.time); +} + +static void +configureVButtons(FdHandler& data) +{ + char vbuttonsPath[PATH_MAX]; + snprintf(vbuttonsPath, sizeof(vbuttonsPath), + "/sys/board_properties/virtualkeys.%s", + data.name); + ScopedClose fd(open(vbuttonsPath, O_RDONLY)); + if (0 > fd.mFd) { + LOG("No vbuttons for mt device %s", data.name); + return; } + + // This device has vbuttons. Process the configuration. + char config[1024]; + ssize_t nread; + do { + nread = read(fd.mFd, config, sizeof(config)); + } while (-1 == nread && EINTR == errno); + + if (0 > nread) { + LOG("Error reading virtualkey configuration"); + return; + } + + config[nread] = '\0'; + + LOG("Device %s has vbutton config '%s'", data.name, config); + + char* startStr = config; + for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) { + FdHandler::VButton& vbutton = data.vbuttons[i]; + char* token; + char* state; + + // XXX not clear what "0x01" is ... maybe a version + // number? See InputManager.java. + if (!(token = strtok_r(startStr, ":", &state)) || + strcmp(token, "0x01")) { + LOG(" magic 0x01 tag missing"); + break; + } + startStr = NULL; + + if (!(token = strtok_r(NULL, ":", &state))) { + LOG(" failed to read keycode"); + break; + } + vbutton.keyCode = atoi(token); + + const char *centerX, *centerY, *width, *height; + if (!((centerX = strtok_r(NULL, ":", &state)) && + (centerY = strtok_r(NULL, ":", &state)) && + (width = strtok_r(NULL, ":", &state)) && + (height = strtok_r(NULL, ":", &state)))) { + LOG(" failed to read bound params"); + break; + } + + // NB: these coordinates are in *screen* space, not input + // space. That means the values in /sys/board_config make + // assumptions about how the raw input events are mapped + // ... le sigh. + nsIntRect rect; + rect.width = atoi(width); + rect.height = atoi(height); + rect.x = atoi(centerX) - rect.width / 2; + rect.y = atoi(centerY) - rect.height / 2; + vbutton.buttonRect = rect; + + LOG(" configured vbutton code=%d at ", + vbutton.keyCode, rect.x, rect.y, rect.width, rect.height); + } +} + +static bool +calibrateMultitouchDevice(FdHandler& data) +{ + if (data.calibrated) + return true; + if (gScreenBounds.IsEmpty()) { + // The framebuffer hasn't been initialized yet. We *could* + // force it to be initialized here, but that's another patch. + LOG("Deferring multitouch calibrate, fb not ready"); + return false; + } + + struct input_absinfo xInfo, yInfo; + if (0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_X), &xInfo) || + 0 > ioctl(data.fd, EVIOCGABS(ABS_MT_POSITION_Y), &yInfo)) { + LOG("Couldn't get absinfo for multitouch axes"); + return false; + } + LOG("Input coordinate bounds: xmin=%d, xmax=%d, ymin=%d, ymax=%d", + xInfo.minimum, xInfo.maximum, yInfo.minimum, yInfo.maximum); + + data.inputMinX = xInfo.minimum; + data.inputMinY = yInfo.minimum; + data.inputToScreenScaleX = + float(gScreenBounds.width) / float(xInfo.maximum - xInfo.minimum); + data.inputToScreenScaleY = + float(gScreenBounds.height) / float(yInfo.maximum - yInfo.minimum); + + configureVButtons(data); + + data.calibrated = true; + return true; } static void multitouchHandler(int fd, FdHandler *data) { + if (!calibrateMultitouchDevice(*data)) + return; + // The Linux's input documentation (Documentation/input/input.txt) // says that we'll always read a multiple of sizeof(input_event) bytes here. input_event events[16]; @@ -258,14 +386,15 @@ multitouchHandler(int fd, FdHandler *data) case ABS_MT_PRESSURE: break; case ABS_MT_POSITION_X: - data->mtX = event->value; + data->mtX = data->inputXToScreenX(event->value); break; case ABS_MT_POSITION_Y: - data->mtY = event->value; + data->mtY = data->inputYToScreenY(event->value); break; default: - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); + break; } } else if (event->type == EV_SYN) { switch (event->code) { @@ -274,30 +403,60 @@ multitouchHandler(int fd, FdHandler *data) data->mtState = FdHandler::MT_IGNORE; break; case SYN_REPORT: - if ((!data->mtMajor || data->mtState == FdHandler::MT_START)) { - sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time, - data->mtX, data->mtY); + if (!data->mtMajor || data->mtState == FdHandler::MT_START) { data->mtDown = false; - //LOG("Up mouse event"); + if (data->keyCode) { + maybeSendKeyEvent(data->keyCode, data->mtDown, + event->time); + data->keyCode = 0; + } else { + sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time, + data->mtX, data->mtY); + } } else if (!data->mtDown) { - sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time, - data->mtX, data->mtY); + int x = data->mtX, y = data->mtY; + + bool isKeyEvent = false; + if (!gScreenBounds.Contains(x, y)) { + // Off-screen mt down. Should be a vbutton. + for (size_t i = 0; i < FdHandler::kMaxVButtons; ++i) { + const FdHandler::VButton& vbutton = data->vbuttons[i]; + if (vbutton.buttonRect.IsEmpty()) + break; + + if (vbutton.buttonRect.Contains(x, y)) { + isKeyEvent = true; + data->keyCode = vbutton.keyCode; + break; + } + } + } data->mtDown = true; - //LOG("Down mouse event"); - } else { - sendMouseEvent(NS_MOUSE_MOVE, &event->time, + + if (isKeyEvent) { + maybeSendKeyEvent(data->keyCode, data->mtDown, + event->time); + } else { + sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time, + data->mtX, data->mtY); + } + } else if (!data->keyCode) { + sendMouseEvent(NS_MOUSE_MOVE, event->time, data->mtX, data->mtY); data->mtDown = true; } + data->mtState = FdHandler::MT_START; + break; default: - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); + } } else - LOG("Got unknown event type 0x%04x with code 0x%04x and value %d", - event->type, event->code, event->value); + VERBOSE_LOG("Got unknown mt event type 0x%04x with code 0x%04x and value %d", + event->type, event->code, event->value); } } @@ -327,8 +486,7 @@ singleTouchHandler(int fd, FdHandler *data) default: maybeSendKeyEvent(*event); } - } - else if (event->type == EV_ABS) { + } else if (event->type == EV_ABS) { switch (event->code) { case ABS_X: data->mtX = event->value; @@ -343,16 +501,16 @@ singleTouchHandler(int fd, FdHandler *data) } else if (event->type == EV_SYN) { if (data->mtState == FdHandler::MT_START) { MOZ_ASSERT(data->mtDown); - sendMouseEvent(NS_MOUSE_BUTTON_DOWN, &event->time, + sendMouseEvent(NS_MOUSE_BUTTON_DOWN, event->time, data->mtX, data->mtY); data->mtState = FdHandler::MT_COLLECT; } else if (data->mtDown) { MOZ_ASSERT(data->mtDown); - sendMouseEvent(NS_MOUSE_MOVE, &event->time, + sendMouseEvent(NS_MOUSE_MOVE, event->time, data->mtX, data->mtY); } else { MOZ_ASSERT(!data->mtDown); - sendMouseEvent(NS_MOUSE_BUTTON_UP, &event->time, + sendMouseEvent(NS_MOUSE_BUTTON_UP, event->time, data->mtX, data->mtY); data->mtDown = false; data->mtState = FdHandler::MT_START; @@ -399,11 +557,6 @@ nsAppShell::~nsAppShell() nsresult nsAppShell::Init() { - epoll_event event = { - EPOLLIN, - { 0 } - }; - nsresult rv = nsBaseAppShell::Init(); NS_ENSURE_SUCCESS(rv, rv); @@ -413,17 +566,13 @@ nsAppShell::Init() int ret = pipe2(signalfds, O_NONBLOCK); NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); - FdHandler *handler = mHandlers.AppendElement(); - handler->fd = signalfds[0]; - handler->func = pipeHandler; - event.data.u32 = mHandlers.Length() - 1; - ret = epoll_ctl(epollfd, EPOLL_CTL_ADD, signalfds[0], &event); - NS_ENSURE_FALSE(ret, NS_ERROR_UNEXPECTED); + rv = AddFdHandler(signalfds[0], pipeHandler, ""); + NS_ENSURE_SUCCESS(rv, rv); DIR *dir = opendir("/dev/input"); NS_ENSURE_TRUE(dir, NS_ERROR_UNEXPECTED); -#define BITSET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7))) +#define IS_BIT_SET(bit, flags) (flags[bit >> 3] & (1 << (bit & 0x7))) struct dirent *entry; while ((entry = readdir(dir))) { @@ -444,12 +593,12 @@ nsAppShell::Init() char flags[(NS_MAX(ABS_MAX, KEY_MAX) + 1) / 8]; if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && - BITSET(ABS_MT_POSITION_X, flags)) { + IS_BIT_SET(ABS_MT_POSITION_X, flags)) { LOG("Found multitouch input device"); handlerFunc = multitouchHandler; } else if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(flags)), flags) >= 0 && - BITSET(ABS_X, flags)) { + IS_BIT_SET(ABS_X, flags)) { LOG("Found single touch input device"); handlerFunc = singleTouchHandler; } else if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(flags)), flags) >= 0) { @@ -461,17 +610,32 @@ nsAppShell::Init() if (!handlerFunc) continue; - handler = mHandlers.AppendElement(); - handler->fd = fd; - handler->func = handlerFunc; - event.data.u32 = mHandlers.Length() - 1; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event)) + rv = AddFdHandler(fd, handlerFunc, entryName); + if (NS_FAILED(rv)) LOG("Failed to add fd to epoll fd"); } return rv; } +nsresult +nsAppShell::AddFdHandler(int fd, FdHandlerCallback handlerFunc, + const char* deviceName) +{ + epoll_event event = { + EPOLLIN, + { 0 } + }; + + FdHandler *handler = mHandlers.AppendElement(); + handler->fd = fd; + strncpy(handler->name, deviceName, sizeof(handler->name) - 1); + handler->func = handlerFunc; + event.data.u32 = mHandlers.Length() - 1; + return epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event) ? + NS_ERROR_UNEXPECTED : NS_OK; +} + void nsAppShell::ScheduleNativeEventCallback() { diff --git a/widget/src/gonk/nsAppShell.h b/widget/src/gonk/nsAppShell.h index bb82c279da7..c28c17c5e29 100644 --- a/widget/src/gonk/nsAppShell.h +++ b/widget/src/gonk/nsAppShell.h @@ -39,6 +39,7 @@ #define nsAppShell_h #include "nsBaseAppShell.h" +#include "nsRect.h" #include "nsTArray.h" namespace mozilla { @@ -53,9 +54,17 @@ typedef void(*FdHandlerCallback)(int, FdHandler *); class FdHandler { public: - FdHandler() : mtState(MT_START), mtDown(false) { } + FdHandler() + : mtState(MT_START) + , keyCode(0) + , mtDown(false) + , calibrated(false) + { + memset(name, 0, sizeof(name)); + } int fd; + char name[64]; FdHandlerCallback func; enum mtStates { MT_START, @@ -64,12 +73,48 @@ public: } mtState; int mtX, mtY; int mtMajor; + int keyCode; bool mtDown; + // FIXME/bug 712973: we should be using libui here instead of + // recreating all that logic ourselves. Please don't extend the + // hacks here further than what's below. + bool calibrated; + // Multitouch events are delivered to us in "input space", which + // is a coordinate space defined by the multitouch device driver. + // The coordinate space has top-left at P_min = when in normal-portrait orientation. The input + // device and the screen might have different resolutions. The + // resolution difference is Scale = . So going from input to screen space + // (when in normal portrait orientation) is an affine transform + // defined by + // + // P_screen = Scale * (P_input - P_min) + // + int inputMinX, inputMinY; + float inputToScreenScaleX, inputToScreenScaleY; + // Some touch devices use virtual buttons instead of hardware + // buttons. When the device uses vbuttons, we convert touch + // events into key events of type |keyCode| when the start of the + // touch is within |buttonRect|. |buttonRect| must be disjoint + // from the screen rect. + static const size_t kMaxVButtons = 4; + struct VButton { + nsIntRect buttonRect; // in screen space + int keyCode; + } vbuttons[kMaxVButtons]; void run() { func(fd, this); } + + int inputXToScreenX(int inputX) { + return inputToScreenScaleX * (inputX - inputMinX); + } + int inputYToScreenY(int inputY) { + return inputToScreenScaleY * (inputY - inputMinY); + } }; class nsAppShell : public nsBaseAppShell { @@ -86,6 +131,10 @@ protected: virtual void ScheduleNativeEventCallback(); +private: + nsresult AddFdHandler(int fd, FdHandlerCallback handlerFunc, + const char* deviceName); + // This is somewhat racy but is perfectly safe given how the callback works bool mNativeCallbackRequest; nsTArray mHandlers; diff --git a/widget/src/gonk/nsWindow.cpp b/widget/src/gonk/nsWindow.cpp index 48d149d4567..e4fa9d70afd 100644 --- a/widget/src/gonk/nsWindow.cpp +++ b/widget/src/gonk/nsWindow.cpp @@ -78,7 +78,7 @@ nsWindow::nsWindow() sGLContext = GLContextProvider::CreateForWindow(this); // CreateForWindow sets up gScreenBounds if (!sGLContext) { - LOG("Failed to create GL context for fb, trying /dev/fb0"); + LOG("Failed to create GL context for fb, trying /dev/graphics/fb0"); // We can't delete gNativeWindow. @@ -87,7 +87,7 @@ nsWindow::nsWindow() gScreenBounds = nsIntRect(nsIntPoint(0, 0), screenSize); if (!sFramebufferOpen) { LOG("Failed to mmap fb(?!?), aborting ..."); - NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/fb0 ..."); + NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/graphics/fb0 ..."); } } } @@ -106,7 +106,8 @@ nsWindow::DoDraw(void) } nsPaintEvent event(true, NS_PAINT, gWindowToRedraw); - event.region = gScreenBounds; + event.region = gWindowToRedraw->mDirtyRegion; + gWindowToRedraw->mDirtyRegion.SetEmpty(); LayerManager* lm = gWindowToRedraw->GetLayerManager(); if (LayerManager::LAYERS_OPENGL == lm->GetBackendType()) { @@ -293,9 +294,15 @@ nsWindow::Invalidate(const nsIntRect &aRect, return NS_OK; } + mDirtyRegion.Or(mDirtyRegion, aRect); gWindowToRedraw = this; - gDrawRequest = true; - mozilla::NotifyEvent(); + if (aIsSynchronous) { + gDrawRequest = false; + DoDraw(); + } else { + gDrawRequest = true; + mozilla::NotifyEvent(); + } return NS_OK; } diff --git a/widget/src/gonk/nsWindow.h b/widget/src/gonk/nsWindow.h index 03647637912..743067c5c39 100644 --- a/widget/src/gonk/nsWindow.h +++ b/widget/src/gonk/nsWindow.h @@ -129,10 +129,10 @@ public: protected: nsWindow* mParent; bool mVisible; + nsIntRegion mDirtyRegion; + InputContext mInputContext; void BringToTop(); - - InputContext mInputContext; }; #endif /* nsWindow_h */ diff --git a/widget/src/windows/AudioSession.cpp b/widget/src/windows/AudioSession.cpp index 83116aad05b..a782fe3c9dc 100644 --- a/widget/src/windows/AudioSession.cpp +++ b/widget/src/windows/AudioSession.cpp @@ -51,6 +51,7 @@ #include "nsAutoPtr.h" #include "nsServiceManagerUtils.h" #include "nsString.h" +#include "nsThreadUtils.h" #include "nsXULAppAPI.h" #include @@ -84,6 +85,9 @@ public: STDMETHODIMP OnGroupingParamChanged(LPCGUID aGroupingParam, LPCGUID aContext); STDMETHODIMP OnIconPathChanged(LPCWSTR aIconPath, LPCGUID aContext); STDMETHODIMP OnSessionDisconnected(AudioSessionDisconnectReason aReason); +private: + nsresult OnSessionDisconnectedInternal(); +public: STDMETHODIMP OnSimpleVolumeChanged(float aVolume, BOOL aMute, LPCGUID aContext); @@ -431,14 +435,26 @@ AudioSession::OnIconPathChanged(LPCWSTR aIconPath, STDMETHODIMP AudioSession::OnSessionDisconnected(AudioSessionDisconnectReason aReason) +{ + // Run our code asynchronously. Per MSDN we can't do anything interesting + // in this callback. + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &AudioSession::OnSessionDisconnectedInternal); + NS_DispatchToMainThread(runnable); + return S_OK; +} + +nsresult +AudioSession::OnSessionDisconnectedInternal() { if (!mAudioSessionControl) - return S_OK; + return NS_OK; mAudioSessionControl->UnregisterAudioSessionNotification(this); mAudioSessionControl = nsnull; - Start(); // If it fails there's not much we can do - return S_OK; + + Start(); // If it fails there's not much we can do. + return NS_OK; } STDMETHODIMP diff --git a/xpcom/base/MapsMemoryReporter.cpp b/xpcom/base/MapsMemoryReporter.cpp index 39b282474b9..1851fad440f 100644 --- a/xpcom/base/MapsMemoryReporter.cpp +++ b/xpcom/base/MapsMemoryReporter.cpp @@ -152,6 +152,13 @@ public: CollectReports(nsIMemoryMultiReporterCallback *aCallback, nsISupports *aClosure); + NS_IMETHOD + GetExplicitNonHeap(PRInt64 *aAmount) { + // This reporter doesn't do any "explicit" measurements. + *aAmount = 0; + return NS_OK; + } + private: // Search through /proc/self/maps for libxul.so, and set mLibxulDir to the // the directory containing libxul. @@ -223,7 +230,7 @@ MapsReporter::CollectReports(nsIMemoryMultiReporterCallback *aCallback, NS_ASSERTION(categoriesSeen.mSeenVsize, "Didn't create a resident node?"); if (!categoriesSeen.mSeenSwap) { aCallback->Callback(NS_LITERAL_CSTRING(""), - NS_LITERAL_CSTRING("map/swap"), + NS_LITERAL_CSTRING("map/swap/total"), nsIMemoryReporter::KIND_NONHEAP, nsIMemoryReporter::UNITS_BYTES, 0, diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index 50513ef737a..1ef11168712 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -213,11 +213,23 @@ interface nsIMemoryMultiReporterCallback : nsISupports * (Compare and contrast this with nsIMemoryReporter, which allows all * fields except |amount| to be accessed without triggering computation.) */ -[scriptable, uuid(eae277ad-b67d-4389-95f4-03fa11c09d06)] +[scriptable, uuid(61d498d5-b460-4398-a8ea-7f75208534b4)] interface nsIMemoryMultiReporter : nsISupports { void collectReports(in nsIMemoryMultiReporterCallback callback, in nsISupports closure); + + /* + * Return the sum of all this multi-reporter's measurements that have a + * path that starts with "explicit" and are KIND_NONHEAP. + * + * This is a hack that's required to implement + * nsIMemoryReporterManager::explicit efficiently, which is important -- + * multi-reporters can special-case this operation so it's much faster + * than getting all the reports, filtering out the unneeded ones, and + * summing the remainder. + */ + readonly attribute PRInt64 explicitNonHeap; }; [scriptable, uuid(84ba9c85-3372-4423-b7ab-74708b9269a6)] diff --git a/xpcom/base/nsMemoryReporterManager.cpp b/xpcom/base/nsMemoryReporterManager.cpp index 459260c3c82..1add056f619 100644 --- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -48,6 +48,19 @@ using namespace mozilla; +static PRInt64 GetExplicit() +{ + nsCOMPtr mgr = do_GetService("@mozilla.org/memory-reporter-manager;1"); + if (mgr == nsnull) + return (PRInt64)-1; + + PRInt64 n; + nsresult rv = mgr->GetExplicit(&n); + NS_ENSURE_SUCCESS(rv, rv); + + return n; +} + #if defined(MOZ_MEMORY) # if defined(XP_WIN) || defined(SOLARIS) || defined(ANDROID) || defined(XP_MACOSX) # define HAVE_JEMALLOC_STATS 1 @@ -320,6 +333,15 @@ NS_MEMORY_REPORTER_IMPLEMENT(PageFaultsHard, "so hard page faults a second.") #endif +NS_MEMORY_REPORTER_IMPLEMENT(Explicit, + "explicit", + KIND_OTHER, + UNITS_BYTES, + GetExplicit, + "This is the same measurement as the root of the 'explicit' tree. " + "However, it is measured at a different time and so gives slightly " + "different results.") + NS_MEMORY_REPORTER_IMPLEMENT(Resident, "resident", KIND_OTHER, @@ -533,6 +555,7 @@ nsMemoryReporterManager::Init() REGISTER(HeapAllocated); REGISTER(HeapUnallocated); + REGISTER(Explicit); REGISTER(Resident); #if defined(XP_LINUX) || defined(XP_MACOSX) || defined(XP_WIN) || defined(SOLARIS) @@ -657,6 +680,7 @@ struct MemoryReport { PRInt64 amount; }; +#ifdef DEBUG // This is just a wrapper for InfallibleTArray that implements // nsISupports, so it can be passed to nsIMemoryMultiReporter::CollectReports. class MemoryReportsWrapper : public nsISupports { @@ -679,8 +703,8 @@ public: { if (aKind == nsIMemoryReporter::KIND_NONHEAP && PromiseFlatCString(aPath).Find("explicit") == 0 && - aAmount != PRInt64(-1)) { - + aAmount != PRInt64(-1)) + { MemoryReportsWrapper *wrappedMRs = static_cast(aWrappedMRs); MemoryReport mr(aPath, aAmount); @@ -693,6 +717,7 @@ NS_IMPL_ISUPPORTS1( MemoryReportCallback , nsIMemoryMultiReporterCallback ) +#endif // Is path1 a prefix, and thus a parent, of path2? Eg. "a/b" is a parent of // "a/b/c", but "a/bb" is not. @@ -709,21 +734,21 @@ isParent(const nsACString &path1, const nsACString &path2) NS_IMETHODIMP nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) { - InfallibleTArray nonheap; - PRInt64 heapUsed = PRInt64(-1); + nsresult rv; - // Get "heap-allocated" and all the KIND_NONHEAP measurements from vanilla - // "explicit" reporters. + // Get "heap-allocated" and all the KIND_NONHEAP measurements from normal + // (i.e. non-multi) "explicit" reporters. + PRInt64 heapAllocated = PRInt64(-1); + InfallibleTArray explicitNonHeapNormalReports; nsCOMPtr e; EnumerateReporters(getter_AddRefs(e)); - bool more; while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr r; e->GetNext(getter_AddRefs(r)); PRInt32 kind; - nsresult rv = r->GetKind(&kind); + rv = r->GetKind(&kind); NS_ENSURE_SUCCESS(rv, rv); nsCString path; @@ -743,57 +768,106 @@ nsMemoryReporterManager::GetExplicit(PRInt64 *aExplicit) // "heap-allocated" is the most important one. if (amount != PRInt64(-1)) { MemoryReport mr(path, amount); - nonheap.AppendElement(mr); + explicitNonHeapNormalReports.AppendElement(mr); } } else if (path.Equals("heap-allocated")) { - rv = r->GetAmount(&heapUsed); + rv = r->GetAmount(&heapAllocated); NS_ENSURE_SUCCESS(rv, rv); - // If "heap-allocated" fails, we give up, because the result - // would be horribly inaccurate. - if (heapUsed == PRInt64(-1)) { - *aExplicit = PRInt64(-1); - return NS_OK; - } } } - // Get KIND_NONHEAP measurements from multi-reporters, too. + // If we don't have "heap-allocated", give up, because the result would be + // horribly inaccurate. + if (heapAllocated == PRInt64(-1)) { + *aExplicit = PRInt64(-1); + return NS_OK; + } + + // Sum all the explicit, NONHEAP reports from normal reporters. + // Ignore (by zeroing its amount) any normal reporter that is a child of + // another normal reporter. Eg. if we have "explicit/a" and + // "explicit/a/b", zero the latter. This is quadratic in the number of + // explicit NONHEAP reporters, but there shouldn't be many. + // + // XXX: bug 700508 will remove the need for this + // + for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { + const nsCString &iPath = explicitNonHeapNormalReports[i].path; + for (PRUint32 j = i + 1; j < explicitNonHeapNormalReports.Length(); j++) { + const nsCString &jPath = explicitNonHeapNormalReports[j].path; + if (isParent(iPath, jPath)) { + explicitNonHeapNormalReports[j].amount = 0; + } else if (isParent(jPath, iPath)) { + explicitNonHeapNormalReports[i].amount = 0; + } + } + } + PRInt64 explicitNonHeapNormalSize = 0; + for (PRUint32 i = 0; i < explicitNonHeapNormalReports.Length(); i++) { + explicitNonHeapNormalSize += explicitNonHeapNormalReports[i].amount; + } + + // For each multi-reporter we could call CollectReports and filter out the + // non-explicit, non-NONHEAP measurements. But that's lots of wasted work, + // so we instead use GetExplicitNonHeap() which exists purely for this + // purpose. + // + // (Actually, in debug builds we also do it the slow way and compare the + // result to the result obtained from GetExplicitNonHeap(). This + // guarantees the two measurement paths are equivalent. This is wise + // because it's easy for memory reporters to have bugs.) + nsCOMPtr e2; EnumerateMultiReporters(getter_AddRefs(e2)); - nsRefPtr wrappedMRs = - new MemoryReportsWrapper(&nonheap); - - // This callback adds only NONHEAP explicit reporters. - nsRefPtr cb = new MemoryReportCallback(); - + PRInt64 explicitNonHeapMultiSize = 0; while (NS_SUCCEEDED(e2->HasMoreElements(&more)) && more) { nsCOMPtr r; e2->GetNext(getter_AddRefs(r)); + PRInt64 n; + rv = r->GetExplicitNonHeap(&n); + NS_ENSURE_SUCCESS(rv, rv); + explicitNonHeapMultiSize += n; + } + +#ifdef DEBUG + InfallibleTArray explicitNonHeapMultiReports; + nsRefPtr cb = new MemoryReportCallback(); + nsRefPtr wrappedMRs = + new MemoryReportsWrapper(&explicitNonHeapMultiReports); + nsCOMPtr e3; + EnumerateMultiReporters(getter_AddRefs(e3)); + while (NS_SUCCEEDED(e3->HasMoreElements(&more)) && more) { + nsCOMPtr r; + e3->GetNext(getter_AddRefs(r)); r->CollectReports(cb, wrappedMRs); } - // Ignore (by zeroing its amount) any reporter that is a child of another - // reporter. Eg. if we have "explicit/a" and "explicit/a/b", zero the - // latter. This is quadratic in the number of explicit NONHEAP reporters, - // but there shouldn't be many. - for (PRUint32 i = 0; i < nonheap.Length(); i++) { - const nsCString &iPath = nonheap[i].path; - for (PRUint32 j = i + 1; j < nonheap.Length(); j++) { - const nsCString &jPath = nonheap[j].path; + // Sum all the explicit, NONHEAP reports from multi-reporters. + // XXX: identical to the explicitNonHeapNormalReports case above; bug + // 700508 will remove the need for this + for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { + const nsCString &iPath = explicitNonHeapMultiReports[i].path; + for (PRUint32 j = i + 1; j < explicitNonHeapMultiReports.Length(); j++) { + const nsCString &jPath = explicitNonHeapMultiReports[j].path; if (isParent(iPath, jPath)) { - nonheap[j].amount = 0; + explicitNonHeapMultiReports[j].amount = 0; } else if (isParent(jPath, iPath)) { - nonheap[i].amount = 0; + explicitNonHeapMultiReports[i].amount = 0; } } } - - // Sum all the nonheap reporters and heapUsed. - *aExplicit = heapUsed; - for (PRUint32 i = 0; i < nonheap.Length(); i++) { - *aExplicit += nonheap[i].amount; + PRInt64 explicitNonHeapMultiSize2 = 0; + for (PRUint32 i = 0; i < explicitNonHeapMultiReports.Length(); i++) { + explicitNonHeapMultiSize2 += explicitNonHeapMultiReports[i].amount; } + // Check the two measurements give the same result. + NS_ASSERTION(explicitNonHeapMultiSize == explicitNonHeapMultiSize2, + "The two measurements of 'explicit' memory usage don't match"); +#endif + + *aExplicit = heapAllocated + explicitNonHeapNormalSize + explicitNonHeapMultiSize; + return NS_OK; } diff --git a/xpcom/ds/Makefile.in b/xpcom/ds/Makefile.in index 584b3b7b2fb..09d6b5bdc8f 100644 --- a/xpcom/ds/Makefile.in +++ b/xpcom/ds/Makefile.in @@ -90,6 +90,7 @@ EXPORTS_NAMESPACES = mozilla EXPORTS_mozilla = \ CharTokenizer.h \ TimeStamp.h \ + StringBuilder.h \ $(NULL) EXPORTS = \ diff --git a/xpcom/ds/StringBuilder.h b/xpcom/ds/StringBuilder.h new file mode 100644 index 00000000000..5fcd478dcf4 --- /dev/null +++ b/xpcom/ds/StringBuilder.h @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:set ts=4 sw=4 sts=4 et cindent: */ +/* ***** 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 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): + * Jeff Muizelaar + * + * 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 ***** */ + +/* We would use std::max but MS makes it painful +// windef.h defines min and max macros that we don't want +// http://support.microsoft.com/kb/143208 +#ifdef _WIN32 +#define NOMINMAX +#endif +*/ + +#include +#include +#include "nsAlgorithm.h" + +/* This is a standard string builder like ones in Java + or C#. It uses a doubling allocation strategy + to grow when out of capacity. + + This does not use nsTArray because nsTArray starts + growing by multiples of page size after it is the + size of one page. We want to keep doubling in size + so that we can continue to append at high speed even + for large strings. + + Eventually, this should be templated for wide characters. + + */ + +namespace mozilla { + +class StringBuilder +{ +public: + StringBuilder() { + mCapacity = 16; + mLength = 0; + mBuffer = static_cast(malloc(sizeof(char)*mCapacity)); + mBuffer[0] = '\0'; + } + + void Append(const char *s) { + size_t newLength = strlen(s); + + EnsureCapacity(mLength + newLength+1); + + // copy the entire string including the null terminator + memcpy(&mBuffer[mLength], s, newLength+1); + mLength += newLength; + } + + char *Buffer() { + return mBuffer; + } + + size_t Length() { + return mLength; + } + + size_t EnsureCapacity(size_t capacity) { + if (capacity > mCapacity) { + // make sure we at least double in size + mCapacity = NS_MAX(capacity, mCapacity*2); + mBuffer = static_cast(realloc(mBuffer, mCapacity)); + mCapacity = moz_malloc_usable_size(mBuffer); + } + return mCapacity; + } + + ~StringBuilder() + { + free(mBuffer); + } + +private: + char *mBuffer; + size_t mLength; // the length of the contained string not including the null terminator + size_t mCapacity; // the total size of mBuffer +}; + +}