From 017d8a0b01a64229e9428ea991c09ff4be499f33 Mon Sep 17 00:00:00 2001 From: Adam Dane Date: Mon, 5 Mar 2012 14:40:56 -0600 Subject: [PATCH 01/88] Bug 612246 - Store the media cache in the profile directory for single process Gecko and the system temp directory for multi-process. r=cpearce --- content/media/nsMediaCache.cpp | 10 +++++++++- xpcom/io/nsMediaCacheRemover.cpp | 7 ++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/content/media/nsMediaCache.cpp b/content/media/nsMediaCache.cpp index 575fe21b5c31..1f283a515b59 100644 --- a/content/media/nsMediaCache.cpp +++ b/content/media/nsMediaCache.cpp @@ -42,6 +42,7 @@ #include "nsMediaCache.h" #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" +#include "nsXULAppAPI.h" #include "nsNetUtil.h" #include "prio.h" #include "nsThreadUtils.h" @@ -545,8 +546,15 @@ nsMediaCache::Init() NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); NS_ASSERTION(!mFD, "Cache file already open?"); + // In single process Gecko, store the media cache in the profile directory + // so that multiple users can use separate media caches concurrently. + // In multi-process Gecko, there is no profile dir, so just store it in the + // system temp directory instead. + nsresult rv; nsCOMPtr tmp; - nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp)); + const char* dir = (XRE_GetProcessType() == GeckoProcessType_Content) ? + NS_OS_TEMP_DIR : NS_APP_USER_PROFILE_LOCAL_50_DIR; + rv = NS_GetSpecialDirectory(dir, getter_AddRefs(tmp)); NS_ENSURE_SUCCESS(rv,rv); nsCOMPtr tmpFile = do_QueryInterface(tmp); diff --git a/xpcom/io/nsMediaCacheRemover.cpp b/xpcom/io/nsMediaCacheRemover.cpp index 69ee923c7e27..f754848e9a8e 100644 --- a/xpcom/io/nsMediaCacheRemover.cpp +++ b/xpcom/io/nsMediaCacheRemover.cpp @@ -45,6 +45,7 @@ #include "nsILocalFile.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDirectoryServiceDefs.h" +#include "nsXULAppAPI.h" #include "nsString.h" #include "nsAutoPtr.h" #include "nsITimer.h" @@ -107,10 +108,14 @@ public: idleSvc->RemoveIdleObserver(this, TEMP_FILE_IDLE_TIME); nsCOMPtr tmpDir; - nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, getter_AddRefs(tmpDir)); if (NS_FAILED(rv)) return; + + NS_ABORT_IF_FALSE(XRE_GetProcessType() == GeckoProcessType_Default, + "Need to update media cache file location"); + rv = tmpDir->AppendNative(nsDependentCString("mozilla-media-cache")); if (NS_FAILED(rv)) return; From c9749670c76ed0ac451e875f220fe6eac3a3a4a1 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Fri, 2 Mar 2012 17:00:22 -0800 Subject: [PATCH 02/88] Bug 732634 - Preserve IME option bits when toggling IME action bits on AwesomeBar Go/Search mode change. r=dougt --- mobile/android/base/AwesomeBar.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index e43d7e9497e8..a364548b37d7 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -309,9 +309,11 @@ public class AwesomeBar extends Activity implements GeckoEventListener { } mGoButton.setImageResource(imageResource); - if ((mText.getImeOptions() & EditorInfo.IME_MASK_ACTION) != imeAction) { + int actionBits = mText.getImeOptions() & EditorInfo.IME_MASK_ACTION; + if (actionBits != imeAction) { InputMethodManager imm = (InputMethodManager) mText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - mText.setImeOptions(imeAction); + int optionBits = mText.getImeOptions() & ~EditorInfo.IME_MASK_ACTION; + mText.setImeOptions(optionBits | imeAction); imm.restartInput(mText); } } From c56541c4859d40b1920c71c3d7b6ac85b3c15693 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Mon, 5 Mar 2012 11:59:57 -0500 Subject: [PATCH 03/88] Bug 732970 - Fix thinko in persistent telemetry merge. r=taras --- toolkit/components/telemetry/TelemetryPing.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index a51df5a7c316..7831c21ddcb0 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -239,8 +239,7 @@ TelemetryPing.prototype = { return retgram; }, - getHistograms: function getHistograms() { - let hls = Telemetry.histogramSnapshots; + getHistograms: function getHistograms(hls) { let info = Telemetry.registeredHistograms; let ret = {}; From 23e960e8cfdd21af7c55ee39a9e22f101c693592 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 5 Mar 2012 16:58:59 -0800 Subject: [PATCH 04/88] Bug 731845 - XPCWrappedNative::ReparentWrapperIfFound should handle preserved wrappers. r=mrbkap --- content/base/src/nsNodeUtils.cpp | 19 ----------- js/xpconnect/src/XPCWrappedNative.cpp | 45 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index f4a75a6c81a7..14e7f429d381 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -597,29 +597,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, if (aCx && wrapper) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { - JSObject *preservedWrapper = nsnull; - - // If reparenting moves us to a new compartment, preserving causes - // problems. In that case, we release ourselves and re-preserve after - // reparenting so we're sure to have the right JS object preserved. - // We use a JSObject stack copy of the wrapper to protect it from GC - // under ReparentWrappedNativeIfFound. - if (aNode->PreservingWrapper()) { - preservedWrapper = wrapper; - nsContentUtils::ReleaseWrapper(aNode, aNode); - NS_ASSERTION(aNode->GetWrapper(), - "ReleaseWrapper cleared our wrapper, this code needs to " - "be changed to deal with that!"); - } - nsCOMPtr oldWrapper; rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode, getter_AddRefs(oldWrapper)); - if (preservedWrapper) { - nsContentUtils::PreserveWrapper(aNode, aNode); - } - if (NS_FAILED(rv)) { aNode->mNodeInfo.swap(nodeInfo); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 26cf95789f41..4372f9d2a4c9 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -53,6 +53,8 @@ #include "WrapperFactory.h" #include "dombindings.h" +#include "nsContentUtils.h" + #include "mozilla/Util.h" bool @@ -1479,6 +1481,43 @@ XPCWrappedNative::SystemIsBeingShutDown() /***************************************************************************/ +// If we have to transplant an object across compartments, we need to be +// careful if the underlying object implements nsWrapperCache and is preserving +// the wrapper. +// +// The class brackets a pair of Unpreserve/Preserve calls in the given scope. +// +// This class _must_ live on the stack, in part so that mPreservedWrapper is +// visible to the stack scanner. The caller wants the wrapper to be preserved, +// so we don't want it to get accidentally GCed. +class AutoWrapperChanger NS_STACK_CLASS { +public: + AutoWrapperChanger() : mCache(nsnull) + , mCOMObj(nsnull) + , mPreservedWrapper(nsnull) + {} + + void init(nsISupports* aCOMObj, nsWrapperCache* aWrapperCache) { + mCOMObj = aCOMObj; + mCache = aWrapperCache; + if (mCache->PreservingWrapper()) { + mPreservedWrapper = mCache->GetWrapper(); + MOZ_ASSERT(mPreservedWrapper); + nsContentUtils::ReleaseWrapper(mCOMObj, mCache); + } + } + + ~AutoWrapperChanger() { + if (mPreservedWrapper) + nsContentUtils::PreserveWrapper(mCOMObj, mCache); + } + +private: + nsWrapperCache* mCache; + nsISupports* mCOMObj; + JSObject* mPreservedWrapper; +}; + // static nsresult XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, @@ -1497,10 +1536,16 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, nsresult rv; nsRefPtr wrapper; + AutoWrapperChanger wrapperChanger; JSObject *flat; nsWrapperCache* cache = nsnull; CallQueryInterface(aCOMObj, &cache); if (cache) { + + // There's a wrapper cache. Make sure we keep it sane no matter what + // happens. + wrapperChanger.init(aCOMObj, cache); + flat = cache->GetWrapper(); if (flat && !IS_SLIM_WRAPPER_OBJECT(flat)) { wrapper = static_cast(xpc_GetJSPrivate(flat)); From 73839b9bd91ffb9f67686ae74e2d84406d4aa4e0 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Mon, 5 Mar 2012 18:11:29 -0800 Subject: [PATCH 05/88] Bug 723971 - unmark gray for shapes. r=billm --- js/xpconnect/src/nsXPConnect.cpp | 47 ++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index 4837c8a65ae7..138f04672833 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -699,6 +699,18 @@ xpc_GCThingIsGrayCCThing(void *thing) xpc_IsGrayGCThing(thing); } +struct UnmarkGrayTracer : public JSTracer +{ + UnmarkGrayTracer() : mTracingShape(false), mPreviousShape(nsnull) {} + UnmarkGrayTracer(JSTracer *trc, bool aTracingShape) + : mTracingShape(aTracingShape), mPreviousShape(nsnull) + { + JS_TracerInit(this, trc->runtime, trc->callback); + } + bool mTracingShape; // true iff we are tracing the immediate children of a shape + void *mPreviousShape; // If mTracingShape, shape child or NULL. Otherwise, NULL. +}; + /* * The GC and CC are run independently. Consequently, the following sequence of * events can occur: @@ -729,15 +741,38 @@ UnmarkGrayChildren(JSTracer *trc, void **thingp, JSGCTraceKind kind) return; } - // If this thing is not a CC-kind or already non-gray then we're done. - if (!AddToCCKind(kind) || !xpc_IsGrayGCThing(thing)) + if (!xpc_IsGrayGCThing(thing)) return; - // Unmark. static_cast(thing)->unmark(js::gc::GRAY); - // Trace children. - JS_TraceChildren(trc, thing, kind); + /* + * Trace children of |thing|. If |thing| and its parent are both shapes, |thing| will + * get saved to mPreviousShape without being traced. The parent will later + * trace |thing|. This is done to avoid increasing the stack depth during shape + * tracing. It is safe to do because a shape can only have one child that is a shape. + */ + UnmarkGrayTracer *tracer = static_cast(trc); + UnmarkGrayTracer childTracer(tracer, kind == JSTRACE_SHAPE); + + if (kind != JSTRACE_SHAPE) { + JS_TraceChildren(&childTracer, thing, kind); + MOZ_ASSERT(!childTracer.mPreviousShape); + return; + } + + if (tracer->mTracingShape) { + MOZ_ASSERT(!tracer->mPreviousShape); + tracer->mPreviousShape = thing; + return; + } + + do { + MOZ_ASSERT(!xpc_IsGrayGCThing(thing)); + JS_TraceChildren(&childTracer, thing, JSTRACE_SHAPE); + thing = childTracer.mPreviousShape; + childTracer.mPreviousShape = nsnull; + } while (thing); } void @@ -749,7 +784,7 @@ xpc_UnmarkGrayObjectRecursive(JSObject *obj) js::gc::AsCell(obj)->unmark(js::gc::GRAY); // Trace children. - JSTracer trc; + UnmarkGrayTracer trc; JS_TracerInit(&trc, JS_GetObjectRuntime(obj), UnmarkGrayChildren); JS_TraceChildren(&trc, obj, JSTRACE_OBJECT); } From fb07fe8847d5f0e06fe4fbc05925298a4983eaf7 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Mon, 5 Mar 2012 18:43:45 -0800 Subject: [PATCH 06/88] Bug 733260: remove typedef jsuint, r=luke --HG-- extra : rebase_source : e8f576e1b5b189b47807c613c0cff79f5c8038e7 --- content/base/src/nsWebSocket.cpp | 2 +- content/canvas/src/CanvasUtils.cpp | 2 +- content/canvas/src/CanvasUtils.h | 4 +- dom/indexedDB/AsyncConnectionHelper.cpp | 2 +- dom/indexedDB/IDBDatabase.cpp | 8 +- dom/indexedDB/IDBIndex.cpp | 2 +- dom/indexedDB/IDBObjectStore.cpp | 8 +- dom/indexedDB/Key.cpp | 6 +- dom/sms/src/SmsFilter.cpp | 4 +- dom/sms/src/SmsManager.cpp | 4 +- js/ipc/ObjectWrapperChild.cpp | 4 +- js/src/ctypes/CTypes.cpp | 20 ++--- js/src/ctypes/CTypes.h | 2 +- js/src/frontend/BytecodeEmitter.cpp | 8 +- js/src/frontend/Parser.cpp | 2 +- .../jsapi-tests/testIsAboutToBeFinalized.cpp | 12 +-- js/src/jsapi-tests/testNewObject.cpp | 2 +- js/src/jsapi.cpp | 10 +-- js/src/jsapi.h | 8 +- js/src/jsarray.cpp | 90 +++++++++---------- js/src/jsarray.h | 8 +- js/src/jsatom.cpp | 6 +- js/src/jsfriendapi.h | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 10 +-- js/src/jsgc.h | 2 +- js/src/jsinterp.cpp | 6 +- js/src/jsinterpinlines.h | 8 +- js/src/jsmath.cpp | 2 +- js/src/jsnum.cpp | 10 +-- js/src/jsobj.cpp | 2 +- js/src/json.cpp | 6 +- js/src/jsopcode.cpp | 4 +- js/src/jsproxy.cpp | 4 +- js/src/jspubtd.h | 3 - js/src/jsscope.cpp | 4 +- js/src/jsstr.cpp | 44 ++++----- js/src/jstypedarray.cpp | 28 +++--- js/src/jstypedarray.h | 4 +- js/src/jsxml.cpp | 6 +- js/src/methodjit/MonoIC.cpp | 2 +- js/src/methodjit/StubCalls.cpp | 12 +-- js/src/shell/jsheaptools.cpp | 2 +- js/src/vm/String-inl.h | 2 +- js/src/vm/String.cpp | 2 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 4 +- js/xpconnect/src/XPCVariant.cpp | 4 +- js/xpconnect/src/dombindings.cpp | 2 +- startupcache/test/TestStartupCache.cpp | 4 +- toolkit/components/places/History.cpp | 10 +-- 50 files changed, 201 insertions(+), 204 deletions(-) diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp index d435033e5895..dc44ffebfe11 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/nsWebSocket.cpp @@ -602,7 +602,7 @@ nsWebSocket::Initialize(nsISupports* aOwner, if (JSVAL_IS_OBJECT(aArgv[1]) && (jsobj = JSVAL_TO_OBJECT(aArgv[1])) && JS_IsArrayObject(aContext, jsobj)) { - jsuint len; + unsigned len; JS_GetArrayLength(aContext, jsobj, &len); for (PRUint32 index = 0; index < len; ++index) { diff --git a/content/canvas/src/CanvasUtils.cpp b/content/canvas/src/CanvasUtils.cpp index 4f8dfae17819..bfd7d3bcb9ba 100644 --- a/content/canvas/src/CanvasUtils.cpp +++ b/content/canvas/src/CanvasUtils.cpp @@ -122,7 +122,7 @@ JSValToMatrixElts(JSContext* cx, const jsval& val, double* (&elts)[N], nsresult* rv) { JSObject* obj; - jsuint length; + unsigned length; if (JSVAL_IS_PRIMITIVE(val) || !(obj = JSVAL_TO_OBJECT(val)) || diff --git a/content/canvas/src/CanvasUtils.h b/content/canvas/src/CanvasUtils.h index e88363978f06..917fba8a59e0 100644 --- a/content/canvas/src/CanvasUtils.h +++ b/content/canvas/src/CanvasUtils.h @@ -146,11 +146,11 @@ JSValToDashArray(JSContext* cx, const jsval& patternArray, { // The cap is pretty arbitrary. 16k should be enough for // anybody... - static const jsuint MAX_NUM_DASHES = 1 << 14; + static const unsigned MAX_NUM_DASHES = 1 << 14; if (!JSVAL_IS_PRIMITIVE(patternArray)) { JSObject* obj = JSVAL_TO_OBJECT(patternArray); - jsuint length; + unsigned length; if (!JS_GetArrayLength(cx, obj, &length)) { // Not an array-like thing return NS_ERROR_INVALID_ARG; diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 26cfa17d4ffb..84c74f5db9fe 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -90,7 +90,7 @@ ConvertCloneReadInfosToArrayInternal( } if (!aReadInfos.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, jsuint(aReadInfos.Length()))) { + if (!JS_SetArrayLength(aCx, array, unsigned(aReadInfos.Length()))) { NS_WARNING("Failed to set array length!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 11154d325535..847a37be93a2 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -410,7 +410,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, JSObject* obj = JSVAL_TO_OBJECT(val); - jsuint length; + unsigned length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -421,7 +421,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, keyPathArray.SetCapacity(length); - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; @@ -566,7 +566,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames, // See if this is a JS array. if (JS_IsArrayObject(aCx, obj)) { - jsuint length; + unsigned length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -577,7 +577,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames, storesToOpen.SetCapacity(length); - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index d039e66e1580..7bb52c4b2107 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -904,7 +904,7 @@ GetAllKeysHelper::GetSuccessResult(JSContext* aCx, } if (!keys.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, jsuint(keys.Length()))) { + if (!JS_SetArrayLength(aCx, array, unsigned(keys.Length()))) { NS_WARNING("Failed to set array length!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index c2376f531f49..e0f21bd211d8 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -618,12 +618,12 @@ IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID, if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) && JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) { JSObject* array = JSVAL_TO_OBJECT(key); - jsuint arrayLength; + unsigned arrayLength; if (!JS_GetArrayLength(aCx, array, &arrayLength)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - for (jsuint arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + for (unsigned arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { jsval arrayItem; if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -1700,7 +1700,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, JSObject* obj = JSVAL_TO_OBJECT(aKeyPath); - jsuint length; + unsigned length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -1711,7 +1711,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, keyPathArray.SetCapacity(length); - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index 5f3544396697..93ed16a2ffcb 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -170,12 +170,12 @@ Key::EncodeJSVal(JSContext* aCx, const jsval aVal, PRUint8 aTypeOffset) aTypeOffset < (eMaxType * MaxArrayCollapse), "Wrong typeoffset"); - jsuint length; + unsigned length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { jsval val; if (!JS_GetElement(aCx, obj, index, &val)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -220,7 +220,7 @@ Key::DecodeJSVal(const unsigned char*& aPos, const unsigned char* aEnd, aTypeOffset = 0; } - jsuint index = 0; + unsigned index = 0; while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) { jsval val; nsresult rv = DecodeJSVal(aPos, aEnd, aCx, aTypeOffset, &val); diff --git a/dom/sms/src/SmsFilter.cpp b/dom/sms/src/SmsFilter.cpp index ae2b046f41c4..3e5a5cd14814 100644 --- a/dom/sms/src/SmsFilter.cpp +++ b/dom/sms/src/SmsFilter.cpp @@ -190,12 +190,12 @@ SmsFilter::SetNumbers(JSContext* aCx, const jsval& aNumbers) return NS_ERROR_INVALID_ARG; } - jsuint size; + unsigned size; JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, &obj, &size)); nsTArray numbers; - for (jsuint i=0; i= 0, "Index of next jsid negative?"); NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?"); - if (jsuint(i) == strIds->Length()) { + if (size_t(i) == strIds->Length()) { *status = JS_TRUE; return JSObject_to_JSVariant(cx, NULL, statep); } diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index ee2f85ed7750..d3e1c47bd9e8 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -1574,7 +1574,7 @@ jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result) template void -IntegerToString(IntegerType i, jsuint radix, Vector& result) +IntegerToString(IntegerType i, int radix, Vector& result) { JS_STATIC_ASSERT(numeric_limits::is_exact); @@ -1955,7 +1955,7 @@ ImplicitConvert(JSContext* cx, JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) { // Convert each element of the array by calling ImplicitConvert. JSObject* sourceArray = JSVAL_TO_OBJECT(val); - jsuint sourceLength; + unsigned sourceLength; if (!JS_GetArrayLength(cx, sourceArray, &sourceLength) || targetLength != size_t(sourceLength)) { JS_ReportError(cx, "ArrayType length does not match source array length"); @@ -1971,7 +1971,7 @@ ImplicitConvert(JSContext* cx, return false; } - for (jsuint i = 0; i < sourceLength; ++i) { + for (unsigned i = 0; i < sourceLength; ++i) { js::AutoValueRooter item(cx); if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr())) return false; @@ -4112,7 +4112,7 @@ StructType::Create(JSContext* cx, unsigned argc, jsval* vp) JSBool StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj) { - jsuint len; + unsigned len; ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len)); // Get the common prototype for CData objects of this type from @@ -4150,7 +4150,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj structSize = 0; structAlign = 0; - for (jsuint i = 0; i < len; ++i) { + for (unsigned i = 0; i < len; ++i) { js::AutoValueRooter item(cx); if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr())) return JS_FALSE; @@ -4925,7 +4925,7 @@ FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp) } arrayObj = JSVAL_TO_OBJECT(argv[2]); - jsuint len; + unsigned len; ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len)); if (!argTypes.appendN(JSVAL_VOID, len)) { @@ -4937,7 +4937,7 @@ FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp) // Pull out the argument types from the array, if any. JS_ASSERT(!argTypes.length() || arrayObj); js::AutoArrayRooter items(cx, argTypes.length(), argTypes.begin()); - for (jsuint i = 0; i < argTypes.length(); ++i) { + for (unsigned i = 0; i < argTypes.length(); ++i) { if (!JS_GetElement(cx, arrayObj, i, &argTypes[i])) return JS_FALSE; } @@ -4956,7 +4956,7 @@ FunctionType::CreateInternal(JSContext* cx, jsval abi, jsval rtype, jsval* argtypes, - jsuint arglen) + unsigned arglen) { // Determine and check the types, and prepare the function CIF. AutoPtr fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen)); @@ -5109,7 +5109,7 @@ FunctionType::Call(JSContext* cx, } jsval* argv = JS_ARGV(cx, vp); - for (jsuint i = 0; i < argcFixed; ++i) + for (unsigned i = 0; i < argcFixed; ++i) if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values[i], &strings)) return false; @@ -6044,7 +6044,7 @@ Int64Base::ToString(JSContext* cx, return JS_FALSE; } - jsuint radix = 10; + int radix = 10; if (argc == 1) { jsval arg = JS_ARGV(cx, vp)[0]; if (JSVAL_IS_INT(arg)) diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 65322775c916..f1a98be08776 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -496,7 +496,7 @@ namespace StructType { namespace FunctionType { JSObject* CreateInternal(JSContext* cx, jsval abi, jsval rtype, - jsval* argtypes, jsuint arglen); + jsval* argtypes, unsigned arglen); JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj, JSObject* refObj, PRFuncPtr fnptr, JSObject* result); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index de7f31316c4b..ffe7cc4d923b 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1010,7 +1010,7 @@ BytecodeEmitter::shouldNoteClosedName(ParseNode *pn) static int AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot) { - JS_ASSERT((jsuint) slot < bce->maxStackDepth); + JS_ASSERT((unsigned) slot < bce->maxStackDepth); if (bce->inFunction()) { slot += bce->bindings.countVars(); if ((unsigned) slot >= SLOTNO_LIMIT) { @@ -2468,7 +2468,7 @@ EmitSwitch(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) continue; } i = pn3->pn_pval->toInt32(); - if ((jsuint)(i + (int)JS_BIT(15)) >= (jsuint)JS_BIT(16)) { + if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) { switchOp = JSOP_LOOKUPSWITCH; continue; } @@ -3068,7 +3068,7 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, { JS_ASSERT(emitOption != DefineVars); - jsuint index; + unsigned index; ParseNode *pn2, *pn3; JSBool doElemOp; @@ -3303,7 +3303,7 @@ static JSBool EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *lhs, ParseNode *rhs) { - jsuint depth, limit, i, nslots; + unsigned depth, limit, i, nslots; ParseNode *pn; depth = limit = (unsigned) bce->stackDepth; diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 81a6de860cf4..29465f3258fa 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -6810,7 +6810,7 @@ Parser::primaryExpr(TokenKind tt, bool afterDoubleDot) case TOK_LB: { JSBool matched; - jsuint index; + unsigned index; pn = ListNode::create(PNK_RB, tc); if (!pn) diff --git a/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp b/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp index c4b30de2b64c..25c70d347e2b 100644 --- a/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp +++ b/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp @@ -8,14 +8,14 @@ static JSGCCallback oldGCCallback; static void **checkPointers; -static jsuint checkPointersLength; +static unsigned checkPointersLength; static size_t checkPointersStaticStrings; static JSBool TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status) { if (status == JSGC_MARK_END && checkPointers) { - for (jsuint i = 0; i != checkPointersLength; ++i) { + for (unsigned i = 0; i != checkPointersLength; ++i) { void *p = checkPointers[i]; JS_ASSERT(p); if (JS_IsAboutToBeFinalized(p)) @@ -52,7 +52,7 @@ BEGIN_TEST(testIsAboutToBeFinalized_bug528645) JS_GC(cx); /* Everything is unrooted except unit strings. */ - for (jsuint i = 0; i != checkPointersLength; ++i) { + for (unsigned i = 0; i != checkPointersLength; ++i) { void *p = checkPointers[i]; if (p) { CHECK(JSString::isStatic(p)); @@ -97,7 +97,7 @@ cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted() CHECK(checkPointers); checkPointersStaticStrings = 0; - for (jsuint i = 0; i != checkPointersLength; ++i) { + for (unsigned i = 0; i != checkPointersLength; ++i) { jsval v; ok = JS_GetElement(cx, array, i, &v); CHECK(ok); @@ -115,7 +115,7 @@ cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted() * All GC things are rooted via the root holding the array containing them * and TestAboutToBeFinalizedCallback must keep them as is. */ - for (jsuint i = 0; i != checkPointersLength; ++i) + for (unsigned i = 0; i != checkPointersLength; ++i) CHECK(checkPointers[i]); /* @@ -127,7 +127,7 @@ cls_testIsAboutToBeFinalized_bug528645::createAndTestRooted() array = JSVAL_TO_OBJECT(root.value()); JS_ASSERT(JS_IsArrayObject(cx, array)); - jsuint tmp; + unsigned tmp; CHECK(JS_GetArrayLength(cx, array, &tmp)); CHECK(ok); diff --git a/js/src/jsapi-tests/testNewObject.cpp b/js/src/jsapi-tests/testNewObject.cpp index 01ae34f685ef..6f8224563465 100644 --- a/js/src/jsapi-tests/testNewObject.cpp +++ b/js/src/jsapi-tests/testNewObject.cpp @@ -55,7 +55,7 @@ BEGIN_TEST(testNewObject_1) CHECK(obj); jsvalRoot rt(cx, OBJECT_TO_JSVAL(obj)); CHECK(JS_IsArrayObject(cx, obj)); - jsuint len; + unsigned len; CHECK(JS_GetArrayLength(cx, obj, &len)); CHECK_EQUAL(len, 0); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0448bec01e7b..98214d498303 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4407,9 +4407,9 @@ JS_NewArrayObject(JSContext *cx, int length, jsval *vector) JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment); AssertNoGC(cx); CHECK_REQUEST(cx); - /* NB: jsuint cast does ToUint32. */ - assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0)); - return NewDenseCopiedArray(cx, (jsuint)length, vector); + + assertSameCompartment(cx, JSValueArray(vector, vector ? (unsigned)length : 0)); + return NewDenseCopiedArray(cx, (unsigned)length, vector); } JS_PUBLIC_API(JSBool) @@ -4420,7 +4420,7 @@ JS_IsArrayObject(JSContext *cx, JSObject *obj) } JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) +JS_GetArrayLength(JSContext *cx, JSObject *obj, unsigned *lengthp) { AssertNoGC(cx); CHECK_REQUEST(cx); @@ -4429,7 +4429,7 @@ JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp) } JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length) +JS_SetArrayLength(JSContext *cx, JSObject *obj, unsigned length) { AssertNoGC(cx); CHECK_REQUEST(cx); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 5a1a6aa84dc9..fe2a8634981c 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1896,8 +1896,8 @@ JSID_TO_INT(jsid id) static JS_ALWAYS_INLINE JSBool INT_FITS_IN_JSID(int32_t i) { - return ((jsuint)(i) - (jsuint)JSID_INT_MIN <= - (jsuint)(JSID_INT_MAX - JSID_INT_MIN)); + return ((unsigned)(i) - (unsigned)JSID_INT_MIN <= + (unsigned)(JSID_INT_MAX - JSID_INT_MIN)); } static JS_ALWAYS_INLINE jsid @@ -3986,10 +3986,10 @@ extern JS_PUBLIC_API(JSBool) JS_IsArrayObject(JSContext *cx, JSObject *obj); extern JS_PUBLIC_API(JSBool) -JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp); +JS_GetArrayLength(JSContext *cx, JSObject *obj, unsigned *lengthp); extern JS_PUBLIC_API(JSBool) -JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length); +JS_SetArrayLength(JSContext *cx, JSObject *obj, unsigned length); extern JS_PUBLIC_API(JSBool) JS_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, jsval value, diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index ad5d9693cbd3..cf39fa4b154c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -148,7 +148,7 @@ using namespace js::gc; using namespace js::types; JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) +js_GetLengthProperty(JSContext *cx, JSObject *obj, unsigned *lengthp) { if (obj->isArray()) { *lengthp = obj->getArrayLength(); @@ -168,11 +168,11 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp) return false; if (tvr.value().isInt32()) { - *lengthp = jsuint(tvr.value().toInt32()); /* jsuint cast does ToUint32_t */ + *lengthp = unsigned(tvr.value().toInt32()); /* unsigned cast does ToUint32_t */ return true; } - JS_STATIC_ASSERT(sizeof(jsuint) == sizeof(uint32_t)); + return ToUint32(cx, tvr.value(), (uint32_t *)lengthp); } @@ -198,7 +198,7 @@ namespace js { * */ JS_FRIEND_API(bool) -StringIsArrayIndex(JSLinearString *str, jsuint *indexp) +StringIsArrayIndex(JSLinearString *str, unsigned *indexp) { const jschar *s = str->chars(); uint32_t length = str->length(); @@ -237,10 +237,10 @@ StringIsArrayIndex(JSLinearString *str, jsuint *indexp) } static JSBool -BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom, +BigIndexToId(JSContext *cx, JSObject *obj, unsigned index, JSBool createAtom, jsid *idp) { - JS_STATIC_ASSERT((jsuint)-1 == 4294967295U); + JS_ASSERT(index > JSID_INT_MAX); jschar buf[10]; @@ -319,8 +319,8 @@ IndexToId(JSContext* cx, JSObject* obj, double index, JSBool* hole, jsid* idp, return JS_TRUE; } - if (index <= jsuint(-1)) { - if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp)) + if (index <= unsigned(-1)) { + if (!BigIndexToId(cx, obj, unsigned(index), createAtom, idp)) return JS_FALSE; if (hole && JSID_IS_VOID(*idp)) *hole = JS_TRUE; @@ -438,7 +438,7 @@ GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp) } bool -GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp) +GetElements(JSContext *cx, JSObject *aobj, unsigned length, Value *vp) { if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() && !js_PrototypeHasIndexedProperties(cx, aobj)) { @@ -476,9 +476,9 @@ SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v) /* Predicted/prefetched code should favor the remains-dense case. */ JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; do { - if (index > jsuint(-1)) + if (index > unsigned(-1)) break; - jsuint idx = jsuint(index); + unsigned idx = unsigned(index); result = obj->ensureDenseArrayElements(cx, idx, 1); if (result != JSObject::ED_OK) break; @@ -630,8 +630,8 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value * us to disregard length when reading from arrays as long we are within * the initialized capacity. */ - jsuint oldcap = obj->getDenseArrayCapacity(); - jsuint oldinit = obj->getDenseArrayInitializedLength(); + unsigned oldcap = obj->getDenseArrayCapacity(); + unsigned oldinit = obj->getDenseArrayInitializedLength(); if (oldinit > newlen) obj->setDenseArrayInitializedLength(newlen); if (oldcap > newlen) @@ -661,13 +661,13 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value if (!iter) return false; - jsuint gap = oldlen - newlen; + unsigned gap = oldlen - newlen; for (;;) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id)) return false; if (JSID_IS_VOID(id)) break; - jsuint index; + unsigned index; Value junk; if (js_IdIsIndex(id, &index) && index - newlen < gap && !obj->deleteElement(cx, index, &junk, false)) { @@ -864,7 +864,7 @@ array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Valu static JSBool slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - jsuint index, length; + unsigned index, length; if (!js_IdIsIndex(id, &index)) return JS_TRUE; @@ -1489,11 +1489,11 @@ array_toSource(JSContext *cx, unsigned argc, Value *vp) if (!sb.append('[')) return false; - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return false; - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { JSBool hole; Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || @@ -1609,7 +1609,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return true; } - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return false; @@ -1650,7 +1650,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, } } } else { - for (jsuint index = 0; index < length; index++) { + for (unsigned index = 0; index < length; index++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; @@ -1794,7 +1794,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, JS_ASSERT(result == JSObject::ED_SPARSE); break; } - jsuint newlen = start + count; + unsigned newlen = start + count; if (newlen > obj->getArrayLength()) obj->setDenseArrayLength(newlen); @@ -1837,7 +1837,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, #if 0 static JSBool -InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector) +InitArrayObject(JSContext *cx, JSObject *obj, unsigned length, const Value *vector) { JS_ASSERT(obj->isArray()); @@ -1856,7 +1856,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector obj->setDenseArrayInitializedLength(length); bool hole = false; - for (jsuint i = 0; i < length; i++) { + for (unsigned i = 0; i < length; i++) { obj->setDenseArrayElement(i, vector[i]); hole |= vector[i].isMagic(JS_ARRAY_HOLE); } @@ -1899,7 +1899,7 @@ array_reverse(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - jsuint len; + unsigned len; if (!js_GetLengthProperty(cx, obj, &len)) return false; @@ -1961,7 +1961,7 @@ array_reverse(JSContext *cx, unsigned argc, Value *vp) } while (false); Value lowval, hival; - for (jsuint i = 0, half = len / 2; i < half; i++) { + for (unsigned i = 0, half = len / 2; i < half; i++) { JSBool hole, hole2; if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, i, &hole, &lowval) || @@ -2203,7 +2203,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - jsuint len; + unsigned len; if (!js_GetLengthProperty(cx, obj, &len)) return false; if (len == 0) { @@ -2250,7 +2250,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) undefs = 0; bool allStrings = true; bool allInts = true; - for (jsuint i = 0; i < len; i++) { + for (unsigned i = 0; i < len; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; @@ -2341,7 +2341,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) } } - if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes)) + if (!InitArrayElements(cx, obj, 0, unsigned(n), result, DontUpdateTypes)) return false; } @@ -2367,7 +2367,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) static bool array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args) { - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return false; @@ -2451,7 +2451,7 @@ js::array_push(JSContext *cx, unsigned argc, Value *vp) static JSBool array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) { - jsuint index; + unsigned index; if (!js_GetLengthProperty(cx, obj, &index)) return false; @@ -2477,7 +2477,7 @@ array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) static JSBool array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args) { - jsuint index = obj->getArrayLength(); + unsigned index = obj->getArrayLength(); if (index == 0) { args.rval().setUndefined(); return JS_TRUE; @@ -2538,7 +2538,7 @@ js::array_shift(JSContext *cx, unsigned argc, Value *vp) if (!obj) return JS_FALSE; - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -2567,7 +2567,7 @@ js::array_shift(JSContext *cx, unsigned argc, Value *vp) /* Slide down the array above the first element. */ AutoValueRooter tvr(cx); - for (jsuint i = 0; i < length; i++) { + for (unsigned i = 0; i < length; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, i + 1, &hole, tvr.addr()) || !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) { @@ -2590,7 +2590,7 @@ array_unshift(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -2936,11 +2936,11 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp) return false; JSObject *nobj; - jsuint length; + unsigned length; if (aobj->isDenseArray()) { length = aobj->getArrayLength(); const Value *vector = aobj->getDenseArrayElements(); - jsuint initlen = aobj->getDenseArrayInitializedLength(); + unsigned initlen = aobj->getDenseArrayInitializedLength(); nobj = NewDenseCopiedArray(cx, initlen, vector); if (!nobj) return JS_FALSE; @@ -2967,7 +2967,7 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp) if (v.isObject()) { JSObject &obj = v.toObject(); if (ObjectClassIs(obj, ESClass_Array, cx)) { - jsuint alength; + unsigned alength; if (!js_GetLengthProperty(cx, &obj, &alength)) return false; for (uint32_t slot = 0; slot < alength; slot++) { @@ -3000,7 +3000,7 @@ static JSBool array_slice(JSContext *cx, unsigned argc, Value *vp) { JSObject *nobj; - jsuint length, begin, end, slot; + unsigned length, begin, end, slot; JSBool hole; CallArgs args = CallArgsFromVp(argc, vp); @@ -3025,7 +3025,7 @@ array_slice(JSContext *cx, unsigned argc, Value *vp) } else if (d > length) { d = length; } - begin = (jsuint)d; + begin = (unsigned)d; if (args.hasDefined(1)) { if (!ToInteger(cx, args[1], &d)) @@ -3037,7 +3037,7 @@ array_slice(JSContext *cx, unsigned argc, Value *vp) } else if (d > length) { d = length; } - end = (jsuint)d; + end = (unsigned)d; } } @@ -3081,7 +3081,7 @@ enum IndexOfKind { static JSBool array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) { - jsuint length, i, stop; + unsigned length, i, stop; Value tosearch; int direction; JSBool hole; @@ -3110,14 +3110,14 @@ array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) goto not_found; i = 0; } else { - i = (jsuint)start; + i = (unsigned)start; } } else if (start >= length) { if (mode == IndexOf) goto not_found; i = length - 1; } else { - i = (jsuint)start; + i = (unsigned)start; } } @@ -3132,7 +3132,7 @@ array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) for (;;) { Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, (jsuint)i, &hole, &elt)) { + !GetElement(cx, obj, (unsigned)i, &hole, &elt)) { return JS_FALSE; } if (!hole) { @@ -3736,7 +3736,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) namespace js { static inline bool -EnsureNewArrayElements(JSContext *cx, JSObject *obj, jsuint length) +EnsureNewArrayElements(JSContext *cx, JSObject *obj, unsigned length) { /* * If ensureElements creates dynamically allocated slots, then having diff --git a/js/src/jsarray.h b/js/src/jsarray.h index c9b541720c4c..7706293d1458 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -57,13 +57,13 @@ const uint32_t MAX_ARRAY_INDEX = 4294967294u; } inline JSBool -js_IdIsIndex(jsid id, jsuint *indexp) +js_IdIsIndex(jsid id, unsigned *indexp) { if (JSID_IS_INT(id)) { int32_t i = JSID_TO_INT(id); if (i < 0) return JS_FALSE; - *indexp = (jsuint)i; + *indexp = (unsigned)i; return JS_TRUE; } @@ -137,7 +137,7 @@ NewSlowEmptyArray(JSContext *cx); } /* namespace js */ extern JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp); +js_GetLengthProperty(JSContext *cx, JSObject *obj, unsigned *lengthp); extern JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, double length); @@ -158,7 +158,7 @@ array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, J * js_GetLengthProperty on aobj. */ extern bool -GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp); +GetElements(JSContext *cx, JSObject *aobj, unsigned length, js::Value *vp); /* Natives exposed for optimization by the interpreter and JITs. */ diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 2666c53d6942..e5f7b417c2a5 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -712,9 +712,9 @@ js_CheckForStringIndex(jsid id) const jschar *cp = s; const jschar *end = s + n; - jsuint index = JS7_UNDEC(*cp++); - jsuint oldIndex = 0; - jsuint c = 0; + unsigned index = JS7_UNDEC(*cp++); + unsigned oldIndex = 0; + unsigned c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 87eee7c7c5fe..048dc3375cc8 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -498,7 +498,7 @@ JS_FRIEND_API(bool) GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector *props); JS_FRIEND_API(bool) -StringIsArrayIndex(JSLinearString *str, jsuint *indexp); +StringIsArrayIndex(JSLinearString *str, unsigned *indexp); JS_FRIEND_API(void) SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 61eede851569..329dc839979d 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1296,7 +1296,7 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp) * original version of ES5). */ JSObject *aobj = &vp[3].toObject(); - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, aobj, &length)) return false; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index ca91d8920dfd..7c42ef86c37a 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -664,7 +664,7 @@ Chunk::init() info.age = 0; /* Initialize the arena header state. */ - for (jsuint i = 0; i < ArenasPerChunk; i++) { + for (unsigned i = 0; i < ArenasPerChunk; i++) { arenas[i].aheader.setAsNotAllocated(); arenas[i].aheader.next = (i + 1 < ArenasPerChunk) ? &arenas[i + 1].aheader @@ -724,14 +724,14 @@ Chunk::removeFromAvailableList() * it to the most recently freed arena when we free, and forcing it to * the last alloc + 1 when we allocate. */ -jsuint +unsigned Chunk::findDecommittedArenaOffset() { /* Note: lastFreeArenaOffset can be past the end of the list. */ - for (jsuint i = info.lastDecommittedArenaOffset; i < ArenasPerChunk; i++) + for (unsigned i = info.lastDecommittedArenaOffset; i < ArenasPerChunk; i++) if (decommittedArenas.get(i)) return i; - for (jsuint i = 0; i < info.lastDecommittedArenaOffset; i++) + for (unsigned i = 0; i < info.lastDecommittedArenaOffset; i++) if (decommittedArenas.get(i)) return i; JS_NOT_REACHED("No decommitted arenas found."); @@ -744,7 +744,7 @@ Chunk::fetchNextDecommittedArena() JS_ASSERT(info.numArenasFreeCommitted == 0); JS_ASSERT(info.numArenasFree > 0); - jsuint offset = findDecommittedArenaOffset(); + unsigned offset = findDecommittedArenaOffset(); info.lastDecommittedArenaOffset = offset + 1; --info.numArenasFree; decommittedArenas.unset(offset); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index d696e33e2039..f59816951fdd 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -808,7 +808,7 @@ struct Chunk { inline void init(); /* Search for a decommitted arena to allocate. */ - jsuint findDecommittedArenaOffset(); + unsigned findDecommittedArenaOffset(); ArenaHeader* fetchNextDecommittedArena(); public: diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 47f19d3115c4..19490be3838e 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2905,7 +2905,7 @@ BEGIN_CASE(JSOP_TABLESWITCH) int32_t high = GET_JUMP_OFFSET(pc2); i -= low; - if ((jsuint)i < (jsuint)(high - low + 1)) { + if ((unsigned)i < (unsigned)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; int32_t off = (int32_t) GET_JUMP_OFFSET(pc2); if (off) @@ -3557,9 +3557,9 @@ BEGIN_CASE(JSOP_INITELEM) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); + JS_ASSERT(unsigned(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT && - !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) { + !js_SetLengthProperty(cx, obj, (unsigned) (JSID_TO_INT(id) + 1))) { goto error; } } else { diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index aa5bb096931b..ef396a47ec9e 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -232,7 +232,7 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp if (lval.isObject()) { JSObject *obj = &lval.toObject(); if (obj->isArray()) { - jsuint length = obj->getArrayLength(); + unsigned length = obj->getArrayLength(); *vp = NumberValue(length); return true; } @@ -804,13 +804,13 @@ SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &va do { if (obj->isDenseArray() && JSID_IS_INT(id)) { - jsuint length = obj->getDenseArrayInitializedLength(); + unsigned length = obj->getDenseArrayInitializedLength(); int32_t i = JSID_TO_INT(id); - if ((jsuint)i < length) { + if ((unsigned)i < length) { if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { if (js_PrototypeHasIndexedProperties(cx, obj)) break; - if ((jsuint)i >= obj->getArrayLength()) + if ((unsigned)i >= obj->getArrayLength()) obj->setArrayLength(cx, i + 1); } obj->setDenseArrayElementWithType(cx, i, value); diff --git a/js/src/jsmath.cpp b/js/src/jsmath.cpp index 17c25123d492..c837982e274e 100644 --- a/js/src/jsmath.cpp +++ b/js/src/jsmath.cpp @@ -435,7 +435,7 @@ js_math_min(JSContext *cx, unsigned argc, Value *vp) static double powi(double x, int y) { - jsuint n = (y < 0) ? -y : y; + unsigned n = (y < 0) ? -y : y; double m = x; double p = 1; while (true) { diff --git a/js/src/jsnum.cpp b/js/src/jsnum.cpp index 44a4cf19d913..40a9777ab820 100644 --- a/js/src/jsnum.cpp +++ b/js/src/jsnum.cpp @@ -577,7 +577,7 @@ js_IntToString(JSContext *cx, int32_t si) static char * IntToCString(ToCStringBuf *cbuf, int i, int base = 10) { - jsuint u = (i < 0) ? -i : i; + unsigned u = (i < 0) ? -i : i; RangedPtr cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize); *cp = '\0'; @@ -589,7 +589,7 @@ IntToCString(ToCStringBuf *cbuf, int i, int base = 10) break; case 16: do { - jsuint newu = u / 16; + unsigned newu = u / 16; *--cp = "0123456789abcdef"[u - newu * 16]; u = newu; } while (u != 0); @@ -597,7 +597,7 @@ IntToCString(ToCStringBuf *cbuf, int i, int base = 10) default: JS_ASSERT(base >= 2 && base <= 36); do { - jsuint newu = u / base; + unsigned newu = u / base; *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base]; u = newu; } while (u != 0); @@ -1127,7 +1127,7 @@ js_NumberToStringWithBase(JSContext *cx, double d, int base) if (JSDOUBLE_IS_INT32(d, &i)) { if (base == 10 && StaticStrings::hasInt(i)) return cx->runtime->staticStrings.getInt(i); - if (jsuint(i) < jsuint(base)) { + if (unsigned(i) < unsigned(base)) { if (i < 10) return cx->runtime->staticStrings.getInt(i); jschar c = 'a' + i - 10; @@ -1346,7 +1346,7 @@ ValueToUint16Slow(JSContext *cx, const Value &v, uint16_t *out) bool neg = (d < 0); d = floor(neg ? -d : d); d = neg ? -d : d; - jsuint m = JS_BIT(16); + unsigned m = JS_BIT(16); d = fmod(d, (double) m); if (d < 0) d += m; diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 2b5a2dccbad5..d6d5e4a65ca1 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2194,7 +2194,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDe if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) return JS_FALSE; - jsuint oldLen = obj->getArrayLength(); + unsigned oldLen = obj->getArrayLength(); if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { /* diff --git a/js/src/json.cpp b/js/src/json.cpp index 42a3d28827eb..80f5087438cc 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -500,7 +500,7 @@ JA(JSContext *cx, JSObject *obj, StringifyContext *scx) return JS_FALSE; /* Step 6. */ - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -649,7 +649,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu */ /* Step 4b(ii). */ - jsuint len; + unsigned len; JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len)); if (replacer->isDenseArray()) len = JS_MIN(len, replacer->getDenseArrayCapacity()); @@ -659,7 +659,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu return false; /* Step 4b(iii). */ - jsuint i = 0; + unsigned i = 0; /* Step 4b(iv). */ for (; i < len; i++) { diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 6aa1a523f9ab..ac0e94313cd7 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1825,7 +1825,7 @@ GetLocal(SprintStack *ss, int i) if (obj->isBlock()) { uint32_t depth = obj->asBlock().stackDepth(); uint32_t count = obj->asBlock().slotCount(); - if (jsuint(i - depth) < jsuint(count)) + if (unsigned(i - depth) < unsigned(count)) return GetLocalInSlot(ss, i, int(i - depth), obj); } } @@ -1838,7 +1838,7 @@ GetLocal(SprintStack *ss, int i) if (obj->isBlock()) { uint32_t depth = obj->asBlock().stackDepth(); uint32_t count = obj->asBlock().slotCount(); - if (jsuint(i - depth) < jsuint(count)) + if (unsigned(i - depth) < unsigned(count)) return GetLocalInSlot(ss, i, int(i - depth), obj); } } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index e8a14d574f07..fe49b0296c71 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -494,11 +494,11 @@ ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) return true; JSObject *obj = &array.toObject(); - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, obj, &length)) return false; - for (jsuint n = 0; n < length; ++n) { + for (unsigned n = 0; n < length; ++n) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; Value v; diff --git a/js/src/jspubtd.h b/js/src/jspubtd.h index e6ef4610a068..0859351f15c4 100644 --- a/js/src/jspubtd.h +++ b/js/src/jspubtd.h @@ -93,9 +93,6 @@ typedef ptrdiff_t jsid; JS_BEGIN_EXTERN_C -/* Scalar typedefs. */ -typedef uint32_t jsuint; - #ifdef WIN32 typedef wchar_t jschar; #else diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 7afca25be9dd..ea20fb175133 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -644,7 +644,7 @@ JSObject::addPropertyInternal(JSContext *cx, jsid id, { shape = self->lastProperty(); - jsuint index; + unsigned index; bool indexed = js_IdIsIndex(id, &index); UnownedBaseShape *nbase; if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) { @@ -758,7 +758,7 @@ JSObject::putProperty(JSContext *cx, jsid id, RootedVar nbase(cx); { - jsuint index; + unsigned index; bool indexed = js_IdIsIndex(id, &index); StackBaseShape base(self->lastProperty()->base()); base.updateGetterSetter(attrs, getter, setter); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index d2c3e1dd6d26..b2f03bc4a460 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -877,31 +877,31 @@ out_of_range: * * Return the index of pat in text, or -1 if not found. */ -static const jsuint sBMHCharSetSize = 256; /* ISO-Latin-1 */ -static const jsuint sBMHPatLenMax = 255; /* skip table element is uint8_t */ -static const int sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */ +static const unsigned sBMHCharSetSize = 256; /* ISO-Latin-1 */ +static const unsigned sBMHPatLenMax = 255; /* skip table element is uint8_t */ +static const int sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */ int -js_BoyerMooreHorspool(const jschar *text, jsuint textlen, - const jschar *pat, jsuint patlen) +js_BoyerMooreHorspool(const jschar *text, unsigned textlen, + const jschar *pat, unsigned patlen) { uint8_t skip[sBMHCharSetSize]; JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax); - for (jsuint i = 0; i < sBMHCharSetSize; i++) + for (unsigned i = 0; i < sBMHCharSetSize; i++) skip[i] = (uint8_t)patlen; - jsuint m = patlen - 1; - for (jsuint i = 0; i < m; i++) { + unsigned m = patlen - 1; + for (unsigned i = 0; i < m; i++) { jschar c = pat[i]; if (c >= sBMHCharSetSize) return sBMHBadPattern; skip[c] = (uint8_t)(m - i); } jschar c; - for (jsuint k = m; + for (unsigned k = m; k < textlen; k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) { - for (jsuint i = k, j = m; ; i--, j--) { + for (unsigned i = k, j = m; ; i--, j--) { if (text[i] != pat[j]) break; if (j == 0) @@ -912,8 +912,8 @@ js_BoyerMooreHorspool(const jschar *text, jsuint textlen, } struct MemCmp { - typedef jsuint Extent; - static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, jsuint patlen) { + typedef unsigned Extent; + static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, unsigned patlen) { return (patlen - 1) * sizeof(jschar); } static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) { @@ -923,7 +923,7 @@ struct MemCmp { struct ManualCmp { typedef const jschar *Extent; - static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, jsuint patlen) { + static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, unsigned patlen) { return pat + patlen; } static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) { @@ -937,7 +937,7 @@ struct ManualCmp { template static int -UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patlen) +UnrolledMatch(const jschar *text, unsigned textlen, const jschar *pat, unsigned patlen) { JS_ASSERT(patlen > 0 && textlen > 0); const jschar *textend = text + textlen - (patlen - 1); @@ -982,8 +982,8 @@ UnrolledMatch(const jschar *text, jsuint textlen, const jschar *pat, jsuint patl } static JS_ALWAYS_INLINE int -StringMatch(const jschar *text, jsuint textlen, - const jschar *pat, jsuint patlen) +StringMatch(const jschar *text, unsigned textlen, + const jschar *pat, unsigned patlen) { if (patlen == 0) return 0; @@ -1046,7 +1046,7 @@ static const size_t sRopeMatchThresholdRatioLog2 = 5; * the 'match' outparam (-1 for not found). */ static bool -RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, jsuint patlen, int *match) +RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, unsigned patlen, int *match) { JS_ASSERT(textstr->isRope()); @@ -1157,21 +1157,21 @@ str_indexOf(JSContext *cx, unsigned argc, Value *vp) if (!patstr) return false; - jsuint textlen = str->length(); + unsigned textlen = str->length(); const jschar *text = str->getChars(cx); if (!text) return false; - jsuint patlen = patstr->length(); + unsigned patlen = patstr->length(); const jschar *pat = patstr->chars(); - jsuint start; + unsigned start; if (args.length() > 1) { if (args[1].isInt32()) { int i = args[1].toInt32(); if (i <= 0) { start = 0; - } else if (jsuint(i) > textlen) { + } else if (unsigned(i) > textlen) { start = textlen; textlen = 0; } else { @@ -3957,7 +3957,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) goto report_bad_uri; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto report_bad_uri; - jsuint B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + unsigned B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { c = (jschar)B; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 95d2ff7e279e..6f599a60ea55 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -81,7 +81,7 @@ using namespace js::types; static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1; static bool -ValueIsLength(JSContext *cx, const Value &v, jsuint *len) +ValueIsLength(JSContext *cx, const Value &v, unsigned *len) { if (v.isInt32()) { int32_t i = v.toInt32(); @@ -96,7 +96,7 @@ ValueIsLength(JSContext *cx, const Value &v, jsuint *len) if (JSDOUBLE_IS_NaN(d)) return false; - jsuint length = jsuint(d); + unsigned length = unsigned(d); if (d != double(length)) return false; @@ -730,9 +730,9 @@ TypedArray::getTypedArray(JSObject *obj) } inline bool -TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip) +TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, unsigned *ip) { - jsuint index; + unsigned index; if (js_IdIsIndex(id, &index) && index < getLength(obj)) { if (ip) *ip = index; @@ -1264,7 +1264,7 @@ class TypedArrayTemplate return true; } - jsuint index; + unsigned index; // We can't just chain to js_SetPropertyHelper, because we're not a normal object. if (!isArrayIndex(cx, tarray, id, &index)) { // Silent ignore is better than an exception here, because @@ -1509,7 +1509,7 @@ class TypedArrayTemplate /* N.B. there may not be an argv[-2]/argv[-1]. */ /* () or (number) */ - jsuint len = 0; + unsigned len = 0; if (argc == 0 || ValueIsLength(cx, argv[0], &len)) { JSObject *bufobj = createBufferWithSizeAndCount(cx, len); if (!bufobj) @@ -1663,7 +1663,7 @@ class TypedArrayTemplate if (!copyFromTypedArray(cx, obj, src, offset)) return false; } else { - jsuint len; + unsigned len; if (!js_GetLengthProperty(cx, arg0, &len)) return false; @@ -1734,7 +1734,7 @@ class TypedArrayTemplate * Otherwise create a new typed array and copy len properties from the * object. */ - jsuint len; + unsigned len; if (!js_GetLengthProperty(cx, other, &len)) return NULL; @@ -1824,7 +1824,7 @@ class TypedArrayTemplate static bool copyFromArray(JSContext *cx, JSObject *thisTypedArrayObj, - JSObject *ar, jsuint len, jsuint offset = 0) + JSObject *ar, unsigned len, unsigned offset = 0) { thisTypedArrayObj = getTypedArray(thisTypedArrayObj); JS_ASSERT(thisTypedArrayObj); @@ -1858,7 +1858,7 @@ class TypedArrayTemplate } static bool - copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, jsuint offset) + copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, unsigned offset) { thisTypedArrayObj = getTypedArray(thisTypedArrayObj); JS_ASSERT(thisTypedArrayObj); @@ -1935,7 +1935,7 @@ class TypedArrayTemplate } static bool - copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, jsuint offset) + copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, unsigned offset) { JS_ASSERT(offset <= getLength(self)); @@ -2532,13 +2532,13 @@ js_IsTypedArray(JSObject *obj) } JS_FRIEND_API(JSObject *) -js_CreateArrayBuffer(JSContext *cx, jsuint nbytes) +js_CreateArrayBuffer(JSContext *cx, unsigned nbytes) { return ArrayBuffer::create(cx, nbytes); } JS_FRIEND_API(JSObject *) -JS_NewArrayBuffer(JSContext *cx, jsuint nbytes) +JS_NewArrayBuffer(JSContext *cx, unsigned nbytes) { return js_CreateArrayBuffer(cx, nbytes); } @@ -2581,7 +2581,7 @@ TypedArrayConstruct(JSContext *cx, int atype, unsigned argc, Value *argv) } JS_FRIEND_API(JSObject *) -js_CreateTypedArray(JSContext *cx, int atype, jsuint nelements) +js_CreateTypedArray(JSContext *cx, int atype, unsigned nelements) { JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX); diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index d19194f98844..ecf65cbb176d 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -252,7 +252,7 @@ struct JS_FRIEND_API(TypedArray) { public: static bool - isArrayIndex(JSContext *cx, JSObject *obj, jsid id, jsuint *ip = NULL); + isArrayIndex(JSContext *cx, JSObject *obj, jsid id, unsigned *ip = NULL); static inline uint32_t slotWidth(int atype) { switch (atype) { @@ -335,7 +335,7 @@ JS_FRIEND_API(JSBool) JS_IsArrayBufferObject(JSObject *obj); JS_FRIEND_API(JSObject *) -JS_NewArrayBuffer(JSContext *cx, jsuint nbytes); +JS_NewArrayBuffer(JSContext *cx, unsigned nbytes); JS_FRIEND_API(uint32_t) JS_GetArrayBufferByteLength(JSObject *obj); diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index cf47ae147de3..b2a548fe7282 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4665,7 +4665,7 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found) } static bool -IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex) +IdValIsIndex(JSContext *cx, jsval id, unsigned *indexp, bool *isIndex) { if (JSVAL_IS_INT(id)) { int32_t i = JSVAL_TO_INT(id); @@ -4673,7 +4673,7 @@ IdValIsIndex(JSContext *cx, jsval id, jsuint *indexp, bool *isIndex) *isIndex = false; return true; } - *indexp = (jsuint)i; + *indexp = (unsigned)i; *isIndex = true; return true; } @@ -6198,7 +6198,7 @@ static JSBool xml_namespace(JSContext *cx, unsigned argc, jsval *vp) { JSLinearString *prefix, *nsprefix; - jsuint i, length; + unsigned i, length; JSObject *ns; NON_LIST_XML_METHOD_PROLOG; diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index cfade926ead5..5c6191d4d1bb 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1143,7 +1143,7 @@ ic::SplatApplyArgs(VMFrame &f) /* Steps 4-5. */ JSObject *aobj = &vp[3].toObject(); - jsuint length; + unsigned length; if (!js_GetLengthProperty(cx, aobj, &length)) THROWV(false); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index ea229603035c..7daffd0a6e50 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -234,13 +234,13 @@ stubs::SetElem(VMFrame &f) do { if (obj->isDenseArray() && JSID_IS_INT(id)) { - jsuint length = obj->getDenseArrayInitializedLength(); + unsigned length = obj->getDenseArrayInitializedLength(); int32_t i = JSID_TO_INT(id); - if ((jsuint)i < length) { + if ((unsigned)i < length) { if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { if (js_PrototypeHasIndexedProperties(cx, obj)) break; - if ((jsuint)i >= obj->getArrayLength()) + if ((unsigned)i >= obj->getArrayLength()) obj->setArrayLength(cx, i + 1); } obj->setDenseArrayElementWithType(cx, i, rval); @@ -1034,8 +1034,8 @@ stubs::InitElem(VMFrame &f, uint32_t last) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); - if (last && !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) + JS_ASSERT(unsigned(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); + if (last && !js_SetLengthProperty(cx, obj, (unsigned) (JSID_TO_INT(id) + 1))) THROW(); } else { if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) @@ -1584,7 +1584,7 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) pc += JUMP_OFFSET_LEN; tableIdx -= low; - if ((jsuint) tableIdx < (jsuint)(high - low + 1)) { + if ((unsigned) tableIdx < (unsigned)(high - low + 1)) { pc += JUMP_OFFSET_LEN * tableIdx; if (uint32_t candidateOffset = GET_JUMP_OFFSET(pc)) jumpOffset = candidateOffset; diff --git a/js/src/shell/jsheaptools.cpp b/js/src/shell/jsheaptools.cpp index 03cc6a56c367..481578fe0a6c 100644 --- a/js/src/shell/jsheaptools.cpp +++ b/js/src/shell/jsheaptools.cpp @@ -532,7 +532,7 @@ ReferenceFinder::addReferrer(jsval referrer, Path *path) JS_ASSERT(JS_IsArrayObject(context, array)); /* Append our referrer to this array. */ - jsuint length; + unsigned length; return JS_GetArrayLength(context, array, &length) && JS_SetElement(context, array, length, &referrer); } diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 44d6039e43af..f0d0152e6510 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -376,7 +376,7 @@ js::StaticStrings::lookup(const jschar *chars, size_t length) (chars[1] - '0') * 10 + (chars[2] - '0'); - if (jsuint(i) < INT_STATIC_LIMIT) + if (unsigned(i) < INT_STATIC_LIMIT) return getInt(i); } return NULL; diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index cf0addadb448..9711d501a48e 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -533,7 +533,7 @@ StaticStrings::isStatic(JSAtom *atom) (chars[1] - '0') * 10 + (chars[2] - '0'); - return (jsuint(i) < INT_STATIC_LIMIT); + return (unsigned(i) < INT_STATIC_LIMIT); } return false; default: diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 1ba7911cbf46..bf6a880b7fa0 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -1214,7 +1214,7 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation, // Iterate over symbols array, installing symbols on targetObj: - jsuint symbolCount = 0; + unsigned symbolCount = 0; if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) { return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH, PromiseFlatCString(aLocation).get()); @@ -1224,7 +1224,7 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation, nsCAutoString logBuffer; #endif - for (jsuint i = 0; i < symbolCount; ++i) { + for (unsigned i = 0; i < symbolCount; ++i) { jsval val; jsid symbolId; diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index a7d1d31a5a60..b672e5bdbca1 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -190,7 +190,7 @@ private: public: static JSBool GetTypeForArray(XPCCallContext& ccx, JSObject* array, - jsuint length, + unsigned length, nsXPTType* resultType, nsID* resultID); }; @@ -215,7 +215,7 @@ XPCArrayHomogenizer::StateTable[tTypeCount][tTypeCount-1] = { // static JSBool XPCArrayHomogenizer::GetTypeForArray(XPCCallContext& ccx, JSObject* array, - jsuint length, + unsigned length, nsXPTType* resultType, nsID* resultID) { Type state = tUnk; diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index 091715db138b..098d9d88ac27 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -607,7 +607,7 @@ GetArrayIndexFromId(JSContext *cx, jsid id) if (NS_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z')) return -1; - jsuint i; + unsigned i; JSLinearString *str = js::AtomToLinearString(JSID_TO_ATOM(id)); return js::StringIsArrayIndex(str, &i) ? i : -1; } diff --git a/startupcache/test/TestStartupCache.cpp b/startupcache/test/TestStartupCache.cpp index 13a3700128a0..90894fb2d43f 100644 --- a/startupcache/test/TestStartupCache.cpp +++ b/startupcache/test/TestStartupCache.cpp @@ -359,7 +359,7 @@ GetHistogramCounts(const char *testmsg, JSContext *cx, jsval *counts) nsresult CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after) { - jsuint before_size, after_size; + unsigned before_size, after_size; if (!(JS_GetArrayLength(cx, before, &before_size) && JS_GetArrayLength(cx, after, &after_size))) { return NS_ERROR_UNEXPECTED; @@ -369,7 +369,7 @@ CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after) return NS_ERROR_UNEXPECTED; } - for (jsuint i = 0; i < before_size; ++i) { + for (unsigned i = 0; i < before_size; ++i) { jsval before_num, after_num; if (!(JS_GetElement(cx, before, i, &before_num) diff --git a/toolkit/components/places/History.cpp b/toolkit/components/places/History.cpp index e30c5cc96a8a..dcf0ddf8720c 100644 --- a/toolkit/components/places/History.cpp +++ b/toolkit/components/places/History.cpp @@ -318,7 +318,7 @@ GetIntFromJSObject(JSContext* aCtx, nsresult GetJSObjectFromArray(JSContext* aCtx, JSObject* aArray, - jsuint aIndex, + unsigned aIndex, JSObject** _rooter) { NS_PRECONDITION(JS_IsArrayObject(aCtx, aArray), @@ -2104,7 +2104,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED); NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(aPlaceInfos), NS_ERROR_INVALID_ARG); - jsuint infosLength = 1; + unsigned infosLength = 1; JSObject* infos; if (JS_IsArrayObject(aCtx, JSVAL_TO_OBJECT(aPlaceInfos))) { infos = JSVAL_TO_OBJECT(aPlaceInfos); @@ -2122,7 +2122,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, } nsTArray visitData; - for (jsuint i = 0; i < infosLength; i++) { + for (unsigned i = 0; i < infosLength; i++) { JSObject* info; nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info); NS_ENSURE_SUCCESS(rv, rv); @@ -2168,7 +2168,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, } NS_ENSURE_ARG(visits); - jsuint visitsLength = 0; + unsigned visitsLength = 0; if (visits) { (void)JS_GetArrayLength(aCtx, visits, &visitsLength); } @@ -2176,7 +2176,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, // Check each visit, and build our array of VisitData objects. visitData.SetCapacity(visitData.Length() + visitsLength); - for (jsuint j = 0; j < visitsLength; j++) { + for (unsigned j = 0; j < visitsLength; j++) { JSObject* visit; rv = GetJSObjectFromArray(aCtx, visits, j, &visit); NS_ENSURE_SUCCESS(rv, rv); From 6b2ac6869b0bd0919e576d471cab738db68595e1 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 07/88] Bug 672175 part.1 Implement MouseScrollHandler for Windows r=jimm --- widget/windows/Makefile.in | 1 + widget/windows/WinMouseScrollHandler.cpp | 85 ++++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 44 ++++++++++++ widget/windows/nsWidgetFactory.cpp | 2 + widget/windows/nsWindow.cpp | 17 ++--- 5 files changed, 141 insertions(+), 8 deletions(-) create mode 100644 widget/windows/WinMouseScrollHandler.cpp create mode 100644 widget/windows/WinMouseScrollHandler.h diff --git a/widget/windows/Makefile.in b/widget/windows/Makefile.in index c9327f88168c..df811acbacaa 100644 --- a/widget/windows/Makefile.in +++ b/widget/windows/Makefile.in @@ -79,6 +79,7 @@ CPPSRCS = \ AudioSession.cpp \ nsWidgetFactory.cpp \ WinUtils.cpp \ + WinMouseScrollHandler.cpp \ $(NULL) ifdef MOZ_CRASHREPORTER diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp new file mode 100644 index 000000000000..02a6da197fe7 --- /dev/null +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifdef MOZ_LOGGING +#define FORCE_PR_LOG /* Allow logging in the release build */ +#endif // MOZ_LOGGING +#include "prlog.h" + +#include "WinMouseScrollHandler.h" +#include "nsWindow.h" + +namespace mozilla { +namespace widget { + +#ifdef PR_LOGGING +PRLogModuleInfo* gMouseScrollLog = nsnull; +#endif + +MouseScrollHandler* MouseScrollHandler::sInstance = nsnull; + + +/****************************************************************************** + * + * MouseScrollHandler + * + ******************************************************************************/ + +/* static */ +void +MouseScrollHandler::Initialize() +{ +#ifdef PR_LOGGING + if (!gMouseScrollLog) { + gMouseScrollLog = PR_NewLogModule("MouseScrollHandlerWidgets"); + } +#endif +} + +/* static */ +void +MouseScrollHandler::Shutdown() +{ + delete sInstance; + sInstance = nsnull; +} + +/* static */ +MouseScrollHandler* +MouseScrollHandler::GetInstance() +{ + if (!sInstance) { + sInstance = new MouseScrollHandler(); + } + return sInstance; +} + +MouseScrollHandler::MouseScrollHandler() +{ + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll: Creating an instance, this=%p, sInstance=%p", + this, sInstance)); +} + +MouseScrollHandler::~MouseScrollHandler() +{ + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll: Destroying an instance, this=%p, sInstance=%p", + this, sInstance)); +} + +/* static */ +bool +MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, + WPARAM wParam, LPARAM lParam, + LRESULT *aRetValue, bool &aEatMessage) +{ + return false; +} + + +} // namespace widget +} // namespace mozilla diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h new file mode 100644 index 000000000000..99cb266af294 --- /dev/null +++ b/widget/windows/WinMouseScrollHandler.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_widget_WinMouseScrollHandler_h__ +#define mozilla_widget_WinMouseScrollHandler_h__ + +#include "nscore.h" +#include "nsDebug.h" +#include "mozilla/Assertions.h" +#include + +class nsWindow; + +namespace mozilla { +namespace widget { + +class MouseScrollHandler { +public: + static MouseScrollHandler* GetInstance(); + + static void Initialize(); + static void Shutdown(); + + static bool ProcessMessage(nsWindow* aWindow, + UINT msg, + WPARAM wParam, + LPARAM lParam, + LRESULT *aRetValue, + bool &aEatMessage); + +private: + MouseScrollHandler(); + ~MouseScrollHandler(); + + static MouseScrollHandler* sInstance; +}; + +} // namespace widget +} // namespace mozilla + +#endif // mozilla_widget_WinMouseScrollHandler_h__ \ No newline at end of file diff --git a/widget/windows/nsWidgetFactory.cpp b/widget/windows/nsWidgetFactory.cpp index 001c4efe107d..dee19fd206e0 100644 --- a/widget/windows/nsWidgetFactory.cpp +++ b/widget/windows/nsWidgetFactory.cpp @@ -53,6 +53,7 @@ #include "nsScreenManagerWin.h" #include "nsSound.h" #include "nsWindow.h" +#include "WinMouseScrollHandler.h" #include "WinTaskbar.h" #include "JumpListBuilder.h" #include "JumpListItem.h" @@ -209,6 +210,7 @@ static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { static void nsWidgetWindowsModuleDtor() { + MouseScrollHandler::Shutdown(); nsLookAndFeel::Shutdown(); nsToolkit::Shutdown(); nsAppShellShutdown(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 85750b47c14c..790bd88dd1f2 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -133,6 +133,7 @@ #include "nsIServiceManager.h" #include "nsIClipboard.h" #include "nsIMM32Handler.h" +#include "WinMouseScrollHandler.h" #include "nsILocalFile.h" #include "nsFontMetrics.h" #include "nsIFontEnumerator.h" @@ -427,27 +428,22 @@ nsWindow::nsWindow() : nsBaseWidget() // Global app registration id for Win7 and up. See // WinTaskbar.cpp for details. mozilla::widget::WinTaskbar::RegisterAppUserModelID(); - gKbdLayout.LoadLayout(::GetKeyboardLayout(0)); - // Init IME handler nsIMM32Handler::Initialize(); - #ifdef NS_ENABLE_TSF nsTextStore::Initialize(); #endif - - if (SUCCEEDED(::OleInitialize(NULL))) + if (SUCCEEDED(::OleInitialize(NULL))) { sIsOleInitialized = TRUE; + } NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n"); - InitInputWorkaroundPrefDefaults(); - + MouseScrollHandler::Initialize(); // Init titlebar button info for custom frames. nsUXThemeData::InitTitlebarInfo(); // Init theme data nsUXThemeData::UpdateNativeThemeInfo(); - ForgetRedirectedKeyDownMessage(); } // !sInstanceCount @@ -4528,6 +4524,11 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, return mWnd ? eatMessage : true; } + if (MouseScrollHandler::ProcessMessage(this, msg, wParam, lParam, aRetValue, + eatMessage)) { + return mWnd ? eatMessage : true; + } + if (PluginHasFocus()) { bool callDefaultWndProc; MSG nativeMsg = WinUtils::InitMSG(msg, wParam, lParam); From 4752ebe8cd49c7980761821d35aa3142f735dfd9 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 08/88] Bug 672175 part.2 MouseScrollHandler should manage system settings r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 80 +++++++++++++++++++++++- widget/windows/WinMouseScrollHandler.h | 35 +++++++++++ widget/windows/nsWindow.cpp | 44 +++---------- widget/windows/nsWindow.h | 2 - 4 files changed, 123 insertions(+), 38 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 02a6da197fe7..a5f2f7772e04 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -11,6 +11,7 @@ #include "WinMouseScrollHandler.h" #include "nsWindow.h" +#include "WinUtils.h" namespace mozilla { namespace widget { @@ -77,9 +78,86 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *aRetValue, bool &aEatMessage) { - return false; + switch (msg) { + case WM_SETTINGCHANGE: + if (!sInstance) { + return false; + } + if (wParam == SPI_SETWHEELSCROLLLINES || + wParam == SPI_SETWHEELSCROLLCHARS) { + sInstance->mSystemSettings.MarkDirty(); + } + return false; + default: + return false; + } } +/****************************************************************************** + * + * SystemSettings + * + ******************************************************************************/ + +void +MouseScrollHandler::SystemSettings::Init() +{ + if (mInitialized) { + return; + } + + mInitialized = true; + + if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &mScrollLines, 0)) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::SystemSettings::Init(): ::SystemParametersInfo(" + "SPI_GETWHEELSCROLLLINES) failed")); + mScrollLines = 3; + } else if (mScrollLines > WHEEL_DELTA) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::SystemSettings::Init(): the result of " + "::SystemParametersInfo(SPI_GETWHEELSCROLLLINES) is too large: %d", + mScrollLines)); + // sScrollLines usually equals 3 or 0 (for no scrolling) + // However, if sScrollLines > WHEEL_DELTA, we assume that + // the mouse driver wants a page scroll. The docs state that + // sScrollLines should explicitly equal WHEEL_PAGESCROLL, but + // since some mouse drivers use an arbitrary large number instead, + // we have to handle that as well. + mScrollLines = WHEEL_PAGESCROLL; + } + + if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &mScrollChars, 0)) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::SystemSettings::Init(): ::SystemParametersInfo(" + "SPI_GETWHEELSCROLLCHARS) failed, %s", + WinUtils::GetWindowsVersion() >= WinUtils::VISTA_VERSION ? + "this is unexpected on Vista or later" : + "but on XP or earlier, this is not a problem")); + mScrollChars = 1; + } else if (mScrollChars > WHEEL_DELTA) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::SystemSettings::Init(): the result of " + "::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS) is too large: %d", + mScrollChars)); + // See the comments for the case mScrollLines > WHEEL_DELTA. + mScrollChars = WHEEL_PAGESCROLL; + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::SystemSettings::Init(): initialized, " + "mScrollLines=%d, mScrollChars=%d", + mScrollLines, mScrollChars)); +} + +void +MouseScrollHandler::SystemSettings::MarkDirty() +{ + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScrollHandler::SystemSettings::MarkDirty(): " + "Marking SystemSettings dirty")); + mInitialized = false; +} } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 99cb266af294..940cf8bdedce 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -36,6 +36,41 @@ private: ~MouseScrollHandler(); static MouseScrollHandler* sInstance; + +public: + class SystemSettings { + public: + SystemSettings() : mInitialized(false) {} + + void Init(); + void MarkDirty(); + + PRInt32 GetScrollAmount(bool aForVertical) const + { + MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); + return aForVertical ? mScrollLines : mScrollChars; + } + + bool IsPageScroll(bool aForVertical) const + { + MOZ_ASSERT(mInitialized, "SystemSettings must be initialized"); + return aForVertical ? (mScrollLines == WHEEL_PAGESCROLL) : + (mScrollChars == WHEEL_PAGESCROLL); + } + + private: + bool mInitialized; + PRInt32 mScrollLines; + PRInt32 mScrollChars; + }; + + SystemSettings& GetSystemSettings() + { + return mSystemSettings; + } + +private: + SystemSettings mSystemSettings; }; } // namespace widget diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 790bd88dd1f2..8015ad686168 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -294,8 +294,6 @@ MSG nsWindow::sRedirectedKeyDown; bool nsWindow::sEnablePixelScrolling = true; bool nsWindow::sNeedsToInitMouseWheelSettings = true; -ULONG nsWindow::sMouseWheelScrollLines = 0; -ULONG nsWindow::sMouseWheelScrollChars = 0; HWND nsWindow::sLastMouseWheelWnd = NULL; PRInt32 nsWindow::sRemainingDeltaForScroll = 0; @@ -6315,30 +6313,6 @@ nsWindow::InitMouseWheelScrollData() sNeedsToInitMouseWheelSettings = false; ResetRemainingWheelDelta(); - if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, - &sMouseWheelScrollLines, 0)) { - NS_WARNING("Failed to get SPI_GETWHEELSCROLLLINES"); - sMouseWheelScrollLines = 3; - } else if (sMouseWheelScrollLines > WHEEL_DELTA) { - // sMouseWheelScrollLines usually equals 3 or 0 (for no scrolling) - // However, if sMouseWheelScrollLines > WHEEL_DELTA, we assume that - // the mouse driver wants a page scroll. The docs state that - // sMouseWheelScrollLines should explicitly equal WHEEL_PAGESCROLL, but - // since some mouse drivers use an arbitrary large number instead, - // we have to handle that as well. - sMouseWheelScrollLines = WHEEL_PAGESCROLL; - } - - if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, - &sMouseWheelScrollChars, 0)) { - NS_ASSERTION(WinUtils::GetWindowsVersion() < WinUtils::VISTA_VERSION, - "Failed to get SPI_GETWHEELSCROLLCHARS"); - sMouseWheelScrollChars = 1; - } else if (sMouseWheelScrollChars > WHEEL_DELTA) { - // See the comments for the case sMouseWheelScrollLines > WHEEL_DELTA. - sMouseWheelScrollChars = WHEEL_PAGESCROLL; - } - sEnablePixelScrolling = Preferences::GetBool("mousewheel.enable_pixel_scrolling", true); } @@ -6367,10 +6341,12 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, LRESULT *aRetValue) { InitMouseWheelScrollData(); + MouseScrollHandler::SystemSettings& systemSettings = + MouseScrollHandler::GetInstance()->GetSystemSettings(); + systemSettings.Init(); bool isVertical = (aMessage == WM_MOUSEWHEEL); - if ((isVertical && sMouseWheelScrollLines == 0) || - (!isVertical && sMouseWheelScrollChars == 0)) { + if (systemSettings.GetScrollAmount(isVertical) == 0) { // XXX I think that we should dispatch mouse wheel events even if the // operation will not scroll because the wheel operation really happened // and web application may want to handle the event for non-scroll action. @@ -6386,9 +6362,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, return; // We cannot process this message } - bool isPageScroll = - ((isVertical && sMouseWheelScrollLines == WHEEL_PAGESCROLL) || - (!isVertical && sMouseWheelScrollChars == WHEEL_PAGESCROLL)); + bool isPageScroll = systemSettings.IsPageScroll(isVertical); // Discard the remaining delta if current wheel message and last one are // received by different window or to scroll different direction or @@ -6445,8 +6419,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, PRInt32 actualScrollAction = nsQueryContentEvent::SCROLL_ACTION_NONE; PRInt32 pixelsPerUnit = 0; // the amount is the number of lines (or pages) per WHEEL_DELTA - PRInt32 computedScrollAmount = isPageScroll ? 1 : - (isVertical ? sMouseWheelScrollLines : sMouseWheelScrollChars); + PRInt32 computedScrollAmount = + isPageScroll ? 1 : systemSettings.GetScrollAmount(isVertical); if (sEnablePixelScrolling) { nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, this); @@ -6519,11 +6493,11 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, double deltaPerUnit; if (isVertical) { scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsVertical; - deltaPerUnit = (double)WHEEL_DELTA / sMouseWheelScrollLines; } else { scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsHorizontal; - deltaPerUnit = (double)WHEEL_DELTA / sMouseWheelScrollChars; } + deltaPerUnit = + (double)WHEEL_DELTA / systemSettings.GetScrollAmount(isVertical); scrollEvent.delta = RoundDelta((double)nativeDeltaForScroll * orienter / deltaPerUnit); PRInt32 recomputedNativeDelta = diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index f9ba80a5db2d..ef0afe97879b 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -610,8 +610,6 @@ protected: static bool sEnablePixelScrolling; static bool sNeedsToInitMouseWheelSettings; - static ULONG sMouseWheelScrollLines; - static ULONG sMouseWheelScrollChars; static void InitMouseWheelScrollData(); static HWND sLastMouseWheelWnd; From 748e4225adf5049f8f9f62d4fb56e28a1b59d466 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 09/88] Bug 672175 part.3 MouseScrollHandler should manage user prefs r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 61 ++++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 42 ++++++++++++++++ widget/windows/nsWindow.cpp | 18 ++----- widget/windows/nsWindow.h | 1 - 4 files changed, 108 insertions(+), 14 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index a5f2f7772e04..9265ae2a5084 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -13,11 +13,18 @@ #include "nsWindow.h" #include "WinUtils.h" +#include "mozilla/Preferences.h" + namespace mozilla { namespace widget { #ifdef PR_LOGGING PRLogModuleInfo* gMouseScrollLog = nsnull; + +static const char* GetBoolName(bool aBool) +{ + return aBool ? "TRUE" : "FALSE"; +} #endif MouseScrollHandler* MouseScrollHandler::sInstance = nsnull; @@ -159,5 +166,59 @@ MouseScrollHandler::SystemSettings::MarkDirty() mInitialized = false; } +/****************************************************************************** + * + * UserPrefs + * + ******************************************************************************/ + +MouseScrollHandler::UserPrefs::UserPrefs() : + mInitialized(false) +{ + // We need to reset mouse wheel transaction when all of mousewheel related + // prefs are changed. + DebugOnly rv = + Preferences::RegisterCallback(OnChange, "mousewheel.", this); + MOZ_ASSERT(NS_SUCCEEDED(rv), + "Failed to register callback for mousewheel."); +} + +MouseScrollHandler::UserPrefs::~UserPrefs() +{ + DebugOnly rv = + Preferences::UnregisterCallback(OnChange, "mousewheel.", this); + MOZ_ASSERT(NS_SUCCEEDED(rv), + "Failed to unregister callback for mousewheel."); +} + +void +MouseScrollHandler::UserPrefs::Init() +{ + if (mInitialized) { + return; + } + + mInitialized = true; + + mPixelScrollingEnabled = + Preferences::GetBool("mousewheel.enable_pixel_scrolling", true); + mScrollMessageHandledAsWheelMessage = + Preferences::GetBool("mousewheel.emulate_at_wm_scroll", false); + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::UserPrefs::Init(): initialized, " + "mPixelScrollingEnabled=%s, mScrollMessageHandledAsWheelMessage=%s", + GetBoolName(mPixelScrollingEnabled), + GetBoolName(mScrollMessageHandledAsWheelMessage))); +} + +void +MouseScrollHandler::UserPrefs::MarkDirty() +{ + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScrollHandler::UserPrefs::MarkDirty(): Marking UserPrefs dirty")); + mInitialized = false; +} + } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 940cf8bdedce..9ba4192bacf9 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -71,6 +71,48 @@ public: private: SystemSettings mSystemSettings; + +public: + class UserPrefs { + public: + UserPrefs(); + ~UserPrefs(); + + void MarkDirty(); + + bool IsPixelScrollingEnabled() + { + Init(); + return mPixelScrollingEnabled; + } + + bool IsScrollMessageHandledAsWheelMessage() + { + Init(); + return mScrollMessageHandledAsWheelMessage; + } + + private: + void Init(); + + static int OnChange(const char* aPrefName, void* aClosure) + { + static_cast(aClosure)->MarkDirty(); + return 0; + } + + bool mInitialized; + bool mPixelScrollingEnabled; + bool mScrollMessageHandledAsWheelMessage; + }; + + UserPrefs& GetUserPrefs() + { + return mUserPrefs; + } + +private: + UserPrefs mUserPrefs; }; } // namespace widget diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 8015ad686168..967795f3c033 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -292,7 +292,6 @@ PRUint32 nsWindow::sOOPPPluginFocusEvent = MSG nsWindow::sRedirectedKeyDown; -bool nsWindow::sEnablePixelScrolling = true; bool nsWindow::sNeedsToInitMouseWheelSettings = true; HWND nsWindow::sLastMouseWheelWnd = NULL; @@ -6312,9 +6311,6 @@ nsWindow::InitMouseWheelScrollData() } sNeedsToInitMouseWheelSettings = false; ResetRemainingWheelDelta(); - - sEnablePixelScrolling = - Preferences::GetBool("mousewheel.enable_pixel_scrolling", true); } /* static */ @@ -6422,7 +6418,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, PRInt32 computedScrollAmount = isPageScroll ? 1 : systemSettings.GetScrollAmount(isVertical); - if (sEnablePixelScrolling) { + if (MouseScrollHandler::GetInstance()-> + GetUserPrefs().IsPixelScrollingEnabled()) { nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, this); InitEvent(testEvent); testEvent.scrollFlags = isPageScroll ? nsMouseScrollEvent::kIsFullPage : 0; @@ -7567,14 +7564,9 @@ nsWindow::OnMouseWheel(UINT aMsg, WPARAM aWParam, LPARAM aLParam, bool nsWindow::OnScroll(UINT aMsg, WPARAM aWParam, LPARAM aLParam) { - static PRInt8 sMouseWheelEmulation = -1; - if (sMouseWheelEmulation < 0) { - bool emulate = - Preferences::GetBool("mousewheel.emulate_at_wm_scroll", false); - sMouseWheelEmulation = PRInt8(emulate); - } - - if (aLParam || sMouseWheelEmulation) { + if (aLParam || + MouseScrollHandler::GetInstance()-> + GetUserPrefs().IsScrollMessageHandledAsWheelMessage()) { // Scroll message generated by Thinkpad Trackpoint Driver or similar // Treat as a mousewheel message and scroll appropriately LRESULT retVal; diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index ef0afe97879b..3d1ac1e7a24c 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -608,7 +608,6 @@ protected: // was reirected to SendInput() API by OnKeyDown(). static MSG sRedirectedKeyDown; - static bool sEnablePixelScrolling; static bool sNeedsToInitMouseWheelSettings; static void InitMouseWheelScrollData(); From 82e8e92ed22b85d2fff4a16f09c854412484c1cc Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 10/88] Bug 672175 part.4 Move HasRegistryKey() in nsWindow.cpp to WinUtils r=jimm --- widget/windows/WinUtils.cpp | 20 ++++++++++++++++++++ widget/windows/WinUtils.h | 11 +++++++++++ widget/windows/nsWindow.cpp | 29 ++++++++++------------------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/widget/windows/WinUtils.cpp b/widget/windows/WinUtils.cpp index d5763a81e90a..2c523dcb785b 100644 --- a/widget/windows/WinUtils.cpp +++ b/widget/windows/WinUtils.cpp @@ -122,6 +122,26 @@ WinUtils::GetRegistryKey(HKEY aRoot, return true; } +/* static */ +bool +WinUtils::HasRegistryKey(HKEY aRoot, const PRUnichar* aKeyName) +{ + MOZ_ASSERT(aRoot, "aRoot must not be NULL"); + MOZ_ASSERT(aKeyName, "aKeyName must not be NULL"); + HKEY key; + LONG result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_32KEY, &key); + if (result != ERROR_SUCCESS) { + result = + ::RegOpenKeyExW(aRoot, aKeyName, 0, KEY_READ | KEY_WOW64_64KEY, &key); + if (result != ERROR_SUCCESS) { + return false; + } + } + ::RegCloseKey(key); + return true; +} + /* static */ HWND WinUtils::GetTopLevelHWND(HWND aWnd, diff --git a/widget/windows/WinUtils.h b/widget/windows/WinUtils.h index 74d52a565e10..d68dc9377807 100644 --- a/widget/windows/WinUtils.h +++ b/widget/windows/WinUtils.h @@ -93,6 +93,17 @@ public: PRUnichar* aBuffer, DWORD aBufferLength); + /** + * Checks whether the registry key exists in either 32bit or 64bit branch on + * the environment. + * + * @param aRoot The registry root of aName. + * @param aKeyName The name of the registry key to check. + * @return TRUE if it exists and is readable. Otherwise, FALSE. + */ + static bool HasRegistryKey(HKEY aRoot, + const PRUnichar* aKeyName); + /** * GetTopLevelHWND() returns a window handle of the top level window which * aWnd belongs to. Note that the result may not be our window, i.e., it diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 967795f3c033..12a6e56b1452 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -8662,20 +8662,6 @@ bool nsWindow::UseTrackPointHack() sDefaultTrackPointHack); } -static bool -HasRegistryKey(HKEY aRoot, PRUnichar* aName) -{ - HKEY key; - LONG result = ::RegOpenKeyExW(aRoot, aName, 0, KEY_READ | KEY_WOW64_32KEY, &key); - if (result != ERROR_SUCCESS) { - result = ::RegOpenKeyExW(aRoot, aName, 0, KEY_READ | KEY_WOW64_64KEY, &key); - if (result != ERROR_SUCCESS) - return false; - } - ::RegCloseKey(key); - return true; -} - static bool IsObsoleteSynapticsDriver() { @@ -8732,14 +8718,19 @@ void nsWindow::InitInputWorkaroundPrefDefaults() { PRUint32 elantechDriverVersion = GetElantechDriverMajorVersion(); - if (HasRegistryKey(HKEY_CURRENT_USER, L"Software\\Lenovo\\TrackPoint")) { + if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Lenovo\\TrackPoint")) { sDefaultTrackPointHack = true; - } else if (HasRegistryKey(HKEY_CURRENT_USER, L"Software\\Lenovo\\UltraNav")) { + } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Lenovo\\UltraNav")) { sDefaultTrackPointHack = true; - } else if (HasRegistryKey(HKEY_CURRENT_USER, L"Software\\Alps\\Apoint\\TrackPoint")) { + } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Alps\\Apoint\\TrackPoint")) { sDefaultTrackPointHack = true; - } else if ((HasRegistryKey(HKEY_CURRENT_USER, L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB") || - HasRegistryKey(HKEY_CURRENT_USER, L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2")) && + } else if ((WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB") || + WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2")) && IsObsoleteSynapticsDriver()) { sDefaultTrackPointHack = true; } From ee28f178e8f623655053a31aa2d6bcbd89a6628c Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 11/88] Bug 672175 part.5 Move device specific code to MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 419 +++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 92 +++++ widget/windows/nsWindow.cpp | 255 +------------- widget/windows/nsWindow.h | 7 - 4 files changed, 519 insertions(+), 254 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 9265ae2a5084..be3d9ebc5784 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -15,6 +15,8 @@ #include "mozilla/Preferences.h" +#include + namespace mozilla { namespace widget { @@ -25,10 +27,43 @@ static const char* GetBoolName(bool aBool) { return aBool ? "TRUE" : "FALSE"; } + +static void LogKeyStateImpl() +{ + if (!PR_LOG_TEST(gMouseScrollLog, PR_LOG_DEBUG)) { + return; + } + BYTE keyboardState[256]; + if (::GetKeyboardState(keyboardState)) { + for (size_t i = 0; i < ArrayLength(keyboardState); i++) { + if (keyboardState[i]) { + PR_LOG(gMouseScrollLog, PR_LOG_DEBUG, + (" Current key state: keyboardState[0x%02X]=0x%02X (%s)", + i, keyboardState[i], + ((keyboardState[i] & 0x81) == 0x81) ? "Pressed and Toggled" : + (keyboardState[i] & 0x80) ? "Pressed" : + (keyboardState[i] & 0x01) ? "Toggled" : "Unknown")); + } + } + } else { + PR_LOG(gMouseScrollLog, PR_LOG_DEBUG, + ("MouseScroll::Device::Elantech::HandleKeyMessage(): Failed to print " + "current keyboard state")); + } +} + +#define LOG_KEYSTATE() LogKeyStateImpl() +#else // PR_LOGGING +#define LOG_KEYSTATE() #endif MouseScrollHandler* MouseScrollHandler::sInstance = nsnull; +bool MouseScrollHandler::Device::sFakeScrollableWindowNeeded = false; + +bool MouseScrollHandler::Device::Elantech::sUseSwipeHack = false; +bool MouseScrollHandler::Device::Elantech::sUsePinchHack = false; +DWORD MouseScrollHandler::Device::Elantech::sZoomUntil = 0; /****************************************************************************** * @@ -45,6 +80,7 @@ MouseScrollHandler::Initialize() gMouseScrollLog = PR_NewLogModule("MouseScrollHandlerWidgets"); } #endif + Device::Init(); } /* static */ @@ -85,6 +121,8 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *aRetValue, bool &aEatMessage) { + Device::Elantech::UpdateZoomUntil(); + switch (msg) { case WM_SETTINGCHANGE: if (!sInstance) { @@ -95,11 +133,35 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, sInstance->mSystemSettings.MarkDirty(); } return false; + + case WM_KEYDOWN: + case WM_KEYUP: + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessMessage(): aWindow=%p, " + "msg=%s(0x%04X), wParam=0x%02X, ::GetMessageTime()=%d", + aWindow, msg == WM_KEYDOWN ? "WM_KEYDOWN" : + msg == WM_KEYUP ? "WM_KEYUP" : "Unknown", msg, wParam, + ::GetMessageTime())); + LOG_KEYSTATE(); + if (Device::Elantech::HandleKeyMessage(aWindow, msg, wParam)) { + *aRetValue = 0; + aEatMessage = true; + return true; + } + return false; + default: return false; } } +/* static */ +bool +MouseScrollHandler::DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent) +{ + return aWindow->DispatchWindowEvent(&aEvent); +} + /****************************************************************************** * * SystemSettings @@ -220,5 +282,362 @@ MouseScrollHandler::UserPrefs::MarkDirty() mInitialized = false; } +/****************************************************************************** + * + * Device + * + ******************************************************************************/ + +/* static */ +bool +MouseScrollHandler::Device::GetWorkaroundPref(const char* aPrefName, + bool aValueIfAutomatic) +{ + if (!aPrefName) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::GetWorkaroundPref(): Failed, aPrefName is NULL")); + return aValueIfAutomatic; + } + + PRInt32 lHackValue = 0; + if (NS_FAILED(Preferences::GetInt(aPrefName, &lHackValue))) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::GetWorkaroundPref(): Preferences::GetInt() failed," + " aPrefName=\"%s\", aValueIfAutomatic=%s", + aPrefName, GetBoolName(aValueIfAutomatic))); + return aValueIfAutomatic; + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::GetWorkaroundPref(): Succeeded, " + "aPrefName=\"%s\", aValueIfAutomatic=%s, lHackValue=%d", + aPrefName, GetBoolName(aValueIfAutomatic), lHackValue)); + + switch (lHackValue) { + case 0: // disabled + return false; + case 1: // enabled + return true; + default: // -1: autodetect + return aValueIfAutomatic; + } +} + +/* static */ +void +MouseScrollHandler::Device::Init() +{ + sFakeScrollableWindowNeeded = + GetWorkaroundPref("ui.trackpoint_hack.enabled", + (TrackPoint::IsDriverInstalled() || + UltraNav::IsObsoleteDriverInstalled())); + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Init(): sFakeScrollableWindowNeeded=%s", + GetBoolName(sFakeScrollableWindowNeeded))); + + Elantech::Init(); +} + +/****************************************************************************** + * + * Device::Elantech + * + ******************************************************************************/ + +/* static */ +void +MouseScrollHandler::Device::Elantech::Init() +{ + PRInt32 version = GetDriverMajorVersion(); + bool needsHack = + Device::GetWorkaroundPref("ui.elantech_gesture_hacks.enabled", + version != 0); + sUseSwipeHack = needsHack && version <= 7; + sUsePinchHack = needsHack && version <= 8; + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Elantech::Init(): version=%d, sUseSwipeHack=%s, " + "sUsePinchHack=%s", + version, GetBoolName(sUseSwipeHack), GetBoolName(sUsePinchHack))); +} + +/* static */ +PRInt32 +MouseScrollHandler::Device::Elantech::GetDriverMajorVersion() +{ + PRUnichar buf[40]; + // The driver version is found in one of these two registry keys. + bool foundKey = + WinUtils::GetRegistryKey(HKEY_CURRENT_USER, + L"Software\\Elantech\\MainOption", + L"DriverVersion", + buf, sizeof buf); + if (!foundKey) { + foundKey = + WinUtils::GetRegistryKey(HKEY_CURRENT_USER, + L"Software\\Elantech", + L"DriverVersion", + buf, sizeof buf); + } + + if (!foundKey) { + return 0; + } + + // Assume that the major version number can be found just after a space + // or at the start of the string. + for (PRUnichar* p = buf; *p; p++) { + if (*p >= L'0' && *p <= L'9' && (p == buf || *(p - 1) == L' ')) { + return wcstol(p, NULL, 10); + } + } + + return 0; +} + +/* static */ +bool +MouseScrollHandler::Device::Elantech::IsHelperWindow(HWND aWnd) +{ + // The helper window cannot be distinguished based on its window class, so we + // need to check if it is owned by the helper process, ETDCtrl.exe. + + const PRUnichar* filenameSuffix = L"\\etdctrl.exe"; + const int filenameSuffixLength = 12; + + DWORD pid; + ::GetWindowThreadProcessId(aWnd, &pid); + + HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!hProcess) { + return false; + } + + bool result = false; + PRUnichar path[256] = {L'\0'}; + if (::GetProcessImageFileNameW(hProcess, path, ArrayLength(path))) { + int pathLength = lstrlenW(path); + if (pathLength >= filenameSuffixLength) { + if (lstrcmpiW(path + pathLength - filenameSuffixLength, + filenameSuffix) == 0) { + result = true; + } + } + } + ::CloseHandle(hProcess); + + return result; +} + +/* static */ +bool +MouseScrollHandler::Device::Elantech::HandleKeyMessage(nsWindow* aWindow, + UINT aMsg, + WPARAM aWParam) +{ + // The Elantech touchpad driver understands three-finger swipe left and + // right gestures, and translates them into Page Up and Page Down key + // events for most applications. For Firefox 3.6, it instead sends + // Alt+Left and Alt+Right to trigger browser back/forward actions. As + // with the Thinkpad Driver hack in nsWindow::Create, the change in + // HWND structure makes Firefox not trigger the driver's heuristics + // any longer. + // + // The Elantech driver actually sends these messages for a three-finger + // swipe right: + // + // WM_KEYDOWN virtual_key = 0xCC or 0xFF (depending on driver version) + // WM_KEYDOWN virtual_key = VK_NEXT + // WM_KEYUP virtual_key = VK_NEXT + // WM_KEYUP virtual_key = 0xCC or 0xFF + // + // so we use the 0xCC or 0xFF key modifier to detect whether the Page Down + // is due to the gesture rather than a regular Page Down keypress. We then + // pretend that we should dispatch "Go Forward" command. Similarly + // for VK_PRIOR and "Go Back" command. + if (sUseSwipeHack && + (aWParam == VK_NEXT || aWParam == VK_PRIOR) && + (IS_VK_DOWN(0xFF) || IS_VK_DOWN(0xCC))) { + if (aMsg == WM_KEYDOWN) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Elantech::HandleKeyMessage(): Dispatching " + "%s command event", + aWParam == VK_NEXT ? "Forward" : "Back")); + + nsCommandEvent commandEvent(true, nsGkAtoms::onAppCommand, + (aWParam == VK_NEXT) ? nsGkAtoms::Forward : nsGkAtoms::Back, aWindow); + aWindow->InitEvent(commandEvent); + MouseScrollHandler::DispatchEvent(aWindow, commandEvent); + } +#ifdef PR_LOGGING + else { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Elantech::HandleKeyMessage(): Consumed")); + } +#endif + return true; // consume the message (doesn't need to dispatch key events) + } + + // Version 8 of the Elantech touchpad driver sends these messages for + // zoom gestures: + // + // WM_KEYDOWN virtual_key = 0xCC time = 10 + // WM_KEYDOWN virtual_key = VK_CONTROL time = 10 + // WM_MOUSEWHEEL time = ::GetTickCount() + // WM_KEYUP virtual_key = VK_CONTROL time = 10 + // WM_KEYUP virtual_key = 0xCC time = 10 + // + // The result of this is that we process all of the WM_KEYDOWN/WM_KEYUP + // messages first because their timestamps make them appear to have + // been sent before the WM_MOUSEWHEEL message. To work around this, + // we store the current time when we process the WM_KEYUP message and + // assume that any WM_MOUSEWHEEL message with a timestamp before that + // time is one that should be processed as if the Control key was down. + if (sUsePinchHack && aMsg == WM_KEYUP && + aWParam == VK_CONTROL && ::GetMessageTime() == 10) { + // We look only at the bottom 31 bits of the system tick count since + // GetMessageTime returns a LONG, which is signed, so we want values + // that are more easily comparable. + sZoomUntil = ::GetTickCount() & 0x7FFFFFFF; + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Elantech::HandleKeyMessage(): sZoomUntil=%d", + sZoomUntil)); + } + + return false; +} + +/* static */ +void +MouseScrollHandler::Device::Elantech::UpdateZoomUntil() +{ + if (!sZoomUntil) { + return; + } + + // For the Elantech Touchpad Zoom Gesture Hack, we should check that the + // system time (32-bit milliseconds) hasn't wrapped around. Otherwise we + // might get into the situation where wheel events for the next 50 days of + // system uptime are assumed to be Ctrl+Wheel events. (It is unlikely that + // we would get into that state, because the system would already need to be + // up for 50 days and the Control key message would need to be processed just + // before the system time overflow and the wheel message just after.) + // + // We also take the chance to reset sZoomUntil if we simply have passed that + // time. + LONG msgTime = ::GetMessageTime(); + if ((sZoomUntil >= 0x3fffffffu && DWORD(msgTime) < 0x40000000u) || + (sZoomUntil < DWORD(msgTime))) { + sZoomUntil = 0; + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::Elantech::UpdateZoomUntil(): " + "sZoomUntil was reset")); + } +} + +/* static */ +bool +MouseScrollHandler::Device::Elantech::IsZooming() +{ + // Assume the Control key is down if the Elantech touchpad has sent the + // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in + // OnKeyUp.) + return (sZoomUntil && static_cast(::GetMessageTime()) < sZoomUntil); +} + +/****************************************************************************** + * + * Device::TrackPoint + * + ******************************************************************************/ + +/* static */ +bool +MouseScrollHandler::Device::TrackPoint::IsDriverInstalled() +{ + if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Lenovo\\TrackPoint")) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::TrackPoint::IsDriverInstalled(): " + "Lenovo's TrackPoint driver is found")); + return true; + } + + if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Alps\\Apoint\\TrackPoint")) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::TrackPoint::IsDriverInstalled(): " + "Alps's TrackPoint driver is found")); + } + + return false; +} + +/****************************************************************************** + * + * Device::UltraNav + * + ******************************************************************************/ + +/* static */ +bool +MouseScrollHandler::Device::UltraNav::IsObsoleteDriverInstalled() +{ + if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Lenovo\\UltraNav")) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " + "Lenovo's UltraNav driver is found")); + return true; + } + + bool installed = false; + if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB")) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " + "Synaptics's UltraNav (USB) driver is found")); + installed = true; + } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, + L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2")) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " + "Synaptics's UltraNav (PS/2) driver is found")); + installed = true; + } + + if (!installed) { + return false; + } + + PRUnichar buf[40]; + bool foundKey = + WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, + L"Software\\Synaptics\\SynTP\\Install", + L"DriverVersion", + buf, sizeof buf); + if (!foundKey) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " + "Failed to get UltraNav driver version")); + return false; + } + + int majorVersion = wcstol(buf, NULL, 10); + int minorVersion = 0; + PRUnichar* p = wcschr(buf, L'.'); + if (p) { + minorVersion = wcstol(p + 1, NULL, 10); + } + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " + "found driver version = %d.%d", + majorVersion, minorVersion)); + return majorVersion < 15 || majorVersion == 15 && minorVersion == 0; +} + } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 9ba4192bacf9..365ffc697191 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -13,6 +13,7 @@ #include class nsWindow; +class nsGUIEvent; namespace mozilla { namespace widget { @@ -37,6 +38,12 @@ private: static MouseScrollHandler* sInstance; + /** + * DispatchEvent() dispatches aEvent on aWindow. + * + * @return TRUE if the event was consumed. Otherwise, FALSE. + */ + static bool DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent); public: class SystemSettings { public: @@ -113,6 +120,91 @@ public: private: UserPrefs mUserPrefs; + +public: + + class Device { + public: + class Elantech { + public: + /** + * GetDriverMajorVersion() returns the installed driver's major version. + * If Elantech's driver was installed, returns 0. + */ + static PRInt32 GetDriverMajorVersion(); + + /** + * IsHelperWindow() checks whether aWnd is a helper window of Elantech's + * touchpad. Returns TRUE if so. Otherwise, FALSE. + */ + static bool IsHelperWindow(HWND aWnd); + + /** + * Key message handler for Elantech's hack. Returns TRUE if the message + * is consumed by this handler. Otherwise, FALSE. + */ + static bool HandleKeyMessage(nsWindow* aWindow, + UINT aMsg, + WPARAM aWParam); + + static void UpdateZoomUntil(); + static bool IsZooming(); + + static void Init(); + + static bool IsPinchHackNeeded() { return sUsePinchHack; } + + + private: + // Whether to enable the Elantech swipe gesture hack. + static bool sUseSwipeHack; + // Whether to enable the Elantech pinch-to-zoom gesture hack. + static bool sUsePinchHack; + static DWORD sZoomUntil; + }; // class Elantech + + class TrackPoint { + public: + /** + * IsDriverInstalled() returns TRUE if TrackPoint's driver is installed. + * Otherwise, returns FALSE. + */ + static bool IsDriverInstalled(); + }; // class TrackPoint + + class UltraNav { + public: + /** + * IsObsoleteDriverInstalled() checks whether obsoleted UltraNav + * is installed on the environment. + * Returns TRUE if it was installed. Otherwise, FALSE. + */ + static bool IsObsoleteDriverInstalled(); + }; // class UltraNav + + static void Init(); + + static bool IsFakeScrollableWindowNeeded() + { + return sFakeScrollableWindowNeeded; + } + + private: + /** + * Gets the bool value of aPrefName used to enable or disable an input + * workaround (like the Trackpoint hack). The pref can take values 0 (for + * disabled), 1 (for enabled) or -1 (to automatically detect whether to + * enable the workaround). + * + * @param aPrefName The name of the pref. + * @param aValueIfAutomatic Whether the given input workaround should be + * enabled by default. + */ + static bool GetWorkaroundPref(const char* aPrefName, + bool aValueIfAutomatic); + + static bool sFakeScrollableWindowNeeded; + }; // class Device }; } // namespace widget diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 12a6e56b1452..7f84ab84183a 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -271,14 +271,8 @@ BYTE nsWindow::sLastMouseButton = 0; // Trim heap on minimize. (initialized, but still true.) int nsWindow::sTrimOnMinimize = 2; -// Default value for Trackpoint hack (used when the pref is set to -1). -bool nsWindow::sDefaultTrackPointHack = false; // Default value for general window class (used when the pref is the empty string). const char* nsWindow::sDefaultMainWindowClass = kClassNameGeneral; -// Whether to enable the Elantech swipe gesture hack. -bool nsWindow::sUseElantechSwipeHack = false; -// Whether to enable the Elantech pinch-to-zoom gesture hack. -bool nsWindow::sUseElantechPinchHack = false; // If we're using D3D9, this will not be allowed during initial 5 seconds. bool nsWindow::sAllowD3D9 = false; @@ -404,7 +398,6 @@ nsWindow::nsWindow() : nsBaseWidget() mOldExStyle = 0; mPainting = 0; mLastKeyboardLayout = 0; - mAssumeWheelIsZoomUntil = 0; mBlurSuppressLevel = 0; mLastPaintEndTime = TimeStamp::Now(); #ifdef MOZ_XUL @@ -435,7 +428,6 @@ nsWindow::nsWindow() : nsBaseWidget() sIsOleInitialized = TRUE; } NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n"); - InitInputWorkaroundPrefDefaults(); MouseScrollHandler::Initialize(); // Init titlebar button info for custom frames. nsUXThemeData::InitTitlebarInfo(); @@ -601,7 +593,7 @@ nsWindow::Create(nsIWidget *aParent, if (mWindowType != eWindowType_plugin && mWindowType != eWindowType_invisible && - UseTrackPointHack()) { + MouseScrollHandler::Device::IsFakeScrollableWindowNeeded()) { // Ugly Thinkpad Driver Hack (Bugs 507222 and 594977) // // We create two zero-sized windows as descendants of the top-level window, @@ -4487,24 +4479,6 @@ static bool CleartypeSettingChanged() bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, LRESULT *aRetValue) { - // For the Elantech Touchpad Zoom Gesture Hack, we should check that the - // system time (32-bit milliseconds) hasn't wrapped around. Otherwise we - // might get into the situation where wheel events for the next 50 days of - // system uptime are assumed to be Ctrl+Wheel events. (It is unlikely that - // we would get into that state, because the system would already need to be - // up for 50 days and the Control key message would need to be processed just - // before the system time overflow and the wheel message just after.) - // - // We also take the chance to reset mAssumeWheelIsZoomUntil if we simply have - // passed that time. - if (mAssumeWheelIsZoomUntil) { - LONG msgTime = ::GetMessageTime(); - if ((mAssumeWheelIsZoomUntil >= 0x3fffffffu && DWORD(msgTime) < 0x40000000u) || - (mAssumeWheelIsZoomUntil < DWORD(msgTime))) { - mAssumeWheelIsZoomUntil = 0; - } - } - // (Large blocks of code should be broken out into OnEvent handlers.) if (mWindowHook.Notify(mWnd, msg, wParam, lParam, aRetValue)) return true; @@ -6388,13 +6362,10 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, // Assume the Control key is down if the Elantech touchpad has sent the // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in - // OnKeyUp.) - bool isControl; - if (mAssumeWheelIsZoomUntil && - static_cast(::GetMessageTime()) < mAssumeWheelIsZoomUntil) { - isControl = true; - } else { - isControl = modKeyState.mIsControlDown; + // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) + if (!modKeyState.mIsControlDown) { + modKeyState.mIsControlDown = + MouseScrollHandler::Device::Elantech::IsZooming(); } // Create line (or page) scroll event. @@ -6404,7 +6375,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, // test event. InitEvent(scrollEvent); scrollEvent.isShift = modKeyState.mIsShiftDown; - scrollEvent.isControl = isControl; + scrollEvent.isControl = modKeyState.mIsControlDown; scrollEvent.isMeta = false; scrollEvent.isAlt = modKeyState.mIsAltDown; @@ -6578,37 +6549,6 @@ bool nsWindow::IsRedirectedKeyDownMessage(const MSG &aMsg) WinUtils::GetScanCode(aMsg.lParam)); } -void -nsWindow::PerformElantechSwipeGestureHack(UINT& aVirtualKeyCode, - nsModifierKeyState& aModKeyState) -{ - // The Elantech touchpad driver understands three-finger swipe left and - // right gestures, and translates them into Page Up and Page Down key - // events for most applications. For Firefox 3.6, it instead sends - // Alt+Left and Alt+Right to trigger browser back/forward actions. As - // with the Thinkpad Driver hack in nsWindow::Create, the change in - // HWND structure makes Firefox not trigger the driver's heuristics - // any longer. - // - // The Elantech driver actually sends these messages for a three-finger - // swipe right: - // - // WM_KEYDOWN virtual_key = 0xCC or 0xFF (depending on driver version) - // WM_KEYDOWN virtual_key = VK_NEXT - // WM_KEYUP virtual_key = VK_NEXT - // WM_KEYUP virtual_key = 0xCC or 0xFF - // - // so we use the 0xCC or 0xFF key modifier to detect whether the Page Down - // is due to the gesture rather than a regular Page Down keypress. We then - // pretend that we were went an Alt+Right keystroke instead. Similarly - // for VK_PRIOR and Alt+Left. - if ((aVirtualKeyCode == VK_NEXT || aVirtualKeyCode == VK_PRIOR) && - (IS_VK_DOWN(0xFF) || IS_VK_DOWN(0xCC))) { - aModKeyState.mIsAltDown = true; - aVirtualKeyCode = aVirtualKeyCode == VK_NEXT ? VK_RIGHT : VK_LEFT; - } -} - /** * nsWindow::OnKeyDown peeks into the message queue and pulls out * WM_CHAR messages for processing. During testing we don't want to @@ -6626,10 +6566,6 @@ LRESULT nsWindow::OnKeyDown(const MSG &aMsg, aMsg.wParam != VK_PROCESSKEY ? aMsg.wParam : ::ImmGetVirtualKey(mWnd); gKbdLayout.OnKeyDown(virtualKeyCode); - if (sUseElantechSwipeHack) { - PerformElantechSwipeGestureHack(virtualKeyCode, aModKeyState); - } - // Use only DOMKeyCode for XP processing. // Use virtualKeyCode for gKbdLayout and native processing. UINT DOMKeyCode = nsIMM32Handler::IsComposingOn(this) ? @@ -6971,34 +6907,6 @@ LRESULT nsWindow::OnKeyUp(const MSG &aMsg, { UINT virtualKeyCode = aMsg.wParam; - if (sUseElantechSwipeHack) { - PerformElantechSwipeGestureHack(virtualKeyCode, aModKeyState); - } - - if (sUseElantechPinchHack) { - // Version 8 of the Elantech touchpad driver sends these messages for - // zoom gestures: - // - // WM_KEYDOWN virtual_key = 0xCC time = 10 - // WM_KEYDOWN virtual_key = VK_CONTROL time = 10 - // WM_MOUSEWHEEL time = ::GetTickCount() - // WM_KEYUP virtual_key = VK_CONTROL time = 10 - // WM_KEYUP virtual_key = 0xCC time = 10 - // - // The result of this is that we process all of the WM_KEYDOWN/WM_KEYUP - // messages first because their timestamps make them appear to have - // been sent before the WM_MOUSEWHEEL message. To work around this, - // we store the current time when we process the WM_KEYUP message and - // assume that any WM_MOUSEWHEEL message with a timestamp before that - // time is one that should be processed as if the Control key was down. - if (virtualKeyCode == VK_CONTROL && aMsg.time == 10) { - // We look only at the bottom 31 bits of the system tick count since - // GetMessageTime returns a LONG, which is signed, so we want values - // that are more easily comparable. - mAssumeWheelIsZoomUntil = ::GetTickCount() & 0x7FFFFFFF; - } - } - PR_LOG(gWindowsLog, PR_LOG_ALWAYS, ("nsWindow::OnKeyUp VK=%d\n", virtualKeyCode)); @@ -7405,37 +7313,6 @@ bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam) return true; } -// Determine whether the given HWND is the handle for the Elantech helper -// window. The helper window cannot be distinguished based on its -// window class, so we need to check if it is owned by the helper process, -// ETDCtrl.exe. -static bool IsElantechHelperWindow(HWND aHWND) -{ - const PRUnichar* filenameSuffix = L"\\etdctrl.exe"; - const int filenameSuffixLength = 12; - - DWORD pid; - ::GetWindowThreadProcessId(aHWND, &pid); - - bool result = false; - - HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); - if (hProcess) { - PRUnichar path[256] = {L'\0'}; - if (GetProcessImageFileName(hProcess, path, ArrayLength(path))) { - int pathLength = lstrlenW(path); - if (pathLength >= filenameSuffixLength) { - if (lstrcmpiW(path + pathLength - filenameSuffixLength, filenameSuffix) == 0) { - result = true; - } - } - } - ::CloseHandle(hProcess); - } - - return result; -} - /** * OnMouseWheel() is called when ProcessMessage() handles WM_MOUSEWHEEL, * WM_MOUSEHWHEEL and also OnScroll() tries to emulate mouse wheel action for @@ -7484,7 +7361,8 @@ nsWindow::OnMouseWheel(UINT aMsg, WPARAM aWParam, LPARAM aLParam, return; } - if (sUseElantechPinchHack && IsElantechHelperWindow(underCursorWnd)) { + if (MouseScrollHandler::Device::Elantech::IsPinchHackNeeded() && + MouseScrollHandler::Device::Elantech::IsHelperWindow(underCursorWnd)) { // The Elantech driver places a window right underneath the cursor // when sending a WM_MOUSEWHEEL event to us as part of a pinch-to-zoom // gesture. We detect that here, and search for our window that would @@ -8625,123 +8503,6 @@ void nsWindow::GetMainWindowClass(nsAString& aClass) } } -/** - * Gets the Boolean value of a pref used to enable or disable an input - * workaround (like the Trackpoint hack). The pref can take values 0 (for - * disabled), 1 (for enabled) or -1 (to automatically detect whether to - * enable the workaround). - * - * @param aPrefName The name of the pref. - * @param aValueIfAutomatic Whether the given input workaround should be - * enabled by default. - */ -bool nsWindow::GetInputWorkaroundPref(const char* aPrefName, - bool aValueIfAutomatic) -{ - if (!aPrefName) { - return aValueIfAutomatic; - } - - PRInt32 lHackValue = 0; - if (NS_SUCCEEDED(Preferences::GetInt(aPrefName, &lHackValue))) { - switch (lHackValue) { - case 0: // disabled - return false; - case 1: // enabled - return true; - default: // -1: autodetect - break; - } - } - return aValueIfAutomatic; -} - -bool nsWindow::UseTrackPointHack() -{ - return GetInputWorkaroundPref("ui.trackpoint_hack.enabled", - sDefaultTrackPointHack); -} - -static bool -IsObsoleteSynapticsDriver() -{ - PRUnichar buf[40]; - bool foundKey = WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, - L"Software\\Synaptics\\SynTP\\Install", - L"DriverVersion", - buf, - sizeof buf); - if (!foundKey) - return false; - - int majorVersion = wcstol(buf, NULL, 10); - int minorVersion = 0; - PRUnichar* p = wcschr(buf, L'.'); - if (p) { - minorVersion = wcstol(p + 1, NULL, 10); - } - return majorVersion < 15 || majorVersion == 15 && minorVersion == 0; -} - -static PRInt32 -GetElantechDriverMajorVersion() -{ - PRUnichar buf[40]; - // The driver version is found in one of these two registry keys. - bool foundKey = WinUtils::GetRegistryKey(HKEY_CURRENT_USER, - L"Software\\Elantech\\MainOption", - L"DriverVersion", - buf, - sizeof buf); - if (!foundKey) - foundKey = WinUtils::GetRegistryKey(HKEY_CURRENT_USER, - L"Software\\Elantech", - L"DriverVersion", - buf, - sizeof buf); - - if (!foundKey) - return false; - - // Assume that the major version number can be found just after a space - // or at the start of the string. - for (PRUnichar* p = buf; *p; p++) { - if (*p >= L'0' && *p <= L'9' && (p == buf || *(p - 1) == L' ')) { - return wcstol(p, NULL, 10); - } - } - - return 0; -} - -void nsWindow::InitInputWorkaroundPrefDefaults() -{ - PRUint32 elantechDriverVersion = GetElantechDriverMajorVersion(); - - if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, - L"Software\\Lenovo\\TrackPoint")) { - sDefaultTrackPointHack = true; - } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, - L"Software\\Lenovo\\UltraNav")) { - sDefaultTrackPointHack = true; - } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, - L"Software\\Alps\\Apoint\\TrackPoint")) { - sDefaultTrackPointHack = true; - } else if ((WinUtils::HasRegistryKey(HKEY_CURRENT_USER, - L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB") || - WinUtils::HasRegistryKey(HKEY_CURRENT_USER, - L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2")) && - IsObsoleteSynapticsDriver()) { - sDefaultTrackPointHack = true; - } - - bool useElantechGestureHacks = - GetInputWorkaroundPref("ui.elantech_gesture_hacks.enabled", - elantechDriverVersion != 0); - sUseElantechSwipeHack = useElantechGestureHacks && elantechDriverVersion <= 7; - sUseElantechPinchHack = useElantechGestureHacks && elantechDriverVersion <= 8; -} - LPARAM nsWindow::lParamToScreen(LPARAM lParam) { POINT pt; diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 3d1ac1e7a24c..fe446cbc775a 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -334,10 +334,6 @@ protected: void ResetLayout(); void InvalidateNonClientRegion(); HRGN ExcludeNonClientFromPaintRegion(HRGN aRegion); - static void InitInputWorkaroundPrefDefaults(); - static bool GetInputWorkaroundPref(const char* aPrefName, bool aValueIfAutomatic); - static bool UseTrackPointHack(); - static void PerformElantechSwipeGestureHack(UINT& aVirtualKeyCode, nsModifierKeyState& aModKeyState); static void GetMainWindowClass(nsAString& aClass); bool HasGlass() const { return mTransparencyMode == eTransparencyGlass || @@ -522,10 +518,7 @@ protected: static bool sJustGotActivate; static bool sIsInMouseCapture; static int sTrimOnMinimize; - static bool sDefaultTrackPointHack; static const char* sDefaultMainWindowClass; - static bool sUseElantechSwipeHack; - static bool sUseElantechPinchHack; static bool sAllowD3D9; // Always use the helper method to read this property. See bug 603793. From 560a08c9957ed056ebb885635e6eb051da3c1bc8 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 12/88] Bug 672175 part.6 Summarize native mouse wheel events by MouseScrollHandler::EventInfo r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 52 ++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 43 +++++++++++++ widget/windows/nsWindow.cpp | 78 ++++++++---------------- 3 files changed, 122 insertions(+), 51 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index be3d9ebc5784..bc74eb1645d8 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -162,6 +162,58 @@ MouseScrollHandler::DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent) return aWindow->DispatchWindowEvent(&aEvent); } +/****************************************************************************** + * + * EventInfo + * + ******************************************************************************/ + +MouseScrollHandler::EventInfo::EventInfo(UINT aMessage, + WPARAM aWParam, LPARAM aLParam) +{ + NS_ABORT_IF_FALSE(aMessage == WM_MOUSEWHEEL || aMessage == WM_MOUSEHWHEEL, + "EventInfo must be initialized with WM_MOUSEWHEEL or WM_MOUSEHWHEEL"); + + MouseScrollHandler::GetInstance()->mSystemSettings.Init(); + + mIsVertical = (aMessage == WM_MOUSEWHEEL); + mIsPage = MouseScrollHandler::sInstance-> + mSystemSettings.IsPageScroll(mIsVertical); + mDelta = (short)HIWORD(aWParam); +} + +bool +MouseScrollHandler::EventInfo::CanDispatchMouseScrollEvent() const +{ + if (!GetScrollAmount()) { + // XXX I think that we should dispatch mouse wheel events even if the + // operation will not scroll because the wheel operation really happened + // and web application may want to handle the event for non-scroll action. + return false; + } + + return (mDelta != 0); +} + +PRInt32 +MouseScrollHandler::EventInfo::GetScrollAmount() const +{ + if (mIsPage) { + return 1; + } + return MouseScrollHandler::sInstance-> + mSystemSettings.GetScrollAmount(mIsVertical); +} + +PRInt32 +MouseScrollHandler::EventInfo::GetScrollFlags() const +{ + PRInt32 result = mIsPage ? nsMouseScrollEvent::kIsFullPage : 0; + result |= mIsVertical ? nsMouseScrollEvent::kIsVertical : + nsMouseScrollEvent::kIsHorizontal; + return result; +} + /****************************************************************************** * * SystemSettings diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 365ffc697191..41bcde0ae388 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -44,6 +44,49 @@ private: * @return TRUE if the event was consumed. Otherwise, FALSE. */ static bool DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent); + +public: + class EventInfo { + public: + /** + * @param aMessage Must be WM_MOUSEWHEEL or WM_MOUSEHWHEEL. + */ + EventInfo(UINT aMessage, WPARAM aWParam, LPARAM aLParam); + + bool CanDispatchMouseScrollEvent() const; + + PRInt32 GetNativeDelta() const { return mDelta; } + bool IsVertical() const { return mIsVertical; } + bool IsPositive() const { return (mDelta > 0); } + bool IsPage() const { return mIsPage; } + + LRESULT ComputeMessageResult(bool aWeProcessed) const + { + return IsVertical() ? !aWeProcessed : aWeProcessed; + } + + /** + * @return Number of lines or pages scrolled per WHEEL_DELTA. + */ + PRInt32 GetScrollAmount() const; + + /** + * @return One or more values of + * nsMouseScrollEvent::nsMouseScrollFlags. + */ + PRInt32 GetScrollFlags() const; + + private: + EventInfo() {} + + // TRUE if event is for vertical scroll. Otherwise, FALSE. + bool mIsVertical; + // TRUE if event scrolls per page, otherwise, FALSE. + bool mIsPage; + // The native delta value. + PRInt32 mDelta; + }; + public: class SystemSettings { public: diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 7f84ab84183a..34e8508e68a2 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6311,54 +6311,41 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, LRESULT *aRetValue) { InitMouseWheelScrollData(); - MouseScrollHandler::SystemSettings& systemSettings = - MouseScrollHandler::GetInstance()->GetSystemSettings(); - systemSettings.Init(); - bool isVertical = (aMessage == WM_MOUSEWHEEL); - if (systemSettings.GetScrollAmount(isVertical) == 0) { - // XXX I think that we should dispatch mouse wheel events even if the - // operation will not scroll because the wheel operation really happened - // and web application may want to handle the event for non-scroll action. + MouseScrollHandler::EventInfo eventInfo(aMessage, aWParam, aLParam); + if (!eventInfo.CanDispatchMouseScrollEvent()) { ResetRemainingWheelDelta(); - *aRetValue = isVertical ? TRUE : FALSE; // means we don't process it + *aRetValue = eventInfo.ComputeMessageResult(false); return; } - PRInt32 nativeDelta = (short)HIWORD(aWParam); - if (!nativeDelta) { - *aRetValue = isVertical ? TRUE : FALSE; // means we don't process it - ResetRemainingWheelDelta(); - return; // We cannot process this message - } - - bool isPageScroll = systemSettings.IsPageScroll(isVertical); - // Discard the remaining delta if current wheel message and last one are // received by different window or to scroll different direction or // different unit scroll. Furthermore, if the last event was too old. PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow()); if (sLastMouseWheelWnd && (sLastMouseWheelWnd != mWnd || - sLastMouseWheelDeltaIsPositive != (nativeDelta > 0) || - sLastMouseWheelOrientationIsVertical != isVertical || - sLastMouseWheelUnitIsPage != isPageScroll || + sLastMouseWheelDeltaIsPositive != eventInfo.IsPositive() || + sLastMouseWheelOrientationIsVertical != eventInfo.IsVertical() || + sLastMouseWheelUnitIsPage != eventInfo.IsPage() || now - sLastMouseWheelTime > 1500)) { ResetRemainingWheelDelta(); } sLastMouseWheelWnd = mWnd; - sLastMouseWheelDeltaIsPositive = (nativeDelta > 0); - sLastMouseWheelOrientationIsVertical = isVertical; - sLastMouseWheelUnitIsPage = isPageScroll; + sLastMouseWheelDeltaIsPositive = eventInfo.IsPositive(); + sLastMouseWheelOrientationIsVertical = eventInfo.IsVertical(); + sLastMouseWheelUnitIsPage = eventInfo.IsPage(); sLastMouseWheelTime = now; - *aRetValue = isVertical ? FALSE : TRUE; // means we process this message + // means we process this message + *aRetValue = eventInfo.ComputeMessageResult(true); + nsModifierKeyState modKeyState; // Our positive delta value means to bottom or right. - // But positive nativeDelta value means to top or right. + // But positive native delta value means to top or right. // Use orienter for computing our delta value with native delta value. - PRInt32 orienter = isVertical ? -1 : 1; + PRInt32 orienter = eventInfo.IsVertical() ? -1 : 1; // Assume the Control key is down if the Elantech touchpad has sent the // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in @@ -6386,24 +6373,21 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, PRInt32 actualScrollAction = nsQueryContentEvent::SCROLL_ACTION_NONE; PRInt32 pixelsPerUnit = 0; // the amount is the number of lines (or pages) per WHEEL_DELTA - PRInt32 computedScrollAmount = - isPageScroll ? 1 : systemSettings.GetScrollAmount(isVertical); + PRInt32 computedScrollAmount = eventInfo.GetScrollAmount(); if (MouseScrollHandler::GetInstance()-> GetUserPrefs().IsPixelScrollingEnabled()) { nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, this); InitEvent(testEvent); - testEvent.scrollFlags = isPageScroll ? nsMouseScrollEvent::kIsFullPage : 0; - testEvent.scrollFlags |= isVertical ? nsMouseScrollEvent::kIsVertical : - nsMouseScrollEvent::kIsHorizontal; + testEvent.scrollFlags = eventInfo.GetScrollFlags(); testEvent.isShift = scrollEvent.isShift; testEvent.isControl = scrollEvent.isControl; testEvent.isMeta = scrollEvent.isMeta; testEvent.isAlt = scrollEvent.isAlt; testEvent.delta = computedScrollAmount; - if ((isVertical && sLastMouseWheelDeltaIsPositive) || - (!isVertical && !sLastMouseWheelDeltaIsPositive)) { + if ((eventInfo.IsVertical() && sLastMouseWheelDeltaIsPositive) || + (!eventInfo.IsVertical() && !sLastMouseWheelDeltaIsPositive)) { testEvent.delta *= -1; } nsQueryContentEvent queryEvent(true, NS_QUERY_SCROLL_TARGET_INFO, this); @@ -6415,7 +6399,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, if (queryEvent.mSucceeded) { actualScrollAction = queryEvent.mReply.mComputedScrollAction; if (actualScrollAction == nsQueryContentEvent::SCROLL_ACTION_PAGE) { - if (isVertical) { + if (eventInfo.IsVertical()) { pixelsPerUnit = queryEvent.mReply.mPageHeight; } else { pixelsPerUnit = queryEvent.mReply.mPageWidth; @@ -6442,30 +6426,21 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, // we should set kHasPixels flag to the line scroll event. scrollEvent.scrollFlags = dispatchPixelScrollEvent ? nsMouseScrollEvent::kHasPixels : 0; + scrollEvent.scrollFlags |= eventInfo.GetScrollFlags(); - PRInt32 nativeDeltaForScroll = nativeDelta + sRemainingDeltaForScroll; + PRInt32 nativeDeltaForScroll = + eventInfo.GetNativeDelta() + sRemainingDeltaForScroll; // NOTE: Don't use computedScrollAmount for computing the delta value of // line/page scroll event. The value will be recomputed in ESM. - if (isPageScroll) { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; - if (isVertical) { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsVertical; - } else { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsHorizontal; - } + if (eventInfo.IsPage()) { scrollEvent.delta = nativeDeltaForScroll * orienter / WHEEL_DELTA; PRInt32 recomputedNativeDelta = scrollEvent.delta * orienter / WHEEL_DELTA; sRemainingDeltaForScroll = nativeDeltaForScroll - recomputedNativeDelta; } else { double deltaPerUnit; - if (isVertical) { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsVertical; - } else { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsHorizontal; - } deltaPerUnit = - (double)WHEEL_DELTA / systemSettings.GetScrollAmount(isVertical); + (double)WHEEL_DELTA / eventInfo.GetScrollAmount(); scrollEvent.delta = RoundDelta((double)nativeDeltaForScroll * orienter / deltaPerUnit); PRInt32 recomputedNativeDelta = @@ -6490,7 +6465,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, nsMouseScrollEvent pixelEvent(true, NS_MOUSE_PIXEL_SCROLL, this); InitEvent(pixelEvent); pixelEvent.scrollFlags = nsMouseScrollEvent::kAllowSmoothScroll; - pixelEvent.scrollFlags |= isVertical ? + pixelEvent.scrollFlags |= eventInfo.IsVertical() ? nsMouseScrollEvent::kIsVertical : nsMouseScrollEvent::kIsHorizontal; if (actualScrollAction == nsQueryContentEvent::SCROLL_ACTION_PAGE) { pixelEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; @@ -6501,7 +6476,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, pixelEvent.isMeta = scrollEvent.isMeta; pixelEvent.isAlt = scrollEvent.isAlt; - PRInt32 nativeDeltaForPixel = nativeDelta + sRemainingDeltaForPixel; + PRInt32 nativeDeltaForPixel = + eventInfo.GetNativeDelta() + sRemainingDeltaForPixel; // Pixel scroll event won't be recomputed the scroll amout and direction by // ESM. Therefore, we need to set the computed amout and direction here. PRInt32 orienterForPixel = reversePixelScrollDirection ? -orienter : orienter; From eac1fdb5dc6df8bed467981af7323bd0cbd465fe Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 13/88] Bug 672175 part.7 Manage last wheel scroll message information by MouseScrollHandler::LastEventInfo r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 63 ++++++++++++++++- widget/windows/WinMouseScrollHandler.h | 53 ++++++++++++++- widget/windows/nsWindow.cpp | 86 ++++++------------------ widget/windows/nsWindow.h | 9 --- 4 files changed, 134 insertions(+), 77 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index bc74eb1645d8..cc4be0b1d88e 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -65,6 +65,11 @@ bool MouseScrollHandler::Device::Elantech::sUseSwipeHack = false; bool MouseScrollHandler::Device::Elantech::sUsePinchHack = false; DWORD MouseScrollHandler::Device::Elantech::sZoomUntil = 0; +// The duration until timeout of events transaction. The value is 1.5 sec, +// it's just a magic number, it was suggested by Logitech's engineer, see +// bug 605648 comment 90. +#define DEFAULT_TIMEOUT_DURATION 1500 + /****************************************************************************** * * MouseScrollHandler @@ -168,7 +173,8 @@ MouseScrollHandler::DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent) * ******************************************************************************/ -MouseScrollHandler::EventInfo::EventInfo(UINT aMessage, +MouseScrollHandler::EventInfo::EventInfo(nsWindow* aWindow, + UINT aMessage, WPARAM aWParam, LPARAM aLParam) { NS_ABORT_IF_FALSE(aMessage == WM_MOUSEWHEEL || aMessage == WM_MOUSEHWHEEL, @@ -180,6 +186,8 @@ MouseScrollHandler::EventInfo::EventInfo(UINT aMessage, mIsPage = MouseScrollHandler::sInstance-> mSystemSettings.IsPageScroll(mIsVertical); mDelta = (short)HIWORD(aWParam); + mWnd = aWindow->GetWindowHandle(); + mTimeStamp = TimeStamp::Now(); } bool @@ -214,6 +222,50 @@ MouseScrollHandler::EventInfo::GetScrollFlags() const return result; } +/****************************************************************************** + * + * LastEventInfo + * + ******************************************************************************/ + +bool +MouseScrollHandler::LastEventInfo::CanContinueTransaction( + const EventInfo& aNewEvent) +{ + return !mWnd || + (mWnd == aNewEvent.GetWindowHandle() && + IsPositive() == aNewEvent.IsPositive() && + mIsVertical == aNewEvent.IsVertical() && + mIsPage == aNewEvent.IsPage() && + TimeStamp::Now() - mTimeStamp <= + TimeDuration::FromMilliseconds(DEFAULT_TIMEOUT_DURATION)); +} + +void +MouseScrollHandler::LastEventInfo::ResetTransaction() +{ + if (!mWnd) { + return; + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::LastEventInfo::ResetTransaction()")); + + mWnd = nsnull; + mRemainingDeltaForScroll = 0; + mRemainingDeltaForPixel = 0; +} + +void +MouseScrollHandler::LastEventInfo::RecordEvent(const EventInfo& aEvent) +{ + mWnd = aEvent.GetWindowHandle(); + mDelta = aEvent.GetNativeDelta(); + mIsVertical = aEvent.IsVertical(); + mIsPage = aEvent.IsPage(); + mTimeStamp = TimeStamp::Now(); +} + /****************************************************************************** * * SystemSettings @@ -278,6 +330,10 @@ MouseScrollHandler::SystemSettings::MarkDirty() ("MouseScrollHandler::SystemSettings::MarkDirty(): " "Marking SystemSettings dirty")); mInitialized = false; + // When system settings are changed, we should reset current transaction. + MOZ_ASSERT(sInstance, + "Must not be called at initializing MouseScrollHandler"); + MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction(); } /****************************************************************************** @@ -332,6 +388,11 @@ MouseScrollHandler::UserPrefs::MarkDirty() PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, ("MouseScrollHandler::UserPrefs::MarkDirty(): Marking UserPrefs dirty")); mInitialized = false; + // When user prefs for mousewheel are changed, we should reset current + // transaction. + MOZ_ASSERT(sInstance, + "Must not be called at initializing MouseScrollHandler"); + MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction(); } /****************************************************************************** diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 41bcde0ae388..26ec433c9577 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -10,6 +10,7 @@ #include "nscore.h" #include "nsDebug.h" #include "mozilla/Assertions.h" +#include "mozilla/TimeStamp.h" #include class nsWindow; @@ -49,13 +50,16 @@ public: class EventInfo { public: /** + * @param aWindow An nsWindow which is handling the event. * @param aMessage Must be WM_MOUSEWHEEL or WM_MOUSEHWHEEL. */ - EventInfo(UINT aMessage, WPARAM aWParam, LPARAM aLParam); + EventInfo(nsWindow* aWindow, UINT aMessage, WPARAM aWParam, LPARAM aLParam); bool CanDispatchMouseScrollEvent() const; PRInt32 GetNativeDelta() const { return mDelta; } + HWND GetWindowHandle() const { return mWnd; } + const TimeStamp& GetTimeStamp() const { return mTimeStamp; } bool IsVertical() const { return mIsVertical; } bool IsPositive() const { return (mDelta > 0); } bool IsPage() const { return mIsPage; } @@ -76,8 +80,11 @@ public: */ PRInt32 GetScrollFlags() const; - private: - EventInfo() {} + protected: + EventInfo() : + mIsVertical(false), mIsPage(false), mDelta(0), mWnd(nsnull) + { + } // TRUE if event is for vertical scroll. Otherwise, FALSE. bool mIsVertical; @@ -85,8 +92,48 @@ public: bool mIsPage; // The native delta value. PRInt32 mDelta; + // The window handle which is handling the event. + HWND mWnd; + // Timestamp of the event. + TimeStamp mTimeStamp; }; + class LastEventInfo : public EventInfo { + public: + LastEventInfo() : + EventInfo(), mRemainingDeltaForScroll(0), mRemainingDeltaForPixel(0) + { + } + + /** + * CanContinueTransaction() checks whether the new event can continue the + * last transaction or not. Note that if there is no transaction, this + * returns true. + */ + bool CanContinueTransaction(const EventInfo& aNewEvent); + + /** + * ResetTransaction() resets the transaction, i.e., the instance forgets + * the last event information. + */ + void ResetTransaction(); + + /** + * RecordEvent() saves the information of new event. + */ + void RecordEvent(const EventInfo& aEvent); + + // The remaining native delta value (i.e., not handled by previous + // message handler). + PRInt32 mRemainingDeltaForScroll; + PRInt32 mRemainingDeltaForPixel; + }; + + LastEventInfo& GetLastEventInfo() { return mLastEventInfo; } + +private: + LastEventInfo mLastEventInfo; + public: class SystemSettings { public: diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 34e8508e68a2..bc50a7d997a9 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -286,16 +286,6 @@ PRUint32 nsWindow::sOOPPPluginFocusEvent = MSG nsWindow::sRedirectedKeyDown; -bool nsWindow::sNeedsToInitMouseWheelSettings = true; - -HWND nsWindow::sLastMouseWheelWnd = NULL; -PRInt32 nsWindow::sRemainingDeltaForScroll = 0; -PRInt32 nsWindow::sRemainingDeltaForPixel = 0; -bool nsWindow::sLastMouseWheelDeltaIsPositive = false; -bool nsWindow::sLastMouseWheelOrientationIsVertical = false; -bool nsWindow::sLastMouseWheelUnitIsPage = false; -PRUint32 nsWindow::sLastMouseWheelTime = 0; - /************************************************************** * * SECTION: globals variables @@ -5160,15 +5150,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, } break; - case WM_SETTINGCHANGE: - switch (wParam) { - case SPI_SETWHEELSCROLLLINES: - case SPI_SETWHEELSCROLLCHARS: - sNeedsToInitMouseWheelSettings = true; - break; - } - break; - case WM_INPUTLANGCHANGEREQUEST: *aRetValue = TRUE; result = false; @@ -6277,25 +6258,6 @@ bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) return true; // Handled } -/* static */ void -nsWindow::InitMouseWheelScrollData() -{ - if (!sNeedsToInitMouseWheelSettings) { - return; - } - sNeedsToInitMouseWheelSettings = false; - ResetRemainingWheelDelta(); -} - -/* static */ -void -nsWindow::ResetRemainingWheelDelta() -{ - sRemainingDeltaForPixel = 0; - sRemainingDeltaForScroll = 0; - sLastMouseWheelWnd = NULL; -} - static PRInt32 RoundDelta(double aDelta) { return aDelta >= 0 ? (PRInt32)floor(aDelta) : (PRInt32)ceil(aDelta); @@ -6310,32 +6272,25 @@ void nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, LRESULT *aRetValue) { - InitMouseWheelScrollData(); - - MouseScrollHandler::EventInfo eventInfo(aMessage, aWParam, aLParam); + MouseScrollHandler* handler = MouseScrollHandler::GetInstance(); + MouseScrollHandler::EventInfo eventInfo(this, aMessage, aWParam, aLParam); if (!eventInfo.CanDispatchMouseScrollEvent()) { - ResetRemainingWheelDelta(); + handler->GetLastEventInfo().ResetTransaction(); *aRetValue = eventInfo.ComputeMessageResult(false); return; } + MouseScrollHandler::LastEventInfo& lastEventInfo = + handler->GetLastEventInfo(); + // Discard the remaining delta if current wheel message and last one are // received by different window or to scroll different direction or // different unit scroll. Furthermore, if the last event was too old. - PRUint32 now = PR_IntervalToMilliseconds(PR_IntervalNow()); - if (sLastMouseWheelWnd && - (sLastMouseWheelWnd != mWnd || - sLastMouseWheelDeltaIsPositive != eventInfo.IsPositive() || - sLastMouseWheelOrientationIsVertical != eventInfo.IsVertical() || - sLastMouseWheelUnitIsPage != eventInfo.IsPage() || - now - sLastMouseWheelTime > 1500)) { - ResetRemainingWheelDelta(); + if (!lastEventInfo.CanContinueTransaction(eventInfo)) { + lastEventInfo.ResetTransaction(); } - sLastMouseWheelWnd = mWnd; - sLastMouseWheelDeltaIsPositive = eventInfo.IsPositive(); - sLastMouseWheelOrientationIsVertical = eventInfo.IsVertical(); - sLastMouseWheelUnitIsPage = eventInfo.IsPage(); - sLastMouseWheelTime = now; + + lastEventInfo.RecordEvent(eventInfo); // means we process this message *aRetValue = eventInfo.ComputeMessageResult(true); @@ -6386,8 +6341,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, testEvent.isAlt = scrollEvent.isAlt; testEvent.delta = computedScrollAmount; - if ((eventInfo.IsVertical() && sLastMouseWheelDeltaIsPositive) || - (!eventInfo.IsVertical() && !sLastMouseWheelDeltaIsPositive)) { + if ((eventInfo.IsVertical() && eventInfo.IsPositive()) || + (!eventInfo.IsVertical() && !eventInfo.IsPositive())) { testEvent.delta *= -1; } nsQueryContentEvent queryEvent(true, NS_QUERY_SCROLL_TARGET_INFO, this); @@ -6429,14 +6384,15 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, scrollEvent.scrollFlags |= eventInfo.GetScrollFlags(); PRInt32 nativeDeltaForScroll = - eventInfo.GetNativeDelta() + sRemainingDeltaForScroll; + eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForScroll; // NOTE: Don't use computedScrollAmount for computing the delta value of // line/page scroll event. The value will be recomputed in ESM. if (eventInfo.IsPage()) { scrollEvent.delta = nativeDeltaForScroll * orienter / WHEEL_DELTA; PRInt32 recomputedNativeDelta = scrollEvent.delta * orienter / WHEEL_DELTA; - sRemainingDeltaForScroll = nativeDeltaForScroll - recomputedNativeDelta; + lastEventInfo.mRemainingDeltaForScroll = + nativeDeltaForScroll - recomputedNativeDelta; } else { double deltaPerUnit; deltaPerUnit = @@ -6445,20 +6401,21 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, RoundDelta((double)nativeDeltaForScroll * orienter / deltaPerUnit); PRInt32 recomputedNativeDelta = (PRInt32)(scrollEvent.delta * orienter * deltaPerUnit); - sRemainingDeltaForScroll = nativeDeltaForScroll - recomputedNativeDelta; + lastEventInfo.mRemainingDeltaForScroll = + nativeDeltaForScroll - recomputedNativeDelta; } if (scrollEvent.delta) { DispatchWindowEvent(&scrollEvent); if (mOnDestroyCalled) { - ResetRemainingWheelDelta(); + lastEventInfo.ResetTransaction(); return; } } // If the query event failed, we cannot send pixel events. if (!dispatchPixelScrollEvent) { - sRemainingDeltaForPixel = 0; + lastEventInfo.mRemainingDeltaForPixel = 0; return; } @@ -6477,7 +6434,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, pixelEvent.isAlt = scrollEvent.isAlt; PRInt32 nativeDeltaForPixel = - eventInfo.GetNativeDelta() + sRemainingDeltaForPixel; + eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForPixel; // Pixel scroll event won't be recomputed the scroll amout and direction by // ESM. Therefore, we need to set the computed amout and direction here. PRInt32 orienterForPixel = reversePixelScrollDirection ? -orienter : orienter; @@ -6488,7 +6445,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, RoundDelta((double)nativeDeltaForPixel * orienterForPixel / deltaPerPixel); PRInt32 recomputedNativeDelta = (PRInt32)(pixelEvent.delta * orienterForPixel * deltaPerPixel); - sRemainingDeltaForPixel = nativeDeltaForPixel - recomputedNativeDelta; + lastEventInfo.mRemainingDeltaForPixel = + nativeDeltaForPixel - recomputedNativeDelta; if (pixelEvent.delta != 0) { DispatchWindowEvent(&pixelEvent); } diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index fe446cbc775a..bc07b0e1aa5c 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -604,15 +604,6 @@ protected: static bool sNeedsToInitMouseWheelSettings; static void InitMouseWheelScrollData(); - static HWND sLastMouseWheelWnd; - static PRInt32 sRemainingDeltaForScroll; - static PRInt32 sRemainingDeltaForPixel; - static bool sLastMouseWheelDeltaIsPositive; - static bool sLastMouseWheelOrientationIsVertical; - static bool sLastMouseWheelUnitIsPage; - static PRUint32 sLastMouseWheelTime; // in milliseconds - static void ResetRemainingWheelDelta(); - // If a window receives WM_KEYDOWN message or WM_SYSKEYDOWM message which is // redirected message, OnKeyDowm() prevents to dispatch NS_KEY_DOWN event // because it has been dispatched before the message was redirected. From aa58d1c7291af12f452b52d456afb48b0e399e5d Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:28 +0900 Subject: [PATCH 14/88] Bug 672175 part.8 Compute modifier key state in MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 14 ++++++++++++++ widget/windows/WinMouseScrollHandler.h | 8 ++++++++ widget/windows/nsWindow.cpp | 10 +--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index cc4be0b1d88e..eb75ee1cc7ef 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -167,6 +167,20 @@ MouseScrollHandler::DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent) return aWindow->DispatchWindowEvent(&aEvent); } +/* static */ +nsModifierKeyState +MouseScrollHandler::GetModifierKeyState() +{ + nsModifierKeyState result; + // Assume the Control key is down if the Elantech touchpad has sent the + // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in + // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) + if (!result.mIsControlDown) { + result.mIsControlDown = Device::Elantech::IsZooming(); + } + return result; +} + /****************************************************************************** * * EventInfo diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 26ec433c9577..d07f1bea7486 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -15,6 +15,7 @@ class nsWindow; class nsGUIEvent; +struct nsModifierKeyState; namespace mozilla { namespace widget { @@ -33,6 +34,13 @@ public: LRESULT *aRetValue, bool &aEatMessage); + /** + * GetModifierKeyState() returns current modifier key state. + * Note that some devices need some hack for the modifier key state. + * This method does it automatically. + */ + static nsModifierKeyState GetModifierKeyState(); + private: MouseScrollHandler(); ~MouseScrollHandler(); diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index bc50a7d997a9..9344d25b5aa9 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6295,21 +6295,13 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, // means we process this message *aRetValue = eventInfo.ComputeMessageResult(true); - nsModifierKeyState modKeyState; + nsModifierKeyState modKeyState = MouseScrollHandler::GetModifierKeyState(); // Our positive delta value means to bottom or right. // But positive native delta value means to top or right. // Use orienter for computing our delta value with native delta value. PRInt32 orienter = eventInfo.IsVertical() ? -1 : 1; - // Assume the Control key is down if the Elantech touchpad has sent the - // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in - // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) - if (!modKeyState.mIsControlDown) { - modKeyState.mIsControlDown = - MouseScrollHandler::Device::Elantech::IsZooming(); - } - // Create line (or page) scroll event. nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, this); From 407835228d5bdd49820aa83a73749b964b18d5dd Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 15/88] Bug 672175 part.9 Implement NS_QUERY_SCROLL_TARGET_INFO event dispatcher on MouseScrollHandler::EventInfo r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 88 ++++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 29 ++++++++ widget/windows/nsWindow.cpp | 78 +++++---------------- 3 files changed, 133 insertions(+), 62 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index eb75ee1cc7ef..aaaabe73fad4 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -181,6 +181,94 @@ MouseScrollHandler::GetModifierKeyState() return result; } +MouseScrollHandler::ScrollTargetInfo +MouseScrollHandler::GetScrollTargetInfo( + nsWindow* aWindow, + const EventInfo& aEventInfo, + const nsModifierKeyState& aModifierKeyState) +{ + ScrollTargetInfo result; + result.dispatchPixelScrollEvent = false; + result.reversePixelScrollDirection = false; + result.actualScrollAmount = aEventInfo.GetScrollAmount(); + result.actualScrollAction = nsQueryContentEvent::SCROLL_ACTION_NONE; + result.pixelsPerUnit = 0; + if (!mUserPrefs.IsPixelScrollingEnabled()) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::GetPixelScrollInfo: Succeeded, aWindow=%p, " + "result: { dispatchPixelScrollEvent: %s, actualScrollAmount: %d }", + aWindow, GetBoolName(result.dispatchPixelScrollEvent), + result.actualScrollAmount)); + return result; + } + + nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, aWindow); + aWindow->InitEvent(testEvent); + testEvent.scrollFlags = aEventInfo.GetScrollFlags(); + testEvent.isShift = aModifierKeyState.mIsShiftDown; + testEvent.isControl = aModifierKeyState.mIsControlDown; + testEvent.isMeta = false; + testEvent.isAlt = aModifierKeyState.mIsAltDown; + + testEvent.delta = result.actualScrollAmount; + if ((aEventInfo.IsVertical() && aEventInfo.IsPositive()) || + (!aEventInfo.IsVertical() && !aEventInfo.IsPositive())) { + testEvent.delta *= -1; + } + + nsQueryContentEvent queryEvent(true, NS_QUERY_SCROLL_TARGET_INFO, aWindow); + aWindow->InitEvent(queryEvent); + queryEvent.InitForQueryScrollTargetInfo(&testEvent); + DispatchEvent(aWindow, queryEvent); + + // If the necessary interger isn't larger than 0, we should assume that + // the event failed for us. + if (!queryEvent.mSucceeded) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::GetPixelScrollInfo: Failed to query the " + "scroll target information, aWindow=%p" + "result: { dispatchPixelScrollEvent: %s, actualScrollAmount: %d }", + aWindow, GetBoolName(result.dispatchPixelScrollEvent), + result.actualScrollAmount)); + return result; + } + + result.actualScrollAction = queryEvent.mReply.mComputedScrollAction; + + if (result.actualScrollAction == nsQueryContentEvent::SCROLL_ACTION_PAGE) { + result.pixelsPerUnit = + aEventInfo.IsVertical() ? queryEvent.mReply.mPageHeight : + queryEvent.mReply.mPageWidth; + } else { + result.pixelsPerUnit = queryEvent.mReply.mLineHeight; + } + + result.actualScrollAmount = queryEvent.mReply.mComputedScrollAmount; + + if (result.pixelsPerUnit > 0 && result.actualScrollAmount != 0 && + result.actualScrollAction != nsQueryContentEvent::SCROLL_ACTION_NONE) { + result.dispatchPixelScrollEvent = true; + // If original delta's sign and computed delta's one are different, + // we need to reverse the pixel scroll direction at dispatching it. + result.reversePixelScrollDirection = + (testEvent.delta > 0 && result.actualScrollAmount < 0) || + (testEvent.delta < 0 && result.actualScrollAmount > 0); + // scroll amount must be positive. + result.actualScrollAmount = NS_ABS(result.actualScrollAmount); + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::GetPixelScrollInfo: Succeeded, aWindow=%p, " + "result: { dispatchPixelScrollEvent: %s, reversePixelScrollDirection: %s, " + "actualScrollAmount: %d, actualScrollAction: 0x%01X, " + "pixelsPerUnit: %d }", + aWindow, GetBoolName(result.dispatchPixelScrollEvent), + GetBoolName(result.reversePixelScrollDirection), result.actualScrollAmount, + result.actualScrollAction, result.pixelsPerUnit)); + + return result; +} + /****************************************************************************** * * EventInfo diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index d07f1bea7486..e2945f97d86d 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -55,6 +55,35 @@ private: static bool DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent); public: + + class EventInfo; + /** + * GetScrollTargetInfo() returns scroll target information which is + * computed from the result of NS_QUERY_SCROLL_TARGET_INFO event. + * + * @param aWindow An nsWindow which is handling the event. + * @param aEventInfo The EventInfo which is being handled. + * @param aModifierKeyState The modifier key state. + */ + struct ScrollTargetInfo { + // TRUE if pixel scroll event is needed. Otherwise, FALSE. + bool dispatchPixelScrollEvent; + // TRUE if pixel scroll event's delta value should be reversed. + // Otherwise, FALSE. + bool reversePixelScrollDirection; + // Actual scroll amount. It might be computed with user prefs. + PRInt32 actualScrollAmount; + // Actual scroll action. It might be computed with user prefs. + // The value is one of nsQueryContentEvent::SCROLL_ACTION_*. + PRInt32 actualScrollAction; + // Pixels per unit (line or page, depends on the action). + PRInt32 pixelsPerUnit; + }; + ScrollTargetInfo GetScrollTargetInfo( + nsWindow* aWindow, + const EventInfo& aEvent, + const nsModifierKeyState& aModiferKeyState); + class EventInfo { public: /** diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 9344d25b5aa9..a1d7313ec849 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6315,71 +6315,22 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, // Before dispatching line scroll event, we should get the current scroll // event target information for pixel scroll. - bool dispatchPixelScrollEvent = false; - bool reversePixelScrollDirection = false; - PRInt32 actualScrollAction = nsQueryContentEvent::SCROLL_ACTION_NONE; - PRInt32 pixelsPerUnit = 0; - // the amount is the number of lines (or pages) per WHEEL_DELTA - PRInt32 computedScrollAmount = eventInfo.GetScrollAmount(); - - if (MouseScrollHandler::GetInstance()-> - GetUserPrefs().IsPixelScrollingEnabled()) { - nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, this); - InitEvent(testEvent); - testEvent.scrollFlags = eventInfo.GetScrollFlags(); - testEvent.isShift = scrollEvent.isShift; - testEvent.isControl = scrollEvent.isControl; - testEvent.isMeta = scrollEvent.isMeta; - testEvent.isAlt = scrollEvent.isAlt; - - testEvent.delta = computedScrollAmount; - if ((eventInfo.IsVertical() && eventInfo.IsPositive()) || - (!eventInfo.IsVertical() && !eventInfo.IsPositive())) { - testEvent.delta *= -1; - } - nsQueryContentEvent queryEvent(true, NS_QUERY_SCROLL_TARGET_INFO, this); - InitEvent(queryEvent); - queryEvent.InitForQueryScrollTargetInfo(&testEvent); - DispatchWindowEvent(&queryEvent); - // If the necessary interger isn't larger than 0, we should assume that - // the event failed for us. - if (queryEvent.mSucceeded) { - actualScrollAction = queryEvent.mReply.mComputedScrollAction; - if (actualScrollAction == nsQueryContentEvent::SCROLL_ACTION_PAGE) { - if (eventInfo.IsVertical()) { - pixelsPerUnit = queryEvent.mReply.mPageHeight; - } else { - pixelsPerUnit = queryEvent.mReply.mPageWidth; - } - } else { - pixelsPerUnit = queryEvent.mReply.mLineHeight; - } - computedScrollAmount = queryEvent.mReply.mComputedScrollAmount; - if (pixelsPerUnit > 0 && computedScrollAmount != 0 && - actualScrollAction != nsQueryContentEvent::SCROLL_ACTION_NONE) { - dispatchPixelScrollEvent = true; - // If original delta's sign and computed delta's one are different, - // we need to reverse the pixel scroll direction at dispatching it. - reversePixelScrollDirection = - (testEvent.delta > 0 && computedScrollAmount < 0) || - (testEvent.delta < 0 && computedScrollAmount > 0); - // scroll amount must be positive. - computedScrollAmount = NS_ABS(computedScrollAmount); - } - } - } + MouseScrollHandler::ScrollTargetInfo scrollTargetInfo = + handler->GetScrollTargetInfo(this, eventInfo, modKeyState); // If we dispatch pixel scroll event after the line scroll event, // we should set kHasPixels flag to the line scroll event. - scrollEvent.scrollFlags = - dispatchPixelScrollEvent ? nsMouseScrollEvent::kHasPixels : 0; - scrollEvent.scrollFlags |= eventInfo.GetScrollFlags(); + scrollEvent.scrollFlags = eventInfo.GetScrollFlags(); + if (scrollTargetInfo.dispatchPixelScrollEvent) { + scrollEvent.scrollFlags |= nsMouseScrollEvent::kHasPixels; + } PRInt32 nativeDeltaForScroll = eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForScroll; - // NOTE: Don't use computedScrollAmount for computing the delta value of - // line/page scroll event. The value will be recomputed in ESM. + // NOTE: Don't use scrollTargetInfo.actualScrollAmount for computing the + // delta value of line/page scroll event. The value will be + // recomputed in ESM. if (eventInfo.IsPage()) { scrollEvent.delta = nativeDeltaForScroll * orienter / WHEEL_DELTA; PRInt32 recomputedNativeDelta = scrollEvent.delta * orienter / WHEEL_DELTA; @@ -6406,7 +6357,7 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, } // If the query event failed, we cannot send pixel events. - if (!dispatchPixelScrollEvent) { + if (!scrollTargetInfo.dispatchPixelScrollEvent) { lastEventInfo.mRemainingDeltaForPixel = 0; return; } @@ -6416,7 +6367,8 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, pixelEvent.scrollFlags = nsMouseScrollEvent::kAllowSmoothScroll; pixelEvent.scrollFlags |= eventInfo.IsVertical() ? nsMouseScrollEvent::kIsVertical : nsMouseScrollEvent::kIsHorizontal; - if (actualScrollAction == nsQueryContentEvent::SCROLL_ACTION_PAGE) { + if (scrollTargetInfo.actualScrollAction == + nsQueryContentEvent::SCROLL_ACTION_PAGE) { pixelEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; } // Use same modifier state for pixel scroll event. @@ -6429,10 +6381,12 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForPixel; // Pixel scroll event won't be recomputed the scroll amout and direction by // ESM. Therefore, we need to set the computed amout and direction here. - PRInt32 orienterForPixel = reversePixelScrollDirection ? -orienter : orienter; + PRInt32 orienterForPixel = + scrollTargetInfo.reversePixelScrollDirection ? -orienter : orienter; double deltaPerPixel = - (double)WHEEL_DELTA / computedScrollAmount / pixelsPerUnit; + (double)WHEEL_DELTA / scrollTargetInfo.actualScrollAmount / + scrollTargetInfo.pixelsPerUnit; pixelEvent.delta = RoundDelta((double)nativeDeltaForPixel * orienterForPixel / deltaPerPixel); PRInt32 recomputedNativeDelta = From a79e6d853c6878ccf2b05e607602926a60df9b8b Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 16/88] Bug 672175 part.10 Initialize mouse scroll events in MouseScrollHandler::LastEventInfo r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 146 ++++++++++++++++++++++- widget/windows/WinMouseScrollHandler.h | 42 +++++++ widget/windows/nsWindow.cpp | 106 +++------------- widget/windows/nsWindowDefs.h | 2 + 4 files changed, 202 insertions(+), 94 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index aaaabe73fad4..6faa446b14fc 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -204,12 +204,9 @@ MouseScrollHandler::GetScrollTargetInfo( nsMouseScrollEvent testEvent(true, NS_MOUSE_SCROLL, aWindow); aWindow->InitEvent(testEvent); - testEvent.scrollFlags = aEventInfo.GetScrollFlags(); - testEvent.isShift = aModifierKeyState.mIsShiftDown; - testEvent.isControl = aModifierKeyState.mIsControlDown; - testEvent.isMeta = false; - testEvent.isAlt = aModifierKeyState.mIsAltDown; + aModifierKeyState.InitInputEvent(testEvent); + testEvent.scrollFlags = aEventInfo.GetScrollFlags(); testEvent.delta = result.actualScrollAmount; if ((aEventInfo.IsVertical() && aEventInfo.IsPositive()) || (!aEventInfo.IsVertical() && !aEventInfo.IsPositive())) { @@ -368,6 +365,145 @@ MouseScrollHandler::LastEventInfo::RecordEvent(const EventInfo& aEvent) mTimeStamp = TimeStamp::Now(); } +/* static */ +PRInt32 +MouseScrollHandler::LastEventInfo::RoundDelta(double aDelta) +{ + return (aDelta >= 0) ? (PRInt32)floor(aDelta) : (PRInt32)ceil(aDelta); +} + +bool +MouseScrollHandler::LastEventInfo::InitMouseScrollEvent( + nsWindow* aWindow, + nsMouseScrollEvent& aMouseScrollEvent, + const ScrollTargetInfo& aScrollTargetInfo, + const nsModifierKeyState& aModKeyState) +{ + NS_ABORT_IF_FALSE(aMouseScrollEvent.message == NS_MOUSE_SCROLL, + "aMouseScrollEvent must be NS_MOUSE_SCROLL"); + + // XXX Why don't we use lParam value? We should use lParam value because + // our internal message is always posted by original message handler. + // So, GetMessagePos() may return different cursor position. + aWindow->InitEvent(aMouseScrollEvent); + + aModKeyState.InitInputEvent(aMouseScrollEvent); + + // If we dispatch pixel scroll event after the line scroll event, + // we should set kHasPixels flag to the line scroll event. + aMouseScrollEvent.scrollFlags = + aScrollTargetInfo.dispatchPixelScrollEvent ? + nsMouseScrollEvent::kHasPixels : 0; + aMouseScrollEvent.scrollFlags |= GetScrollFlags(); + + // Our positive delta value means to bottom or right. + // But positive native delta value means to top or right. + // Use orienter for computing our delta value with native delta value. + PRInt32 orienter = mIsVertical ? -1 : 1; + + // NOTE: Don't use aScrollTargetInfo.actualScrollAmount for computing the + // delta value of line/page scroll event. The value will be recomputed + // in ESM. + PRInt32 nativeDelta = mDelta + mRemainingDeltaForScroll; + if (IsPage()) { + aMouseScrollEvent.delta = nativeDelta * orienter / WHEEL_DELTA; + PRInt32 recomputedNativeDelta = + aMouseScrollEvent.delta * orienter / WHEEL_DELTA; + mRemainingDeltaForScroll = nativeDelta - recomputedNativeDelta; + } else { + double deltaPerUnit = (double)WHEEL_DELTA / GetScrollAmount(); + aMouseScrollEvent.delta = + RoundDelta((double)nativeDelta * orienter / deltaPerUnit); + PRInt32 recomputedNativeDelta = + (PRInt32)(aMouseScrollEvent.delta * orienter * deltaPerUnit); + mRemainingDeltaForScroll = nativeDelta - recomputedNativeDelta; + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::LastEventInfo::InitMouseScrollEvent: aWindow=%p, " + "aMouseScrollEvent { refPoint: { x: %d, y: %d }, delta: %d, " + "scrollFlags: 0x%04X, isShift: %s, isControl: %s, isAlt: %s, " + "isMeta: %s }, mRemainingDeltaForScroll: %d", + aWindow, aMouseScrollEvent.refPoint.x, aMouseScrollEvent.refPoint.y, + aMouseScrollEvent.delta, aMouseScrollEvent.scrollFlags, + GetBoolName(aMouseScrollEvent.isShift), + GetBoolName(aMouseScrollEvent.isControl), + GetBoolName(aMouseScrollEvent.isAlt), + GetBoolName(aMouseScrollEvent.isMeta), mRemainingDeltaForScroll)); + + return (aMouseScrollEvent.delta != 0); +} + +bool +MouseScrollHandler::LastEventInfo::InitMousePixelScrollEvent( + nsWindow* aWindow, + nsMouseScrollEvent& aPixelScrollEvent, + const ScrollTargetInfo& aScrollTargetInfo, + const nsModifierKeyState& aModKeyState) +{ + NS_ABORT_IF_FALSE(aPixelScrollEvent.message == NS_MOUSE_PIXEL_SCROLL, + "aPixelScrollEvent must be NS_MOUSE_PIXEL_SCROLL"); + + if (!aScrollTargetInfo.dispatchPixelScrollEvent) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::LastEventInfo::InitMousePixelScrollEvent: aWindow=%p, " + "PixelScroll is disabled", + aWindow, mRemainingDeltaForPixel)); + + mRemainingDeltaForPixel = 0; + return false; + } + + // XXX Why don't we use lParam value? We should use lParam value because + // our internal message is always posted by original message handler. + // So, GetMessagePos() may return different cursor position. + aWindow->InitEvent(aPixelScrollEvent); + + aModKeyState.InitInputEvent(aPixelScrollEvent); + + aPixelScrollEvent.scrollFlags = nsMouseScrollEvent::kAllowSmoothScroll; + aPixelScrollEvent.scrollFlags |= mIsVertical ? + nsMouseScrollEvent::kIsVertical : nsMouseScrollEvent::kIsHorizontal; + if (aScrollTargetInfo.actualScrollAction == + nsQueryContentEvent::SCROLL_ACTION_PAGE) { + aPixelScrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; + } + + // Our positive delta value means to bottom or right. + // But positive native delta value means to top or right. + // Use orienter for computing our delta value with native delta value. + PRInt32 orienter = mIsVertical ? -1 : 1; + // However, pixel scroll event won't be recomputed the scroll amout and + // direction by ESM. Therefore, we need to set the computed amout and + // direction here. + if (aScrollTargetInfo.reversePixelScrollDirection) { + orienter *= -1; + } + + PRInt32 nativeDelta = mDelta + mRemainingDeltaForPixel; + double deltaPerPixel = (double)WHEEL_DELTA / + aScrollTargetInfo.actualScrollAmount / aScrollTargetInfo.pixelsPerUnit; + aPixelScrollEvent.delta = + RoundDelta((double)nativeDelta * orienter / deltaPerPixel); + PRInt32 recomputedNativeDelta = + (PRInt32)(aPixelScrollEvent.delta * orienter * deltaPerPixel); + mRemainingDeltaForPixel = nativeDelta - recomputedNativeDelta; + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::LastEventInfo::InitMousePixelScrollEvent: aWindow=%p, " + "aPixelScrollEvent { refPoint: { x: %d, y: %d }, delta: %d, " + "scrollFlags: 0x%04X, isShift: %s, isControl: %s, isAlt: %s, " + "isMeta: %s }, mRemainingDeltaForScroll: %d", + aWindow, aPixelScrollEvent.refPoint.x, aPixelScrollEvent.refPoint.y, + aPixelScrollEvent.delta, aPixelScrollEvent.scrollFlags, + GetBoolName(aPixelScrollEvent.isShift), + GetBoolName(aPixelScrollEvent.isControl), + GetBoolName(aPixelScrollEvent.isAlt), + GetBoolName(aPixelScrollEvent.isMeta), mRemainingDeltaForPixel)); + + return (aPixelScrollEvent.delta != 0); +} + /****************************************************************************** * * SystemSettings diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index e2945f97d86d..af895af1d4fe 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -15,6 +15,7 @@ class nsWindow; class nsGUIEvent; +class nsMouseScrollEvent; struct nsModifierKeyState; namespace mozilla { @@ -160,6 +161,47 @@ public: */ void RecordEvent(const EventInfo& aEvent); + /** + * InitMouseScrollEvent() initializes NS_MOUSE_SCROLL event and + * recomputes the remaning detla for the event. + * This must be called only once during handling a message and after + * RecordEvent() is called. + * + * @param aWindow A window which will dispatch the event. + * @param aMouseScrollEvent An NS_MOUSE_SCROLL event, this will be + * initialized. + * @param aScrollTargetInfo The result of GetScrollTargetInfo(). + * @param aModKeyState Current modifier key state. + * @return TRUE if the event is ready to dispatch. + * Otherwise, FALSE. + */ + bool InitMouseScrollEvent(nsWindow* aWindow, + nsMouseScrollEvent& aMouseScrollEvent, + const ScrollTargetInfo& aScrollTargetInfo, + const nsModifierKeyState& aModKeyState); + + /** + * InitMousePixelScrollEvent() initializes NS_MOUSE_PIXEL_SCROLL event and + * recomputes the remaning detla for the event. + * This must be called only once during handling a message and after + * RecordEvent() is called. + * + * @param aWindow A window which will dispatch the event. + * @param aMouseScrollEvent An NS_MOUSE_PIXEL_SCROLL event, this will be + * initialized. + * @param aScrollTargetInfo The result of GetScrollTargetInfo(). + * @param aModKeyState Current modifier key state. + * @return TRUE if the event is ready to dispatch. + * Otherwise, FALSE. + */ + bool InitMousePixelScrollEvent(nsWindow* aWindow, + nsMouseScrollEvent& aPixelScrollEvent, + const ScrollTargetInfo& aScrollTargetInfo, + const nsModifierKeyState& aModKeyState); + + private: + static PRInt32 RoundDelta(double aDelta); + // The remaining native delta value (i.e., not handled by previous // message handler). PRInt32 mRemainingDeltaForScroll; diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index a1d7313ec849..2ade34875fa4 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6258,11 +6258,6 @@ bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) return true; // Handled } -static PRInt32 RoundDelta(double aDelta) -{ - return aDelta >= 0 ? (PRInt32)floor(aDelta) : (PRInt32)ceil(aDelta); -} - /** * OnMouseWheelInternal - mouse wheel event processing. * aMessage may be WM_MOUSEWHEEL or WM_MOUSEHWHEEL but this is called when @@ -6297,58 +6292,14 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, nsModifierKeyState modKeyState = MouseScrollHandler::GetModifierKeyState(); - // Our positive delta value means to bottom or right. - // But positive native delta value means to top or right. - // Use orienter for computing our delta value with native delta value. - PRInt32 orienter = eventInfo.IsVertical() ? -1 : 1; - - // Create line (or page) scroll event. - nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, this); - - // Initialize common members on line scroll event, pixel scroll event and - // test event. - InitEvent(scrollEvent); - scrollEvent.isShift = modKeyState.mIsShiftDown; - scrollEvent.isControl = modKeyState.mIsControlDown; - scrollEvent.isMeta = false; - scrollEvent.isAlt = modKeyState.mIsAltDown; - // Before dispatching line scroll event, we should get the current scroll // event target information for pixel scroll. MouseScrollHandler::ScrollTargetInfo scrollTargetInfo = handler->GetScrollTargetInfo(this, eventInfo, modKeyState); - // If we dispatch pixel scroll event after the line scroll event, - // we should set kHasPixels flag to the line scroll event. - scrollEvent.scrollFlags = eventInfo.GetScrollFlags(); - if (scrollTargetInfo.dispatchPixelScrollEvent) { - scrollEvent.scrollFlags |= nsMouseScrollEvent::kHasPixels; - } - - PRInt32 nativeDeltaForScroll = - eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForScroll; - - // NOTE: Don't use scrollTargetInfo.actualScrollAmount for computing the - // delta value of line/page scroll event. The value will be - // recomputed in ESM. - if (eventInfo.IsPage()) { - scrollEvent.delta = nativeDeltaForScroll * orienter / WHEEL_DELTA; - PRInt32 recomputedNativeDelta = scrollEvent.delta * orienter / WHEEL_DELTA; - lastEventInfo.mRemainingDeltaForScroll = - nativeDeltaForScroll - recomputedNativeDelta; - } else { - double deltaPerUnit; - deltaPerUnit = - (double)WHEEL_DELTA / eventInfo.GetScrollAmount(); - scrollEvent.delta = - RoundDelta((double)nativeDeltaForScroll * orienter / deltaPerUnit); - PRInt32 recomputedNativeDelta = - (PRInt32)(scrollEvent.delta * orienter * deltaPerUnit); - lastEventInfo.mRemainingDeltaForScroll = - nativeDeltaForScroll - recomputedNativeDelta; - } - - if (scrollEvent.delta) { + nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, this); + if (lastEventInfo.InitMouseScrollEvent(this, scrollEvent, + scrollTargetInfo, modKeyState)) { DispatchWindowEvent(&scrollEvent); if (mOnDestroyCalled) { lastEventInfo.ResetTransaction(); @@ -6356,45 +6307,14 @@ nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, } } - // If the query event failed, we cannot send pixel events. - if (!scrollTargetInfo.dispatchPixelScrollEvent) { - lastEventInfo.mRemainingDeltaForPixel = 0; - return; - } - nsMouseScrollEvent pixelEvent(true, NS_MOUSE_PIXEL_SCROLL, this); - InitEvent(pixelEvent); - pixelEvent.scrollFlags = nsMouseScrollEvent::kAllowSmoothScroll; - pixelEvent.scrollFlags |= eventInfo.IsVertical() ? - nsMouseScrollEvent::kIsVertical : nsMouseScrollEvent::kIsHorizontal; - if (scrollTargetInfo.actualScrollAction == - nsQueryContentEvent::SCROLL_ACTION_PAGE) { - pixelEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; - } - // Use same modifier state for pixel scroll event. - pixelEvent.isShift = scrollEvent.isShift; - pixelEvent.isControl = scrollEvent.isControl; - pixelEvent.isMeta = scrollEvent.isMeta; - pixelEvent.isAlt = scrollEvent.isAlt; - - PRInt32 nativeDeltaForPixel = - eventInfo.GetNativeDelta() + lastEventInfo.mRemainingDeltaForPixel; - // Pixel scroll event won't be recomputed the scroll amout and direction by - // ESM. Therefore, we need to set the computed amout and direction here. - PRInt32 orienterForPixel = - scrollTargetInfo.reversePixelScrollDirection ? -orienter : orienter; - - double deltaPerPixel = - (double)WHEEL_DELTA / scrollTargetInfo.actualScrollAmount / - scrollTargetInfo.pixelsPerUnit; - pixelEvent.delta = - RoundDelta((double)nativeDeltaForPixel * orienterForPixel / deltaPerPixel); - PRInt32 recomputedNativeDelta = - (PRInt32)(pixelEvent.delta * orienterForPixel * deltaPerPixel); - lastEventInfo.mRemainingDeltaForPixel = - nativeDeltaForPixel - recomputedNativeDelta; - if (pixelEvent.delta != 0) { + if (lastEventInfo.InitMousePixelScrollEvent(this, pixelEvent, + scrollTargetInfo, modKeyState)) { DispatchWindowEvent(&pixelEvent); + if (mOnDestroyCalled) { + lastEventInfo.ResetTransaction(); + return; + } } return; } @@ -8312,6 +8232,14 @@ nsModifierKeyState::nsModifierKeyState() mIsAltDown = IS_VK_DOWN(NS_VK_ALT); } +void +nsModifierKeyState::InitInputEvent(nsInputEvent& aInputEvent) const +{ + aInputEvent.isShift = mIsShiftDown; + aInputEvent.isControl = mIsControlDown; + aInputEvent.isMeta = false; + aInputEvent.isAlt = mIsAltDown; +} // Note that the result of GetTopLevelWindow method can be different from the // result of WinUtils::GetTopLevelHWND(). The result can be non-floating diff --git a/widget/windows/nsWindowDefs.h b/widget/windows/nsWindowDefs.h index 40bdf221539b..ad9d86448627 100644 --- a/widget/windows/nsWindowDefs.h +++ b/widget/windows/nsWindowDefs.h @@ -266,6 +266,8 @@ struct nsModifierKeyState { mIsAltDown(aIsAltDown) { } + + void InitInputEvent(nsInputEvent& aInputEvent) const; }; // Used for synthesizing events From 7bfc7cc5d93fa0b2b29f9c7285d6c7930ce85793 Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 17/88] Bug 672175 part.11 Move MOZ_WM_MOUSE*WHEEL handler into MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 99 ++++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 44 +++++------ widget/windows/nsWindow.cpp | 76 ------------------ widget/windows/nsWindow.h | 3 - 4 files changed, 120 insertions(+), 102 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 6faa446b14fc..f87065f3d156 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -139,6 +139,13 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, } return false; + case MOZ_WM_MOUSEVWHEEL: + case MOZ_WM_MOUSEHWHEEL: + GetInstance()->HandleMouseWheelMessage(aWindow, msg, wParam, lParam); + // Doesn't need to call next wndproc for internal wheel message. + aEatMessage = true; + return true; + case WM_KEYDOWN: case WM_KEYUP: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, @@ -266,6 +273,98 @@ MouseScrollHandler::GetScrollTargetInfo( return result; } +void +MouseScrollHandler::HandleMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + NS_ABORT_IF_FALSE( + (aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == MOZ_WM_MOUSEHWHEEL), + "HandleMouseWheelMessage must be called with " + "MOZ_WM_MOUSEVWHEEL or MOZ_WM_MOUSEHWHEEL"); + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: aWindow=%p, " + "aMessage=MOZ_WM_MOUSE%sWHEEL, aWParam=0x%08X, aLParam=0x%08X", + aWindow, aMessage == MOZ_WM_MOUSEVWHEEL ? "V" : "H", + aWParam, aLParam)); + + EventInfo eventInfo(aWindow, WinUtils::GetNativeMessage(aMessage), + aWParam, aLParam); + if (!eventInfo.CanDispatchMouseScrollEvent()) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: Cannot dispatch the events")); + mLastEventInfo.ResetTransaction(); + return; + } + + // Discard the remaining delta if current wheel message and last one are + // received by different window or to scroll different direction or + // different unit scroll. Furthermore, if the last event was too old. + if (!mLastEventInfo.CanContinueTransaction(eventInfo)) { + mLastEventInfo.ResetTransaction(); + } + + mLastEventInfo.RecordEvent(eventInfo); + + nsModifierKeyState modKeyState = GetModifierKeyState(); + + // Before dispatching line scroll event, we should get the current scroll + // event target information for pixel scroll. + ScrollTargetInfo scrollTargetInfo = + GetScrollTargetInfo(aWindow, eventInfo, modKeyState); + + // Grab the widget, it might be destroyed by a DOM event handler. + nsRefPtr kungFuDethGrip(aWindow); + + nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, aWindow); + if (mLastEventInfo.InitMouseScrollEvent(aWindow, scrollEvent, + scrollTargetInfo, modKeyState)) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: dispatching " + "NS_MOUSE_SCROLL event")); + DispatchEvent(aWindow, scrollEvent); + if (aWindow->Destroyed()) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: The window was destroyed " + "by NS_MOUSE_SCROLL event")); + mLastEventInfo.ResetTransaction(); + return; + } + } +#ifdef PR_LOGGING + else { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: NS_MOUSE_SCROLL event is not " + "dispatched")); + } +#endif + + nsMouseScrollEvent pixelEvent(true, NS_MOUSE_PIXEL_SCROLL, aWindow); + if (mLastEventInfo.InitMousePixelScrollEvent(aWindow, pixelEvent, + scrollTargetInfo, modKeyState)) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: dispatching " + "NS_MOUSE_PIXEL_SCROLL event")); + DispatchEvent(aWindow, pixelEvent); + if (aWindow->Destroyed()) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: The window was destroyed " + "by NS_MOUSE_PIXEL_SCROLL event")); + mLastEventInfo.ResetTransaction(); + return; + } + } +#ifdef PR_LOGGING + else { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleMouseWheelMessage: NS_MOUSE_PIXEL_SCROLL event is " + "not dispatched")); + } +#endif +} + /****************************************************************************** * * EventInfo diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index af895af1d4fe..362bfec41d32 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -35,13 +35,6 @@ public: LRESULT *aRetValue, bool &aEatMessage); - /** - * GetModifierKeyState() returns current modifier key state. - * Note that some devices need some hack for the modifier key state. - * This method does it automatically. - */ - static nsModifierKeyState GetModifierKeyState(); - private: MouseScrollHandler(); ~MouseScrollHandler(); @@ -55,7 +48,27 @@ private: */ static bool DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent); -public: + /** + * GetModifierKeyState() returns current modifier key state. + * Note that some devices need some hack for the modifier key state. + * This method does it automatically. + */ + static nsModifierKeyState GetModifierKeyState(); + + /** + * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and + * MOZ_WM_MOUSEHWHEEL which are posted when one of our windows received + * WM_MOUSEWHEEL or WM_MOUSEHWHEEL for avoiding deadlock with OOPP. + * + * @param aWindow A window which receives the wheel message. + * @param aMessage MOZ_WM_MOUSEWHEEL or MOZ_WM_MOUSEHWHEEL. + * @param aWParam The wParam value of the original message. + * @param aLParam The lParam value of the original message. + */ + void HandleMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); class EventInfo; /** @@ -102,11 +115,6 @@ public: bool IsPositive() const { return (mDelta > 0); } bool IsPage() const { return mIsPage; } - LRESULT ComputeMessageResult(bool aWeProcessed) const - { - return IsVertical() ? !aWeProcessed : aWeProcessed; - } - /** * @return Number of lines or pages scrolled per WHEEL_DELTA. */ @@ -208,12 +216,8 @@ public: PRInt32 mRemainingDeltaForPixel; }; - LastEventInfo& GetLastEventInfo() { return mLastEventInfo; } - -private: LastEventInfo mLastEventInfo; -public: class SystemSettings { public: SystemSettings() : mInitialized(false) {} @@ -240,12 +244,6 @@ public: PRInt32 mScrollChars; }; - SystemSettings& GetSystemSettings() - { - return mSystemSettings; - } - -private: SystemSettings mSystemSettings; public: diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 2ade34875fa4..67736366fd2a 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -5221,21 +5221,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, // our window again, it causes making infinite message loop. return true; - case MOZ_WM_MOUSEVWHEEL: - case MOZ_WM_MOUSEHWHEEL: - { - UINT nativeMessage = WinUtils::GetNativeMessage(msg); - // If OnMouseWheel returns true, the event was forwarded directly to another - // mozilla window message handler (ProcessMessage). In this case the return - // value of the forwarded event is in 'result' which we should return immediately. - // If OnMouseWheel returns false, OnMouseWheel processed the event internally. - // 'result' and 'aRetValue' will be set based on what we did with the event, so - // we should fall through. - OnMouseWheelInternal(nativeMessage, wParam, lParam, aRetValue); - // Doesn't need to call next wndproc for internal message. - return true; - } - case WM_DWMCOMPOSITIONCHANGED: // First, update the compositor state to latest one. All other methods // should use same state as here for consistency painting. @@ -6258,67 +6243,6 @@ bool nsWindow::OnGesture(WPARAM wParam, LPARAM lParam) return true; // Handled } -/** - * OnMouseWheelInternal - mouse wheel event processing. - * aMessage may be WM_MOUSEWHEEL or WM_MOUSEHWHEEL but this is called when - * ProcessMessage() handles MOZ_WM_MOUSEVWHEEL or MOZ_WM_MOUSEHWHEEL. - */ -void -nsWindow::OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, LPARAM aLParam, - LRESULT *aRetValue) -{ - MouseScrollHandler* handler = MouseScrollHandler::GetInstance(); - MouseScrollHandler::EventInfo eventInfo(this, aMessage, aWParam, aLParam); - if (!eventInfo.CanDispatchMouseScrollEvent()) { - handler->GetLastEventInfo().ResetTransaction(); - *aRetValue = eventInfo.ComputeMessageResult(false); - return; - } - - MouseScrollHandler::LastEventInfo& lastEventInfo = - handler->GetLastEventInfo(); - - // Discard the remaining delta if current wheel message and last one are - // received by different window or to scroll different direction or - // different unit scroll. Furthermore, if the last event was too old. - if (!lastEventInfo.CanContinueTransaction(eventInfo)) { - lastEventInfo.ResetTransaction(); - } - - lastEventInfo.RecordEvent(eventInfo); - - // means we process this message - *aRetValue = eventInfo.ComputeMessageResult(true); - - nsModifierKeyState modKeyState = MouseScrollHandler::GetModifierKeyState(); - - // Before dispatching line scroll event, we should get the current scroll - // event target information for pixel scroll. - MouseScrollHandler::ScrollTargetInfo scrollTargetInfo = - handler->GetScrollTargetInfo(this, eventInfo, modKeyState); - - nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, this); - if (lastEventInfo.InitMouseScrollEvent(this, scrollEvent, - scrollTargetInfo, modKeyState)) { - DispatchWindowEvent(&scrollEvent); - if (mOnDestroyCalled) { - lastEventInfo.ResetTransaction(); - return; - } - } - - nsMouseScrollEvent pixelEvent(true, NS_MOUSE_PIXEL_SCROLL, this); - if (lastEventInfo.InitMousePixelScrollEvent(this, pixelEvent, - scrollTargetInfo, modKeyState)) { - DispatchWindowEvent(&pixelEvent); - if (mOnDestroyCalled) { - lastEventInfo.ResetTransaction(); - return; - } - } - return; -} - static bool StringCaseInsensitiveEquals(const PRUnichar* aChars1, const PRUint32 aNumChars1, const PRUnichar* aChars2, const PRUint32 aNumChars2) diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index bc07b0e1aa5c..6d918a246296 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -407,9 +407,6 @@ protected: void OnWindowPosChanged(WINDOWPOS *wp, bool& aResult); void OnMouseWheel(UINT aMsg, WPARAM aWParam, LPARAM aLParam, LRESULT *aRetValue); - void OnMouseWheelInternal(UINT aMessage, WPARAM aWParam, - LPARAM aLParam, - LRESULT *aRetValue); void OnWindowPosChanging(LPWINDOWPOS& info); void OnSysColorChanged(); From 7c0c3c99dc2ea9c58eb9d3dbd691ea78e33897fd Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 18/88] Bug 672175 part.12 Move MOZ_WM_*SCROLL handler into MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 68 ++++++++++++++++++++++-- widget/windows/WinMouseScrollHandler.h | 20 ++++++- widget/windows/nsWindow.cpp | 44 --------------- widget/windows/nsWindow.h | 2 - 4 files changed, 84 insertions(+), 50 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index f87065f3d156..947b70750a8b 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -146,6 +146,14 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, aEatMessage = true; return true; + case MOZ_WM_HSCROLL: + case MOZ_WM_VSCROLL: + GetInstance()-> + HandleScrollMessageAsMouseWheelMessage(aWindow, msg, wParam, lParam); + // Doesn't need to call next wndproc for internal scroll message. + aEatMessage = true; + return true; + case WM_KEYDOWN: case WM_KEYUP: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, @@ -176,13 +184,14 @@ MouseScrollHandler::DispatchEvent(nsWindow* aWindow, nsGUIEvent& aEvent) /* static */ nsModifierKeyState -MouseScrollHandler::GetModifierKeyState() +MouseScrollHandler::GetModifierKeyState(UINT aMessage) { nsModifierKeyState result; // Assume the Control key is down if the Elantech touchpad has sent the // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) - if (!result.mIsControlDown) { + if ((aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == WM_MOUSEWHEEL) && + !result.mIsControlDown) { result.mIsControlDown = Device::Elantech::IsZooming(); } return result; @@ -308,7 +317,7 @@ MouseScrollHandler::HandleMouseWheelMessage(nsWindow* aWindow, mLastEventInfo.RecordEvent(eventInfo); - nsModifierKeyState modKeyState = GetModifierKeyState(); + nsModifierKeyState modKeyState = GetModifierKeyState(aMessage); // Before dispatching line scroll event, we should get the current scroll // event target information for pixel scroll. @@ -365,6 +374,59 @@ MouseScrollHandler::HandleMouseWheelMessage(nsWindow* aWindow, #endif } +void +MouseScrollHandler::HandleScrollMessageAsMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + NS_ABORT_IF_FALSE( + (aMessage == MOZ_WM_VSCROLL || aMessage == MOZ_WM_HSCROLL), + "HandleScrollMessageAsMouseWheelMessage must be called with " + "MOZ_WM_VSCROLL or MOZ_WM_HSCROLL"); + + nsModifierKeyState modKeyState = GetModifierKeyState(aMessage); + + nsMouseScrollEvent scrollEvent(true, NS_MOUSE_SCROLL, aWindow); + scrollEvent.scrollFlags = + (aMessage == MOZ_WM_VSCROLL) ? nsMouseScrollEvent::kIsVertical : + nsMouseScrollEvent::kIsHorizontal; + switch (LOWORD(aWParam)) { + case SB_PAGEDOWN: + scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; + case SB_LINEDOWN: + scrollEvent.delta = 1; + break; + case SB_PAGEUP: + scrollEvent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; + case SB_LINEUP: + scrollEvent.delta = -1; + break; + default: + return; + } + modKeyState.InitInputEvent(scrollEvent); + // XXX Current mouse position may not be same as when the original message + // is received. We need to know the actual mouse cursor position when + // the original message was received. + aWindow->InitEvent(scrollEvent); + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::HandleScrollMessageAsMouseWheelMessage: aWindow=%p, " + "aMessage=MOZ_WM_%sSCROLL, aWParam=0x%08X, aLParam=0x%08X, " + "scrollEvent { refPoint: { x: %d, y: %d }, delta: %d, " + "scrollFlags: 0x%04X, isShift: %s, isControl: %s, isAlt: %s, isMeta: %s }", + aWindow, (aMessage == MOZ_WM_VSCROLL) ? "V" : "H", + aWParam, aLParam, scrollEvent.refPoint.x, scrollEvent.refPoint.y, + scrollEvent.delta, scrollEvent.scrollFlags, + GetBoolName(scrollEvent.isShift), + GetBoolName(scrollEvent.isControl), + GetBoolName(scrollEvent.isAlt), + GetBoolName(scrollEvent.isMeta))); + + DispatchEvent(aWindow, scrollEvent); +} + /****************************************************************************** * * EventInfo diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 362bfec41d32..c441c9d9a891 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -52,8 +52,10 @@ private: * GetModifierKeyState() returns current modifier key state. * Note that some devices need some hack for the modifier key state. * This method does it automatically. + * + * @param aMessage Handling message. */ - static nsModifierKeyState GetModifierKeyState(); + static nsModifierKeyState GetModifierKeyState(UINT aMessage); /** * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and @@ -70,6 +72,22 @@ private: WPARAM aWParam, LPARAM aLParam); + /** + * HandleScrollMessageAsMouseWheelMessage() processes the MOZ_WM_VSCROLL and + * MOZ_WM_HSCROLL which are posted when one of mouse windows received + * WM_VSCROLL or WM_HSCROLL and user wants them to emulate mouse wheel + * message's behavior. + * + * @param aWindow A window which receives the scroll message. + * @param aMessage MOZ_WM_VSCROLL or MOZ_WM_HSCROLL. + * @param aWParam The wParam value of the original message. + * @param aLParam The lParam value of the original message. + */ + void HandleScrollMessageAsMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + class EventInfo; /** * GetScrollTargetInfo() returns scroll target information which is diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index 67736366fd2a..fb5ee58fd5df 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -5062,13 +5062,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, result = OnScroll(msg, wParam, lParam); break; - case MOZ_WM_HSCROLL: - case MOZ_WM_VSCROLL: - *aRetValue = 0; - OnScrollInternal(WinUtils::GetNativeMessage(msg), wParam, lParam); - // Doesn't need to call next wndproc for internal message. - return true; - // The WM_ACTIVATE event is fired when a window is raised or lowered, // and the loword of wParam specifies which. But we don't want to tell // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS @@ -7218,43 +7211,6 @@ nsWindow::OnScroll(UINT aMsg, WPARAM aWParam, LPARAM aLParam) return true; } -/** - * OnScrollInternal() is called when ProcessMessage() handles MOZ_WM_VSCROLL or - * MOZ_WM_HSCROLL but aMsg may be WM_VSCROLL or WM_HSCROLL. - * These internal messages used only when OnScroll() tries to emulate mouse - * wheel action for the WM_VSCROLL or WM_HSCROLL message. - */ -void -nsWindow::OnScrollInternal(UINT aMsg, WPARAM aWParam, LPARAM aLParam) -{ - nsMouseScrollEvent scrollevent(true, NS_MOUSE_SCROLL, this); - scrollevent.scrollFlags = (aMsg == WM_VSCROLL) - ? nsMouseScrollEvent::kIsVertical - : nsMouseScrollEvent::kIsHorizontal; - switch (LOWORD(aWParam)) { - case SB_PAGEDOWN: - scrollevent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; - case SB_LINEDOWN: - scrollevent.delta = 1; - break; - case SB_PAGEUP: - scrollevent.scrollFlags |= nsMouseScrollEvent::kIsFullPage; - case SB_LINEUP: - scrollevent.delta = -1; - break; - default: - return; - } - scrollevent.isShift = IS_VK_DOWN(NS_VK_SHIFT); - scrollevent.isControl = IS_VK_DOWN(NS_VK_CONTROL); - scrollevent.isMeta = false; - scrollevent.isAlt = IS_VK_DOWN(NS_VK_ALT); - InitEvent(scrollevent); - if (mEventCallback) { - DispatchWindowEvent(&scrollevent); - } -} - // Can be overriden. Controls auto-erase of background. bool nsWindow::AutoErase(HDC dc) { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 6d918a246296..2d36c7b2a281 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -397,8 +397,6 @@ protected: const MSG *aMsg = nsnull, bool *aEventDispatched = nsnull); bool OnScroll(UINT aMsg, WPARAM aWParam, LPARAM aLParam); - void OnScrollInternal(UINT aMsg, WPARAM aWParam, - LPARAM aLParam); bool OnGesture(WPARAM wParam, LPARAM lParam); bool OnTouch(WPARAM wParam, LPARAM lParam); bool OnHotKey(WPARAM wParam, LPARAM lParam); From abc50845c27d67685bdc7b1aa24388b3507ca35e Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 19/88] Bug 672175 part.13 Compute cursor position at WM_MOUSEWHEEL and WM_MOUSEHWHEEL in MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 68 ++++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 29 ++++++++++ widget/windows/nsWindow.cpp | 33 +----------- 3 files changed, 99 insertions(+), 31 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 947b70750a8b..06dfd9f23dd3 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -65,6 +65,8 @@ bool MouseScrollHandler::Device::Elantech::sUseSwipeHack = false; bool MouseScrollHandler::Device::Elantech::sUsePinchHack = false; DWORD MouseScrollHandler::Device::Elantech::sZoomUntil = 0; +bool MouseScrollHandler::Device::SetPoint::sMightBeUsing = false; + // The duration until timeout of events transaction. The value is 1.5 sec, // it's just a magic number, it was suggested by Logitech's engineer, see // bug 605648 comment 90. @@ -197,6 +199,26 @@ MouseScrollHandler::GetModifierKeyState(UINT aMessage) return result; } +POINT +MouseScrollHandler::ComputeMessagePos(UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + POINT point; + if (Device::SetPoint::IsGetMessagePosResponseValid(aMessage, + aWParam, aLParam)) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ComputeMessagePos: Using ::GetCursorPos()")); + ::GetCursorPos(&point); + } else { + DWORD dwPoints = ::GetMessagePos(); + point.x = GET_X_LPARAM(dwPoints); + point.y = GET_Y_LPARAM(dwPoints); + } + + return point; +} + MouseScrollHandler::ScrollTargetInfo MouseScrollHandler::GetScrollTargetInfo( nsWindow* aWindow, @@ -1151,5 +1173,51 @@ MouseScrollHandler::Device::UltraNav::IsObsoleteDriverInstalled() return majorVersion < 15 || majorVersion == 15 && minorVersion == 0; } +/****************************************************************************** + * + * Device::SetPoint + * + ******************************************************************************/ + +/* static */ +bool +MouseScrollHandler::Device::SetPoint::IsGetMessagePosResponseValid( + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + if (aMessage != WM_MOUSEHWHEEL) { + return false; + } + + DWORD messagePos = ::GetMessagePos(); + + // XXX We should check whether SetPoint is installed or not by registry. + + // SetPoint, Logitech (Logicool) mouse driver, (confirmed with 4.82.11 and + // MX-1100) always sets 0 to the lParam of WM_MOUSEHWHEEL. The driver SENDs + // one message at first time, this time, ::GetMessagePos() works fine. + // Then, we will return 0 (0 means we process it) to the message. Then, the + // driver will POST the same messages continuously during the wheel tilted. + // But ::GetMessagePos() API always returns (0, 0) for them, even if the + // actual mouse cursor isn't 0,0. Therefore, we cannot trust the result of + // ::GetMessagePos API if the sender is SetPoint. + if (!sMightBeUsing && !aLParam && (DWORD)aLParam != messagePos && + ::InSendMessage()) { + sMightBeUsing = true; + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " + "Might using SetPoint")); + } else if (sMightBeUsing && aLParam != 0 && ::InSendMessage()) { + // The user has changed the mouse from Logitech's to another one (e.g., + // the user has changed to the touchpad of the notebook. + sMightBeUsing = false; + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " + "Might stop using SetPoint")); + } + return (sMightBeUsing && !aLParam && !messagePos); +} + } // namespace widget } // namespace mozilla diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index c441c9d9a891..2795239c2b3f 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -35,6 +35,21 @@ public: LRESULT *aRetValue, bool &aEatMessage); + /** + * ComputeMessagePos() computes the cursor position when the message was + * added to the queue. + * + * @param aMessage Handling message. + * @param aWParam Handling message's wParam. + * @param aLParam Handling message's lParam. + * @return Mouse cursor position when the message is added to + * the queue or current cursor position if the result of + * ::GetMessagePos() is broken. + */ + POINT ComputeMessagePos(UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + private: MouseScrollHandler(); ~MouseScrollHandler(); @@ -367,6 +382,20 @@ public: static bool IsObsoleteDriverInstalled(); }; // class UltraNav + class SetPoint { + public: + /** + * SetPoint, Logitech's mouse driver, may report wrong cursor position + * for WM_MOUSEHWHEEL message. See comment in the implementation for + * the detail. + */ + static bool IsGetMessagePosResponseValid(UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + private: + static bool sMightBeUsing; + }; + static void Init(); static bool IsFakeScrollableWindowNeeded() diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index fb5ee58fd5df..cd4bb3470b22 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -7042,37 +7042,8 @@ nsWindow::OnMouseWheel(UINT aMsg, WPARAM aWParam, LPARAM aLParam, { *aRetValue = (aMsg != WM_MOUSEHWHEEL) ? TRUE : FALSE; - POINT point; - DWORD dwPoints = ::GetMessagePos(); - point.x = GET_X_LPARAM(dwPoints); - point.y = GET_Y_LPARAM(dwPoints); - - static bool sMayBeUsingLogitechMouse = false; - if (aMsg == WM_MOUSEHWHEEL) { - // Logitech (Logicool) mouse driver (confirmed with 4.82.11 and MX-1100) - // always sets 0 to the lParam of WM_MOUSEHWHEEL. The driver SENDs one - // message at first time, this time, ::GetMessagePos works fine. - // Then, we will return 0 (0 means we process it) to the message. Then, the - // driver will POST the same messages continuously during the wheel tilted. - // But ::GetMessagePos API always returns (0, 0), even if the actual mouse - // cursor isn't 0,0. Therefore, we cannot trust the result of - // ::GetMessagePos API if the sender is the driver. - if (!sMayBeUsingLogitechMouse && aLParam == 0 && (DWORD)aLParam != dwPoints && - ::InSendMessage()) { - sMayBeUsingLogitechMouse = true; - } else if (sMayBeUsingLogitechMouse && aLParam != 0 && ::InSendMessage()) { - // The user has changed the mouse from Logitech's to another one (e.g., - // the user has changed to the touchpad of the notebook. - sMayBeUsingLogitechMouse = false; - } - // If the WM_MOUSEHWHEEL comes from Logitech's mouse driver, and the - // ::GetMessagePos isn't correct, probably, we should use ::GetCursorPos - // instead. - if (sMayBeUsingLogitechMouse && aLParam == 0 && dwPoints == 0) { - ::GetCursorPos(&point); - } - } - + MouseScrollHandler* handler = MouseScrollHandler::GetInstance(); + POINT point = handler->ComputeMessagePos(aMsg, aWParam, aLParam); HWND underCursorWnd = ::WindowFromPoint(point); if (!underCursorWnd) { return; From d58c77b157b573ea853ad17f6cb836e82247f4ec Mon Sep 17 00:00:00 2001 From: Masayuki Nakano Date: Tue, 6 Mar 2012 12:20:29 +0900 Subject: [PATCH 20/88] Bug 672175 part.14 Move WM_MOUSE*WHEEL and WM_*SCROLL handlers into MouseScrollHandler r=jimm --- widget/windows/WinMouseScrollHandler.cpp | 215 +++++++++++++++++++++++ widget/windows/WinMouseScrollHandler.h | 73 +++++--- widget/windows/nsWindow.cpp | 166 ----------------- widget/windows/nsWindow.h | 3 - widget/xpwidgets/nsBaseWidget.h | 2 + 5 files changed, 268 insertions(+), 191 deletions(-) diff --git a/widget/windows/WinMouseScrollHandler.cpp b/widget/windows/WinMouseScrollHandler.cpp index 06dfd9f23dd3..4a755f97d439 100644 --- a/widget/windows/WinMouseScrollHandler.cpp +++ b/widget/windows/WinMouseScrollHandler.cpp @@ -141,6 +141,25 @@ MouseScrollHandler::ProcessMessage(nsWindow* aWindow, UINT msg, } return false; + case WM_MOUSEWHEEL: + case WM_MOUSEHWHEEL: + GetInstance()-> + ProcessNativeMouseWheelMessage(aWindow, msg, wParam, lParam); + // We don't need to call next wndproc for WM_MOUSEWHEEL and + // WM_MOUSEHWHEEL. We should consume them always. If the messages + // would be handled by our window again, it caused making infinite + // message loop. + aEatMessage = true; + *aRetValue = (msg != WM_MOUSEHWHEEL); + return true; + + case WM_HSCROLL: + case WM_VSCROLL: + aEatMessage = + GetInstance()->ProcessNativeScrollMessage(aWindow, msg, wParam, lParam); + *aRetValue = 0; + return true; + case MOZ_WM_MOUSEVWHEEL: case MOZ_WM_MOUSEHWHEEL: GetInstance()->HandleMouseWheelMessage(aWindow, msg, wParam, lParam); @@ -304,6 +323,202 @@ MouseScrollHandler::GetScrollTargetInfo( return result; } +void +MouseScrollHandler::ProcessNativeMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + POINT point = ComputeMessagePos(aMessage, aWParam, aLParam); + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: aWindow=%p, " + "aMessage=%s, wParam=0x%08X, lParam=0x%08X, point: { x=%d, y=%d }", + aWindow, aMessage == WM_MOUSEWHEEL ? "WM_MOUSEWHEEL" : + aMessage == WM_MOUSEHWHEEL ? "WM_MOUSEHWHEEL" : + aMessage == WM_VSCROLL ? "WM_VSCROLL" : "WM_HSCROLL", + aWParam, aLParam, point.x, point.y)); + LOG_KEYSTATE(); + + HWND underCursorWnd = ::WindowFromPoint(point); + if (!underCursorWnd) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "No window is not found under the cursor")); + return; + } + + if (Device::Elantech::IsPinchHackNeeded() && + Device::Elantech::IsHelperWindow(underCursorWnd)) { + // The Elantech driver places a window right underneath the cursor + // when sending a WM_MOUSEWHEEL event to us as part of a pinch-to-zoom + // gesture. We detect that here, and search for our window that would + // be beneath the cursor if that window wasn't there. + underCursorWnd = WinUtils::FindOurWindowAtPoint(point); + if (!underCursorWnd) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "Our window is not found under the Elantech helper window")); + return; + } + } + + // Handle most cases first. If the window under mouse cursor is our window + // except plugin window (MozillaWindowClass), we should handle the message + // on the window. + if (WinUtils::IsOurProcessWindow(underCursorWnd)) { + nsWindow* destWindow = WinUtils::GetNSWindowPtr(underCursorWnd); + if (!destWindow) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "Found window under the cursor isn't managed by nsWindow...")); + HWND wnd = ::GetParent(underCursorWnd); + for (; wnd; wnd = ::GetParent(wnd)) { + destWindow = WinUtils::GetNSWindowPtr(wnd); + if (destWindow) { + break; + } + } + if (!wnd) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: Our window which is " + "managed by nsWindow is not found under the cursor")); + return; + } + } + + MOZ_ASSERT(destWindow, "destWindow must not be NULL"); + + // If the found window is our plugin window, it means that the message + // has been handled by the plugin but not consumed. We should handle the + // message on its parent window. However, note that the DOM event may + // cause accessing the plugin. Therefore, we should unlock the plugin + // process by using PostMessage(). + if (destWindow->GetWindowType() == eWindowType_plugin) { + destWindow = destWindow->GetParentWindow(false); + if (!destWindow) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "Our window which is a parent of a plugin window is not found")); + return; + } + } + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " + "Posting internal message to an nsWindow (%p)...", + destWindow)); + UINT internalMessage = WinUtils::GetInternalMessage(aMessage); + ::PostMessage(destWindow->GetWindowHandle(), internalMessage, + aWParam, aLParam); + return; + } + + // If the window under cursor is not in our process, it means: + // 1. The window may be a plugin window (GeckoPluginWindow or its descendant). + // 2. The window may be another application's window. + HWND pluginWnd = WinUtils::FindOurProcessWindow(underCursorWnd); + if (!pluginWnd) { + // If there is no plugin window in ancestors of the window under cursor, + // the window is for another applications (case 2). + // We don't need to handle this message. + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: " + "Our window is not found under the cursor")); + return; + } + + // If we're a plugin window (MozillaWindowClass) and cursor in this window, + // the message shouldn't go to plugin's wndproc again. So, we should handle + // it on parent window. However, note that the DOM event may cause accessing + // the plugin. Therefore, we should unlock the plugin process by using + // PostMessage(). + if (aWindow->GetWindowType() == eWindowType_plugin && + aWindow->GetWindowHandle() == pluginWnd) { + nsWindow* destWindow = aWindow->GetParentWindow(false); + if (!destWindow) { + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: Our normal window which " + "is a parent of this plugin window is not found")); + return; + } + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " + "Posting internal message to an nsWindow (%p) which is parent of this " + "plugin window...", + destWindow)); + UINT internalMessage = WinUtils::GetInternalMessage(aMessage); + ::PostMessage(destWindow->GetWindowHandle(), internalMessage, + aWParam, aLParam); + return; + } + + // If the window is a part of plugin, we should post the message to it. + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " + "Redirecting the message to a window which is a plugin child window")); + ::PostMessage(underCursorWnd, aMessage, aWParam, aLParam); +} + +bool +MouseScrollHandler::ProcessNativeScrollMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam) +{ + if (aLParam || mUserPrefs.IsScrollMessageHandledAsWheelMessage()) { + // Scroll message generated by Thinkpad Trackpoint Driver or similar + // Treat as a mousewheel message and scroll appropriately + ProcessNativeMouseWheelMessage(aWindow, aMessage, aWParam, aLParam); + // Always consume the scroll message if we try to emulate mouse wheel + // action. + return true; + } + + PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, + ("MouseScroll::ProcessNativeScrollMessage: aWindow=%p, " + "aMessage=%s, wParam=0x%08X, lParam=0x%08X", + aWindow, aMessage == WM_VSCROLL ? "WM_VSCROLL" : "WM_HSCROLL", + aWParam, aLParam)); + + // Scroll message generated by external application + nsContentCommandEvent commandEvent(true, NS_CONTENT_COMMAND_SCROLL, aWindow); + + commandEvent.mScroll.mIsHorizontal = (aMessage == WM_HSCROLL); + + switch (LOWORD(aWParam)) { + case SB_LINEUP: // SB_LINELEFT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Line; + commandEvent.mScroll.mAmount = -1; + break; + case SB_LINEDOWN: // SB_LINERIGHT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Line; + commandEvent.mScroll.mAmount = 1; + break; + case SB_PAGEUP: // SB_PAGELEFT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Page; + commandEvent.mScroll.mAmount = -1; + break; + case SB_PAGEDOWN: // SB_PAGERIGHT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Page; + commandEvent.mScroll.mAmount = 1; + break; + case SB_TOP: // SB_LEFT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Whole; + commandEvent.mScroll.mAmount = -1; + break; + case SB_BOTTOM: // SB_RIGHT + commandEvent.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Whole; + commandEvent.mScroll.mAmount = 1; + break; + default: + return false; + } + // XXX If this is a plugin window, we should dispatch the event from + // parent window. + DispatchEvent(aWindow, commandEvent); + return true; +} + void MouseScrollHandler::HandleMouseWheelMessage(nsWindow* aWindow, UINT aMessage, diff --git a/widget/windows/WinMouseScrollHandler.h b/widget/windows/WinMouseScrollHandler.h index 2795239c2b3f..d5d1901885f6 100644 --- a/widget/windows/WinMouseScrollHandler.h +++ b/widget/windows/WinMouseScrollHandler.h @@ -35,21 +35,6 @@ public: LRESULT *aRetValue, bool &aEatMessage); - /** - * ComputeMessagePos() computes the cursor position when the message was - * added to the queue. - * - * @param aMessage Handling message. - * @param aWParam Handling message's wParam. - * @param aLParam Handling message's lParam. - * @return Mouse cursor position when the message is added to - * the queue or current cursor position if the result of - * ::GetMessagePos() is broken. - */ - POINT ComputeMessagePos(UINT aMessage, - WPARAM aWParam, - LPARAM aLParam); - private: MouseScrollHandler(); ~MouseScrollHandler(); @@ -72,6 +57,42 @@ private: */ static nsModifierKeyState GetModifierKeyState(UINT aMessage); + /** + * ProcessNativeMouseWheelMessage() processes WM_MOUSEWHEEL and + * WM_MOUSEHWHEEL. Additionally, processes WM_VSCROLL and WM_HSCROLL if they + * should be processed as mouse wheel message. + * This method posts MOZ_WM_MOUSEVWHEEL, MOZ_WM_MOUSEHWHEEL, + * MOZ_WM_VSCROLL or MOZ_WM_HSCROLL if we need to dispatch mouse scroll + * events. That avoids deadlock with plugin process. + * + * @param aWindow A window which receives the message. + * @param aMessage WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or + * WM_HSCROLL. + * @param aWParam The wParam value of the message. + * @param aLParam The lParam value of the message. + */ + void ProcessNativeMouseWheelMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + + /** + * ProcessNativeScrollMessage() processes WM_VSCROLL and WM_HSCROLL. + * This method just call ProcessMouseWheelMessage() if the message should be + * processed as mouse wheel message. Otherwise, dispatches a content + * command event. + * + * @param aWindow A window which receives the message. + * @param aMessage WM_VSCROLL or WM_HSCROLL. + * @param aWParam The wParam value of the message. + * @param aLParam The lParam value of the message. + * @return TRUE if the message is processed. Otherwise, FALSE. + */ + bool ProcessNativeScrollMessage(nsWindow* aWindow, + UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + /** * HandleMouseWheelMessage() processes MOZ_WM_MOUSEVWHEEL and * MOZ_WM_MOUSEHWHEEL which are posted when one of our windows received @@ -103,6 +124,21 @@ private: WPARAM aWParam, LPARAM aLParam); + /** + * ComputeMessagePos() computes the cursor position when the message was + * added to the queue. + * + * @param aMessage Handling message. + * @param aWParam Handling message's wParam. + * @param aLParam Handling message's lParam. + * @return Mouse cursor position when the message is added to + * the queue or current cursor position if the result of + * ::GetMessagePos() is broken. + */ + POINT ComputeMessagePos(UINT aMessage, + WPARAM aWParam, + LPARAM aLParam); + class EventInfo; /** * GetScrollTargetInfo() returns scroll target information which is @@ -279,7 +315,6 @@ private: SystemSettings mSystemSettings; -public: class UserPrefs { public: UserPrefs(); @@ -313,12 +348,6 @@ public: bool mScrollMessageHandledAsWheelMessage; }; - UserPrefs& GetUserPrefs() - { - return mUserPrefs; - } - -private: UserPrefs mUserPrefs; public: diff --git a/widget/windows/nsWindow.cpp b/widget/windows/nsWindow.cpp index cd4bb3470b22..e499c5bcdbd1 100644 --- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -5056,12 +5056,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, } break; - case WM_HSCROLL: - case WM_VSCROLL: - *aRetValue = 0; - result = OnScroll(msg, wParam, lParam); - break; - // The WM_ACTIVATE event is fired when a window is raised or lowered, // and the loword of wParam specifies which. But we don't want to tell // the focus system about this until the WM_SETFOCUS or WM_KILLFOCUS @@ -5206,14 +5200,6 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, } break; - case WM_MOUSEWHEEL: - case WM_MOUSEHWHEEL: - OnMouseWheel(msg, wParam, lParam, aRetValue); - // We don't need to call next wndproc WM_MOUSEWHEEL and WM_MOUSEHWHEEL. - // We should consume them always. If the messages would be handled by - // our window again, it causes making infinite message loop. - return true; - case WM_DWMCOMPOSITIONCHANGED: // First, update the compositor state to latest one. All other methods // should use same state as here for consistency painting. @@ -7030,158 +7016,6 @@ bool nsWindow::OnHotKey(WPARAM wParam, LPARAM lParam) return true; } -/** - * OnMouseWheel() is called when ProcessMessage() handles WM_MOUSEWHEEL, - * WM_MOUSEHWHEEL and also OnScroll() tries to emulate mouse wheel action for - * WM_VSCROLL or WM_HSCROLL. - * So, aMsg may be WM_MOUSEWHEEL, WM_MOUSEHWHEEL, WM_VSCROLL or WM_HSCROLL. - */ -void -nsWindow::OnMouseWheel(UINT aMsg, WPARAM aWParam, LPARAM aLParam, - LRESULT *aRetValue) -{ - *aRetValue = (aMsg != WM_MOUSEHWHEEL) ? TRUE : FALSE; - - MouseScrollHandler* handler = MouseScrollHandler::GetInstance(); - POINT point = handler->ComputeMessagePos(aMsg, aWParam, aLParam); - HWND underCursorWnd = ::WindowFromPoint(point); - if (!underCursorWnd) { - return; - } - - if (MouseScrollHandler::Device::Elantech::IsPinchHackNeeded() && - MouseScrollHandler::Device::Elantech::IsHelperWindow(underCursorWnd)) { - // The Elantech driver places a window right underneath the cursor - // when sending a WM_MOUSEWHEEL event to us as part of a pinch-to-zoom - // gesture. We detect that here, and search for our window that would - // be beneath the cursor if that window wasn't there. - underCursorWnd = WinUtils::FindOurWindowAtPoint(point); - if (!underCursorWnd) { - return; - } - } - - // Handle most cases first. If the window under mouse cursor is our window - // except plugin window (MozillaWindowClass), we should handle the message - // on the window. - if (WinUtils::IsOurProcessWindow(underCursorWnd)) { - nsWindow* destWindow = WinUtils::GetNSWindowPtr(underCursorWnd); - if (!destWindow) { - NS_WARNING("We're not sure what cause this is."); - HWND wnd = ::GetParent(underCursorWnd); - for (; wnd; wnd = ::GetParent(wnd)) { - destWindow = WinUtils::GetNSWindowPtr(wnd); - if (destWindow) { - break; - } - } - if (!wnd) { - return; - } - } - - NS_ASSERTION(destWindow, "destWindow must not be NULL"); - // If the found window is our plugin window, it means that the message - // has been handled by the plugin but not consumed. We should handle the - // message on its parent window. However, note that the DOM event may - // cause accessing the plugin. Therefore, we should unlock the plugin - // process by using PostMessage(). - if (destWindow->mWindowType == eWindowType_plugin) { - destWindow = destWindow->GetParentWindow(false); - NS_ENSURE_TRUE(destWindow, ); - } - UINT internalMessage = WinUtils::GetInternalMessage(aMsg); - ::PostMessage(destWindow->mWnd, internalMessage, aWParam, aLParam); - return; - } - - // If the window under cursor is not in our process, it means: - // 1. The window may be a plugin window (GeckoPluginWindow or its descendant). - // 2. The window may be another application's window. - HWND pluginWnd = WinUtils::FindOurProcessWindow(underCursorWnd); - if (!pluginWnd) { - // If there is no plugin window in ancestors of the window under cursor, - // the window is for another applications (case 2). - // We don't need to handle this message. - return; - } - - // If we're a plugin window (MozillaWindowClass) and cursor in this window, - // the message shouldn't go to plugin's wndproc again. So, we should handle - // it on parent window. However, note that the DOM event may cause accessing - // the plugin. Therefore, we should unlock the plugin process by using - // PostMessage(). - if (mWindowType == eWindowType_plugin && pluginWnd == mWnd) { - nsWindow* destWindow = GetParentWindow(false); - NS_ENSURE_TRUE(destWindow, ); - UINT internalMessage = WinUtils::GetInternalMessage(aMsg); - ::PostMessage(destWindow->mWnd, internalMessage, aWParam, aLParam); - return; - } - - // If the window is a part of plugin, we should post the message to it. - ::PostMessage(underCursorWnd, aMsg, aWParam, aLParam); -} - -/** - * OnScroll() is called when ProcessMessage() handles WM_VSCROLL or WM_HSCROLL. - * aMsg may be WM_VSCROLL or WM_HSCROLL. - */ -bool -nsWindow::OnScroll(UINT aMsg, WPARAM aWParam, LPARAM aLParam) -{ - if (aLParam || - MouseScrollHandler::GetInstance()-> - GetUserPrefs().IsScrollMessageHandledAsWheelMessage()) { - // Scroll message generated by Thinkpad Trackpoint Driver or similar - // Treat as a mousewheel message and scroll appropriately - LRESULT retVal; - OnMouseWheel(aMsg, aWParam, aLParam, &retVal); - // Always consume the scroll message if we try to emulate mouse wheel - // action. - return true; - } - - // Scroll message generated by external application - nsContentCommandEvent command(true, NS_CONTENT_COMMAND_SCROLL, this); - - command.mScroll.mIsHorizontal = (aMsg == WM_HSCROLL); - - switch (LOWORD(aWParam)) - { - case SB_LINEUP: // SB_LINELEFT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Line; - command.mScroll.mAmount = -1; - break; - case SB_LINEDOWN: // SB_LINERIGHT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Line; - command.mScroll.mAmount = 1; - break; - case SB_PAGEUP: // SB_PAGELEFT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Page; - command.mScroll.mAmount = -1; - break; - case SB_PAGEDOWN: // SB_PAGERIGHT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Page; - command.mScroll.mAmount = 1; - break; - case SB_TOP: // SB_LEFT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Whole; - command.mScroll.mAmount = -1; - break; - case SB_BOTTOM: // SB_RIGHT - command.mScroll.mUnit = nsContentCommandEvent::eCmdScrollUnit_Whole; - command.mScroll.mAmount = 1; - break; - default: - return false; - } - // XXX If this is a plugin window, we should dispatch the event from - // parent window. - DispatchWindowEvent(&command); - return true; -} - // Can be overriden. Controls auto-erase of background. bool nsWindow::AutoErase(HDC dc) { diff --git a/widget/windows/nsWindow.h b/widget/windows/nsWindow.h index 2d36c7b2a281..d7690d23e7c7 100644 --- a/widget/windows/nsWindow.h +++ b/widget/windows/nsWindow.h @@ -396,15 +396,12 @@ protected: PRUint32 aFlags = 0, const MSG *aMsg = nsnull, bool *aEventDispatched = nsnull); - bool OnScroll(UINT aMsg, WPARAM aWParam, LPARAM aLParam); bool OnGesture(WPARAM wParam, LPARAM lParam); bool OnTouch(WPARAM wParam, LPARAM lParam); bool OnHotKey(WPARAM wParam, LPARAM lParam); BOOL OnInputLangChange(HKL aHKL); bool OnPaint(HDC aDC, PRUint32 aNestingLevel); void OnWindowPosChanged(WINDOWPOS *wp, bool& aResult); - void OnMouseWheel(UINT aMsg, WPARAM aWParam, - LPARAM aLParam, LRESULT *aRetValue); void OnWindowPosChanging(LPWINDOWPOS& info); void OnSysColorChanged(); diff --git a/widget/xpwidgets/nsBaseWidget.h b/widget/xpwidgets/nsBaseWidget.h index 44895698b7f0..c367c7f14fc1 100644 --- a/widget/xpwidgets/nsBaseWidget.h +++ b/widget/xpwidgets/nsBaseWidget.h @@ -237,6 +237,8 @@ public: bool Destroyed() { return mOnDestroyCalled; } + nsWindowType GetWindowType() { return mWindowType; } + protected: virtual void ResolveIconName(const nsAString &aIconName, From 92fde76754d422b55a5f58298818471e00b56afe Mon Sep 17 00:00:00 2001 From: Mark Hammond Date: Tue, 6 Mar 2012 15:44:37 +1100 Subject: [PATCH 21/88] Bug 728986: dump() writes to stdout instead of stderr. r=bent --- dom/workers/WorkerScope.cpp | 4 ++-- js/xpconnect/src/XPCComponents.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index cce8087b8b13..811ee8c9a758 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -528,8 +528,8 @@ private: #ifdef ANDROID __android_log_print(ANDROID_LOG_INFO, "Gecko", buffer.ptr()); #endif - fputs(buffer.ptr(), stderr); - fflush(stderr); + fputs(buffer.ptr(), stdout); + fflush(stdout); } return true; diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index d42c550265d4..e34cf533c982 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -2799,8 +2799,8 @@ SandboxDump(JSContext *cx, unsigned argc, jsval *vp) } #endif - fputs(cstr, stderr); - fflush(stderr); + fputs(cstr, stdout); + fflush(stdout); NS_Free(cstr); JS_SET_RVAL(cx, vp, JSVAL_TRUE); return true; From 3e39a7f97aa6bc0a45d8ef1be6c67b140a180278 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Mon, 5 Mar 2012 20:53:13 -0800 Subject: [PATCH 22/88] Bug 732640 - Part 1: fix some Cobertura warnings. r=trivial --- mobile/android/base/sync/CryptoRecord.java | 2 + .../android/base/sync/ExtendedJSONObject.java | 2 +- mobile/android/base/sync/GlobalSession.java | 7 ++- .../android/base/sync/SyncConfiguration.java | 61 +++++-------------- .../base/sync/repositories/domain/Record.java | 17 +++--- .../base/sync/stage/ServerSyncStage.java | 2 +- 6 files changed, 31 insertions(+), 60 deletions(-) diff --git a/mobile/android/base/sync/CryptoRecord.java b/mobile/android/base/sync/CryptoRecord.java index 4e3c0dc45e7a..8f69218caa45 100644 --- a/mobile/android/base/sync/CryptoRecord.java +++ b/mobile/android/base/sync/CryptoRecord.java @@ -152,6 +152,8 @@ public class CryptoRecord extends Record { * * @param jsonRecord * @return + * A CryptoRecord that encapsulates the provided record. + * * @throws NonObjectJSONException * @throws ParseException * @throws IOException diff --git a/mobile/android/base/sync/ExtendedJSONObject.java b/mobile/android/base/sync/ExtendedJSONObject.java index 0e9f195fd5d0..1ac2b57310ac 100644 --- a/mobile/android/base/sync/ExtendedJSONObject.java +++ b/mobile/android/base/sync/ExtendedJSONObject.java @@ -133,7 +133,7 @@ public class ExtendedJSONObject { /** * Return a server timestamp value as milliseconds since epoch. - * @param string + * @param key * @return A Long, or null if the value is non-numeric or doesn't exist. */ public Long getTimestamp(String key) { diff --git a/mobile/android/base/sync/GlobalSession.java b/mobile/android/base/sync/GlobalSession.java index d448ba8fa392..2fc068d12f4f 100644 --- a/mobile/android/base/sync/GlobalSession.java +++ b/mobile/android/base/sync/GlobalSession.java @@ -211,6 +211,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { * Advance and loop around the stages of a sync. * @param current * @return + * The next stage to execute. */ public static Stage nextStage(Stage current) { int index = current.ordinal() + 1; @@ -220,9 +221,6 @@ public class GlobalSession implements CredentialsSource, PrefsSource { /** * Move to the next stage in the syncing process. - * @param next - * The next stage. - * @throws NoSuchStageException if the stage does not exist. */ public void advance() { this.callback.handleStageCompleted(this.currentState, this); @@ -680,6 +678,9 @@ public class GlobalSession implements CredentialsSource, PrefsSource { * * @param engineName * @return + * true if the engine with the provided name is present in the + * meta/global "engines" object. + * * @throws MetaGlobalException */ public boolean engineIsEnabled(String engineName) throws MetaGlobalException { diff --git a/mobile/android/base/sync/SyncConfiguration.java b/mobile/android/base/sync/SyncConfiguration.java index 4a38bdd28f11..b0019136965b 100644 --- a/mobile/android/base/sync/SyncConfiguration.java +++ b/mobile/android/base/sync/SyncConfiguration.java @@ -1,39 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync; @@ -46,7 +13,6 @@ import org.mozilla.gecko.sync.crypto.KeyBundle; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; -import android.util.Log; public class SyncConfiguration implements CredentialsSource { @@ -229,7 +195,7 @@ public class SyncConfiguration implements CredentialsSource { * provide access to preferences. * * @param prefsPath - * @param context + * @param prefsSource */ public SyncConfiguration(String prefsPath, PrefsSource prefsSource) { this.prefsPath = prefsPath; @@ -238,7 +204,7 @@ public class SyncConfiguration implements CredentialsSource { } public SharedPreferences getPrefs() { - Log.d(LOG_TAG, "Returning prefs for " + prefsPath); + Logger.debug(LOG_TAG, "Returning prefs for " + prefsPath); return prefsSource.getPrefs(prefsPath, Utils.SHARED_PREFERENCES_MODE); } @@ -246,6 +212,8 @@ public class SyncConfiguration implements CredentialsSource { * Return a convenient accessor for part of prefs. * @param prefix * @return + * A ConfigurationBranch object representing this + * section of the preferences space. */ public ConfigurationBranch getBranch(String prefix) { return new ConfigurationBranch(this, prefix); @@ -257,14 +225,14 @@ public class SyncConfiguration implements CredentialsSource { String u = prefs.getString("clusterURL", null); try { clusterURL = new URI(u); - Log.i(LOG_TAG, "Set clusterURL from bundle: " + u); + Logger.info(LOG_TAG, "Set clusterURL from bundle: " + u); } catch (URISyntaxException e) { - Log.w(LOG_TAG, "Ignoring bundle clusterURL (" + u + "): invalid URI.", e); + Logger.warn(LOG_TAG, "Ignoring bundle clusterURL (" + u + "): invalid URI.", e); } } if (prefs.contains("syncID")) { syncID = prefs.getString("syncID", null); - Log.i(LOG_TAG, "Set syncID from bundle: " + syncID); + Logger.info(LOG_TAG, "Set syncID from bundle: " + syncID); } // TODO: MetaGlobal, password, infoCollections, collectionKeys. } @@ -371,8 +339,8 @@ public class SyncConfiguration implements CredentialsSource { public void setAndPersistClusterURL(URI u, SharedPreferences prefs) { boolean shouldPersist = (prefs != null) && (clusterURL == null); - Log.d(LOG_TAG, "Setting cluster URL to " + u.toASCIIString() + - (shouldPersist ? ". Persisting." : ". Not persisting.")); + Logger.debug(LOG_TAG, "Setting cluster URL to " + u.toASCIIString() + + (shouldPersist ? ". Persisting." : ". Not persisting.")); clusterURL = u; if (shouldPersist) { Editor edit = prefs.edit(); @@ -387,7 +355,7 @@ public class SyncConfiguration implements CredentialsSource { public void setClusterURL(URI u, SharedPreferences prefs) { if (u == null) { - Log.w(LOG_TAG, "Refusing to set cluster URL to null."); + Logger.warn(LOG_TAG, "Refusing to set cluster URL to null."); return; } URI uri = u.normalize(); @@ -396,7 +364,7 @@ public class SyncConfiguration implements CredentialsSource { return; } setAndPersistClusterURL(uri.resolve("/"), prefs); - Log.i(LOG_TAG, "Set cluster URL to " + clusterURL.toASCIIString() + ", given input " + u.toASCIIString()); + Logger.info(LOG_TAG, "Set cluster URL to " + clusterURL.toASCIIString() + ", given input " + u.toASCIIString()); } public void setClusterURL(String url) throws URISyntaxException { @@ -405,7 +373,6 @@ public class SyncConfiguration implements CredentialsSource { /** * Used for direct management of related prefs. - * @return */ public Editor getEditor() { return this.getPrefs().edit(); diff --git a/mobile/android/base/sync/repositories/domain/Record.java b/mobile/android/base/sync/repositories/domain/Record.java index 29ab7904234b..4ce59b4b6c17 100644 --- a/mobile/android/base/sync/repositories/domain/Record.java +++ b/mobile/android/base/sync/repositories/domain/Record.java @@ -150,11 +150,11 @@ public abstract class Record { } /** - * Return true iff the input is a Record which is substantially the - * same as this object. - * * @param o + * The object to which this object should be compared. * @return + * true iff the input is a Record which is substantially the + * same as this object. */ public boolean equalPayloads(Object o) { if (!this.equalIdentifiers(o)) { @@ -165,12 +165,14 @@ public abstract class Record { } /** - * Return true iff the input is a Record which is substantially the - * same as this object, considering the ability and desire two - * reconcile the two objects if possible. + * * * @param o + * The object to which this object should be compared. * @return + * true iff the input is a Record which is substantially the + * same as this object, considering the ability and desire to + * reconcile the two objects if possible. */ public boolean congruentWith(Object o) { if (!this.equalIdentifiers(o)) { @@ -306,13 +308,12 @@ public abstract class Record { } /** - * Return an identical copy of this record with the provided two values. - * * Oh for persistent data structures. * * @param guid * @param androidID * @return + * An identical copy of this record with the provided two values. */ public abstract Record copyWithIDs(String guid, long androidID); } diff --git a/mobile/android/base/sync/stage/ServerSyncStage.java b/mobile/android/base/sync/stage/ServerSyncStage.java index c591b00e2d6d..6679ed088ae3 100644 --- a/mobile/android/base/sync/stage/ServerSyncStage.java +++ b/mobile/android/base/sync/stage/ServerSyncStage.java @@ -73,7 +73,7 @@ public abstract class ServerSyncStage implements /** * Override these in your subclasses. * - * @return + * @return true if this stage should be executed. * @throws MetaGlobalException */ protected boolean isEnabled() throws MetaGlobalException { From eac977d975818979920cde44cdf8ba2386648654 Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Mon, 5 Mar 2012 20:53:14 -0800 Subject: [PATCH 23/88] Bug 732768 - Eliminate shared state and concurrency problems in tests. r=nalexander --- .../Crypto5MiddlewareRepositorySession.java | 148 +--------------- .../sync/middleware/MiddlewareRepository.java | 1 - .../MiddlewareRepositorySession.java | 164 ++++++++++++++++++ .../sync/repositories/RepositorySession.java | 122 ++++++++----- .../StoreTrackingRepositorySession.java | 4 +- .../AndroidBrowserBookmarksRepository.java | 43 +---- ...roidBrowserBookmarksRepositorySession.java | 6 +- .../AndroidBrowserRepositorySession.java | 16 +- .../sync/synchronizer/RecordsChannel.java | 10 +- .../synchronizer/SynchronizerSession.java | 20 ++- mobile/android/sync/java-sources.mn | 2 +- 11 files changed, 295 insertions(+), 241 deletions(-) create mode 100644 mobile/android/base/sync/middleware/MiddlewareRepositorySession.java diff --git a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java index e2af12a8b7da..cf37738bebfe 100644 --- a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java +++ b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java @@ -1,39 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.middleware; @@ -43,16 +10,12 @@ import java.util.concurrent.ExecutorService; import org.mozilla.gecko.sync.CryptoRecord; import org.mozilla.gecko.sync.crypto.CryptoException; import org.mozilla.gecko.sync.crypto.KeyBundle; +import org.mozilla.gecko.sync.repositories.InactiveSessionException; import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; import org.mozilla.gecko.sync.repositories.RecordFactory; import org.mozilla.gecko.sync.repositories.RepositorySession; -import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; -import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate; import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFetchRecordsDelegate; -import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate; -import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate; import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionStoreDelegate; -import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate; import org.mozilla.gecko.sync.repositories.domain.Record; /** @@ -94,14 +57,12 @@ import org.mozilla.gecko.sync.repositories.domain.Record; * @author rnewman * */ -public class Crypto5MiddlewareRepositorySession extends RepositorySession { +public class Crypto5MiddlewareRepositorySession extends MiddlewareRepositorySession { private KeyBundle keyBundle; - private RepositorySession inner; private RecordFactory recordFactory; public Crypto5MiddlewareRepositorySession(RepositorySession session, Crypto5MiddlewareRepository repository, RecordFactory recordFactory) { - super(repository); - this.inner = session; + super(session, repository); this.keyBundle = repository.keyBundle; this.recordFactory = recordFactory; } @@ -180,13 +141,6 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { return new DecryptingTransformingFetchDelegate(inner, this.keyBundle, this.recordFactory); } - @Override - public void guidsSince(long timestamp, - RepositorySessionGuidsSinceDelegate delegate) { - // TODO: need to do anything here? - inner.guidsSince(timestamp, delegate); - } - @Override public void fetchSince(long timestamp, RepositorySessionFetchRecordsDelegate delegate) { @@ -195,7 +149,7 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { @Override public void fetch(String[] guids, - RepositorySessionFetchRecordsDelegate delegate) { + RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException { inner.fetch(guids, makeUnwrappingDelegate(delegate)); } @@ -230,92 +184,4 @@ public class Crypto5MiddlewareRepositorySession extends RepositorySession { // Allow the inner session to do delegate handling. inner.store(rec); } - - @Override - public void wipe(RepositorySessionWipeDelegate delegate) { - inner.wipe(delegate); - } - - @Override - public void storeDone() { - inner.storeDone(); - } - - @Override - public void storeDone(long storeEnd) { - inner.storeDone(storeEnd); - } - - public class Crypto5MiddlewareRepositorySessionBeginDelegate implements RepositorySessionBeginDelegate { - private Crypto5MiddlewareRepositorySession outerSession; - private RepositorySessionBeginDelegate next; - - public Crypto5MiddlewareRepositorySessionBeginDelegate(Crypto5MiddlewareRepositorySession outerSession, RepositorySessionBeginDelegate next) { - this.outerSession = outerSession; - this.next = next; - } - - @Override - public void onBeginFailed(Exception ex) { - next.onBeginFailed(ex); - } - - @Override - public void onBeginSucceeded(RepositorySession session) { - outerSession.setStatus(SessionStatus.ACTIVE); - next.onBeginSucceeded(outerSession); - } - - @Override - public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) { - return this; - } - } - - public void begin(RepositorySessionBeginDelegate delegate) { - inner.begin(new Crypto5MiddlewareRepositorySessionBeginDelegate(this, delegate)); - } - - public class Crypto5MiddlewareRepositorySessionFinishDelegate implements RepositorySessionFinishDelegate { - private Crypto5MiddlewareRepositorySession outerSession; - private RepositorySessionFinishDelegate next; - - public Crypto5MiddlewareRepositorySessionFinishDelegate(Crypto5MiddlewareRepositorySession outerSession, RepositorySessionFinishDelegate next) { - this.outerSession = outerSession; - this.next = next; - } - - @Override - public void onFinishFailed(Exception ex) { - next.onFinishFailed(ex); - } - - @Override - public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) { - outerSession.setStatus(SessionStatus.DONE); - next.onFinishSucceeded(outerSession, bundle); - } - - @Override - public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) { - return this; - } - } - - @Override - public void finish(RepositorySessionFinishDelegate delegate) { - inner.finish(new Crypto5MiddlewareRepositorySessionFinishDelegate(this, delegate)); - } - - @Override - public void abort() { - setStatus(SessionStatus.ABORTED); - inner.abort(); - } - - @Override - public void abort(RepositorySessionFinishDelegate delegate) { - this.status = SessionStatus.DONE; // TODO: ABORTED? - inner.abort(new Crypto5MiddlewareRepositorySessionFinishDelegate(this, delegate)); - } } diff --git a/mobile/android/base/sync/middleware/MiddlewareRepository.java b/mobile/android/base/sync/middleware/MiddlewareRepository.java index 473bc1dbb6d0..abbc9200bcd9 100644 --- a/mobile/android/base/sync/middleware/MiddlewareRepository.java +++ b/mobile/android/base/sync/middleware/MiddlewareRepository.java @@ -51,6 +51,5 @@ public abstract class MiddlewareRepository extends Repository { public RepositorySessionCreationDelegate deferredCreationDelegate() { return this; } - } } diff --git a/mobile/android/base/sync/middleware/MiddlewareRepositorySession.java b/mobile/android/base/sync/middleware/MiddlewareRepositorySession.java new file mode 100644 index 000000000000..af9d9acf3b7b --- /dev/null +++ b/mobile/android/base/sync/middleware/MiddlewareRepositorySession.java @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.middleware; + +import java.util.concurrent.ExecutorService; + +import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.repositories.InactiveSessionException; +import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; +import org.mozilla.gecko.sync.repositories.RepositorySession; +import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionBeginDelegate; +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionFinishDelegate; +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionGuidsSinceDelegate; +import org.mozilla.gecko.sync.repositories.delegates.RepositorySessionWipeDelegate; + +public abstract class MiddlewareRepositorySession extends RepositorySession { + private static final String LOG_TAG = "MiddlewareSession"; + protected final RepositorySession inner; + + public MiddlewareRepositorySession(RepositorySession innerSession, MiddlewareRepository repository) { + super(repository); + this.inner = innerSession; + } + + @Override + public void wipe(RepositorySessionWipeDelegate delegate) { + inner.wipe(delegate); + } + + public class MiddlewareRepositorySessionBeginDelegate implements RepositorySessionBeginDelegate { + + private MiddlewareRepositorySession outerSession; + private RepositorySessionBeginDelegate next; + + public MiddlewareRepositorySessionBeginDelegate(MiddlewareRepositorySession outerSession, RepositorySessionBeginDelegate next) { + this.outerSession = outerSession; + this.next = next; + } + + @Override + public void onBeginFailed(Exception ex) { + next.onBeginFailed(ex); + } + + @Override + public void onBeginSucceeded(RepositorySession session) { + next.onBeginSucceeded(outerSession); + } + + @Override + public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) { + final RepositorySessionBeginDelegate deferred = next.deferredBeginDelegate(executor); + return new RepositorySessionBeginDelegate() { + @Override + public void onBeginSucceeded(RepositorySession session) { + if (inner != session) { + Logger.warn(LOG_TAG, "Got onBeginSucceeded for session " + session + ", not our inner session!"); + } + deferred.onBeginSucceeded(outerSession); + } + + @Override + public void onBeginFailed(Exception ex) { + deferred.onBeginFailed(ex); + } + + @Override + public RepositorySessionBeginDelegate deferredBeginDelegate(ExecutorService executor) { + return this; + } + }; + } + } + + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { + inner.begin(new MiddlewareRepositorySessionBeginDelegate(this, delegate)); + } + + public class MiddlewareRepositorySessionFinishDelegate implements RepositorySessionFinishDelegate { + private final MiddlewareRepositorySession outerSession; + private final RepositorySessionFinishDelegate next; + + public MiddlewareRepositorySessionFinishDelegate(MiddlewareRepositorySession outerSession, RepositorySessionFinishDelegate next) { + this.outerSession = outerSession; + this.next = next; + } + + @Override + public void onFinishFailed(Exception ex) { + next.onFinishFailed(ex); + } + + @Override + public void onFinishSucceeded(RepositorySession session, RepositorySessionBundle bundle) { + next.onFinishSucceeded(outerSession, bundle); + } + + @Override + public RepositorySessionFinishDelegate deferredFinishDelegate(ExecutorService executor) { + return this; + } + } + + @Override + public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException { + inner.finish(new MiddlewareRepositorySessionFinishDelegate(this, delegate)); + } + + + @Override + public synchronized void ensureActive() throws InactiveSessionException { + inner.ensureActive(); + } + + @Override + public synchronized boolean isActive() { + return inner.isActive(); + } + + @Override + public synchronized SessionStatus getStatus() { + return inner.getStatus(); + } + + @Override + public synchronized void setStatus(SessionStatus status) { + inner.setStatus(status); + } + + @Override + public synchronized void transitionFrom(SessionStatus from, SessionStatus to) + throws InvalidSessionTransitionException { + inner.transitionFrom(from, to); + } + + @Override + public void abort() { + inner.abort(); + } + + @Override + public void abort(RepositorySessionFinishDelegate delegate) { + inner.abort(new MiddlewareRepositorySessionFinishDelegate(this, delegate)); + } + + @Override + public void guidsSince(long timestamp, RepositorySessionGuidsSinceDelegate delegate) { + // TODO: need to do anything here? + inner.guidsSince(timestamp, delegate); + } + + @Override + public void storeDone() { + inner.storeDone(); + } + + @Override + public void storeDone(long storeEnd) { + inner.storeDone(storeEnd); + } +} \ No newline at end of file diff --git a/mobile/android/base/sync/repositories/RepositorySession.java b/mobile/android/base/sync/repositories/RepositorySession.java index f30777b616cc..443b763c0292 100644 --- a/mobile/android/base/sync/repositories/RepositorySession.java +++ b/mobile/android/base/sync/repositories/RepositorySession.java @@ -80,7 +80,7 @@ public abstract class RepositorySession { Logger.trace(LOG_TAG, message); } - protected SessionStatus status = SessionStatus.UNSTARTED; + private SessionStatus status = SessionStatus.UNSTARTED; protected Repository repository; protected RepositorySessionStoreDelegate delegate; @@ -109,7 +109,7 @@ public abstract class RepositorySession { public abstract void guidsSince(long timestamp, RepositorySessionGuidsSinceDelegate delegate); public abstract void fetchSince(long timestamp, RepositorySessionFetchRecordsDelegate delegate); - public abstract void fetch(String[] guids, RepositorySessionFetchRecordsDelegate delegate); + public abstract void fetch(String[] guids, RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException; public abstract void fetchAll(RepositorySessionFetchRecordsDelegate delegate); /** @@ -182,21 +182,19 @@ public abstract class RepositorySession { * */ protected void sharedBegin() throws InvalidSessionTransitionException { - if (this.status == SessionStatus.UNSTARTED) { - this.status = SessionStatus.ACTIVE; - } else { - Logger.error(LOG_TAG, "Tried to begin() an already active or finished session"); + Logger.debug(LOG_TAG, "Shared begin."); + if (delegateQueue.isShutdown()) { throw new InvalidSessionTransitionException(null); } + if (storeWorkQueue.isShutdown()) { + throw new InvalidSessionTransitionException(null); + } + this.transitionFrom(SessionStatus.UNSTARTED, SessionStatus.ACTIVE); } - public void begin(RepositorySessionBeginDelegate delegate) { - try { - sharedBegin(); - delegate.deferredBeginDelegate(delegateQueue).onBeginSucceeded(this); - } catch (Exception e) { - delegate.deferredBeginDelegate(delegateQueue).onBeginFailed(e); - } + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { + sharedBegin(); + delegate.deferredBeginDelegate(delegateQueue).onBeginSucceeded(this); } protected RepositorySessionBundle getBundle() { @@ -231,43 +229,85 @@ public abstract class RepositorySession { * @param delegate */ public void abort(RepositorySessionFinishDelegate delegate) { - this.status = SessionStatus.DONE; // TODO: ABORTED? + this.abort(); delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null)); } - public void finish(final RepositorySessionFinishDelegate delegate) { - if (this.status == SessionStatus.ACTIVE) { - this.status = SessionStatus.DONE; - delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null)); - } else { - Logger.error(LOG_TAG, "Tried to finish() an unstarted or already finished session"); - Exception e = new InvalidSessionTransitionException(null); - delegate.deferredFinishDelegate(delegateQueue).onFinishFailed(e); - } - Logger.info(LOG_TAG, "Shutting down work queues."); - // storeWorkQueue.shutdown(); - // delegateQueue.shutdown(); - } - - public boolean isActive() { - return status == SessionStatus.ACTIVE; - } - - public SessionStatus getStatus() { - return status; - } - - public void setStatus(SessionStatus status) { - this.status = status; - } - public void abort() { // TODO: do something here. - status = SessionStatus.ABORTED; + this.setStatus(SessionStatus.ABORTED); + try { + storeWorkQueue.shutdown(); + } catch (Exception e) { + Logger.error(LOG_TAG, "Caught exception shutting down store work queue.", e); + } + try { + delegateQueue.shutdown(); + } catch (Exception e) { + Logger.error(LOG_TAG, "Caught exception shutting down delegate queue.", e); + } + } + + public void finish(final RepositorySessionFinishDelegate delegate) throws InactiveSessionException { + try { + this.transitionFrom(SessionStatus.ACTIVE, SessionStatus.DONE); + delegate.deferredFinishDelegate(delegateQueue).onFinishSucceeded(this, this.getBundle(null)); + } catch (InvalidSessionTransitionException e) { + Logger.error(LOG_TAG, "Tried to finish() an unstarted or already finished session"); + InactiveSessionException ex = new InactiveSessionException(null); + ex.initCause(e); + throw ex; + } + + Logger.info(LOG_TAG, "Shutting down work queues."); storeWorkQueue.shutdown(); delegateQueue.shutdown(); } + /** + * Run the provided command if we're active and our delegate queue + * is not shut down. + * + * @param command + * @throws InactiveSessionException + */ + protected synchronized void executeDelegateCommand(Runnable command) + throws InactiveSessionException { + if (!isActive() || delegateQueue.isShutdown()) { + throw new InactiveSessionException(null); + } + delegateQueue.execute(command); + } + + public synchronized void ensureActive() throws InactiveSessionException { + if (!isActive()) { + throw new InactiveSessionException(null); + } + } + + public synchronized boolean isActive() { + return status == SessionStatus.ACTIVE; + } + + public synchronized SessionStatus getStatus() { + return status; + } + + public synchronized void setStatus(SessionStatus status) { + this.status = status; + } + + public synchronized void transitionFrom(SessionStatus from, SessionStatus to) throws InvalidSessionTransitionException { + if (from == null || this.status == from) { + Logger.trace(LOG_TAG, "Successfully transitioning from " + this.status + " to " + to); + + this.status = to; + return; + } + Logger.warn(LOG_TAG, "Wanted to transition from " + from + " but in state " + this.status); + throw new InvalidSessionTransitionException(null); + } + /** * Produce a record that is some combination of the remote and local records * provided. diff --git a/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java b/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java index 7c9743ee1ea4..32a792ec4df4 100644 --- a/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java +++ b/mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java @@ -24,7 +24,7 @@ public abstract class StoreTrackingRepositorySession extends RepositorySession { } @Override - public void begin(RepositorySessionBeginDelegate delegate) { + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { RepositorySessionBeginDelegate deferredDelegate = delegate.deferredBeginDelegate(delegateQueue); try { super.sharedBegin(); @@ -74,7 +74,7 @@ public abstract class StoreTrackingRepositorySession extends RepositorySession { } @Override - public void finish(RepositorySessionFinishDelegate delegate) { + public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException { super.finish(delegate); this.storeTracker = null; } diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepository.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepository.java index 6de0ac72fc3c..c23e6c08c769 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepository.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepository.java @@ -1,40 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Jason Voll - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.repositories.android; @@ -48,7 +14,8 @@ public class AndroidBrowserBookmarksRepository extends AndroidBrowserRepository @Override protected void sessionCreator(RepositorySessionCreationDelegate delegate, Context context) { AndroidBrowserBookmarksRepositorySession session = new AndroidBrowserBookmarksRepositorySession(AndroidBrowserBookmarksRepository.this, context); - delegate.onSessionCreated(session); + final RepositorySessionCreationDelegate deferredCreationDelegate = delegate.deferredCreationDelegate(); + deferredCreationDelegate.onSessionCreated(session); } @Override diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java index d229cd5bad5b..8f0044c04e9b 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java @@ -16,6 +16,8 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.repositories.InactiveSessionException; +import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; import org.mozilla.gecko.sync.repositories.NoGuidForIdException; import org.mozilla.gecko.sync.repositories.NullCursorException; import org.mozilla.gecko.sync.repositories.ParentNotFoundException; @@ -478,7 +480,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo } @Override - public void begin(RepositorySessionBeginDelegate delegate) { + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { // Check for the existence of special folders // and insert them if they don't exist. Cursor cur; @@ -526,7 +528,7 @@ public class AndroidBrowserBookmarksRepositorySession extends AndroidBrowserRepo } @Override - public void finish(RepositorySessionFinishDelegate delegate) { + public void finish(RepositorySessionFinishDelegate delegate) throws InactiveSessionException { // Override finish to do this check; make sure all records // needing re-parenting have been re-parented. if (needsReparenting != 0) { diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java index 566dfc0474bd..37beaa02972e 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java @@ -141,14 +141,9 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } @Override - public void begin(RepositorySessionBeginDelegate delegate) { + public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException { RepositorySessionBeginDelegate deferredDelegate = delegate.deferredBeginDelegate(delegateQueue); - try { - super.sharedBegin(); - } catch (InvalidSessionTransitionException e) { - deferredDelegate.onBeginFailed(e); - return; - } + super.sharedBegin(); try { // We do this check here even though it results in one extra call to the DB @@ -241,9 +236,9 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos @Override public void fetch(String[] guids, - RepositorySessionFetchRecordsDelegate delegate) { + RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException { FetchRunnable command = new FetchRunnable(guids, now(), null, delegate); - delegateQueue.execute(command); + executeDelegateCommand(command); } abstract class FetchingRunnable implements Runnable { @@ -289,7 +284,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos } } - class FetchRunnable extends FetchingRunnable { + public class FetchRunnable extends FetchingRunnable { private String[] guids; private long end; private RecordFilter filter; @@ -392,6 +387,7 @@ public abstract class AndroidBrowserRepositorySession extends StoreTrackingRepos @Override public void run() { if (!isActive()) { + Logger.warn(LOG_TAG, "AndroidBrowserRepositorySession is inactive. Store failing."); delegate.onRecordStoreFailed(new InactiveSessionException(null)); return; } diff --git a/mobile/android/base/sync/synchronizer/RecordsChannel.java b/mobile/android/base/sync/synchronizer/RecordsChannel.java index ff936364c12f..5f749f802cde 100644 --- a/mobile/android/base/sync/synchronizer/RecordsChannel.java +++ b/mobile/android/base/sync/synchronizer/RecordsChannel.java @@ -42,6 +42,7 @@ import java.util.concurrent.ExecutorService; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.ThreadPool; +import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; import org.mozilla.gecko.sync.repositories.NoStoreDelegateException; import org.mozilla.gecko.sync.repositories.RepositorySession; import org.mozilla.gecko.sync.repositories.delegates.DeferredRepositorySessionBeginDelegate; @@ -166,8 +167,9 @@ class RecordsChannel implements /** * Begin both sessions, invoking flow() when done. + * @throws InvalidSessionTransitionException */ - public void beginAndFlow() { + public void beginAndFlow() throws InvalidSessionTransitionException { Logger.info(LOG_TAG, "Beginning source."); source.begin(this); } @@ -251,7 +253,11 @@ class RecordsChannel implements public void onBeginSucceeded(RepositorySession session) { if (session == source) { Logger.info(LOG_TAG, "Source session began. Beginning sink session."); - sink.begin(this); + try { + sink.begin(this); + } catch (InvalidSessionTransitionException e) { + onBeginFailed(e); + } } if (session == sink) { Logger.info(LOG_TAG, "Sink session began. Beginning flow."); diff --git a/mobile/android/base/sync/synchronizer/SynchronizerSession.java b/mobile/android/base/sync/synchronizer/SynchronizerSession.java index 4f124d8bfa9b..328c1291b63b 100644 --- a/mobile/android/base/sync/synchronizer/SynchronizerSession.java +++ b/mobile/android/base/sync/synchronizer/SynchronizerSession.java @@ -40,6 +40,8 @@ package org.mozilla.gecko.sync.synchronizer; import java.util.concurrent.ExecutorService; +import org.mozilla.gecko.sync.repositories.InactiveSessionException; +import org.mozilla.gecko.sync.repositories.InvalidSessionTransitionException; import org.mozilla.gecko.sync.repositories.RepositorySession; import org.mozilla.gecko.sync.repositories.RepositorySessionBundle; import org.mozilla.gecko.sync.repositories.delegates.DeferrableRepositorySessionCreationDelegate; @@ -170,7 +172,11 @@ implements RecordsChannelDelegate, }; final RecordsChannel channelAToB = new RecordsChannel(this.sessionA, this.sessionB, channelDelegate); info("Starting A to B flow. Channel is " + channelAToB); - channelAToB.beginAndFlow(); + try { + channelAToB.beginAndFlow(); + } catch (InvalidSessionTransitionException e) { + onFlowBeginFailed(channelAToB, e); + } } @Override @@ -183,7 +189,11 @@ implements RecordsChannelDelegate, flowBToACompleted = true; // Finish the two sessions. - this.sessionA.finish(this); + try { + this.sessionA.finish(this); + } catch (InactiveSessionException e) { + this.onFinishFailed(e); + } } @Override @@ -297,7 +307,11 @@ implements RecordsChannelDelegate, if (this.sessionB != null) { info("Finishing session B."); // On to the next. - this.sessionB.finish(this); + try { + this.sessionB.finish(this); + } catch (InactiveSessionException e) { + this.onFinishFailed(e); + } } } else if (session == sessionB) { if (flowBToACompleted) { diff --git a/mobile/android/sync/java-sources.mn b/mobile/android/sync/java-sources.mn index ad12b40ea5e0..b7de774963af 100644 --- a/mobile/android/sync/java-sources.mn +++ b/mobile/android/sync/java-sources.mn @@ -1 +1 @@ -sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java +sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java From ac725614fe41bae7b88d5e7cb90fa44f38384bd3 Mon Sep 17 00:00:00 2001 From: Nick Alexander Date: Mon, 5 Mar 2012 20:53:14 -0800 Subject: [PATCH 24/88] Bug 732114 - Untangle Bookmark specific code in AndroidBrowserRepositoryTest. r=rnewman --- .../AndroidBrowserBookmarksDataAccessor.java | 72 +++++++++---------- ...ndroidBrowserHistoryRepositorySession.java | 1 - .../AndroidBrowserRepositoryDataAccessor.java | 19 ++++- .../sync/repositories/android/RepoUtils.java | 54 ++++++++++++++ 4 files changed, 103 insertions(+), 43 deletions(-) diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java index ae14f93160e5..35c21e4518dd 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java @@ -1,40 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Jason Voll - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.repositories.android; @@ -62,8 +28,29 @@ public class AndroidBrowserBookmarksDataAccessor extends AndroidBrowserRepositor */ private static final String BOOKMARK_IS_FOLDER = BrowserContract.Bookmarks.IS_FOLDER + " = 1"; private static final String GUID_NOT_TAGS_OR_PLACES = BrowserContract.SyncColumns.GUID + " NOT IN ('" + - BrowserContract.Bookmarks.TAGS_FOLDER_GUID + "', '" + - BrowserContract.Bookmarks.PLACES_FOLDER_GUID + "')"; + BrowserContract.Bookmarks.TAGS_FOLDER_GUID + "', '" + + BrowserContract.Bookmarks.PLACES_FOLDER_GUID + "')"; + + private static final String EXCLUDE_SPECIAL_GUIDS_WHERE_CLAUSE; + static { + if (AndroidBrowserBookmarksRepositorySession.SPECIAL_GUIDS.length > 0) { + StringBuilder b = new StringBuilder(BrowserContract.SyncColumns.GUID + " NOT IN ("); + + int remaining = AndroidBrowserBookmarksRepositorySession.SPECIAL_GUIDS.length - 1; + for (String specialGuid : AndroidBrowserBookmarksRepositorySession.SPECIAL_GUIDS) { + b.append('"'); + b.append(specialGuid); + b.append('"'); + if (remaining-- > 0) { + b.append(", "); + } + } + b.append(')'); + EXCLUDE_SPECIAL_GUIDS_WHERE_CLAUSE = b.toString(); + } else { + EXCLUDE_SPECIAL_GUIDS_WHERE_CLAUSE = null; // null is a valid WHERE clause. + } + } public static final String TYPE_FOLDER = "folder"; public static final String TYPE_BOOKMARK = "bookmark"; @@ -84,6 +71,13 @@ public class AndroidBrowserBookmarksDataAccessor extends AndroidBrowserRepositor return BrowserContractHelpers.BOOKMARKS_POSITIONS_CONTENT_URI; } + @Override + public void wipe() { + Uri uri = getUri(); + Logger.info(LOG_TAG, "wiping (except for special guids): " + uri); + context.getContentResolver().delete(uri, EXCLUDE_SPECIAL_GUIDS_WHERE_CLAUSE, null); + } + protected Cursor getGuidsIDsForFolders() throws NullCursorException { // Exclude "places" and "tags", in case they've ended up in the DB. String where = BOOKMARK_IS_FOLDER + " AND " + GUID_NOT_TAGS_OR_PLACES; diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java index a4737d7b41b3..19282ffc8824 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java @@ -6,7 +6,6 @@ package org.mozilla.gecko.sync.repositories.android; import org.json.simple.JSONArray; import org.json.simple.JSONObject; -import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.repositories.NullCursorException; import org.mozilla.gecko.sync.repositories.Repository; import org.mozilla.gecko.sync.repositories.domain.HistoryRecord; diff --git a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java index 4fc081725410..b9439aedaa0b 100644 --- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java +++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java @@ -64,14 +64,27 @@ public abstract class AndroidBrowserRepositoryDataAccessor { protected abstract ContentValues getContentValues(Record record); protected abstract Uri getUri(); + public void dumpDB() { + Cursor cur = null; + try { + cur = queryHelper.safeQuery(".dumpDB", null, null, null, null); + RepoUtils.dumpCursor(cur); + } catch (NullCursorException e) { + } finally { + if (cur != null) { + cur.close(); + } + } + } + public String dateModifiedWhere(long timestamp) { return BrowserContract.SyncColumns.DATE_MODIFIED + " >= " + Long.toString(timestamp); } public void wipe() { - Logger.info(LOG_TAG, "wiping: " + getUri()); - String where = BrowserContract.SyncColumns.GUID + " NOT IN ('mobile')"; - context.getContentResolver().delete(getUri(), where, null); + Uri uri = getUri(); + Logger.info(LOG_TAG, "wiping: " + uri); + context.getContentResolver().delete(uri, null, null); } public void purgeDeleted() throws NullCursorException { diff --git a/mobile/android/base/sync/repositories/android/RepoUtils.java b/mobile/android/base/sync/repositories/android/RepoUtils.java index e5d2ec421959..cc4730db28fe 100644 --- a/mobile/android/base/sync/repositories/android/RepoUtils.java +++ b/mobile/android/base/sync/repositories/android/RepoUtils.java @@ -187,4 +187,58 @@ public class RepoUtils { return a.equals(b); } + + private static String fixedWidth(int width, String s) { + if (s == null) { + return spaces(width); + } + int length = s.length(); + if (width == length) { + return s; + } + if (width > length) { + return s + spaces(width - length); + } + return s.substring(0, width); + } + + private static String spaces(int i) { + return " ".substring(0, i); + } + + public static void dumpCursor(Cursor cur) { + int originalPosition = cur.getPosition(); + try { + String[] columnNames = cur.getColumnNames(); + int columnCount = cur.getColumnCount(); + + // 12 chars each column. + for (int i = 0; i < columnCount; ++i) { + System.out.print(fixedWidth(12, columnNames[i]) + " | "); + } + System.out.println(""); + for (int i = 0; i < columnCount; ++i) { + System.out.print("------------" + " | "); + } + System.out.println(""); + if (!cur.moveToFirst()) { + System.out.println("EMPTY"); + return; + } + + cur.moveToFirst(); + while (cur.moveToNext()) { + for (int i = 0; i < columnCount; ++i) { + System.out.print(fixedWidth(12, cur.getString(i)) + " | "); + } + System.out.println(""); + } + for (int i = 0; i < columnCount; ++i) { + System.out.print("---------------"); + } + System.out.println(""); + } finally { + cur.moveToPosition(originalPosition); + } + } } From 74fc2920eeea95fb70436a4dfa8c040d75445ccd Mon Sep 17 00:00:00 2001 From: Marina Samuel Date: Mon, 5 Mar 2012 20:53:14 -0800 Subject: [PATCH 25/88] Bug 715796 - Clients engine. r=rnewman --- mobile/android/base/sync/GlobalSession.java | 12 +- .../android/base/sync/KeyBundleProvider.java | 11 + .../sync/delegates/ClientsDataDelegate.java | 12 + .../sync/net/SyncStorageRequestDelegate.java | 39 +-- .../net/WBOCollectionRequestDelegate.java | 48 +-- .../base/sync/net/WBORequestDelegate.java | 14 + .../repositories/android/ClientsDatabase.java | 106 +++++++ .../android/ClientsDatabaseAccessor.java | 109 +++++++ .../sync/repositories/android/RepoUtils.java | 11 + .../repositories/domain/ClientRecord.java | 90 ++++++ .../domain/ClientRecordFactory.java | 18 ++ mobile/android/base/sync/setup/Constants.java | 8 + .../base/sync/stage/GlobalSyncStage.java | 2 + .../sync/stage/SyncClientsEngineStage.java | 277 ++++++++++++++++++ .../base/sync/syncadapter/SyncAdapter.java | 47 ++- mobile/android/sync/java-sources.mn | 2 +- 16 files changed, 726 insertions(+), 80 deletions(-) create mode 100644 mobile/android/base/sync/KeyBundleProvider.java create mode 100644 mobile/android/base/sync/delegates/ClientsDataDelegate.java create mode 100644 mobile/android/base/sync/net/WBORequestDelegate.java create mode 100644 mobile/android/base/sync/repositories/android/ClientsDatabase.java create mode 100644 mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java create mode 100644 mobile/android/base/sync/repositories/domain/ClientRecord.java create mode 100644 mobile/android/base/sync/repositories/domain/ClientRecordFactory.java create mode 100644 mobile/android/base/sync/stage/SyncClientsEngineStage.java diff --git a/mobile/android/base/sync/GlobalSession.java b/mobile/android/base/sync/GlobalSession.java index 2fc068d12f4f..82d700a1deee 100644 --- a/mobile/android/base/sync/GlobalSession.java +++ b/mobile/android/base/sync/GlobalSession.java @@ -48,6 +48,7 @@ import java.util.Map; import org.json.simple.parser.ParseException; import org.mozilla.gecko.sync.crypto.CryptoException; import org.mozilla.gecko.sync.crypto.KeyBundle; +import org.mozilla.gecko.sync.delegates.ClientsDataDelegate; import org.mozilla.gecko.sync.delegates.FreshStartDelegate; import org.mozilla.gecko.sync.delegates.GlobalSessionCallback; import org.mozilla.gecko.sync.delegates.InfoCollectionsDelegate; @@ -69,6 +70,7 @@ import org.mozilla.gecko.sync.stage.FetchMetaGlobalStage; import org.mozilla.gecko.sync.stage.GlobalSyncStage; import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage; import org.mozilla.gecko.sync.stage.NoSuchStageException; +import org.mozilla.gecko.sync.stage.SyncClientsEngineStage; import android.content.Context; import android.content.SharedPreferences; @@ -92,6 +94,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { private GlobalSessionCallback callback; private Context context; + private ClientsDataDelegate clientsDelegate; /* * Key accessors. @@ -148,7 +151,8 @@ public class GlobalSession implements CredentialsSource, PrefsSource { KeyBundle syncKeyBundle, GlobalSessionCallback callback, Context context, - Bundle extras) + Bundle extras, + ClientsDataDelegate clientsDelegate) throws SyncConfigurationException, IllegalArgumentException, IOException, ParseException, NonObjectJSONException { if (callback == null) { throw new IllegalArgumentException("Must provide a callback to GlobalSession constructor."); @@ -174,6 +178,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { this.callback = callback; this.context = context; + this.clientsDelegate = clientsDelegate; config = new SyncConfiguration(prefsPath, this); config.userAPI = userAPI; @@ -192,6 +197,7 @@ public class GlobalSession implements CredentialsSource, PrefsSource { stages.put(Stage.fetchInfoCollections, new FetchInfoCollectionsStage()); stages.put(Stage.fetchMetaGlobal, new FetchMetaGlobalStage()); stages.put(Stage.ensureKeysStage, new EnsureKeysStage()); + stages.put(Stage.syncClientsEngine, new SyncClientsEngineStage()); // TODO: more stages. stages.put(Stage.syncBookmarks, new AndroidBrowserBookmarksServerSyncStage()); @@ -692,4 +698,8 @@ public class GlobalSession implements CredentialsSource, PrefsSource { } return this.config.metaGlobal.engines.get(engineName) != null; } + + public ClientsDataDelegate getClientsDelegate() { + return this.clientsDelegate; + } } diff --git a/mobile/android/base/sync/KeyBundleProvider.java b/mobile/android/base/sync/KeyBundleProvider.java new file mode 100644 index 000000000000..b6a8f80c6e9c --- /dev/null +++ b/mobile/android/base/sync/KeyBundleProvider.java @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync; + +import org.mozilla.gecko.sync.crypto.KeyBundle; + +public interface KeyBundleProvider { + public abstract KeyBundle keyBundle(); +} \ No newline at end of file diff --git a/mobile/android/base/sync/delegates/ClientsDataDelegate.java b/mobile/android/base/sync/delegates/ClientsDataDelegate.java new file mode 100644 index 000000000000..84b555f99f37 --- /dev/null +++ b/mobile/android/base/sync/delegates/ClientsDataDelegate.java @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.delegates; + +public interface ClientsDataDelegate { + public String getAccountGUID(); + public String getClientName(); + public void setClientsCount(int clientsCount); + public int getClientsCount(); +} diff --git a/mobile/android/base/sync/net/SyncStorageRequestDelegate.java b/mobile/android/base/sync/net/SyncStorageRequestDelegate.java index 237e374674c1..d00f5871740f 100644 --- a/mobile/android/base/sync/net/SyncStorageRequestDelegate.java +++ b/mobile/android/base/sync/net/SyncStorageRequestDelegate.java @@ -1,39 +1,6 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.net; diff --git a/mobile/android/base/sync/net/WBOCollectionRequestDelegate.java b/mobile/android/base/sync/net/WBOCollectionRequestDelegate.java index e4f03ce885cc..4428cf7da090 100644 --- a/mobile/android/base/sync/net/WBOCollectionRequestDelegate.java +++ b/mobile/android/base/sync/net/WBOCollectionRequestDelegate.java @@ -1,55 +1,25 @@ -/* ***** 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 Android Sync Client. - * - * 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): - * Richard Newman - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.sync.net; import org.mozilla.gecko.sync.crypto.KeyBundle; import org.mozilla.gecko.sync.CryptoRecord; +import org.mozilla.gecko.sync.KeyBundleProvider; /** * Subclass this to handle collection fetches. * @author rnewman * */ -public abstract class WBOCollectionRequestDelegate extends - SyncStorageCollectionRequestDelegate { +public abstract class WBOCollectionRequestDelegate +extends SyncStorageCollectionRequestDelegate +implements KeyBundleProvider { - public abstract void handleWBO(CryptoRecord record); + @Override public abstract KeyBundle keyBundle(); + public abstract void handleWBO(CryptoRecord record); @Override public void handleRequestProgress(String progress) { diff --git a/mobile/android/base/sync/net/WBORequestDelegate.java b/mobile/android/base/sync/net/WBORequestDelegate.java new file mode 100644 index 000000000000..fdec955d9f97 --- /dev/null +++ b/mobile/android/base/sync/net/WBORequestDelegate.java @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.net; + +import org.mozilla.gecko.sync.KeyBundleProvider; +import org.mozilla.gecko.sync.crypto.KeyBundle; + +public abstract class WBORequestDelegate +implements SyncStorageRequestDelegate, KeyBundleProvider { + @Override + public abstract KeyBundle keyBundle(); +} diff --git a/mobile/android/base/sync/repositories/android/ClientsDatabase.java b/mobile/android/base/sync/repositories/android/ClientsDatabase.java new file mode 100644 index 000000000000..ac50979c253c --- /dev/null +++ b/mobile/android/base/sync/repositories/android/ClientsDatabase.java @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.repositories.android; + +import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.repositories.NullCursorException; +import org.mozilla.gecko.sync.repositories.domain.ClientRecord; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +public class ClientsDatabase extends CachedSQLiteOpenHelper { + + public static final String LOG_TAG = "ClientsDatabase"; + + // Database Specifications. + protected static final String DB_NAME = "clients_database"; + protected static final int SCHEMA_VERSION = 1; + + // Clients Table. + public static final String TBL_CLIENTS = "clients"; + public static final String COL_ACCOUNT_GUID = "guid"; + public static final String COL_PROFILE = "profile"; + public static final String COL_NAME = "name"; + public static final String COL_TYPE = "device_type"; + + public static final String[] TBL_COLUMNS = new String[] { COL_ACCOUNT_GUID, COL_PROFILE, COL_NAME, COL_TYPE }; + public static final String TBL_KEY = COL_ACCOUNT_GUID + " = ? AND " + + COL_PROFILE + " = ?"; + + private final RepoUtils.QueryHelper queryHelper; + + public ClientsDatabase(Context context) { + super(context, DB_NAME, null, SCHEMA_VERSION); + this.queryHelper = new RepoUtils.QueryHelper(context, null, LOG_TAG); + } + + @Override + public void onCreate(SQLiteDatabase db) { + String createTableSql = "CREATE TABLE " + TBL_CLIENTS + " (" + + COL_ACCOUNT_GUID + " TEXT, " + + COL_PROFILE + " TEXT, " + + COL_NAME + " TEXT, " + + COL_TYPE + " TEXT, " + + "PRIMARY KEY (" + COL_ACCOUNT_GUID + ", " + COL_PROFILE + "))"; + db.execSQL(createTableSql); + } + + public void wipe() { + SQLiteDatabase db = this.getCachedWritableDatabase(); + onUpgrade(db, SCHEMA_VERSION, SCHEMA_VERSION); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // For now we'll just drop and recreate the tables. + db.execSQL("DROP TABLE IF EXISTS " + TBL_CLIENTS); + onCreate(db); + } + + // If a record with given GUID exists, we'll update it, + // otherwise we'll insert it. + public void store(String profileId, ClientRecord record) { + SQLiteDatabase db = this.getCachedWritableDatabase(); + + ContentValues cv = new ContentValues(); + cv.put(COL_ACCOUNT_GUID, record.guid); + cv.put(COL_PROFILE, profileId); + cv.put(COL_NAME, record.name); + cv.put(COL_TYPE, record.type); + + String[] args = new String[] { record.guid, profileId }; + int rowsUpdated = db.update(TBL_CLIENTS, cv, TBL_KEY, args); + + if (rowsUpdated >= 1) { + Logger.debug(LOG_TAG, "Replaced client record for row with accountGUID " + record.guid); + } else { + long rowId = db.insert(TBL_CLIENTS, null, cv); + Logger.debug(LOG_TAG, "Inserted client record into row: " + rowId); + } + } + + public Cursor fetch(String accountGuid, String profileId) throws NullCursorException { + String[] args = new String[] { accountGuid, profileId }; + SQLiteDatabase db = this.getCachedReadableDatabase(); + + return queryHelper.safeQuery(db, ".fetch", TBL_CLIENTS, TBL_COLUMNS, TBL_KEY, args); + } + + public Cursor fetchAll() throws NullCursorException { + SQLiteDatabase db = this.getCachedReadableDatabase(); + + return queryHelper.safeQuery(db, ".fetch", TBL_CLIENTS, TBL_COLUMNS, null, null); + } + + public void delete(String accountGUID, String profileId) { + String[] args = new String[] { accountGUID, profileId }; + + SQLiteDatabase db = this.getCachedWritableDatabase(); + db.delete(TBL_CLIENTS, TBL_KEY, args); + } +} diff --git a/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java b/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java new file mode 100644 index 000000000000..d1346a81ff80 --- /dev/null +++ b/mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.repositories.android; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.mozilla.gecko.sync.repositories.NullCursorException; +import org.mozilla.gecko.sync.repositories.domain.ClientRecord; +import org.mozilla.gecko.sync.setup.Constants; + +import android.content.Context; +import android.database.Cursor; + +public class ClientsDatabaseAccessor { + + public static final String LOG_TAG = "ClientsDatabaseAccessor"; + + private ClientsDatabase db; + + // Need this so we can properly stub out the class for testing. + public ClientsDatabaseAccessor() {} + + public ClientsDatabaseAccessor(Context context) { + db = new ClientsDatabase(context); + } + + public void store(ClientRecord record) { + db.store(getProfileId(), record); + } + + public void store(Collection records) { + for (ClientRecord record : records) { + this.store(record); + } + } + + public ClientRecord fetch(String accountGUID) throws NullCursorException { + Cursor cur = null; + try { + cur = db.fetch(accountGUID, getProfileId()); + + if (cur == null || !cur.moveToFirst()) { + return null; + } + return recordFromCursor(cur); + } finally { + if (cur != null) { + cur.close(); + } + } + } + + public Map fetchAll() throws NullCursorException { + HashMap map = new HashMap(); + Cursor cur = null; + try { + cur = db.fetchAll(); + if (cur == null || !cur.moveToFirst()) { + return Collections.unmodifiableMap(map); + } + while (!cur.isAfterLast()) { + ClientRecord clientRecord = recordFromCursor(cur); + map.put(clientRecord.guid, clientRecord); + cur.moveToNext(); + } + + return Collections.unmodifiableMap(map); + } finally { + if (cur != null) { + cur.close(); + } + } + } + + protected ClientRecord recordFromCursor(Cursor cur) { + String accountGUID = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_ACCOUNT_GUID); + String clientName = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_NAME); + String clientType = RepoUtils.getStringFromCursor(cur, ClientsDatabase.COL_TYPE); + ClientRecord record = new ClientRecord(accountGUID); + record.name = clientName; + record.type = clientType; + return record; + } + + public int clientsCount() { + try { + return db.fetchAll().getCount(); + } catch (NullCursorException e) { + return 0; + } + } + + private String getProfileId() { + return Constants.PROFILE_ID; + } + + public void wipe() { + db.wipe(); + } + + public void close() { + db.close(); + } +} diff --git a/mobile/android/base/sync/repositories/android/RepoUtils.java b/mobile/android/base/sync/repositories/android/RepoUtils.java index cc4730db28fe..247510601231 100644 --- a/mobile/android/base/sync/repositories/android/RepoUtils.java +++ b/mobile/android/base/sync/repositories/android/RepoUtils.java @@ -10,6 +10,7 @@ import org.json.simple.parser.ParseException; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.sync.Logger; import org.mozilla.gecko.sync.repositories.NullCursorException; +import org.mozilla.gecko.sync.repositories.domain.ClientRecord; import org.mozilla.gecko.sync.repositories.domain.HistoryRecord; import org.mozilla.gecko.sync.repositories.domain.PasswordRecord; @@ -150,6 +151,16 @@ public class RepoUtils { return rec; } + public static void logClient(ClientRecord rec) { + if (Logger.logVerbose(LOG_TAG)) { + Logger.trace(LOG_TAG, "Returning client record " + rec.guid + " (" + rec.androidID + ")"); + Logger.trace(LOG_TAG, "Client Name: " + rec.name); + Logger.trace(LOG_TAG, "Client Type: " + rec.type); + Logger.trace(LOG_TAG, "Last Modified: " + rec.lastModified); + Logger.trace(LOG_TAG, "Deleted: " + rec.deleted); + } + } + public static PasswordRecord passwordFromMirrorCursor(Cursor cur) { String guid = getStringFromCursor(cur, BrowserContract.SyncColumns.GUID); diff --git a/mobile/android/base/sync/repositories/domain/ClientRecord.java b/mobile/android/base/sync/repositories/domain/ClientRecord.java new file mode 100644 index 000000000000..bfb4ff89649c --- /dev/null +++ b/mobile/android/base/sync/repositories/domain/ClientRecord.java @@ -0,0 +1,90 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.repositories.domain; + +import org.mozilla.gecko.sync.ExtendedJSONObject; +import org.mozilla.gecko.sync.Utils; +import org.mozilla.gecko.sync.repositories.android.RepoUtils; +import org.mozilla.gecko.sync.setup.Constants; + +public class ClientRecord extends Record { + + public static final String COLLECTION_NAME = "clients"; + + public ClientRecord(String guid, String collection, long lastModified, boolean deleted) { + super(guid, collection, lastModified, deleted); + } + + public ClientRecord(String guid, String collection, long lastModified) { + this(guid, collection, lastModified, false); + } + + public ClientRecord(String guid, String collection) { + this(guid, collection, 0, false); + } + + public ClientRecord(String guid) { + this(guid, COLLECTION_NAME, 0, false); + } + + public ClientRecord() { + this(Utils.generateGuid(), COLLECTION_NAME, 0, false); + } + + public String name = Constants.DEFAULT_CLIENT_NAME; + public String type = Constants.CLIENT_TYPE; + + @Override + protected void initFromPayload(ExtendedJSONObject payload) { + this.name = (String) payload.get("name"); + this.type = (String) payload.get("type"); + } + + @Override + protected void populatePayload(ExtendedJSONObject payload) { + putPayload(payload, "id", this.guid); + putPayload(payload, "name", this.name); + putPayload(payload, "type", this.type); + } + + public boolean equals(Object o) { + if (!(o instanceof ClientRecord) || !super.equals(o)) { + return false; + } + + ClientRecord other = (ClientRecord) o; + if (!RepoUtils.stringsEqual(other.name, this.name) || + !RepoUtils.stringsEqual(other.type, this.type)) { + return false; + } + return true; + } + + @Override + public Record copyWithIDs(String guid, long androidID) { + ClientRecord out = new ClientRecord(guid, this.collection, this.lastModified, this.deleted); + out.androidID = androidID; + out.sortIndex = this.sortIndex; + + out.name = this.name; + out.type = this.type; + return out; + } + +/* +Example record: + +{id:"relf31w7B4F1", + name:"marina_mac", + type:"mobile" + commands:[{"args":["bookmarks"],"command":"wipeEngine"}, + {"args":["forms"],"command":"wipeEngine"}, + {"args":["history"],"command":"wipeEngine"}, + {"args":["passwords"],"command":"wipeEngine"}, + {"args":["prefs"],"command":"wipeEngine"}, + {"args":["tabs"],"command":"wipeEngine"}, + {"args":["addons"],"command":"wipeEngine"}]} +*/ +} diff --git a/mobile/android/base/sync/repositories/domain/ClientRecordFactory.java b/mobile/android/base/sync/repositories/domain/ClientRecordFactory.java new file mode 100644 index 000000000000..9a549f3507a1 --- /dev/null +++ b/mobile/android/base/sync/repositories/domain/ClientRecordFactory.java @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.repositories.domain; + +import org.mozilla.gecko.sync.CryptoRecord; +import org.mozilla.gecko.sync.repositories.RecordFactory; + +public class ClientRecordFactory extends RecordFactory { + + @Override + public Record createRecord(Record record) { + ClientRecord r = new ClientRecord(); + r.initFromEnvelope((CryptoRecord) record); + return r; + } +} diff --git a/mobile/android/base/sync/setup/Constants.java b/mobile/android/base/sync/setup/Constants.java index ab70baa41287..de04a7c475a1 100644 --- a/mobile/android/base/sync/setup/Constants.java +++ b/mobile/android/base/sync/setup/Constants.java @@ -13,6 +13,14 @@ public class Constants { public static final String OPTION_USERNAME = "option.username"; public static final String AUTHTOKEN_TYPE_PLAIN = "auth.plain"; public static final String OPTION_SERVER = "option.serverUrl"; + public static final String ACCOUNT_GUID = "account.guid"; + public static final String CLIENT_NAME = "account.clientName"; + public static final String NUM_CLIENTS = "account.numClients"; + + // Constants for client records. + public static final String PROFILE_ID = "default"; // Generic profile id for now, until multiple profiles are implemented. + public static final String CLIENT_TYPE = "mobile"; + public static final String DEFAULT_CLIENT_NAME = "Default Name"; // Constants for Activities. public static final String INTENT_EXTRA_IS_SETUP = "isSetup"; diff --git a/mobile/android/base/sync/stage/GlobalSyncStage.java b/mobile/android/base/sync/stage/GlobalSyncStage.java index 6f6849ae2d58..babecd4119b9 100644 --- a/mobile/android/base/sync/stage/GlobalSyncStage.java +++ b/mobile/android/base/sync/stage/GlobalSyncStage.java @@ -50,7 +50,9 @@ public interface GlobalSyncStage { /* ensureSpecialRecords, updateEngineTimestamps, + */ syncClientsEngine, + /* processFirstSyncPref, processClientCommands, updateEnabledEngines, diff --git a/mobile/android/base/sync/stage/SyncClientsEngineStage.java b/mobile/android/base/sync/stage/SyncClientsEngineStage.java new file mode 100644 index 000000000000..8d57806a2325 --- /dev/null +++ b/mobile/android/base/sync/stage/SyncClientsEngineStage.java @@ -0,0 +1,277 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko.sync.stage; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.net.URISyntaxException; + +import org.mozilla.gecko.sync.CryptoRecord; +import org.mozilla.gecko.sync.GlobalSession; +import org.mozilla.gecko.sync.HTTPFailureException; +import org.mozilla.gecko.sync.Logger; +import org.mozilla.gecko.sync.NoCollectionKeysSetException; +import org.mozilla.gecko.sync.crypto.CryptoException; +import org.mozilla.gecko.sync.crypto.KeyBundle; +import org.mozilla.gecko.sync.delegates.ClientsDataDelegate; +import org.mozilla.gecko.sync.net.SyncResourceDelegate; +import org.mozilla.gecko.sync.net.SyncStorageCollectionRequest; +import org.mozilla.gecko.sync.net.SyncStorageRecordRequest; +import org.mozilla.gecko.sync.net.SyncStorageResponse; +import org.mozilla.gecko.sync.net.WBOCollectionRequestDelegate; +import org.mozilla.gecko.sync.net.WBORequestDelegate; +import org.mozilla.gecko.sync.repositories.android.ClientsDatabaseAccessor; +import org.mozilla.gecko.sync.repositories.android.RepoUtils; +import org.mozilla.gecko.sync.repositories.domain.ClientRecord; +import org.mozilla.gecko.sync.repositories.domain.ClientRecordFactory; + +import ch.boye.httpclientandroidlib.HttpResponse; + +public class SyncClientsEngineStage implements GlobalSyncStage { + protected static final String LOG_TAG = "SyncClientsEngineStage"; + protected static final String COLLECTION_NAME = "clients"; + + protected GlobalSession session; + protected final ClientRecordFactory factory = new ClientRecordFactory(); + protected ClientUploadDelegate clientUploadDelegate; + protected ClientDownloadDelegate clientDownloadDelegate; + protected ClientsDatabaseAccessor db; + + // Account/Profile info + protected boolean shouldWipe; + + /** + * The following two delegates, ClientDownloadDelegate and ClientUploadDelegate + * are both triggered in a chain, starting when execute() calls + * downloadClientRecords(). + * + * Client records are downloaded using a get() request. Upon success of the + * get() request, the local client record is uploaded. + * + * @author Marina Samuel + * + */ + public class ClientDownloadDelegate extends WBOCollectionRequestDelegate { + + @Override + public String credentials() { + return session.credentials(); + } + + @Override + public String ifUnmodifiedSince() { + // TODO last client download time? + return null; + } + + @Override + public void handleRequestSuccess(SyncStorageResponse response) { + try { + clientUploadDelegate = new ClientUploadDelegate(); + session.getClientsDelegate().setClientsCount(db.clientsCount()); + checkAndUpload(); + } finally { + // Close the database to clear cached readableDatabase/writableDatabase + // after we've completed our last transaction (db.store()). + db.close(); + } + } + + @Override + public void handleRequestFailure(SyncStorageResponse response) { + try { + Logger.info(LOG_TAG, "Client upload failed. Aborting sync."); + session.abort(new HTTPFailureException(response), "Client download failed."); + } finally { + // Close the database upon failure. + db.close(); + } + } + + @Override + public void handleRequestError(Exception ex) { + try { + Logger.info(LOG_TAG, "Client upload error. Aborting sync."); + session.abort(ex, "Failure fetching client record."); + } finally { + // Close the database upon error. + db.close(); + } + } + + @Override + public void handleWBO(CryptoRecord record) { + ClientRecord r; + try { + r = (ClientRecord) factory.createRecord(record.decrypt()); + RepoUtils.logClient(r); + } catch (Exception e) { + session.abort(e, "Exception handling client WBO."); + return; + } + wipeAndStore(r); + } + + @Override + public KeyBundle keyBundle() { + try { + return session.keyForCollection(COLLECTION_NAME); + } catch (NoCollectionKeysSetException e) { + session.abort(e, "No collection keys set."); + return null; + } + } + } + + public class ClientUploadDelegate extends WBORequestDelegate { + protected static final String LOG_TAG = "ClientUploadDelegate"; + + @Override + public String credentials() { + return session.credentials(); + } + + @Override + public String ifUnmodifiedSince() { + // TODO last client upload time? + return null; + } + + @Override + public void handleRequestSuccess(SyncStorageResponse response) { + try { + // Response entity must be consumed in order to reuse the connection. + HttpResponse httpResponse = response.httpResponse(); + if (httpResponse != null) { + SyncResourceDelegate.consumeEntity(httpResponse.getEntity()); + } + } finally { + session.advance(); + } + } + + @Override + public void handleRequestFailure(SyncStorageResponse response) { + Logger.info(LOG_TAG, "Client upload failed. Aborting sync."); + session.abort(new HTTPFailureException(response), "Client upload failed."); + } + + @Override + public void handleRequestError(Exception ex) { + Logger.info(LOG_TAG, "Client upload error. Aborting sync."); + session.abort(ex, "Client upload failed."); + } + + @Override + public KeyBundle keyBundle() { + try { + return session.keyForCollection(COLLECTION_NAME); + } catch (NoCollectionKeysSetException e) { + session.abort(e, "No collection keys set."); + return null; + } + } + } + + @Override + public void execute(GlobalSession session) throws NoSuchStageException { + this.session = session; + init(); + + if (shouldDownload()) { + downloadClientRecords(); // Will kick off upload, too… + } else { + // Upload if necessary. + } + } + + protected ClientRecord newLocalClientRecord(ClientsDataDelegate delegate) { + final String ourGUID = delegate.getAccountGUID(); + final String ourName = delegate.getClientName(); + + ClientRecord r = new ClientRecord(ourGUID); + r.name = ourName; + return r; + } + + protected void init() { + db = new ClientsDatabaseAccessor(session.getContext()); + } + + // TODO: Bug 726055 - More considered handling of when to sync. + protected boolean shouldDownload() { + // Ask info/collections whether a download is needed. + return true; + } + + // TODO: Bug 729248 - Smarter upload of client records. + protected boolean shouldUpload() { + return true; + } + + protected void checkAndUpload() { + if (!shouldUpload()) { + Logger.trace(LOG_TAG, "Not uploading client record."); + session.advance(); + return; + } + + // Generate CryptoRecord from ClientRecord to upload. + String encryptionFailure = "Couldn't encrypt new client record."; + ClientRecord localClient = newLocalClientRecord(session.getClientsDelegate()); + CryptoRecord cryptoRecord = localClient.getEnvelope(); + try { + cryptoRecord.keyBundle = clientUploadDelegate.keyBundle(); + cryptoRecord.encrypt(); + this.wipeAndStore(localClient); + this.uploadClientRecord(cryptoRecord); + } catch (UnsupportedEncodingException e) { + session.abort(e, encryptionFailure + " Unsupported encoding."); + } catch (CryptoException e) { + session.abort(e, encryptionFailure); + } + } + + protected void downloadClientRecords() { + shouldWipe = true; + clientDownloadDelegate = makeClientDownloadDelegate(); + + try { + URI getURI = session.config.collectionURI(COLLECTION_NAME, true); + + SyncStorageCollectionRequest request = new SyncStorageCollectionRequest(getURI); + request.delegate = clientDownloadDelegate; + + Logger.trace(LOG_TAG, "Downloading client records."); + request.get(); + } catch (URISyntaxException e) { + session.abort(e, "Invalid URI."); + } + } + + protected void uploadClientRecord(CryptoRecord record) { + try { + URI putURI = session.config.wboURI(COLLECTION_NAME, record.guid); + + SyncStorageRecordRequest request = new SyncStorageRecordRequest(putURI); + request.delegate = clientUploadDelegate; + request.put(record); + } catch (URISyntaxException e) { + session.abort(e, "Invalid URI."); + } + } + + protected ClientDownloadDelegate makeClientDownloadDelegate() { + return new ClientDownloadDelegate(); + } + + protected void wipeAndStore(ClientRecord record) { + if (shouldWipe) { + db.wipe(); + shouldWipe = false; + } + db.store(record); + } +} diff --git a/mobile/android/base/sync/syncadapter/SyncAdapter.java b/mobile/android/base/sync/syncadapter/SyncAdapter.java index 8fb971acd7d8..b1f5cb6f62a3 100644 --- a/mobile/android/base/sync/syncadapter/SyncAdapter.java +++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java @@ -44,6 +44,7 @@ import java.util.concurrent.TimeUnit; import org.json.simple.parser.ParseException; import org.mozilla.gecko.sync.AlreadySyncingException; +import org.mozilla.gecko.sync.GlobalConstants; import org.mozilla.gecko.sync.GlobalSession; import org.mozilla.gecko.sync.NonObjectJSONException; import org.mozilla.gecko.sync.SyncConfiguration; @@ -51,6 +52,7 @@ import org.mozilla.gecko.sync.SyncConfigurationException; import org.mozilla.gecko.sync.SyncException; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.KeyBundle; +import org.mozilla.gecko.sync.delegates.ClientsDataDelegate; import org.mozilla.gecko.sync.delegates.GlobalSessionCallback; import org.mozilla.gecko.sync.setup.Constants; import org.mozilla.gecko.sync.stage.GlobalSyncStage.Stage; @@ -73,7 +75,7 @@ import android.os.Bundle; import android.os.Handler; import android.util.Log; -public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSessionCallback { +public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSessionCallback, ClientsDataDelegate { private static final String LOG_TAG = "SyncAdapter"; private static final String PREFS_EARLIEST_NEXT_SYNC = "earliestnextsync"; @@ -197,6 +199,8 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe public Object syncMonitor = new Object(); private SyncResult syncResult; + private Account localAccount; + /** * Return the number of milliseconds until we're allowed to sync again, * or 0 if now is fine. @@ -340,11 +344,12 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe IOException, ParseException, NonObjectJSONException { Log.i(LOG_TAG, "Performing sync."); - this.syncResult = syncResult; + this.syncResult = syncResult; + this.localAccount = account; // TODO: default serverURL. GlobalSession globalSession = new GlobalSession(SyncConfiguration.DEFAULT_USER_API, serverURL, username, password, prefsPath, - keyBundle, this, this.mContext, extras); + keyBundle, this, this.mContext, extras, this); globalSession.start(); } @@ -407,4 +412,40 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe this.extendEarliestNextSync(System.currentTimeMillis() + backoff); } } + + @Override + public synchronized String getAccountGUID() { + String accountGUID = mAccountManager.getUserData(localAccount, Constants.ACCOUNT_GUID); + if (accountGUID == null) { + accountGUID = Utils.generateGuid(); + mAccountManager.setUserData(localAccount, Constants.ACCOUNT_GUID, accountGUID); + } + return accountGUID; + } + + @Override + public synchronized String getClientName() { + String clientName = mAccountManager.getUserData(localAccount, Constants.CLIENT_NAME); + if (clientName == null) { + clientName = GlobalConstants.PRODUCT_NAME + " on " + android.os.Build.MODEL; + mAccountManager.setUserData(localAccount, Constants.CLIENT_NAME, clientName); + } + return clientName; + } + + @Override + public synchronized void setClientsCount(int clientsCount) { + mAccountManager.setUserData(localAccount, Constants.NUM_CLIENTS, + Integer.toString(clientsCount)); + } + + @Override + public synchronized int getClientsCount() { + String clientsCount = mAccountManager.getUserData(localAccount, Constants.NUM_CLIENTS); + if (clientsCount == null) { + clientsCount = "0"; + mAccountManager.setUserData(localAccount, Constants.NUM_CLIENTS, clientsCount); + } + return Integer.parseInt(clientsCount); + } } \ No newline at end of file diff --git a/mobile/android/sync/java-sources.mn b/mobile/android/sync/java-sources.mn index b7de774963af..c89902fc47ef 100644 --- a/mobile/android/sync/java-sources.mn +++ b/mobile/android/sync/java-sources.mn @@ -1 +1 @@ -sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java +sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java From d877788df32698dbd63e6430a41dac17e7761908 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 5 Mar 2012 21:04:38 -0800 Subject: [PATCH 26/88] Bug 729403 (follow-up) - Use the right KIND for runtime/gc-marker, thus avoiding warnings about explicit mismatches. r=me. --HG-- extra : rebase_source : 4625531f438d5e1e6a2e600134dee959a45e8204 --- js/xpconnect/src/XPCJSRuntime.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index fc711aa66297..e0e6fd433dd4 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1684,7 +1684,7 @@ ReportJSRuntimeExplicitTreeStats(const JS::RuntimeStats &rtStats, const nsACStri callback, closure); ReportMemoryBytes(pathPrefix + NS_LITERAL_CSTRING("runtime/gc-marker"), - nsIMemoryReporter::KIND_NONHEAP, rtStats.runtimeGCMarker, + nsIMemoryReporter::KIND_HEAP, rtStats.runtimeGCMarker, "Memory used for the GC mark stack and gray roots.", callback, closure); From ea818a8a3c80165f6a90458573932831d9702f2a Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Tue, 6 Mar 2012 15:58:40 +0900 Subject: [PATCH 27/88] Bug 608161 - Don't rely on HasAttr to detect whether gradient attributes have values; r=longsonr --- .../anim-gradient-attr-presence-01-ref.svg | 142 ++++++++ .../smil/anim-gradient-attr-presence-01.svg | 193 +++++++++++ layout/reftests/svg/smil/reftest.list | 1 + layout/svg/base/src/nsSVGGradientFrame.cpp | 318 +++++++++++------- layout/svg/base/src/nsSVGGradientFrame.h | 59 ++-- 5 files changed, 560 insertions(+), 153 deletions(-) create mode 100644 layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg create mode 100644 layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg diff --git a/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg b/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg new file mode 100644 index 000000000000..44e380bc0262 --- /dev/null +++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01-ref.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg b/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg new file mode 100644 index 000000000000..71a6aa89cf57 --- /dev/null +++ b/layout/reftests/svg/smil/anim-gradient-attr-presence-01.svg @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/layout/reftests/svg/smil/reftest.list b/layout/reftests/svg/smil/reftest.list index a029ca4d4b87..9e713e93bb40 100644 --- a/layout/reftests/svg/smil/reftest.list +++ b/layout/reftests/svg/smil/reftest.list @@ -226,6 +226,7 @@ random == anim-text-x-y-dx-dy-01.svg anim-text-x-y-dx-dy-01-ref.svg # bug 579588 == anim-pattern-attr-presence-01.svg anim-pattern-attr-presence-01-ref.svg fails == anim-pattern-attr-presence-02.svg anim-pattern-attr-presence-02-ref.svg # ^ bug 621651 +== anim-gradient-attr-presence-01.svg anim-gradient-attr-presence-01-ref.svg == api-sanity-1.svg lime.svg diff --git a/layout/svg/base/src/nsSVGGradientFrame.cpp b/layout/svg/base/src/nsSVGGradientFrame.cpp index 9c0e49cd2156..8a1b7c2bb53e 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.cpp +++ b/layout/svg/base/src/nsSVGGradientFrame.cpp @@ -51,6 +51,27 @@ using mozilla::SVGAnimatedTransformList; +//---------------------------------------------------------------------- +// Helper classes + +class nsSVGGradientFrame::AutoGradientReferencer +{ +public: + AutoGradientReferencer(nsSVGGradientFrame *aFrame) + : mFrame(aFrame) + { + // Reference loops should normally be detected in advance and handled, so + // we're not expecting to encounter them here + NS_ABORT_IF_FALSE(!mFrame->mLoopFlag, "Undetected reference loop!"); + mFrame->mLoopFlag = true; + } + ~AutoGradientReferencer() { + mFrame->mLoopFlag = false; + } +private: + nsSVGGradientFrame *mFrame; +}; + //---------------------------------------------------------------------- // Implementation @@ -134,6 +155,40 @@ nsSVGGradientFrame::GetStopInformation(PRInt32 aIndex, *aStopOpacity = stopFrame->GetStyleSVGReset()->mStopOpacity; } +PRUint16 +nsSVGGradientFrame::GetEnumValue(PRUint32 aIndex, nsIContent *aDefault) +{ + const nsSVGEnum& thisEnum = + static_cast(mContent)->mEnumAttributes[aIndex]; + + if (thisEnum.IsExplicitlySet()) + return thisEnum.GetAnimValue(); + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetEnumValue(aIndex, aDefault) : + static_cast(aDefault)-> + mEnumAttributes[aIndex].GetAnimValue(); +} + +const SVGAnimatedTransformList* +nsSVGGradientFrame::GetGradientTransformList(nsIContent* aDefault) +{ + SVGAnimatedTransformList *thisTransformList = + static_cast(mContent)->GetAnimatedTransformList(); + + if (thisTransformList->IsExplicitlySet()) + return thisTransformList; + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetGradientTransformList(aDefault) : + static_cast(aDefault) + ->mGradientTransform.get(); +} + gfxMatrix nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, const gfxRect *aOverrideBounds) @@ -150,19 +205,19 @@ nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, else mSource = aSource; } else { - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); // objectBoundingBox is the default anyway - gfxRect bbox = aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); - bboxMatrix = gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); + gfxRect bbox = + aOverrideBounds ? *aOverrideBounds : nsSVGUtils::GetBBox(aSource); + bboxMatrix = + gfxMatrix(bbox.Width(), 0, 0, bbox.Height(), bbox.X(), bbox.Y()); } - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::gradientTransform, mContent); - - SVGAnimatedTransformList* animTransformList = - element->GetAnimatedTransformList(); + const SVGAnimatedTransformList* animTransformList = + GetGradientTransformList(mContent); if (!animTransformList) return bboxMatrix; @@ -171,13 +226,32 @@ nsSVGGradientFrame::GetGradientTransform(nsIFrame *aSource, return bboxMatrix.PreMultiply(gradientTransform); } -PRUint16 -nsSVGGradientFrame::GetSpreadMethod() +nsSVGLinearGradientElement * +nsSVGGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex, + nsSVGLinearGradientElement* aDefault) { - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::spreadMethod, mContent); + // If this was a linear gradient with the required length, we would have + // already found it in nsSVGLinearGradientFrame::GetLinearGradientWithLength. + // Since we didn't find the length, continue looking down the chain. - return element->mEnumAttributes[nsSVGGradientElement::SPREADMETHOD].GetAnimValue(); + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetLinearGradientWithLength(aIndex, aDefault) : aDefault; +} + +nsSVGRadialGradientElement * +nsSVGGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex, + nsSVGRadialGradientElement* aDefault) +{ + // If this was a radial gradient with the required length, we would have + // already found it in nsSVGRadialGradientFrame::GetRadialGradientWithLength. + // Since we didn't find the length, continue looking down the chain. + + AutoGradientReferencer gradientRef(this); + + nsSVGGradientFrame *next = GetReferencedGradientIfNotInUse(); + return next ? next->GetRadialGradientWithLength(aIndex, aDefault) : aDefault; } //---------------------------------------------------------------------- @@ -289,53 +363,20 @@ nsSVGGradientFrame::GetReferencedGradient() return static_cast(result); } -nsSVGGradientElement * -nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) +nsSVGGradientFrame * +nsSVGGradientFrame::GetReferencedGradientIfNotInUse() { - if (mContent->HasAttr(kNameSpaceID_None, aAttrName)) - return static_cast(mContent); + nsSVGGradientFrame *referenced = GetReferencedGradient(); + if (!referenced) + return nsnull; - nsSVGGradientElement *grad = static_cast(aDefault); + if (referenced->mLoopFlag) { + // XXXjwatt: we should really send an error to the JavaScript Console here: + NS_WARNING("gradient reference loop detected while inheriting attribute!"); + return nsnull; + } - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) - return grad; - - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting attribute!"); - if (!next->mLoopFlag) - grad = next->GetGradientWithAttr(aAttrName, aDefault); - mLoopFlag = false; - - return grad; -} - -nsSVGGradientElement * -nsSVGGradientFrame::GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType, - nsIContent *aDefault) -{ - if (GetType() == aGradType && mContent->HasAttr(kNameSpaceID_None, aAttrName)) - return static_cast(mContent); - - nsSVGGradientElement *grad = static_cast(aDefault); - - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) - return grad; - - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting attribute!"); - if (!next->mLoopFlag) - grad = next->GetGradientWithAttr(aAttrName, aGradType, aDefault); - mLoopFlag = false; - - return grad; + return referenced; } PRInt32 @@ -359,33 +400,12 @@ nsSVGGradientFrame::GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame) // Our gradient element doesn't have stops - try to "inherit" them - nsSVGGradientFrame *next = GetReferencedGradient(); - if (!next) { - if (aStopFrame) - *aStopFrame = nsnull; - return 0; - } + AutoGradientReferencer gradientRef(this); + nsSVGGradientFrame* next = GetReferencedGradientIfNotInUse(); + if (!next) + return nsnull; - // Set mLoopFlag before checking mNextGrad->mLoopFlag in case we are mNextGrad - mLoopFlag = true; - // XXXjwatt: we should really send an error to the JavaScript Console here: - NS_WARN_IF_FALSE(!next->mLoopFlag, "gradient reference loop detected " - "while inheriting stop!"); - if (!next->mLoopFlag) - stopCount = next->GetStopFrame(aIndex, aStopFrame); - mLoopFlag = false; - - return stopCount; -} - -PRUint16 -nsSVGGradientFrame::GetGradientUnits() -{ - // This getter is called every time the others are called - maybe cache it? - - nsSVGGradientElement *element = - GetGradientWithAttr(nsGkAtoms::gradientUnits, mContent); - return element->mEnumAttributes[nsSVGGradientElement::GRADIENTUNITS].GetAnimValue(); + return next->GetStopFrame(aIndex, aStopFrame); } // ------------------------------------------------------------------------- @@ -431,11 +451,16 @@ nsSVGLinearGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, //---------------------------------------------------------------------- float -nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, - PRUint16 aEnumName) +nsSVGLinearGradientFrame::GetLengthValue(PRUint32 aIndex) { - nsSVGLinearGradientElement *element = - GetLinearGradientWithAttr(aAtomName, mContent); + nsSVGLinearGradientElement* lengthElement = + GetLinearGradientWithLength(aIndex, + static_cast(mContent)); + // We passed in mContent as a fallback, so, assuming mContent is non-null, the + // return value should also be non-null. + NS_ABORT_IF_FALSE(lengthElement, + "Got unexpected null element from GetLinearGradientWithLength"); + const nsSVGLength2 &length = lengthElement->mLengthAttributes[aIndex]; // Object bounding box units are handled by setting the appropriate // transform in GetGradientTransform, but we need to handle user @@ -443,15 +468,30 @@ nsSVGLinearGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 gradientUnits = GetGradientUnits(); if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { - return nsSVGUtils::UserSpace(mSource, - &element->mLengthAttributes[aEnumName]); + return nsSVGUtils::UserSpace(mSource, &length); } - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); - return element->mLengthAttributes[aEnumName]. - GetAnimValue(static_cast(nsnull)); + return length.GetAnimValue(static_cast(nsnull)); +} + +nsSVGLinearGradientElement * +nsSVGLinearGradientFrame::GetLinearGradientWithLength(PRUint32 aIndex, + nsSVGLinearGradientElement* aDefault) +{ + nsSVGLinearGradientElement* thisElement = + static_cast(mContent); + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return nsSVGLinearGradientFrameBase::GetLinearGradientWithLength(aIndex, + aDefault); } already_AddRefed @@ -459,10 +499,10 @@ nsSVGLinearGradientFrame::CreateGradient() { float x1, y1, x2, y2; - x1 = GradientLookupAttribute(nsGkAtoms::x1, nsSVGLinearGradientElement::X1); - y1 = GradientLookupAttribute(nsGkAtoms::y1, nsSVGLinearGradientElement::Y1); - x2 = GradientLookupAttribute(nsGkAtoms::x2, nsSVGLinearGradientElement::X2); - y2 = GradientLookupAttribute(nsGkAtoms::y2, nsSVGLinearGradientElement::Y2); + x1 = GetLengthValue(nsSVGLinearGradientElement::X1); + y1 = GetLengthValue(nsSVGLinearGradientElement::Y1); + x2 = GetLengthValue(nsSVGLinearGradientElement::X2); + y2 = GetLengthValue(nsSVGLinearGradientElement::Y2); gfxPattern *pattern = new gfxPattern(x1, y1, x2, y2); NS_IF_ADDREF(pattern); @@ -513,17 +553,33 @@ nsSVGRadialGradientFrame::AttributeChanged(PRInt32 aNameSpaceID, //---------------------------------------------------------------------- float -nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, - PRUint16 aEnumName, - nsSVGRadialGradientElement *aElement) +nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex) { - nsSVGRadialGradientElement *element; + nsSVGRadialGradientElement* lengthElement = + GetRadialGradientWithLength(aIndex, + static_cast(mContent)); + // We passed in mContent as a fallback, so, assuming mContent is non-null, + // the return value should also be non-null. + NS_ABORT_IF_FALSE(lengthElement, + "Got unexpected null element from GetRadialGradientWithLength"); + return GetLengthValueFromElement(aIndex, *lengthElement); +} - if (aElement) { - element = aElement; - } else { - element = GetRadialGradientWithAttr(aAtomName, mContent); - } +float +nsSVGRadialGradientFrame::GetLengthValue(PRUint32 aIndex, float aDefaultValue) +{ + nsSVGRadialGradientElement* lengthElement = + GetRadialGradientWithLength(aIndex, nsnull); + + return lengthElement ? GetLengthValueFromElement(aIndex, *lengthElement) + : aDefaultValue; +} + +float +nsSVGRadialGradientFrame::GetLengthValueFromElement(PRUint32 aIndex, + nsSVGRadialGradientElement& aElement) +{ + const nsSVGLength2 &length = aElement.mLengthAttributes[aIndex]; // Object bounding box units are handled by setting the appropriate // transform in GetGradientTransform, but we need to handle user @@ -531,15 +587,30 @@ nsSVGRadialGradientFrame::GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 gradientUnits = GetGradientUnits(); if (gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_USERSPACEONUSE) { - return nsSVGUtils::UserSpace(mSource, - &element->mLengthAttributes[aEnumName]); + return nsSVGUtils::UserSpace(mSource, &length); } - NS_ASSERTION(gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, - "Unknown gradientUnits type"); + NS_ASSERTION( + gradientUnits == nsIDOMSVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX, + "Unknown gradientUnits type"); - return element->mLengthAttributes[aEnumName]. - GetAnimValue(static_cast(nsnull)); + return length.GetAnimValue(static_cast(nsnull)); +} + +nsSVGRadialGradientElement * +nsSVGRadialGradientFrame::GetRadialGradientWithLength(PRUint32 aIndex, + nsSVGRadialGradientElement* aDefault) +{ + nsSVGRadialGradientElement* thisElement = + static_cast(mContent); + const nsSVGLength2 &length = thisElement->mLengthAttributes[aIndex]; + + if (length.IsExplicitlySet()) { + return thisElement; + } + + return nsSVGRadialGradientFrameBase::GetRadialGradientWithLength(aIndex, + aDefault); } already_AddRefed @@ -547,21 +618,12 @@ nsSVGRadialGradientFrame::CreateGradient() { float cx, cy, r, fx, fy; - cx = GradientLookupAttribute(nsGkAtoms::cx, nsSVGRadialGradientElement::CX); - cy = GradientLookupAttribute(nsGkAtoms::cy, nsSVGRadialGradientElement::CY); - r = GradientLookupAttribute(nsGkAtoms::r, nsSVGRadialGradientElement::R); - - nsSVGRadialGradientElement *gradient; - - if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fx, nsnull))) - fx = cx; // if fx isn't set, we must use cx - else - fx = GradientLookupAttribute(nsGkAtoms::fx, nsSVGRadialGradientElement::FX, gradient); - - if (!(gradient = GetRadialGradientWithAttr(nsGkAtoms::fy, nsnull))) - fy = cy; // if fy isn't set, we must use cy - else - fy = GradientLookupAttribute(nsGkAtoms::fy, nsSVGRadialGradientElement::FY, gradient); + cx = GetLengthValue(nsSVGRadialGradientElement::CX); + cy = GetLengthValue(nsSVGRadialGradientElement::CY); + r = GetLengthValue(nsSVGRadialGradientElement::R); + // If fx or fy are not set, use cx/cy instead + fx = GetLengthValue(nsSVGRadialGradientElement::FX, cx); + fy = GetLengthValue(nsSVGRadialGradientElement::FY, cy); if (fx != cx || fy != cy) { // The focal point (fFx and fFy) must be clamped to be *inside* - not on - diff --git a/layout/svg/base/src/nsSVGGradientFrame.h b/layout/svg/base/src/nsSVGGradientFrame.h index 07294fa29c42..3a19414417cb 100644 --- a/layout/svg/base/src/nsSVGGradientFrame.h +++ b/layout/svg/base/src/nsSVGGradientFrame.h @@ -86,45 +86,48 @@ private: // the referenced gradient's frame if available, null otherwise. nsSVGGradientFrame* GetReferencedGradient(); - // Helpers to look at our gradient and then along its reference chain (if any) - // to find the first gradient with the specified attribute. - // Returns aDefault if no content with that attribute is found - nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault); - - // Some attributes are only valid on one type of gradient, and we *must* get - // the right type or we won't have the data structures we require. - // Returns aDefault if no content with that attribute is found - nsSVGGradientElement* GetGradientWithAttr(nsIAtom *aAttrName, nsIAtom *aGradType, - nsIContent *aDefault); - // Optionally get a stop frame (returns stop index/count) PRInt32 GetStopFrame(PRInt32 aIndex, nsIFrame * *aStopFrame); - PRUint16 GetSpreadMethod(); PRUint32 GetStopCount(); void GetStopInformation(PRInt32 aIndex, float *aOffset, nscolor *aColor, float *aStopOpacity); + const mozilla::SVGAnimatedTransformList* GetGradientTransformList( + nsIContent* aDefault); // Will be singular for gradientUnits="objectBoundingBox" with an empty bbox. - gfxMatrix GetGradientTransform(nsIFrame *aSource, const gfxRect *aOverrideBounds); + gfxMatrix GetGradientTransform(nsIFrame *aSource, + const gfxRect *aOverrideBounds); protected: virtual already_AddRefed CreateGradient() = 0; - // Use these inline methods instead of GetGradientWithAttr(..., aGradType) - nsSVGLinearGradientElement* GetLinearGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) + // Internal methods for handling referenced gradients + class AutoGradientReferencer; + nsSVGGradientFrame* GetReferencedGradientIfNotInUse(); + + // Accessors to lookup gradient attributes + PRUint16 GetEnumValue(PRUint32 aIndex, nsIContent *aDefault); + PRUint16 GetEnumValue(PRUint32 aIndex) { - return static_cast( - GetGradientWithAttr(aAttrName, nsGkAtoms::svgLinearGradientFrame, aDefault)); + return GetEnumValue(aIndex, mContent); } - nsSVGRadialGradientElement* GetRadialGradientWithAttr(nsIAtom *aAttrName, nsIContent *aDefault) + PRUint16 GetGradientUnits() { - return static_cast( - GetGradientWithAttr(aAttrName, nsGkAtoms::svgRadialGradientFrame, aDefault)); + // This getter is called every time the others are called - maybe cache it? + return GetEnumValue(nsSVGGradientElement::GRADIENTUNITS); + } + PRUint16 GetSpreadMethod() + { + return GetEnumValue(nsSVGGradientElement::SPREADMETHOD); } - // Get the value of our gradientUnits attribute - PRUint16 GetGradientUnits(); + // Gradient-type-specific lookups since the length values differ between + // linear and radial gradients + virtual nsSVGLinearGradientElement * GetLinearGradientWithLength( + PRUint32 aIndex, nsSVGLinearGradientElement* aDefault); + virtual nsSVGRadialGradientElement * GetRadialGradientWithLength( + PRUint32 aIndex, nsSVGRadialGradientElement* aDefault); // The frame our gradient is (currently) being applied to nsIFrame* mSource; @@ -178,7 +181,9 @@ public: #endif // DEBUG protected: - float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName); + float GetLengthValue(PRUint32 aIndex); + virtual nsSVGLinearGradientElement * GetLinearGradientWithLength( + PRUint32 aIndex, nsSVGLinearGradientElement* aDefault); virtual already_AddRefed CreateGradient(); }; @@ -220,8 +225,12 @@ public: #endif // DEBUG protected: - float GradientLookupAttribute(nsIAtom *aAtomName, PRUint16 aEnumName, - nsSVGRadialGradientElement *aElement = nsnull); + float GetLengthValue(PRUint32 aIndex); + float GetLengthValue(PRUint32 aIndex, float aDefaultValue); + float GetLengthValueFromElement(PRUint32 aIndex, + nsSVGRadialGradientElement& aElement); + virtual nsSVGRadialGradientElement * GetRadialGradientWithLength( + PRUint32 aIndex, nsSVGRadialGradientElement* aDefault); virtual already_AddRefed CreateGradient(); }; From b723672dbe798f57e5aa8260b1041bed757c5afe Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Mon, 5 Mar 2012 23:06:09 -0800 Subject: [PATCH 28/88] Bug 648878. e10s: fix Get(Local|Remote)(Address|Port) in HttpChannelChild. r=jdm --- netwerk/protocol/http/HttpChannelChild.cpp | 35 ++------------------- netwerk/protocol/http/HttpChannelChild.h | 4 --- netwerk/test/unit/test_traceable_channel.js | 24 ++++++++------ 3 files changed, 17 insertions(+), 46 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 4df4d51f61a5..dcaf4dcc2b03 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -291,6 +291,8 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, mCacheEntryAvailable = cacheEntryAvailable; mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; + mSelfAddr = selfAddr; + mPeerAddr = peerAddr; AutoEventEnqueuer ensureSerialDispatch(mEventQ); @@ -313,9 +315,6 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, rv = ApplyContentConversions(); if (NS_FAILED(rv)) Cancel(rv); - - mSelfAddr = selfAddr; - mPeerAddr = peerAddr; } class TransportAndDataEvent : public ChannelEvent @@ -1113,36 +1112,6 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) DROP_DEAD(); } -// The next four _should_ be implemented, but we need to figure out how -// to transfer the data from the chrome process first. - -NS_IMETHODIMP -HttpChannelChild::GetRemoteAddress(nsACString & _result) -{ - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetRemotePort(PRInt32 * _result) -{ - NS_ENSURE_ARG_POINTER(_result); - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetLocalAddress(nsACString & _result) -{ - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetLocalPort(PRInt32 * _result) -{ - NS_ENSURE_ARG_POINTER(_result); - return NS_ERROR_NOT_AVAILABLE; -} - - //----------------------------------------------------------------------------- // HttpChannelChild::nsICacheInfoChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 43617ef5589c..f9f7c2e0003a 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -106,10 +106,6 @@ public: bool aMerge); // nsIHttpChannelInternal NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); - NS_IMETHOD GetLocalAddress(nsACString& addr); - NS_IMETHOD GetLocalPort(PRInt32* port); - NS_IMETHOD GetRemoteAddress(nsACString& addr); - NS_IMETHOD GetRemotePort(PRInt32* port); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); // nsIResumableChannel diff --git a/netwerk/test/unit/test_traceable_channel.js b/netwerk/test/unit/test_traceable_channel.js index a62b8a2c92b3..8fb562ea57fa 100644 --- a/netwerk/test/unit/test_traceable_channel.js +++ b/netwerk/test/unit/test_traceable_channel.js @@ -22,14 +22,18 @@ TracingListener.prototype = { request.QueryInterface(Components.interfaces.nsIHttpChannelInternal); -// local/remote addresses broken in e10s: disable for now -/* - do_check_eq(request.localAddress, "127.0.0.1"); - do_check_eq(request.localPort > 0, true); - do_check_neq(request.localPort, 4444); - do_check_eq(request.remoteAddress, "127.0.0.1"); - do_check_eq(request.remotePort, 4444); -*/ + try { + do_check_eq(request.localAddress, "127.0.0.1"); + do_check_eq(request.localPort > 0, true); + do_check_neq(request.localPort, 4444); + do_check_eq(request.remoteAddress, "127.0.0.1"); + do_check_eq(request.remotePort, 4444); + } catch(e) { + do_throw("failed to get local/remote socket info"); + } + + request.QueryInterface(Components.interfaces.nsISupportsPriority); + request.priority = Ci.nsISupportsPriority.PRIORITY_LOW; // Make sure listener can't be replaced after OnStartRequest was called. request.QueryInterface(Components.interfaces.nsITraceableChannel); @@ -138,8 +142,10 @@ function channel_finished(request, input, ctx) { httpserver.stop(do_test_finished); } +// needs to be global or it'll go out of scope before it observes request +var observer = new HttpResponseExaminer(); + function run_test() { - var observer = new HttpResponseExaminer(); observer.register(); httpserver = new nsHttpServer(); From bb442bc879d1c0220d8d7fd1c9b26acc71103a6b Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Mon, 5 Mar 2012 23:10:19 -0800 Subject: [PATCH 29/88] Backed out changeset 84a2ed6120c3: wrong bug name in comment msg --- netwerk/protocol/http/HttpChannelChild.cpp | 35 +++++++++++++++++++-- netwerk/protocol/http/HttpChannelChild.h | 4 +++ netwerk/test/unit/test_traceable_channel.js | 24 ++++++-------- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index dcaf4dcc2b03..4df4d51f61a5 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -291,8 +291,6 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, mCacheEntryAvailable = cacheEntryAvailable; mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; - mSelfAddr = selfAddr; - mPeerAddr = peerAddr; AutoEventEnqueuer ensureSerialDispatch(mEventQ); @@ -315,6 +313,9 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, rv = ApplyContentConversions(); if (NS_FAILED(rv)) Cancel(rv); + + mSelfAddr = selfAddr; + mPeerAddr = peerAddr; } class TransportAndDataEvent : public ChannelEvent @@ -1112,6 +1113,36 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) DROP_DEAD(); } +// The next four _should_ be implemented, but we need to figure out how +// to transfer the data from the chrome process first. + +NS_IMETHODIMP +HttpChannelChild::GetRemoteAddress(nsACString & _result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetRemotePort(PRInt32 * _result) +{ + NS_ENSURE_ARG_POINTER(_result); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetLocalAddress(nsACString & _result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetLocalPort(PRInt32 * _result) +{ + NS_ENSURE_ARG_POINTER(_result); + return NS_ERROR_NOT_AVAILABLE; +} + + //----------------------------------------------------------------------------- // HttpChannelChild::nsICacheInfoChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index f9f7c2e0003a..43617ef5589c 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -106,6 +106,10 @@ public: bool aMerge); // nsIHttpChannelInternal NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); + NS_IMETHOD GetLocalAddress(nsACString& addr); + NS_IMETHOD GetLocalPort(PRInt32* port); + NS_IMETHOD GetRemoteAddress(nsACString& addr); + NS_IMETHOD GetRemotePort(PRInt32* port); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); // nsIResumableChannel diff --git a/netwerk/test/unit/test_traceable_channel.js b/netwerk/test/unit/test_traceable_channel.js index 8fb562ea57fa..a62b8a2c92b3 100644 --- a/netwerk/test/unit/test_traceable_channel.js +++ b/netwerk/test/unit/test_traceable_channel.js @@ -22,18 +22,14 @@ TracingListener.prototype = { request.QueryInterface(Components.interfaces.nsIHttpChannelInternal); - try { - do_check_eq(request.localAddress, "127.0.0.1"); - do_check_eq(request.localPort > 0, true); - do_check_neq(request.localPort, 4444); - do_check_eq(request.remoteAddress, "127.0.0.1"); - do_check_eq(request.remotePort, 4444); - } catch(e) { - do_throw("failed to get local/remote socket info"); - } - - request.QueryInterface(Components.interfaces.nsISupportsPriority); - request.priority = Ci.nsISupportsPriority.PRIORITY_LOW; +// local/remote addresses broken in e10s: disable for now +/* + do_check_eq(request.localAddress, "127.0.0.1"); + do_check_eq(request.localPort > 0, true); + do_check_neq(request.localPort, 4444); + do_check_eq(request.remoteAddress, "127.0.0.1"); + do_check_eq(request.remotePort, 4444); +*/ // Make sure listener can't be replaced after OnStartRequest was called. request.QueryInterface(Components.interfaces.nsITraceableChannel); @@ -142,10 +138,8 @@ function channel_finished(request, input, ctx) { httpserver.stop(do_test_finished); } -// needs to be global or it'll go out of scope before it observes request -var observer = new HttpResponseExaminer(); - function run_test() { + var observer = new HttpResponseExaminer(); observer.register(); httpserver = new nsHttpServer(); From 6d24bfde173c82349afaf4ead728b16782e65351 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Mon, 5 Mar 2012 23:11:19 -0800 Subject: [PATCH 30/88] bug 664163 - Fix Get(Local|Remote)(Address|Port) in HttpChannelChild. r=jdm --- netwerk/protocol/http/HttpChannelChild.cpp | 35 ++------------------- netwerk/protocol/http/HttpChannelChild.h | 4 --- netwerk/test/unit/test_traceable_channel.js | 24 ++++++++------ 3 files changed, 17 insertions(+), 46 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index 4df4d51f61a5..dcaf4dcc2b03 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -291,6 +291,8 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, mCacheEntryAvailable = cacheEntryAvailable; mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; + mSelfAddr = selfAddr; + mPeerAddr = peerAddr; AutoEventEnqueuer ensureSerialDispatch(mEventQ); @@ -313,9 +315,6 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, rv = ApplyContentConversions(); if (NS_FAILED(rv)) Cancel(rv); - - mSelfAddr = selfAddr; - mPeerAddr = peerAddr; } class TransportAndDataEvent : public ChannelEvent @@ -1113,36 +1112,6 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) DROP_DEAD(); } -// The next four _should_ be implemented, but we need to figure out how -// to transfer the data from the chrome process first. - -NS_IMETHODIMP -HttpChannelChild::GetRemoteAddress(nsACString & _result) -{ - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetRemotePort(PRInt32 * _result) -{ - NS_ENSURE_ARG_POINTER(_result); - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetLocalAddress(nsACString & _result) -{ - return NS_ERROR_NOT_AVAILABLE; -} - -NS_IMETHODIMP -HttpChannelChild::GetLocalPort(PRInt32 * _result) -{ - NS_ENSURE_ARG_POINTER(_result); - return NS_ERROR_NOT_AVAILABLE; -} - - //----------------------------------------------------------------------------- // HttpChannelChild::nsICacheInfoChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index 43617ef5589c..f9f7c2e0003a 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -106,10 +106,6 @@ public: bool aMerge); // nsIHttpChannelInternal NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); - NS_IMETHOD GetLocalAddress(nsACString& addr); - NS_IMETHOD GetLocalPort(PRInt32* port); - NS_IMETHOD GetRemoteAddress(nsACString& addr); - NS_IMETHOD GetRemotePort(PRInt32* port); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); // nsIResumableChannel diff --git a/netwerk/test/unit/test_traceable_channel.js b/netwerk/test/unit/test_traceable_channel.js index a62b8a2c92b3..8fb562ea57fa 100644 --- a/netwerk/test/unit/test_traceable_channel.js +++ b/netwerk/test/unit/test_traceable_channel.js @@ -22,14 +22,18 @@ TracingListener.prototype = { request.QueryInterface(Components.interfaces.nsIHttpChannelInternal); -// local/remote addresses broken in e10s: disable for now -/* - do_check_eq(request.localAddress, "127.0.0.1"); - do_check_eq(request.localPort > 0, true); - do_check_neq(request.localPort, 4444); - do_check_eq(request.remoteAddress, "127.0.0.1"); - do_check_eq(request.remotePort, 4444); -*/ + try { + do_check_eq(request.localAddress, "127.0.0.1"); + do_check_eq(request.localPort > 0, true); + do_check_neq(request.localPort, 4444); + do_check_eq(request.remoteAddress, "127.0.0.1"); + do_check_eq(request.remotePort, 4444); + } catch(e) { + do_throw("failed to get local/remote socket info"); + } + + request.QueryInterface(Components.interfaces.nsISupportsPriority); + request.priority = Ci.nsISupportsPriority.PRIORITY_LOW; // Make sure listener can't be replaced after OnStartRequest was called. request.QueryInterface(Components.interfaces.nsITraceableChannel); @@ -138,8 +142,10 @@ function channel_finished(request, input, ctx) { httpserver.stop(do_test_finished); } +// needs to be global or it'll go out of scope before it observes request +var observer = new HttpResponseExaminer(); + function run_test() { - var observer = new HttpResponseExaminer(); observer.register(); httpserver = new nsHttpServer(); From c30de3580b1d506bd477fa0f6ced53fa7055bc4a Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Tue, 7 Feb 2012 11:35:00 +0200 Subject: [PATCH 31/88] Bug 731961 - Include cstdio from Mappable.cpp for sprintf. r=glandium --- mozglue/linker/Mappable.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/mozglue/linker/Mappable.cpp b/mozglue/linker/Mappable.cpp index 94c876ef2fe0..6a867a5d3368 100644 --- a/mozglue/linker/Mappable.cpp +++ b/mozglue/linker/Mappable.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "Mappable.h" #ifdef ANDROID #include From 7cbebe89b80afe9123c34ef36a696fc7cd5b1d3a Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Tue, 6 Mar 2012 09:28:09 +0100 Subject: [PATCH 32/88] Bug 733087 - Support DT_FLAGS a little bit. r=glandium --- mozglue/linker/CustomElf.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/mozglue/linker/CustomElf.cpp b/mozglue/linker/CustomElf.cpp index 1a13824e6233..949e441b6693 100644 --- a/mozglue/linker/CustomElf.cpp +++ b/mozglue/linker/CustomElf.cpp @@ -547,6 +547,16 @@ CustomElf::InitDyn(const Phdr *pt_dyn) return false; } break; + case DT_FLAGS: + { + Word flags = dyn->d_un.d_val; + /* we can treat this like having a DT_SYMBOLIC tag */ + flags &= ~DF_SYMBOLIC; + if (flags) + log("%s: Warning: unhandled flags #%" PRIxAddr" not handled", + GetPath(), flags); + } + break; case DT_SONAME: /* Should match GetName(), but doesn't matter */ case DT_SYMBOLIC: /* Indicates internal symbols should be looked up in * the library itself first instead of the executable, From fb9fd1183057486023b032279f3cac59e901d0f0 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 28 Feb 2012 10:48:16 +0100 Subject: [PATCH 33/88] Bug 731151 - Avoid creating (and leaving around) an a.out during configure. r=khuey --- configure.in | 3 ++- js/src/configure.in | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.in b/configure.in index 755fbf1b03b5..091841e731d9 100644 --- a/configure.in +++ b/configure.in @@ -628,9 +628,10 @@ if test "$GXX" = "yes"; then GNU_CXX=1 CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'` fi -if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then +if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then GNU_AS=1 fi +rm -f conftest.out if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then GNU_LD=1 fi diff --git a/js/src/configure.in b/js/src/configure.in index 82693952662e..8f4fd37850f7 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -616,9 +616,10 @@ if test "$GXX" = "yes"; then GNU_CXX=1 CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'` fi -if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then +if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then GNU_AS=1 fi +rm -f conftest.out if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then GNU_LD=1 fi From 5773947c34dcd92b50b58f3fcfea54b3f19f6643 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 6 Mar 2012 09:54:17 +0100 Subject: [PATCH 34/88] Fixup for bug 733087. rs=dougt --- mozglue/linker/CustomElf.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mozglue/linker/CustomElf.h b/mozglue/linker/CustomElf.h index b21ede03a342..86906328d831 100644 --- a/mozglue/linker/CustomElf.h +++ b/mozglue/linker/CustomElf.h @@ -147,6 +147,12 @@ #ifndef DT_VERNEEDNUM #define DT_VERNEEDNUM 0x6fffffff #endif +#ifndef DT_FLAGS +#define DT_FLAGS 30 +#endif +#ifndef DF_SYMBOLIC +#define DF_SYMBOLIC 0x00000002 +#endif namespace Elf { From 7f830029fb3b72ba6b58702d9584da351f45b621 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Tue, 6 Mar 2012 19:01:55 +0900 Subject: [PATCH 35/88] Bug 732908 - Expose 'Firefox' and 'Thunderbird' as the accessible app names -- even for pre-release versions, r=ginn --- .../src/atk/nsApplicationAccessibleWrap.cpp | 10 ++++ .../src/atk/nsApplicationAccessibleWrap.h | 2 + .../mochitest/elm/test_nsApplicationAcc.html | 55 ++++++++++--------- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/accessible/src/atk/nsApplicationAccessibleWrap.cpp b/accessible/src/atk/nsApplicationAccessibleWrap.cpp index bca6caa54a4f..9b58acfa6ec5 100644 --- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp +++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp @@ -696,6 +696,16 @@ nsApplicationAccessibleWrap::Unload() // } } +NS_IMETHODIMP +nsApplicationAccessibleWrap::GetName(nsAString& aName) +{ + // ATK doesn't provide a way to obtain an application name (for example, + // Firefox or Thunderbird) like IA2 does. Thus let's return an application + // name as accessible name that was used to get a branding name (for example, + // Minefield aka nightly Firefox or Daily aka nightly Thunderbird). + return GetAppName(aName); +} + NS_IMETHODIMP nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible) { diff --git a/accessible/src/atk/nsApplicationAccessibleWrap.h b/accessible/src/atk/nsApplicationAccessibleWrap.h index ba17c0c31a8f..924eeefba398 100644 --- a/accessible/src/atk/nsApplicationAccessibleWrap.h +++ b/accessible/src/atk/nsApplicationAccessibleWrap.h @@ -57,6 +57,8 @@ public: virtual bool Init(); // nsAccessible + NS_IMETHOD GetName(nsAString &aName); + virtual bool AppendChild(nsAccessible* aChild); virtual bool RemoveChild(nsAccessible* aChild); diff --git a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html index dece93c690b2..58763e437233 100644 --- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html +++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html @@ -15,41 +15,46 @@ From 28d175dae4e699cb22ab6ab8ac41d2c22a3d2a10 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 6 Mar 2012 10:58:16 +0000 Subject: [PATCH 36/88] bug 729626 - fix cluster formation in Indic, cherrypicked from harfbuzz-ng commit 461b9b6347e4f58589f5be82c40a2df61da2c715. r=behdad --- .../src/hb-ot-shape-complex-indic-machine.hh | 16 ++++++++-------- .../src/hb-ot-shape-complex-indic-machine.rl | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh index 334e607fdbed..28160f2c0697 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh @@ -232,25 +232,25 @@ _resume: #line 62 "hb-ot-shape-complex-indic-machine.rl" { found_consonant_syllable (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 3: #line 63 "hb-ot-shape-complex-indic-machine.rl" { found_vowel_syllable (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 4: #line 64 "hb-ot-shape-complex-indic-machine.rl" { found_standalone_cluster (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 1: #line 65 "hb-ot-shape-complex-indic-machine.rl" { found_non_indic (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; #line 256 "hb-ot-shape-complex-indic-machine.hh" } @@ -268,25 +268,25 @@ _again: #line 62 "hb-ot-shape-complex-indic-machine.rl" { found_consonant_syllable (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 3: #line 63 "hb-ot-shape-complex-indic-machine.rl" { found_vowel_syllable (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 4: #line 64 "hb-ot-shape-complex-indic-machine.rl" { found_standalone_cluster (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; case 1: #line 65 "hb-ot-shape-complex-indic-machine.rl" { found_non_indic (map, buffer, mask_array, last, p); } #line 67 "hb-ot-shape-complex-indic-machine.rl" - { set_cluster (buffer, p, last); last = p; } + { set_cluster (buffer, last, p); last = p; } break; #line 292 "hb-ot-shape-complex-indic-machine.hh" } diff --git a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl index 53dc20df54b4..bcca6dab26f7 100644 --- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl +++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl @@ -64,7 +64,7 @@ action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, las action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); } action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); } -action next_syllable { set_cluster (buffer, p, last); last = p; } +action next_syllable { set_cluster (buffer, last, p); last = p; } consonant_syllable = (c.N? (z.H|H.z?))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable); vowel_syllable = (Ra H)? V N? (z.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable); From 8dc457643f9b5fce2e4019d9b8786ddd39ec5c25 Mon Sep 17 00:00:00 2001 From: Geoff Lankow Date: Wed, 7 Mar 2012 00:41:25 +1300 Subject: [PATCH 37/88] Bug 731041 - Tidy up addon inline preferences code, toolkit; r=mbrubeck, Unfocused --- .../mozapps/extensions/content/extensions.js | 24 +--- .../mozapps/extensions/content/setting.xml | 124 ++++++++---------- .../browser_inlinesettings1/options.xul | 2 +- .../test/browser/browser_inlinesettings.js | 28 +--- .../mozapps/extensions/extensions.css | 16 ++- .../mozapps/extensions/extensions.css | 14 +- .../mozapps/extensions/extensions.css | 14 +- 7 files changed, 99 insertions(+), 123 deletions(-) diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index ff57058f8746..3c83b4b9174b 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -2882,12 +2882,9 @@ var gDetailView = { for (var i = 0; i < settings.length; i++) { var setting = settings[i]; - // Remove setting description, for replacement later var desc = stripTextNodes(setting).trim(); - if (setting.hasAttribute("desc")) { - desc = setting.getAttribute("desc").trim(); - setting.removeAttribute("desc"); - } + if (!setting.hasAttribute("desc")) + setting.setAttribute("desc", desc); var type = setting.getAttribute("type"); if (type == "file" || type == "directory") @@ -2899,23 +2896,10 @@ var gDetailView = { setting.setAttribute("first-row", true); firstSetting = setting; } - - // Add a new row containing the description - if (desc) { - var row = document.createElement("row"); - if (!visible) { - row.setAttribute("unsupported", "true"); - } - var label = document.createElement("label"); - label.className = "preferences-description"; - label.textContent = desc; - row.appendChild(label); - rows.appendChild(row); - } } - // Ensure the page has loaded and force the XBL bindings to be synchronously applied, - // then notify observers. + // Ensure the page has loaded and force the XBL bindings to be synchronously applied, + // then notify observers. if (gViewController.viewPort.selectedPanel.hasAttribute("loading")) { gDetailView.node.addEventListener("ViewChanged", function viewChangedEventListener() { gDetailView.node.removeEventListener("ViewChanged", viewChangedEventListener, false); diff --git a/toolkit/mozapps/extensions/content/setting.xml b/toolkit/mozapps/extensions/content/setting.xml index 2c19075c3228..a0af4a601e8e 100644 --- a/toolkit/mozapps/extensions/content/setting.xml +++ b/toolkit/mozapps/extensions/content/setting.xml @@ -51,7 +51,7 @@ - + @@ -160,7 +160,6 @@ false document.getAnonymousElementByAttribute(this, "anonid", "input"); - this.parentNode.localName == "settings" ? this.parentNode : null; @@ -169,14 +168,14 @@ - - - - - + + + + + - - + + @@ -204,19 +203,7 @@ - - - - - - - - - - - - - + @@ -234,9 +221,6 @@ ]]> - - - @@ -263,20 +247,20 @@ ]]> - - - - - - + + + + + - - + + @@ -297,21 +281,18 @@ ]]> - - - - - - - - + + + + + - + @@ -319,14 +300,15 @@ - - - - - + + + + + - - + + @@ -350,20 +332,18 @@ ]]> - - - - - - - + + + + + - + @@ -394,15 +374,15 @@ - - - - - + + + + + - - - + + + @@ -476,14 +456,14 @@ - - - - - + + + + + - - + + diff --git a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul index febf7bbc8a00..095d3bcef20d 100644 --- a/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul +++ b/toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul @@ -1,6 +1,6 @@ - + diff --git a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js index 1e331027b156..849c11cc7c13 100644 --- a/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js +++ b/toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js @@ -21,7 +21,7 @@ var observer = { // Test if the binding has applied before the observers are notified. We test the second setting here, // because the code operates on the first setting and we want to check it applies to all. var setting = aSubject.querySelector("rows > setting[first-row] ~ setting"); - var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "setting-label"); + var input = gManagerWindow.document.getAnonymousElementByAttribute(setting, "class", "preferences-title"); isnot(input, null, "XBL binding should be applied"); // Add some extra height to the scrolling pane to ensure that it needs to scroll when appropriate. @@ -174,6 +174,7 @@ add_test(function() { Services.prefs.setBoolPref("extensions.inlinesettings1.bool", false); var input = gManagerWindow.document.getAnonymousElementByAttribute(settings[0], "anonid", "input"); isnot(input.checked, true, "Checkbox should have initial value"); + is(input.label, "Check box label", "Checkbox should be labelled"); EventUtils.synthesizeMouseAtCenter(input, { clickCount: 1 }, gManagerWindow); is(input.checked, true, "Checkbox should have updated value"); is(Services.prefs.getBoolPref("extensions.inlinesettings1.bool"), true, "Bool pref should have been updated"); @@ -381,45 +382,26 @@ add_test(function() { is(node.nodeName, "setting", "Should be a setting node"); ok(node.hasAttribute("first-row"), "First visible row should have first-row attribute"); var description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description"); - is(description.textContent.trim(), "", "Description node should be empty"); - - node = node.nextSibling; - is(node.nodeName, "row", "Setting should be followed by a row node"); - is_element_visible(node, "Description should be visible"); - is(node.textContent, "Description Attribute", "Description should be in this row"); + is(description.textContent, "Description Attribute", "Description node should contain description"); node = settings[2]; is(node.nodeName, "setting", "Should be a setting node"); ok(!node.hasAttribute("first-row"), "Not the first row"); description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description"); - is(description.textContent.trim(), "", "Description node should be empty"); - - node = node.nextSibling; - is(node.nodeName, "row", "Setting should be followed by a row node"); - is_element_visible(node, "Description should be visible"); - is(node.textContent, "Description Text Node", "Description should be in this row"); + is(description.textContent, "Description Text Node", "Description node should contain description"); node = settings[3]; is(node.nodeName, "setting", "Should be a setting node"); ok(!node.hasAttribute("first-row"), "Not the first row"); description = gManagerWindow.document.getAnonymousElementByAttribute(node, "class", "preferences-description"); - is(description.textContent.trim(), "", "Description node should be empty"); + is(description.textContent, "This is a test, all this text should be visible", "Description node should contain description"); var button = node.firstElementChild; isnot(button, null, "There should be a button"); - node = node.nextSibling; - is(node.nodeName, "row", "Setting should be followed by a row node"); - is_element_visible(node, "Description should be visible"); - is(node.textContent.trim(), "This is a test, all this text should be visible", "Description should be in this row"); - node = settings[4]; is_element_hidden(node, "Unsupported settings should not be visible"); ok(!node.hasAttribute("first-row"), "Hidden row is not the first row"); - node = node.nextSibling; - is(node.nodeName, "row", "Setting should be followed by a row node"); - is_element_hidden(node, "Descriptions of unsupported settings should not be visible"); - var button = gManagerWindow.document.getElementById("detail-prefs-btn"); is_element_hidden(button, "Preferences button should not be visible"); diff --git a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css index 385c91462546..dff215039818 100644 --- a/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/gnomestripe/mozapps/extensions/extensions.css @@ -739,7 +739,7 @@ setting[first-row="true"] { setting { border-top: 1px solid ThreeDShadow; -moz-box-align: center; - min-height: 33px; + min-height: 32px; } #detail-controls { @@ -755,15 +755,25 @@ setting[first-row="true"] { margin-top: 2em; } +setting { + -moz-box-align: start; +} + +.preferences-alignment { + min-height: 32px; + -moz-box-align: center; +} + .preferences-description { font-size: 90.9%; color: graytext; margin-top: -2px; -moz-margin-start: 2em; + white-space: pre-wrap; } -setting[type="string"] > .setting-input > textbox { - -moz-box-flex: 1; +.preferences-description:empty { + display: none; } menulist { /* Fixes some styling inconsistencies */ diff --git a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css index 93f8775bb824..095dfc2bfe83 100644 --- a/toolkit/themes/pinstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/pinstripe/mozapps/extensions/extensions.css @@ -934,15 +934,25 @@ setting[first-row="true"] { margin-top: 2em; } +setting { + -moz-box-align: start; +} + +.preferences-alignment { + min-height: 30px; + -moz-box-align: center; +} + .preferences-description { font-size: 90.9%; color: graytext; margin-top: -2px; -moz-margin-start: 2em; + white-space: pre-wrap; } -setting[type="string"] > .setting-input > textbox { - -moz-box-flex: 1; +.preferences-description:empty { + display: none; } setting[type="radio"] > radiogroup { diff --git a/toolkit/themes/winstripe/mozapps/extensions/extensions.css b/toolkit/themes/winstripe/mozapps/extensions/extensions.css index c5e4156ab509..0c32802e0626 100644 --- a/toolkit/themes/winstripe/mozapps/extensions/extensions.css +++ b/toolkit/themes/winstripe/mozapps/extensions/extensions.css @@ -913,15 +913,25 @@ setting[first-row="true"] { margin-top: 2em; } +setting { + -moz-box-align: start; +} + +.preferences-alignment { + min-height: 30px; + -moz-box-align: center; +} + .preferences-description { font-size: 90.9%; color: graytext; margin-top: -2px; -moz-margin-start: 2em; + white-space: pre-wrap; } -setting[type="string"] > .setting-input > textbox { - -moz-box-flex: 1; +.preferences-description:empty { + display: none; } setting[type="radio"] > radiogroup { From 4feba03c7ce48b890b34ade472f5e6ec67cbcdb1 Mon Sep 17 00:00:00 2001 From: Geoff Lankow Date: Wed, 7 Mar 2012 00:41:57 +1300 Subject: [PATCH 38/88] Bug 731041 - Tidy up addon inline preferences code, mobile; r=mbrubeck, Unfocused --- mobile/android/chrome/content/aboutAddons.js | 31 ++++++++++++----- .../chrome/content/bindings/extensions.xml | 34 ++++++++++++------- mobile/xul/chrome/content/browser.css | 13 +++++++ mobile/xul/themes/core/browser.css | 24 ++++++++----- .../xul/themes/core/gingerbread/browser.css | 24 ++++++++----- mobile/xul/themes/core/honeycomb/browser.css | 24 ++++++++----- 6 files changed, 105 insertions(+), 45 deletions(-) diff --git a/mobile/android/chrome/content/aboutAddons.js b/mobile/android/chrome/content/aboutAddons.js index b8a867b872a7..d1efd95a9027 100644 --- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -247,18 +247,31 @@ var Addons = { xhr.open("GET", optionsURL, false); xhr.send(); if (xhr.responseXML) { - let currentNode; - let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, NodeFilter.SHOW_TEXT, null, false); - while (currentNode = nodeIterator.nextNode()) { - let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); - if (!trimmed.length) - currentNode.parentNode.removeChild(currentNode); + // This function removes and returns the text content of aNode without + // removing any child elements. Removing the text nodes ensures any XBL + // bindings apply properly. + function stripTextNodes(aNode) { + var text = ''; + for (var i = 0; i < aNode.childNodes.length; i++) { + if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) { + text += aNode.childNodes[i].textContent; + aNode.removeChild(aNode.childNodes[i--]); + } else { + text += stripTextNodes(aNode.childNodes[i]); + } + } + return text; } // Only allow for now - let prefs = xhr.responseXML.querySelectorAll(":root > setting"); - for (let i = 0; i < prefs.length; i++) - box.appendChild(prefs.item(i)); + let settings = xhr.responseXML.querySelectorAll(":root > setting"); + for (let i = 0; i < settings.length; i++) { + var setting = settings[i]; + var desc = stripTextNodes(setting).trim(); + if (!setting.hasAttribute("desc")) + setting.setAttribute("desc", desc); + box.appendChild(setting); + } /* // Send an event so add-ons can prepopulate any non-preference based // settings diff --git a/mobile/xul/chrome/content/bindings/extensions.xml b/mobile/xul/chrome/content/bindings/extensions.xml index 8ba48f46713b..6986eb856112 100644 --- a/mobile/xul/chrome/content/bindings/extensions.xml +++ b/mobile/xul/chrome/content/bindings/extensions.xml @@ -117,21 +117,31 @@ if (!xhr.responseXML) return; - let currentNode; - let nodeIterator = xhr.responseXML.createNodeIterator(xhr.responseXML, - NodeFilter.SHOW_TEXT, - null, - false); - while (currentNode = nodeIterator.nextNode()) { - let trimmed = currentNode.nodeValue.replace(/^\s\s*/, "").replace(/\s\s*$/, ""); - if (!trimmed.length) - currentNode.parentNode.removeChild(currentNode); + // This function removes and returns the text content of aNode without + // removing any child elements. Removing the text nodes ensures any XBL + // bindings apply properly. + function stripTextNodes(aNode) { + var text = ''; + for (var i = 0; i < aNode.childNodes.length; i++) { + if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) { + text += aNode.childNodes[i].textContent; + aNode.removeChild(aNode.childNodes[i--]); + } else { + text += stripTextNodes(aNode.childNodes[i]); + } + } + return text; } // Only allow for now - let prefs = xhr.responseXML.querySelectorAll(":root > setting"); - for (let i = 0; i < prefs.length; i++) - box.appendChild(prefs.item(i)); + let settings = xhr.responseXML.querySelectorAll(":root > setting"); + for (let i = 0; i < settings.length; i++) { + var setting = settings[i]; + var desc = stripTextNodes(setting).trim(); + if (!setting.hasAttribute("desc")) + setting.setAttribute("desc", desc); + box.appendChild(setting); + } // Send an event so add-ons can prepopulate any non-preference based // settings diff --git a/mobile/xul/chrome/content/browser.css b/mobile/xul/chrome/content/browser.css index 10c27e0f8c95..b9863e6f9073 100644 --- a/mobile/xul/chrome/content/browser.css +++ b/mobile/xul/chrome/content/browser.css @@ -30,41 +30,54 @@ settings { -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#settings"); } +setting { + display: none; +} + setting[type="bool"] { + display: -moz-box; -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-bool"); } setting[type="bool"][localized="true"] { + display: -moz-box; -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-localized-bool"); } setting[type="boolint"] { + display: -moz-box; -moz-binding: url("chrome://browser/content/bindings.xml#setting-fulltoggle-boolint"); } setting[type="integer"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-integer"); } setting[type="control"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-control"); } setting[type="string"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-string"); } setting[type="color"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-color"); } setting[type="file"], setting[type="directory"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-path"); } setting[type="radio"], setting[type="menulist"] { + display: -moz-box; -moz-binding: url("chrome://mozapps/content/extensions/setting.xml#setting-multi"); } diff --git a/mobile/xul/themes/core/browser.css b/mobile/xul/themes/core/browser.css index ca13c214909b..aae6d6479ee1 100644 --- a/mobile/xul/themes/core/browser.css +++ b/mobile/xul/themes/core/browser.css @@ -1273,10 +1273,14 @@ setting { -moz-box-orient: horizontal; } -.setting-label { +setting > vbox { -moz-box-flex: 1; } +.preferences-description:empty { + display: none; +} + .setting-group > setting { border-bottom: none; } @@ -1291,20 +1295,24 @@ setting { } /* Put setting textboxes on a separate row in portrait */ -@media (@orientation@: portrait) { +@media (@orientation@: portrait) and (max-width: 600px) { setting[type="integer"], setting[type="string"], setting[type="file"], setting[type="directory"] { - -moz-box-align: start; + -moz-box-align: stretch; -moz-box-orient: vertical; } +} - setting[type="integer"] > .setting-input > textbox, - setting[type="string"] > .setting-input > textbox { - width: 499px; /* textboxes seem to need a width in order to flex */ - -moz-box-flex: 1; - } +setting[type="integer"] > .preferences-alignment, +setting[type="string"] > .preferences-alignment { + -moz-box-flex: 3; +} + +setting[type="file"] > .preferences-alignment, +setting[type="directory"] > .preferences-alignment { + -moz-box-align: center; } .options-box { diff --git a/mobile/xul/themes/core/gingerbread/browser.css b/mobile/xul/themes/core/gingerbread/browser.css index dc4667e62ca1..fdf402870a2a 100644 --- a/mobile/xul/themes/core/gingerbread/browser.css +++ b/mobile/xul/themes/core/gingerbread/browser.css @@ -1264,10 +1264,14 @@ setting:hover:active { background-color: @color_background_highlight@; } -.setting-label { +setting > vbox { -moz-box-flex: 1; } +.preferences-description:empty { + display: none; +} + .setting-group > setting { border-bottom: none; } @@ -1282,18 +1286,22 @@ setting:hover:active { } /* Put setting textboxes on a separate row in portrait */ -@media (@orientation@: portrait) { +@media (@orientation@: portrait) and (max-width: 600px) { setting[type="integer"], setting[type="string"] { - -moz-box-align: start; + -moz-box-align: stretch; -moz-box-orient: vertical; } +} - setting[type="integer"] > .setting-input > textbox, - setting[type="string"] > .setting-input > textbox { - width: 499px; /* textboxes seem to need a width in order to flex */ - -moz-box-flex: 1; - } +setting[type="integer"] > .preferences-alignment, +setting[type="string"] > .preferences-alignment { + -moz-box-flex: 3; +} + +setting[type="file"] > .preferences-alignment, +setting[type="directory"] > .preferences-alignment { + -moz-box-align: center; } .options-box { diff --git a/mobile/xul/themes/core/honeycomb/browser.css b/mobile/xul/themes/core/honeycomb/browser.css index fc5d51b36fdd..9807e85f9707 100644 --- a/mobile/xul/themes/core/honeycomb/browser.css +++ b/mobile/xul/themes/core/honeycomb/browser.css @@ -1482,10 +1482,14 @@ setting[type="bool"]:hover:active .setting-input > checkbox[checked="true"] > .c background-image: url("chrome://browser/skin/images/check-selected-tap-hdpi.png"); } -.setting-label { +setting > vbox { -moz-box-flex: 1; } +.preferences-description:empty { + display: none; +} + .setting-group > setting { border-bottom: none; } @@ -1500,18 +1504,22 @@ setting[type="bool"]:hover:active .setting-input > checkbox[checked="true"] > .c } /* Put setting textboxes on a separate row in portrait */ -@media (@orientation@: portrait) { +@media (@orientation@: portrait) and (max-width: 600px) { setting[type="integer"], setting[type="string"] { - -moz-box-align: start; + -moz-box-align: stretch; -moz-box-orient: vertical; } +} - setting[type="integer"] > .setting-input > textbox, - setting[type="string"] > .setting-input > textbox { - width: 499px; /* textboxes seem to need a width in order to flex */ - -moz-box-flex: 1; - } +setting[type="integer"] > .preferences-alignment, +setting[type="string"] > .preferences-alignment { + -moz-box-flex: 3; +} + +setting[type="file"] > .preferences-alignment, +setting[type="directory"] > .preferences-alignment { + -moz-box-align: center; } .options-box { From ead959ffd1f1e577a28d15fe7630c1230b4083d1 Mon Sep 17 00:00:00 2001 From: Gervase Markham Date: Tue, 6 Mar 2012 11:49:00 +0000 Subject: [PATCH 39/88] Bug 712640 - Split PSL into "registry" and "owner-requested" sections. r=pkasting. --- netwerk/dns/effective_tld_names.dat | 154 ++++++++++++++-------------- 1 file changed, 75 insertions(+), 79 deletions(-) diff --git a/netwerk/dns/effective_tld_names.dat b/netwerk/dns/effective_tld_names.dat index 889bcd5c495c..7fbce465e982 100644 --- a/netwerk/dns/effective_tld_names.dat +++ b/netwerk/dns/effective_tld_names.dat @@ -40,6 +40,8 @@ // // ***** END LICENSE BLOCK ***** +// ===BEGIN ICANN DOMAINS=== + // ac : http://en.wikipedia.org/wiki/.ac ac com.ac @@ -247,14 +249,6 @@ co.at gv.at or.at -// http://www.info.at/ -biz.at -info.at - -// priv.at : http://www.nic.priv.at/ -// Submitted by registry 2008-06-09 -priv.at - // au : http://en.wikipedia.org/wiki/.au // http://www.auda.org.au/ // 2LDs @@ -587,9 +581,6 @@ yk.ca // see also: http://registry.gc.ca/en/SubdomainFAQ gc.ca -// co.ca: http://registry.co.ca -co.ca - // cat : http://en.wikipedia.org/wiki/.cat cat @@ -713,35 +704,6 @@ web.co // com : http://en.wikipedia.org/wiki/.com com -// CentralNic names : http://www.centralnic.com/names/domains -// Confirmed by registry 2008-06-09 -// Updated by registry 2011-05-27 -ar.com -br.com -cn.com -de.com -eu.com -gb.com -gr.com -hu.com -jpn.com -kr.com -no.com -qc.com -ru.com -sa.com -se.com -uk.com -us.com -uy.com -za.com - -// Requested by Yngve Pettersen 2009-11-26 -operaunite.com - -// Requested by Eduardo Vela 2010-09-06 -appspot.com - // coop : http://en.wikipedia.org/wiki/.coop coop @@ -783,10 +745,6 @@ cz // reservations) 2008-07-01 de -// CentralNic names : http://www.centralnic.com/names/domains -// Submitted by registry 2011-05-27 -com.de - // dj : http://en.wikipedia.org/wiki/.dj dj @@ -895,8 +853,6 @@ fi // completely removed. // TODO: Check for updates (expected to be phased out around Q1/2009) aland.fi -// iki.fi : Submitted by Hannu Aronsson 2009-11-05 -iki.fi // fj : http://en.wikipedia.org/wiki/.fj *.fj @@ -1819,8 +1775,6 @@ gov.la per.la com.la org.la -// see http://www.c.la/ -c.la // lb : http://en.wikipedia.org/wiki/.lb // Submitted by registry 2008-06-17 @@ -2660,17 +2614,6 @@ ne // net : http://en.wikipedia.org/wiki/.net net -// CentralNic names : http://www.centralnic.com/names/domains -// Submitted by registry 2008-06-17 -gb.net -jp.net -se.net -uk.net - -// ZaNiC names : http://www.za.net/ -// Confirmed by registry 2009-10-03 -za.net - // nf : http://en.wikipedia.org/wiki/.nf nf com.nf @@ -2704,9 +2647,6 @@ nl // BV.nl will be a registry for dutch BV's (besloten vennootschap) bv.nl -// the co.nl domain is managed by CoDNS B.V. Added 2010-05-23. -co.nl - // no : http://www.norid.no/regelverk/index.en.html // The Norwegian registry has declined to notify us of updates. The web pages // referenced below are the official source of the data. There is also an @@ -3474,9 +3414,6 @@ våler.østfold.no valer.hedmark.no våler.hedmark.no -// the co.no domain is managed by CoDNS B.V. Added 2010-05-23. -co.no - // np : http://www.mos.com.np/register.html *.np @@ -3513,16 +3450,6 @@ nu // org : http://en.wikipedia.org/wiki/.org org -// CentralNic names : http://www.centralnic.com/names/domains -// Submitted by registry 2008-06-17 -// Updated by registry 2011-05-27 -ae.org -us.org - -// ZaNiC names : http://www.za.net/ -// Confirmed by registry 2009-10-03 -za.org - // pa : http://www.nic.pa/ // Some additional second level "domains" resolve directly as hostnames, such as // pannet.pa, so we add a rule for "pa". @@ -3776,9 +3703,6 @@ poznan.pl wroc.pl zakopane.pl -// co.pl : Mainseek Sp. z o.o. http://www.co.pl -co.pl - // pn : http://www.government.pn/PnRegistry/policies.htm pn gov.pn @@ -4942,7 +4866,77 @@ xxx // zw : http://en.wikipedia.org/wiki/.zw *.zw -// DynDNS.com Dynamic DNS zones : http://www.dyndns.com/services/dns/dyndns/ +// ===END ICANN DOMAINS=== +// ===BEGIN PRIVATE DOMAINS=== + +// info.at : http://www.info.at/ +biz.at +info.at + +// priv.at : http://www.nic.priv.at/ +// Submitted by registry 2008-06-09 +priv.at + +// co.ca : http://registry.co.ca +co.ca + +// CentralNic : http://www.centralnic.com/names/domains +// Confirmed by registry 2008-06-09 +ar.com +br.com +cn.com +de.com +eu.com +gb.com +gr.com +hu.com +jpn.com +kr.com +no.com +qc.com +ru.com +sa.com +se.com +uk.com +us.com +uy.com +za.com +gb.net +jp.net +se.net +uk.net +ae.org +us.org +com.de + +// Opera Software, A.S.A. +// Requested by Yngve Pettersen 2009-11-26 +operaunite.com + +// Google, Inc. +// Requested by Eduardo Vela 2010-09-06 +appspot.com + +// iki.fi : Submitted by Hannu Aronsson 2009-11-05 +iki.fi + +// c.la : http://www.c.la/ +c.la + +// ZaNiC : http://www.za.net/ +// Confirmed by registry 2009-10-03 +za.net +za.org + +// CoDNS B.V. +// Added 2010-05-23. +co.nl +co.no + +// Mainseek Sp. z o.o. : http://www.co.pl/ +co.pl + +// DynDNS.com : http://www.dyndns.com/services/dns/dyndns/ dyndns-at-home.com dyndns-at-work.com dyndns-blog.com @@ -5222,3 +5216,5 @@ webhop.net webhop.org worse-than.tv writesthisblog.com + +// ===END PRIVATE DOMAINS=== From 030491ff141c0d61c03320e6bafa87953fec7cb8 Mon Sep 17 00:00:00 2001 From: Gervase Markham Date: Tue, 6 Mar 2012 11:54:20 +0000 Subject: [PATCH 40/88] Bug 730814 - Add "mpl" anchor. --- toolkit/content/license.html | 38 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 9c367671cdab..0914bbfa986f 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -109,7 +109,7 @@
  • Other Required Notices
  • Optional Notices #ifdef XP_WIN -
  • Proprietary Operating System Libraries +
  • Proprietary Operating System Components #endif @@ -117,7 +117,7 @@
    -

    Mozilla Public License 2.0

    +

    Mozilla Public License 2.0

    1. Definitions

    @@ -2622,30 +2622,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. that specific operating system. The following license statements apply to such inclusions.

    -

    Microsoft Windows: Microsoft DirectX License

    +

    Microsoft Windows: 'Microsoft Distributable Code' License

    This license applies to the following files; they are referred to below as "Distributable Code": - d3dx9_*.dll and d3dcompiler_*.dll.

    +
      +
    • d3d*.dll (Direct3D libraries)
    • +
    • msvc*.dll (C and C++ runtime libraries)
    • +
    +

     Copyright (c) Microsoft Corporation.
    +
     The Distributable Code may be used and distributed only if you comply with the
    -following terms: (i) You may use, copy, and distribute the Distributable Code
    -only as part of this product; (ii) You may not use the Distributable Code on a
    -platform other than Windows; (iii) You may not alter any copyright, trademark
    -or patent notice in the Distributable Code; (iv) You may not modify or
    -distribute the source code of any Distributable Code so that any part of the
    -source code becomes subject to the MPL or any other copyleft license; (v) You
    -must comply with any technical limitations in the Distributable Code that only
    -allow you to use it in certain ways; and (vi) You must comply with all domestic
    -and international export laws and regulations that apply to the Distributable
    -Code.
    +following terms:
    +
    +(i)   You may use, copy, and distribute the Distributable Code only as part of
    +      this product;
    +(ii)  You may not use the Distributable Code on a platform other than Windows;
    +(iii) You may not alter any copyright, trademark or patent notice in the
    +      Distributable Code;
    +(iv)  You may not modify or distribute the source code of any Distributable
    +      Code so that any part of the source code becomes subject to the MPL or
    +      any other copyleft license;
    +(v)   You must comply with any technical limitations in the Distributable Code
    +      that only allow you to use it in certain ways; and
    +(vi)  You must comply with all domestic and international export laws and
    +      regulations that apply to the Distributable Code.
     
    #endif -
    From 9ffe4bb537318489ba8f8acb1cb3ac7cdba4ca57 Mon Sep 17 00:00:00 2001 From: Gervase Markham Date: Tue, 6 Mar 2012 13:43:46 +0000 Subject: [PATCH 41/88] Bug 731166 - add README to other-licenses, discouraging use. --- other-licenses/README | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 other-licenses/README diff --git a/other-licenses/README b/other-licenses/README new file mode 100644 index 000000000000..03de0ff2093b --- /dev/null +++ b/other-licenses/README @@ -0,0 +1,8 @@ +This directory was created for code which is used in the Mozilla project in +some way but is not under the MPL or a compatible license like the Apache 2, +BSD or MIT licenses. + +It is _NOT_ for "all non-MPLed code". + +Before putting any new code in here, please consult licensing@mozilla.org. It +is quite likely that this is not the right place. From 50b93219f8c8eb11f18e9017eb880ea8fd266478 Mon Sep 17 00:00:00 2001 From: Derrick Rice Date: Tue, 6 Mar 2012 05:02:00 -0500 Subject: [PATCH 42/88] Bug 732363 - Avoid oranges in test_net_addr.js by retaining references to the transport and output stream. r=jdm --- netwerk/test/unit/test_net_addr.js | 31 +++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/netwerk/test/unit/test_net_addr.js b/netwerk/test/unit/test_net_addr.js index d4bbffe60fa3..eb7555c484d1 100644 --- a/netwerk/test/unit/test_net_addr.js +++ b/netwerk/test/unit/test_net_addr.js @@ -121,22 +121,33 @@ var serv; */ var connectTimeout = 5*1000; +/** + * A place for individual tests to place Objects of importance for access + * throughout asynchronous testing. Particularly important for any output or + * input streams opened, as cleanup of those objects (by the garbage collector) + * causes the stream to close and may have other side effects. + */ +var testDataStore = null; + /** * IPv4 test. */ function testIpv4() { - var transport; + testDataStore = { + transport : null , + ouput : null + } serv.acceptCallback = function() { // disable the timeoutCallback serv.timeoutCallback = function(){}; - var selfAddr = transport.getScriptableSelfAddr(); - var peerAddr = transport.getScriptablePeerAddr(); + var selfAddr = testDataStore.transport.getScriptableSelfAddr(); + var peerAddr = testDataStore.transport.getScriptablePeerAddr(); // check peerAddr against expected values do_check_eq(peerAddr.family, Ci.nsINetAddr.FAMILY_INET); - do_check_eq(peerAddr.port, transport.port); + do_check_eq(peerAddr.port, testDataStore.transport.port); do_check_eq(peerAddr.port, serv.port); do_check_eq(peerAddr.address, "127.0.0.1"); @@ -148,6 +159,7 @@ function testIpv4() { checkAddrEqual(selfAddr, serv.peerAddr); checkAddrEqual(peerAddr, serv.selfAddr); + testDataStore = null; do_execute_soon(run_next_test); }; @@ -158,11 +170,16 @@ function testIpv4() { }; do_timeout(connectTimeout, function(){ serv.timeoutCallback('testIpv4'); });*/ - transport = sts.createTransport(null, 0, '127.0.0.1', serv.port, null); - transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING,0,0); + testDataStore.transport = sts.createTransport(null, 0, '127.0.0.1', serv.port, null); + /* + * Need to hold |output| so that the output stream doesn't close itself and + * the associated connection. + */ + testDataStore.output = testDataStore.transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING,0,0); + /* NEXT: * openOutputStream -> onSocketAccepted -> acceptedCallback -> run_next_test - * OR + * OR (if the above timeout is uncommented) * -> timeoutCallback -> do_throw */ } From 93fec03e76b07498e31f65e6fe9851ee83c9c8b4 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Tue, 6 Mar 2012 09:58:36 -0600 Subject: [PATCH 43/88] Bug 664707. Followup. Use the right point for touch events. r=matspal --- layout/base/nsLayoutUtils.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 425c67fed243..1a46283afac5 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1048,14 +1048,14 @@ nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIView* view = aFrame->GetView(); if (view) { - nsIWidget* fwidget = view->GetWidget(); - if (fwidget && fwidget == GUIEvent->widget) { + nsIWidget* frameWidget = view->GetWidget(); + if (frameWidget && frameWidget == GUIEvent->widget) { // Special case this cause it happens a lot. // This also fixes bug 664707, events in the extra-special case of select // dropdown popups that are transformed. nsPresContext* presContext = aFrame->PresContext(); - nsPoint pt(presContext->DevPixelsToAppUnits(GUIEvent->refPoint.x), - presContext->DevPixelsToAppUnits(GUIEvent->refPoint.y)); + nsPoint pt(presContext->DevPixelsToAppUnits(aPoint.x), + presContext->DevPixelsToAppUnits(aPoint.y)); return pt - view->ViewToWidgetOffset(); } } From d5ecc5c5abaaf48b707a29e3dba026f75696a35d Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 6 Mar 2012 11:38:04 -0500 Subject: [PATCH 44/88] Bug 733264 - Define FramePointerStackWalk on platforms where stackwalking is not supported; r=jrmuizel --- xpcom/base/nsStackWalk.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/xpcom/base/nsStackWalk.cpp b/xpcom/base/nsStackWalk.cpp index dc37eb9d5071..647f1267ef4d 100644 --- a/xpcom/base/nsStackWalk.cpp +++ b/xpcom/base/nsStackWalk.cpp @@ -1262,6 +1262,15 @@ NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames, return NS_ERROR_NOT_IMPLEMENTED; } +namespace mozilla { +nsresult +FramePointerStackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames, + void *aClosure, void **bp) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +} + EXPORT_XPCOM_API(nsresult) NS_DescribeCodeAddress(void *aPC, nsCodeAddressDetails *aDetails) { From 5ad8556084efe367a59283c6fe6d8c26f4d36de5 Mon Sep 17 00:00:00 2001 From: Gervase Markham Date: Tue, 6 Mar 2012 17:01:11 +0000 Subject: [PATCH 45/88] Bug 730814- back out last set of changes; there was another patch caught up in there. --HG-- extra : rebase_source : d30d97a1b494caef81ce1a6613e6084b551510af --- toolkit/content/license.html | 38 ++++++++++++++---------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 0914bbfa986f..9c367671cdab 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -109,7 +109,7 @@
  • Other Required Notices
  • Optional Notices #ifdef XP_WIN -
  • Proprietary Operating System Components +
  • Proprietary Operating System Libraries #endif @@ -117,7 +117,7 @@
    -

    Mozilla Public License 2.0

    +

    Mozilla Public License 2.0

    1. Definitions

    @@ -2622,38 +2622,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. that specific operating system. The following license statements apply to such inclusions.

    -

    Microsoft Windows: 'Microsoft Distributable Code' License

    +

    Microsoft Windows: Microsoft DirectX License

    This license applies to the following files; they are referred to below as "Distributable Code": -

      -
    • d3d*.dll (Direct3D libraries)
    • -
    • msvc*.dll (C and C++ runtime libraries)
    • -
    -

    + d3dx9_*.dll and d3dcompiler_*.dll.

     Copyright (c) Microsoft Corporation.
    -
     The Distributable Code may be used and distributed only if you comply with the
    -following terms:
    -
    -(i)   You may use, copy, and distribute the Distributable Code only as part of
    -      this product;
    -(ii)  You may not use the Distributable Code on a platform other than Windows;
    -(iii) You may not alter any copyright, trademark or patent notice in the
    -      Distributable Code;
    -(iv)  You may not modify or distribute the source code of any Distributable
    -      Code so that any part of the source code becomes subject to the MPL or
    -      any other copyleft license;
    -(v)   You must comply with any technical limitations in the Distributable Code
    -      that only allow you to use it in certain ways; and
    -(vi)  You must comply with all domestic and international export laws and
    -      regulations that apply to the Distributable Code.
    +following terms: (i) You may use, copy, and distribute the Distributable Code
    +only as part of this product; (ii) You may not use the Distributable Code on a
    +platform other than Windows; (iii) You may not alter any copyright, trademark
    +or patent notice in the Distributable Code; (iv) You may not modify or
    +distribute the source code of any Distributable Code so that any part of the
    +source code becomes subject to the MPL or any other copyleft license; (v) You
    +must comply with any technical limitations in the Distributable Code that only
    +allow you to use it in certain ways; and (vi) You must comply with all domestic
    +and international export laws and regulations that apply to the Distributable
    +Code.
     
    #endif +
    From b1a8dac696e7cc050fe696b94486bfe9de498766 Mon Sep 17 00:00:00 2001 From: Gervase Markham Date: Tue, 6 Mar 2012 17:03:46 +0000 Subject: [PATCH 46/88] Bug 730814 - Add "mpl" anchor. (Corrected patch) --- toolkit/content/license.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 9c367671cdab..10dc6974b638 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -117,7 +117,7 @@
    -

    Mozilla Public License 2.0

    +

    Mozilla Public License 2.0

    1. Definitions

    From a31674b55cad7eaa2f631dda7fbce345e3864553 Mon Sep 17 00:00:00 2001 From: Trevor Saunders Date: Wed, 7 Mar 2012 02:08:02 +0900 Subject: [PATCH 47/88] Bug 726287 - misc decomtamination in nsHypertextAccessible, r=surkov --- accessible/src/html/nsHyperTextAccessible.cpp | 82 +++++++------------ accessible/src/html/nsHyperTextAccessible.h | 2 +- 2 files changed, 29 insertions(+), 55 deletions(-) diff --git a/accessible/src/html/nsHyperTextAccessible.cpp b/accessible/src/html/nsHyperTextAccessible.cpp index c1060c317002..31426a26ef29 100644 --- a/accessible/src/html/nsHyperTextAccessible.cpp +++ b/accessible/src/html/nsHyperTextAccessible.cpp @@ -47,6 +47,7 @@ #include "States.h" #include "nsIClipboard.h" +#include "nsContentUtils.h" #include "nsFocusManager.h" #include "nsIDOMCharacterData.h" #include "nsIDOMDocument.h" @@ -1153,15 +1154,14 @@ nsHyperTextAccessible::GetTextAttributes(bool aIncludeDefAttrs, // Compute spelling attributes on text accessible only. nsIFrame *offsetFrame = accAtOffset->GetFrame(); if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) { - nsCOMPtr node = accAtOffset->DOMNode(); - PRInt32 nodeOffset = 0; nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc, &nodeOffset); NS_ENSURE_SUCCESS(rv, rv); // Set 'misspelled' text attribute. - rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset, + rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset, + &startOffset, &endOffset, aAttributes ? *aAttributes : nsnull); NS_ENSURE_SUCCESS(rv, rv); } @@ -1789,13 +1789,11 @@ nsHyperTextAccessible::GetSelectionDOMRanges(PRInt16 aType, // Remove collapsed ranges PRUint32 numRanges = aRanges->Length(); - for (PRUint32 count = 0; count < numRanges; count ++) { - bool isCollapsed = false; - (*aRanges)[count]->GetCollapsed(&isCollapsed); - if (isCollapsed) { - aRanges->RemoveElementAt(count); + for (PRUint32 idx = 0; idx < numRanges; idx ++) { + if ((*aRanges)[idx]->Collapsed()) { + aRanges->RemoveElementAt(idx); --numRanges; - --count; + --idx; } } } @@ -1837,29 +1835,19 @@ nsHyperTextAccessible::GetSelectionBounds(PRInt32 aSelectionNum, nsRange* range = ranges[aSelectionNum]; - // Get start point - nsCOMPtr startDOMNode; - range->GetStartContainer(getter_AddRefs(startDOMNode)); - nsCOMPtr startNode(do_QueryInterface(startDOMNode)); - PRInt32 startOffset = 0; - range->GetStartOffset(&startOffset); + // Get start and end points. + nsINode* startNode = range->GetStartParent(); + nsINode* endNode = range->GetEndParent(); + PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset(); - // Get end point - nsCOMPtr endDOMNode; - range->GetEndContainer(getter_AddRefs(endDOMNode)); - nsCOMPtr endNode(do_QueryInterface(endDOMNode)); - PRInt32 endOffset = 0; - range->GetEndOffset(&endOffset); - - PRInt16 rangeCompareResult = 0; - nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range, - &rangeCompareResult); - NS_ENSURE_SUCCESS(rv, rv); - - if (rangeCompareResult < 0) { - // Make sure start is before end, by swapping offsets - // This occurs when the user selects backwards in the text - startNode.swap(endNode); + // Make sure start is before end, by swapping DOM points. This occurs when + // the user selects backwards in the text. + PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset, + startNode, startOffset); + if (rangeCompare < 0) { + nsINode* tempNode = startNode; + startNode = endNode; + endNode = tempNode; PRInt32 tempOffset = startOffset; startOffset = endOffset; endOffset = tempOffset; @@ -2324,25 +2312,17 @@ nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange, bool aIsStartHTOffset, PRInt32 *aHTOffset) { - nsCOMPtr DOMNode; + nsINode* node = nsnull; PRInt32 nodeOffset = 0; - nsresult rv; if (aIsStartBound) { - rv = aRange->GetStartContainer(getter_AddRefs(DOMNode)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aRange->GetStartOffset(&nodeOffset); - NS_ENSURE_SUCCESS(rv, rv); + node = aRange->GetStartParent(); + nodeOffset = aRange->StartOffset(); } else { - rv = aRange->GetEndContainer(getter_AddRefs(DOMNode)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aRange->GetEndOffset(&nodeOffset); - NS_ENSURE_SUCCESS(rv, rv); + node = aRange->GetEndParent(); + nodeOffset = aRange->EndOffset(); } - nsCOMPtr node(do_QueryInterface(DOMNode)); nsAccessible *startAcc = DOMPointToHypertextOffset(node, nodeOffset, aHTOffset); @@ -2354,7 +2334,7 @@ nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange, // nsHyperTextAccessible nsresult -nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode, +nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset, PRInt32 *aHTStartOffset, PRInt32 *aHTEndOffset, @@ -2367,25 +2347,19 @@ nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode, if (!rangeCount) return NS_OK; + nsCOMPtr DOMNode = do_QueryInterface(aNode); for (PRUint32 index = 0; index < rangeCount; index++) { nsRange* range = ranges[index]; PRInt16 result; - nsresult rv = range->ComparePoint(aNode, aNodeOffset, &result); + nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result); NS_ENSURE_SUCCESS(rv, rv); // ComparePoint checks boundary points, but we need to check that // text at aNodeOffset is inside the range. // See also bug 460690. if (result == 0) { - nsCOMPtr end; - rv = range->GetEndContainer(getter_AddRefs(end)); - NS_ENSURE_SUCCESS(rv, rv); - PRInt32 endOffset; - rv = range->GetEndOffset(&endOffset); - NS_ENSURE_SUCCESS(rv, rv); - if (aNode == end && aNodeOffset == endOffset) { + if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset()) result = 1; - } } if (result == 1) { // range is before point diff --git a/accessible/src/html/nsHyperTextAccessible.h b/accessible/src/html/nsHyperTextAccessible.h index 44e93ca4cfe8..a53d20bb7b76 100644 --- a/accessible/src/html/nsHyperTextAccessible.h +++ b/accessible/src/html/nsHyperTextAccessible.h @@ -409,7 +409,7 @@ protected: * @param aEndOffset [in, out] the end offset * @param aAttributes [out, optional] result attributes */ - nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset, + nsresult GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset, PRInt32 *aStartOffset, PRInt32 *aEndOffset, nsIPersistentProperties *aAttributes); From 49eeb01f2a1ca155995078e6e3caef2f234dd373 Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Tue, 6 Mar 2012 17:38:55 +0000 Subject: [PATCH 48/88] bug 719410 pt 1 - don't use subpixel AA under GDI/DirectWrite if ClearType is disabled system-wide. r=bas --- gfx/cairo/cairo/src/cairo-dwrite-font.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp index 22d7396523d3..9df827b8a428 100644 --- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp +++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp @@ -1157,7 +1157,17 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, surface->dc, area.left, area.top, SRCCOPY | NOMIRRORBITMAP); - HRESULT hr = rt->DrawGlyphRun(0, 0, DWRITE_MEASURING_MODE_NATURAL, run, params, color); + DWRITE_MEASURING_MODE measureMode; + switch (scaled_font->rendering_mode) { + case cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC: + case cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE: + measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC; + break; + default: + measureMode = DWRITE_MEASURING_MODE_NATURAL; + break; + } + HRESULT hr = rt->DrawGlyphRun(0, 0, measureMode, run, params, color); BitBlt(surface->dc, area.left, area.top, area.right - area.left, area.bottom - area.top, From c14bb741070b6192dbe5a8b4ab9fe50033b73ef7 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 6 Mar 2012 09:48:33 -0800 Subject: [PATCH 49/88] Bug 730418 - Don't open a new tab when the search key is pressed [r=sriram] --- mobile/android/base/BrowserToolbar.java | 2 +- mobile/android/base/GeckoApp.java | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 487528cb098f..6638c927b230 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -196,7 +196,7 @@ public class BrowserToolbar extends LinearLayout { } private void onAwesomeBarSearch() { - GeckoApp.mAppContext.onEditRequested(); + GeckoApp.mAppContext.onSearchRequested(); } private void addTab() { diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index f33c8da9ebed..783dce941c11 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -2383,10 +2383,6 @@ abstract public class GeckoApp @Override public boolean onSearchRequested() { - return showAwesomebar(AwesomeBar.Type.ADD); - } - - public boolean onEditRequested() { return showAwesomebar(AwesomeBar.Type.EDIT); } From e24272dc05f0bb1429475ed42ed8076b6f5ceab6 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 6 Mar 2012 11:05:37 -0800 Subject: [PATCH 50/88] Back out 2fbc7d9ac670 (bug 731845) on suspicion of causing test_native_mouse_mac.xul timeouts --- content/base/src/nsNodeUtils.cpp | 19 +++++++++++ js/xpconnect/src/XPCWrappedNative.cpp | 45 --------------------------- 2 files changed, 19 insertions(+), 45 deletions(-) diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index 14e7f429d381..f4a75a6c81a7 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -597,10 +597,29 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, if (aCx && wrapper) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { + JSObject *preservedWrapper = nsnull; + + // If reparenting moves us to a new compartment, preserving causes + // problems. In that case, we release ourselves and re-preserve after + // reparenting so we're sure to have the right JS object preserved. + // We use a JSObject stack copy of the wrapper to protect it from GC + // under ReparentWrappedNativeIfFound. + if (aNode->PreservingWrapper()) { + preservedWrapper = wrapper; + nsContentUtils::ReleaseWrapper(aNode, aNode); + NS_ASSERTION(aNode->GetWrapper(), + "ReleaseWrapper cleared our wrapper, this code needs to " + "be changed to deal with that!"); + } + nsCOMPtr oldWrapper; rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode, getter_AddRefs(oldWrapper)); + if (preservedWrapper) { + nsContentUtils::PreserveWrapper(aNode, aNode); + } + if (NS_FAILED(rv)) { aNode->mNodeInfo.swap(nodeInfo); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 4372f9d2a4c9..26cf95789f41 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -53,8 +53,6 @@ #include "WrapperFactory.h" #include "dombindings.h" -#include "nsContentUtils.h" - #include "mozilla/Util.h" bool @@ -1481,43 +1479,6 @@ XPCWrappedNative::SystemIsBeingShutDown() /***************************************************************************/ -// If we have to transplant an object across compartments, we need to be -// careful if the underlying object implements nsWrapperCache and is preserving -// the wrapper. -// -// The class brackets a pair of Unpreserve/Preserve calls in the given scope. -// -// This class _must_ live on the stack, in part so that mPreservedWrapper is -// visible to the stack scanner. The caller wants the wrapper to be preserved, -// so we don't want it to get accidentally GCed. -class AutoWrapperChanger NS_STACK_CLASS { -public: - AutoWrapperChanger() : mCache(nsnull) - , mCOMObj(nsnull) - , mPreservedWrapper(nsnull) - {} - - void init(nsISupports* aCOMObj, nsWrapperCache* aWrapperCache) { - mCOMObj = aCOMObj; - mCache = aWrapperCache; - if (mCache->PreservingWrapper()) { - mPreservedWrapper = mCache->GetWrapper(); - MOZ_ASSERT(mPreservedWrapper); - nsContentUtils::ReleaseWrapper(mCOMObj, mCache); - } - } - - ~AutoWrapperChanger() { - if (mPreservedWrapper) - nsContentUtils::PreserveWrapper(mCOMObj, mCache); - } - -private: - nsWrapperCache* mCache; - nsISupports* mCOMObj; - JSObject* mPreservedWrapper; -}; - // static nsresult XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, @@ -1536,16 +1497,10 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, nsresult rv; nsRefPtr wrapper; - AutoWrapperChanger wrapperChanger; JSObject *flat; nsWrapperCache* cache = nsnull; CallQueryInterface(aCOMObj, &cache); if (cache) { - - // There's a wrapper cache. Make sure we keep it sane no matter what - // happens. - wrapperChanger.init(aCOMObj, cache); - flat = cache->GetWrapper(); if (flat && !IS_SLIM_WRAPPER_OBJECT(flat)) { wrapper = static_cast(xpc_GetJSPrivate(flat)); From 6f456cc9da1e711b686e2f4bb5585662fa1bdd50 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Tue, 6 Mar 2012 20:28:51 +0100 Subject: [PATCH 51/88] Bug 731563 - Make Places view markers proper elements in the popups. r=mano --- .../places/content/browserPlacesViews.js | 227 ++++++++---------- browser/components/places/content/menu.xml | 23 +- browser/themes/gnomestripe/browser.css | 2 +- browser/themes/pinstripe/browser.css | 52 ++-- 4 files changed, 138 insertions(+), 166 deletions(-) diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js index b9bc5475e0b1..0cf3c274c0f1 100644 --- a/browser/components/places/content/browserPlacesViews.js +++ b/browser/components/places/content/browserPlacesViews.js @@ -167,73 +167,33 @@ PlacesViewBase.prototype = { }, _cleanPopup: function PVB_cleanPopup(aPopup) { - // Remove places popup children and update markers to keep track of - // their indices. - let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0; - let end = aPopup._endMarker != -1 ? aPopup._endMarker : - aPopup.childNodes.length; - let items = []; - - // Automatically adjust the start and the end markers. - let firstNonStaticNodeFound = false; - for (let i = start; i < end; ++i) { - let item = aPopup.childNodes[i]; - if (item.getAttribute("builder") == "end") { - // we need to do this for menus that have static content at the end but - // are initially empty, eg. the history menu, we need to know where to - // start inserting new items. - aPopup._endMarker = i; - break; - } - - if (item._placesNode) { - items.push(item); - firstNonStaticNodeFound = true; - } - else { - // This is static content. - if (!firstNonStaticNodeFound) { - // We are at the beginning of the popup, in static content. - // The markers are initialized in menu.xml, in the base binding. - aPopup._startMarker++; - } - else { - // We are at the end of the popup, after places nodes - aPopup._endMarker = i; - break; - } - } - } - - for (let i = 0; i < items.length; ++i) { - aPopup.removeChild(items[i]); - if (aPopup._endMarker != -1) - aPopup._endMarker--; + // Remove Places nodes from the popup. + let child = aPopup._startMarker; + while (child.nextSibling != aPopup._endMarker) { + if (child.nextSibling._placesNode) + aPopup.removeChild(child.nextSibling); + else + child = child.nextSibling; } }, _rebuildPopup: function PVB__rebuildPopup(aPopup) { - this._cleanPopup(aPopup); - let resultNode = aPopup._placesNode; if (!resultNode.containerOpen) return; if (resultNode._feedURI) { - aPopup.removeAttribute("emptyplacesresult"); - if (aPopup._emptyMenuItem) { - aPopup._emptyMenuItem.hidden = true; - } + this._setEmptyPopupStatus(aPopup, false); aPopup._built = true; this._populateLivemarkPopup(aPopup); return; } + this._cleanPopup(aPopup); + let cc = resultNode.childCount; if (cc > 0) { - aPopup.removeAttribute("emptyplacesresult"); - if (aPopup._emptyMenuItem) - aPopup._emptyMenuItem.hidden = true; + this._setEmptyPopupStatus(aPopup, false); for (let i = 0; i < cc; ++i) { let child = resultNode.getChild(i); @@ -241,11 +201,7 @@ PlacesViewBase.prototype = { } } else { - aPopup.setAttribute("emptyplacesresult", "true"); - // This menu is empty. If there is no static content, add - // an element to show it is empty. - if (aPopup._startMarker == -1 && aPopup._endMarker == -1) - this._showEmptyMenuItem(aPopup); + this._setEmptyPopupStatus(aPopup, true); } aPopup._built = true; }, @@ -260,17 +216,28 @@ PlacesViewBase.prototype = { aChild.parentNode.removeChild(aChild); }, - _showEmptyMenuItem: function PVB__showEmptyMenuItem(aPopup) { - if (aPopup._emptyMenuItem) { - aPopup._emptyMenuItem.hidden = false; - return; + _setEmptyPopupStatus: + function PVB__setEmptyPopupStatus(aPopup, aEmpty) { + if (!aPopup._emptyMenuitem) { + let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder"); + aPopup._emptyMenuitem = document.createElement("menuitem"); + aPopup._emptyMenuitem.setAttribute("label", label); + aPopup._emptyMenuitem.setAttribute("disabled", true); } - let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder"); - aPopup._emptyMenuItem = document.createElement("menuitem"); - aPopup._emptyMenuItem.setAttribute("label", label); - aPopup._emptyMenuItem.setAttribute("disabled", true); - aPopup.appendChild(aPopup._emptyMenuItem); + if (aEmpty) { + aPopup.setAttribute("emptyplacesresult", "true"); + // Don't add the menuitem if there is static content. + if (!aPopup._startMarker.previousSibling && + !aPopup._endMarker.nextSibling) + aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker); + } + else { + aPopup.removeAttribute("emptyplacesresult"); + try { + aPopup.removeChild(aPopup._emptyMenuitem); + } catch (ex) {} + } }, _createMenuItemForPlacesNode: @@ -308,6 +275,12 @@ PlacesViewBase.prototype = { function (aStatus, aLivemark) { if (Components.isSuccessCode(aStatus)) { element.setAttribute("livemark", "true"); +#ifdef XP_MACOSX + // OS X native menubar doesn't track list-style-images since + // it doesn't have a frame (bug 733415). Thus enforce updating. + element.setAttribute("image", ""); + element.removeAttribute("image"); +#endif // Set an expando on the node, controller will use it to build // its metadata. aPlacesNode._feedURI = aLivemark.feedURI; @@ -319,12 +292,11 @@ PlacesViewBase.prototype = { let popup = document.createElement("menupopup"); popup._placesNode = PlacesUtils.asContainer(aPlacesNode); - if (this._nativeView) { - popup._startMarker = -1; - popup._endMarker = -1; - } - else + + if (!this._nativeView) { popup.setAttribute("placespopup", "true"); + } + #ifdef XP_MACOSX // No context menu on mac. popup.setAttribute("context", "placesContext"); @@ -354,26 +326,8 @@ PlacesViewBase.prototype = { _insertNewItemToPopup: function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) { let element = this._createMenuItemForPlacesNode(aNewChild); - - if (aBefore) { - aPopup.insertBefore(element, aBefore); - } - else { - // Add the new element to the menu. If there is static content at - // the end of the menu, add the element before that. Otherwise, - // just add to the end. - if (aPopup._endMarker != -1) { - let lastElt = aPopup.childNodes[aPopup._endMarker]; - aPopup.insertBefore(element, lastElt); - } - else { - aPopup.appendChild(element); - } - } - - if (aPopup._endMarker != -1) - aPopup._endMarker++; - + let before = aBefore || aPopup._endMarker; + aPopup.insertBefore(element, before); return element; }, @@ -384,10 +338,8 @@ PlacesViewBase.prototype = { if (!siteUrl && aPopup._siteURIMenuitem) { aPopup.removeChild(aPopup._siteURIMenuitem); aPopup._siteURIMenuitem = null; - aPopup._startMarker--; aPopup.removeChild(aPopup._siteURIMenuseparator); aPopup._siteURIMenuseparator = null; - aPopup._startMarker--; } else if (siteUrl && !aPopup._siteURIMenuitem) { // Add "Open (Feed Name)" menuitem. @@ -407,14 +359,10 @@ PlacesViewBase.prototype = { PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label", [aPopup.parentNode.getAttribute("label")]) aPopup._siteURIMenuitem.setAttribute("label", label); - aPopup.insertBefore(aPopup._siteURIMenuitem, - aPopup.childNodes.item(aPopup._startMarker + 1)); - aPopup._startMarker++; + aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker); aPopup._siteURIMenuseparator = document.createElement("menuseparator"); - aPopup.insertBefore(aPopup._siteURIMenuseparator, - aPopup.childNodes.item(aPopup._startMarker + 1)); - aPopup._startMarker++; + aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker); } }, @@ -427,7 +375,6 @@ PlacesViewBase.prototype = { */ _setLivemarkStatusMenuItem: function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) { - let itemId = aPopup._placesNode.itemId; let statusMenuitem = aPopup._statusMenuitem; let stringId = ""; if (aStatus == Ci.mozILivemark.STATUS_LOADING) @@ -439,12 +386,11 @@ PlacesViewBase.prototype = { // Create the status menuitem and cache it in the popup object. statusMenuitem = document.createElement("menuitem"); statusMenuitem.setAttribute("livemarkStatus", stringId); + statusMenuitem.className = "livemarkstatus-menuitem"; statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId)); statusMenuitem.setAttribute("disabled", true); - aPopup.insertBefore(statusMenuitem, - aPopup.childNodes.item(aPopup._startMarker + 1)); + aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling); aPopup._statusMenuitem = statusMenuitem; - aPopup._startMarker++; } else if (stringId && statusMenuitem.getAttribute("livemarkStatus") != stringId) { @@ -455,7 +401,6 @@ PlacesViewBase.prototype = { // The livemark has finished loading. aPopup.removeChild(aPopup._statusMenuitem); aPopup._statusMenuitem = null; - aPopup._startMarker--; } }, @@ -517,6 +462,12 @@ PlacesViewBase.prototype = { let menu = elt.parentNode; if (!menu.hasAttribute("livemark")) { menu.setAttribute("livemark", "true"); +#ifdef XP_MACOSX + // OS X native menubar doesn't track list-style-images since + // it doesn't have a frame (bug 733415). Thus enforce updating. + menu.setAttribute("image", ""); + menu.removeAttribute("image"); +#endif } PlacesUtils.livemarks.getLivemark( @@ -580,13 +531,8 @@ PlacesViewBase.prototype = { // Figure out if we need to show the "" menu-item. // TODO Bug 517701: This doesn't seem to handle the case of an empty // root. - if (!parentElt.hasChildNodes() || - (parentElt.childNodes.length == 1 && - parentElt.firstChild == parentElt._emptyMenuItem)) - this._showEmptyMenuItem(parentElt); - - if (parentElt._endMarker != -1) - parentElt._endMarker--; + if (parentElt._startMarker.nextSibling == parentElt._endMarker) + this._setEmptyPopupStatus(parentElt, true); } }, @@ -620,8 +566,9 @@ PlacesViewBase.prototype = { if (aPlacesNode.parent && aPlacesNode.parent._feedURI) { // Find the node in the parent. let popup = aPlacesNode.parent._DOMElement; - for (let i = popup._startMarker; i < popup.childNodes.length; i++) { - let child = popup.childNodes[i]; + for (let child = popup._startMarker.nextSibling; + child != popup._endMarker; + child = child.nextSibling) { if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) { if (aCount) child.setAttribute("visited", "true"); @@ -649,11 +596,11 @@ PlacesViewBase.prototype = { if (!parentElt._built) return; - let index = parentElt._startMarker + 1 + aIndex; + let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) + + aIndex + 1; this._insertNewItemToPopup(aPlacesNode, parentElt, parentElt.childNodes[index]); - if (parentElt._emptyMenuItem) - parentElt._emptyMenuItem.hidden = true; + this._setEmptyPopupStatus(parentElt, false); }, nodeMoved: @@ -684,7 +631,8 @@ PlacesViewBase.prototype = { if (parentElt._built) { // Move the node. parentElt.removeChild(elt); - let index = parentElt._startMarker + 1 + aNewIndex; + let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) + + aNewIndex + 1; parentElt.insertBefore(elt, parentElt.childNodes[index]); } }, @@ -821,11 +769,9 @@ PlacesViewBase.prototype = { if (aPopup._endOptOpenAllInTabs) { aPopup.removeChild(aPopup._endOptOpenAllInTabs); aPopup._endOptOpenAllInTabs = null; - aPopup._endMarker--; aPopup.removeChild(aPopup._endOptSeparator); aPopup._endOptSeparator = null; - aPopup._endMarker--; } } else if (!aPopup._endOptOpenAllInTabs) { @@ -833,7 +779,6 @@ PlacesViewBase.prototype = { aPopup._endOptSeparator = document.createElement("menuseparator"); aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator"; aPopup.appendChild(aPopup._endOptSeparator); - aPopup._endMarker++; // Add the "Open All in Tabs" menuitem. aPopup._endOptOpenAllInTabs = document.createElement("menuitem"); @@ -846,13 +791,51 @@ PlacesViewBase.prototype = { aPopup._endOptOpenAllInTabs.setAttribute("label", gNavigatorBundle.getString("menuOpenAllInTabs.label")); aPopup.appendChild(aPopup._endOptOpenAllInTabs); - aPopup._endMarker++; + } + }, + + _ensureMarkers: function PVB__ensureMarkers(aPopup) { + if (aPopup._startMarker) + return; + + // _startMarker is an hidden menuseparator that lives before places nodes. + aPopup._startMarker = document.createElement("menuseparator"); + aPopup._startMarker.hidden = true; + aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild); + + // _endMarker is an hidden menuseparator that lives after places nodes. + aPopup._endMarker = document.createElement("menuseparator"); + aPopup._endMarker.hidden = true; + aPopup.appendChild(aPopup._endMarker); + + // Move the markers to the right position. + let firstNonStaticNodeFound = false; + for (let i = 0; i < aPopup.childNodes.length; i++) { + let child = aPopup.childNodes[i]; + // Menus that have static content at the end, but are initially empty, + // use a special "builder" attribute to figure out where to start + // inserting places nodes. + if (child.getAttribute("builder") == "end") { + aPopup.insertBefore(aPopup._endMarker, child); + break; + } + + if (child._placesNode && !firstNonStaticNodeFound) { + firstNonStaticNodeFound = true; + aPopup.insertBefore(aPopup._startMarker, child); + } + } + if (!firstNonStaticNodeFound) { + aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker); } }, _onPopupShowing: function PVB__onPopupShowing(aEvent) { // Avoid handling popupshowing of inner views. let popup = aEvent.originalTarget; + + this._ensureMarkers(popup); + if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) { if (!popup._placesNode.containerOpen) popup._placesNode.containerOpen = true; @@ -1808,8 +1791,6 @@ function PlacesMenu(aPopupShowingEvent, aPlace) { #ifdef XP_MACOSX if (this._viewElt.parentNode.localName == "menubar") { this._nativeView = true; - this._rootElt._startMarker = -1; - this._rootElt._endMarker = -1; } #endif @@ -1829,8 +1810,6 @@ PlacesMenu.prototype = { _removeChild: function PM_removeChild(aChild) { PlacesViewBase.prototype._removeChild.apply(this, arguments); - if (this._endMarker != -1) - this._endMarker--; }, uninit: function PM_uninit() { diff --git a/browser/components/places/content/menu.xml b/browser/components/places/content/menu.xml index 52d31524a8ca..f11dc3a1766f 100644 --- a/browser/components/places/content/menu.xml +++ b/browser/components/places/content/menu.xml @@ -72,10 +72,6 @@ "popup-internal-box"); - - -1 - -1 - PlacesUIUtils.getViewForNode(this); @@ -83,17 +79,16 @@ = this.childNodes[this._endMarker].boxObject.y) - betweenMarkers = false; + // Don't draw the drop indicator outside of markers. + // The markers are hidden, since otherwise sometimes popups acquire + // scrollboxes on OS X, so we can't use them directly. + let firstChildTop = this._startMarker.nextSibling.boxObject.y; + let lastChildBottom = this._endMarker.previousSibling.boxObject.y + + this._endMarker.previousSibling.boxObject.height; + let betweenMarkers = target.boxObject.y >= firstChildTop || + target.boxObject.y <= lastChildBottom; // Hide the dropmarker if current node is not a Places node. return !(target && target._placesNode && betweenMarkers); diff --git a/browser/themes/gnomestripe/browser.css b/browser/themes/gnomestripe/browser.css index 5b91352180ad..49d6d673f925 100644 --- a/browser/themes/gnomestripe/browser.css +++ b/browser/themes/gnomestripe/browser.css @@ -199,7 +199,7 @@ menuitem.bookmark-item { } /* Bookmark items */ -.bookmark-item:not([container]) { +.bookmark-item { list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png"); } diff --git a/browser/themes/pinstripe/browser.css b/browser/themes/pinstripe/browser.css index 43b51b2786da..f5ba4f3e2c32 100644 --- a/browser/themes/pinstripe/browser.css +++ b/browser/themes/pinstripe/browser.css @@ -229,15 +229,20 @@ toolbarbutton.bookmark-item > menupopup { list-style-image: url("chrome://global/skin/tree/folder.png"); } -.query-item[container] { - list-style-image: url("chrome://browser/skin/places/history.png"); -} - -.bookmark-item[livemark] { +.bookmark-item[container][livemark] { list-style-image: url("chrome://browser/skin/page-livemarks.png"); } -.bookmark-item[query] { +.bookmark-item[container][livemark] .bookmark-item { + list-style-image: url("chrome://browser/skin/places/livemark-item.png"); + -moz-image-region: rect(0px, 16px, 16px, 0px); +} + +.bookmark-item[container][livemark] .bookmark-item[visited] { + -moz-image-region: rect(0px, 32px, 16px, 16px); +} + +.bookmark-item[container][query] { list-style-image: url("chrome://browser/skin/places/query.png"); } @@ -257,20 +262,23 @@ toolbarbutton.bookmark-item > menupopup { list-style-image: url("chrome://global/skin/tree/folder.png"); } -.bookmark-item[livemark] .menuitem-iconic { - list-style-image: url("chrome://browser/skin/places/livemark-item.png"); - -moz-image-region: rect(0px, 16px, 16px, 0px); -} - -.bookmark-item[livemark] .menuitem-iconic[visited] { - -moz-image-region: rect(0px, 32px, 16px, 16px); -} - -.bookmark-item menuitem[openInTabs], -.bookmark-item menuitem[siteURI] { +/* Workaround for native menubar inheritance */ +.openintabs-menuitem, +.openlivemarksite-menuitem, +.livemarkstatus-menuitem { list-style-image: none; } +.bookmark-item[cutting] > .toolbarbutton-icon, +.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon { + opacity: 0.5; +} + +.bookmark-item[cutting] > .toolbarbutton-text, +.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text { + opacity: 0.7; +} + #wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box { background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center; } @@ -287,16 +295,6 @@ toolbarbutton.bookmark-item > menupopup { height: 16px; } -.bookmark-item[cutting] > .toolbarbutton-icon, -.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon { - opacity: 0.5; -} - -.bookmark-item[cutting] > .toolbarbutton-text, -.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text { - opacity: 0.7; -} - #bookmarksToolbarFolderMenu, #BMB_bookmarksToolbar { list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png"); From a5a2893e5dc9820d4dc3aa8cd2f3b7d5f74692bb Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Tue, 6 Mar 2012 11:30:25 -0800 Subject: [PATCH 52/88] Bug 733372 - Fix an infinite recursion in GC marking; r=billm Since the private data is sometimes packed in strange ways into the underlying structure, we cannot just pass a reference to it to the marker. Instead, we store the gotten value on the C stack, do marking and potentially update its value there, then pass the updated privates location to the object using setPrivate. If the setPrivate function triggers write barriers that end up marking, this can recurse. For now we should simply not do this. --HG-- extra : rebase_source : ce2607907cc66f8beaeab433e21ba302cbf1b34e --- js/src/jsapi.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 98214d498303..657363e15417 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4267,7 +4267,7 @@ prop_iter_trace(JSTracer *trc, JSObject *obj) */ Shape *tmp = (Shape *)pdata; MarkShapeUnbarriered(trc, &tmp, "prop iter shape"); - obj->setPrivate(tmp); + JS_ASSERT(tmp == pdata); } else { /* Non-native case: mark each id in the JSIdArray private. */ JSIdArray *ida = (JSIdArray *) pdata; From f24f98c9e3bdf17567d4656d653db402d27059e7 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 6 Mar 2012 11:38:44 -0800 Subject: [PATCH 53/88] Bug 729369 - Expose the same set of SpiderMonkey testing APIs to debug shell and debug browser chrome (r=Waldo) --- js/src/Makefile.in | 1 + js/src/builtin/TestingFunctions.cpp | 552 ++++++++++++++ js/src/builtin/TestingFunctions.h | 16 + js/src/jscntxt.cpp | 31 + js/src/jscntxt.h | 8 + js/src/jsfriendapi.cpp | 60 ++ js/src/jsfriendapi.h | 18 + js/src/shell/js.cpp | 1089 ++++++++------------------- js/xpconnect/idl/xpccomponents.idl | 5 +- js/xpconnect/src/XPCComponents.cpp | 12 + 10 files changed, 1018 insertions(+), 774 deletions(-) create mode 100644 js/src/builtin/TestingFunctions.cpp create mode 100644 js/src/builtin/TestingFunctions.h diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 5dab375008ee..5c38969cbace 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -165,6 +165,7 @@ CPPSRCS = \ Parser.cpp \ SemanticAnalysis.cpp \ TokenStream.cpp \ + TestingFunctions.cpp \ LifoAlloc.cpp \ MapObject.cpp \ MemoryMetrics.cpp \ diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp new file mode 100644 index 000000000000..3bb74f032328 --- /dev/null +++ b/js/src/builtin/TestingFunctions.cpp @@ -0,0 +1,552 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jsapi.h" +#include "jsbool.h" +#include "jscntxt.h" +#include "jscompartment.h" +#include "jsfriendapi.h" +#include "jsobj.h" +#include "jsprf.h" +#include "jswrapper.h" + +#include "methodjit/MethodJIT.h" + +using namespace js; +using namespace JS; + +static JSBool +GC(JSContext *cx, unsigned argc, jsval *vp) +{ + JSCompartment *comp = NULL; + if (argc == 1) { + Value arg = vp[2]; + if (arg.isObject()) + comp = UnwrapObject(&arg.toObject())->compartment(); + } + +#ifndef JS_MORE_DETERMINISTIC + size_t preBytes = cx->runtime->gcBytes; +#endif + + JS_CompartmentGC(cx, comp); + + char buf[256] = { '\0' }; +#ifndef JS_MORE_DETERMINISTIC + JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n", + (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes); +#endif + JSString *str = JS_NewStringCopyZ(cx, buf); + if (!str) + return false; + *vp = STRING_TO_JSVAL(str); + return true; +} + +static const struct ParamPair { + const char *name; + JSGCParamKey param; +} paramMap[] = { + {"maxBytes", JSGC_MAX_BYTES }, + {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, + {"gcBytes", JSGC_BYTES}, + {"gcNumber", JSGC_NUMBER}, + {"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET} +}; + +static JSBool +GCParameter(JSContext *cx, unsigned argc, jsval *vp) +{ + JSString *str; + if (argc == 0) { + str = JS_ValueToString(cx, JSVAL_VOID); + JS_ASSERT(str); + } else { + str = JS_ValueToString(cx, vp[2]); + if (!str) + return JS_FALSE; + vp[2] = STRING_TO_JSVAL(str); + } + + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return false; + + size_t paramIndex = 0; + for (;; paramIndex++) { + if (paramIndex == ArrayLength(paramMap)) { + JS_ReportError(cx, + "the first argument argument must be maxBytes, " + "maxMallocBytes, gcStackpoolLifespan, gcBytes or " + "gcNumber"); + return false; + } + if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) + break; + } + JSGCParamKey param = paramMap[paramIndex].param; + + if (argc == 1) { + uint32_t value = JS_GetGCParameter(cx->runtime, param); + return JS_NewNumberValue(cx, value, &vp[0]); + } + + if (param == JSGC_NUMBER || + param == JSGC_BYTES) { + JS_ReportError(cx, "Attempt to change read-only parameter %s", + paramMap[paramIndex].name); + return false; + } + + uint32_t value; + if (!JS_ValueToECMAUint32(cx, vp[3], &value)) { + JS_ReportError(cx, + "the second argument must be convertable to uint32_t " + "with non-zero value"); + return false; + } + + if (param == JSGC_MAX_BYTES) { + uint32_t gcBytes = JS_GetGCParameter(cx->runtime, JSGC_BYTES); + if (value < gcBytes) { + JS_ReportError(cx, + "attempt to set maxBytes to the value less than the current " + "gcBytes (%u)", + gcBytes); + return false; + } + } + + JS_SetGCParameter(cx->runtime, param, value); + *vp = JSVAL_VOID; + return true; +} + +static JSBool +InternalConst(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc != 1) { + JS_ReportError(cx, "the function takes exactly one argument"); + return false; + } + + JSString *str = JS_ValueToString(cx, vp[2]); + if (!str) + return false; + JSFlatString *flat = JS_FlattenString(cx, str); + if (!flat) + return false; + + if (JS_FlatStringEqualsAscii(flat, "MARK_STACK_LENGTH")) { + vp[0] = UINT_TO_JSVAL(js::MARK_STACK_LENGTH); + } else { + JS_ReportError(cx, "unknown const name"); + return false; + } + return true; +} + +#ifdef JS_GC_ZEAL +static JSBool +GCZeal(JSContext *cx, unsigned argc, jsval *vp) +{ + uint32_t zeal, frequency = JS_DEFAULT_ZEAL_FREQ; + JSBool compartment = JS_FALSE; + + if (argc > 3) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments"); + return JS_FALSE; + } + if (!JS_ValueToECMAUint32(cx, argc < 1 ? JSVAL_VOID : vp[2], &zeal)) + return JS_FALSE; + if (argc >= 2) + if (!JS_ValueToECMAUint32(cx, vp[3], &frequency)) + return JS_FALSE; + if (argc >= 3) + compartment = js_ValueToBoolean(vp[3]); + + JS_SetGCZeal(cx, (uint8_t)zeal, frequency, compartment); + *vp = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +ScheduleGC(JSContext *cx, unsigned argc, jsval *vp) +{ + uint32_t count; + bool compartment = false; + + if (argc != 1 && argc != 2) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments"); + return JS_FALSE; + } + if (!JS_ValueToECMAUint32(cx, vp[2], &count)) + return JS_FALSE; + if (argc == 2) + compartment = js_ValueToBoolean(vp[3]); + + JS_ScheduleGC(cx, count, compartment); + *vp = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +VerifyBarriers(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments"); + return JS_FALSE; + } + gc::VerifyBarriers(cx); + *vp = JSVAL_VOID; + return JS_TRUE; +} + +static JSBool +GCSlice(JSContext *cx, unsigned argc, jsval *vp) +{ + uint32_t budget; + + if (argc != 1) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments"); + return JS_FALSE; + } + + if (!JS_ValueToECMAUint32(cx, vp[2], &budget)) + return JS_FALSE; + + GCDebugSlice(cx, budget); + *vp = JSVAL_VOID; + return JS_TRUE; +} +#endif /* JS_GC_ZEAL */ + +typedef struct JSCountHeapNode JSCountHeapNode; + +struct JSCountHeapNode { + void *thing; + JSGCTraceKind kind; + JSCountHeapNode *next; +}; + +typedef struct JSCountHeapTracer { + JSTracer base; + JSDHashTable visited; + bool ok; + JSCountHeapNode *traceList; + JSCountHeapNode *recycleList; +} JSCountHeapTracer; + +static void +CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) +{ + JSCountHeapTracer *countTracer; + JSDHashEntryStub *entry; + JSCountHeapNode *node; + void *thing = *thingp; + + JS_ASSERT(trc->callback == CountHeapNotify); + countTracer = (JSCountHeapTracer *)trc; + if (!countTracer->ok) + return; + + entry = (JSDHashEntryStub *) + JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD); + if (!entry) { + countTracer->ok = false; + return; + } + if (entry->key) + return; + entry->key = thing; + + node = countTracer->recycleList; + if (node) { + countTracer->recycleList = node->next; + } else { + node = (JSCountHeapNode *) js_malloc(sizeof *node); + if (!node) { + countTracer->ok = false; + return; + } + } + node->thing = thing; + node->kind = kind; + node->next = countTracer->traceList; + countTracer->traceList = node; +} + +static const struct TraceKindPair { + const char *name; + int32_t kind; +} traceKindNames[] = { + { "all", -1 }, + { "object", JSTRACE_OBJECT }, + { "string", JSTRACE_STRING }, +#if JS_HAS_XML_SUPPORT + { "xml", JSTRACE_XML }, +#endif +}; + +static JSBool +CountHeap(JSContext *cx, unsigned argc, jsval *vp) +{ + void* startThing; + JSGCTraceKind startTraceKind; + jsval v; + int32_t traceKind; + JSString *str; + JSCountHeapTracer countTracer; + JSCountHeapNode *node; + size_t counter; + + startThing = NULL; + startTraceKind = JSTRACE_OBJECT; + if (argc > 0) { + v = JS_ARGV(cx, vp)[0]; + if (JSVAL_IS_TRACEABLE(v)) { + startThing = JSVAL_TO_TRACEABLE(v); + startTraceKind = JSVAL_TRACE_KIND(v); + } else if (!JSVAL_IS_NULL(v)) { + JS_ReportError(cx, + "the first argument is not null or a heap-allocated " + "thing"); + return JS_FALSE; + } + } + + traceKind = -1; + if (argc > 1) { + str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]); + if (!str) + return JS_FALSE; + JSFlatString *flatStr = JS_FlattenString(cx, str); + if (!flatStr) + return JS_FALSE; + for (size_t i = 0; ;) { + if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { + traceKind = traceKindNames[i].kind; + break; + } + if (++i == ArrayLength(traceKindNames)) { + JSAutoByteString bytes(cx, str); + if (!!bytes) + JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr()); + return JS_FALSE; + } + } + } + + JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify); + if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(), + NULL, sizeof(JSDHashEntryStub), + JS_DHASH_DEFAULT_CAPACITY(100))) { + JS_ReportOutOfMemory(cx); + return JS_FALSE; + } + countTracer.ok = true; + countTracer.traceList = NULL; + countTracer.recycleList = NULL; + + if (!startThing) { + JS_TraceRuntime(&countTracer.base); + } else { + JS_SET_TRACING_NAME(&countTracer.base, "root"); + JS_CallTracer(&countTracer.base, startThing, startTraceKind); + } + + counter = 0; + while ((node = countTracer.traceList) != NULL) { + if (traceKind == -1 || node->kind == traceKind) + counter++; + countTracer.traceList = node->next; + node->next = countTracer.recycleList; + countTracer.recycleList = node; + JS_TraceChildren(&countTracer.base, node->thing, node->kind); + } + while ((node = countTracer.recycleList) != NULL) { + countTracer.recycleList = node->next; + js_free(node); + } + JS_DHashTableFinish(&countTracer.visited); + if (!countTracer.ok) { + JS_ReportOutOfMemory(cx); + return false; + } + + return JS_NewNumberValue(cx, (double) counter, vp); +} + +static unsigned finalizeCount = 0; + +static void +finalize_counter_finalize(JSContext *cx, JSObject *obj) +{ + JS_ATOMIC_INCREMENT(&finalizeCount); +} + +static JSClass FinalizeCounterClass = { + "FinalizeCounter", JSCLASS_IS_ANONYMOUS, + JS_PropertyStub, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + JS_StrictPropertyStub, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub, + finalize_counter_finalize +}; + +static JSBool +MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp) +{ + JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, NULL, + JS_GetGlobalObject(cx)); + if (!obj) + return false; + *vp = OBJECT_TO_JSVAL(obj); + return true; +} + +static JSBool +FinalizeCount(JSContext *cx, unsigned argc, jsval *vp) +{ + *vp = INT_TO_JSVAL(finalizeCount); + return true; +} + +JSBool +MJitCodeStats(JSContext *cx, unsigned argc, jsval *vp) +{ +#ifdef JS_METHODJIT + JSRuntime *rt = cx->runtime; + AutoLockGC lock(rt); + size_t n = 0; + for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { + n += (*c)->sizeOfMjitCode(); + } + JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n)); +#else + JS_SET_RVAL(cx, vp, JSVAL_VOID); +#endif + return true; +} + +JSBool +MJitChunkLimit(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc != 1) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments"); + return JS_FALSE; + } + + double t; + if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t)) + return JS_FALSE; + +#ifdef JS_METHODJIT + mjit::SetChunkLimit((uint32_t) t); +#endif + + // Clear out analysis information which might refer to code compiled with + // the previous chunk limit. + JS_GC(cx); + + vp->setUndefined(); + return true; +} + +static JSBool +Terminate(JSContext *cx, unsigned arg, jsval *vp) +{ + JS_ClearPendingException(cx); + return JS_FALSE; +} + +static JSFunctionSpecWithHelp TestingFunctions[] = { + JS_FN_HELP("gc", ::GC, 0, 0, +"gc([obj])", +" Run the garbage collector. When obj is given, GC only its compartment."), + + JS_FN_HELP("gcparam", GCParameter, 2, 0, +"gcparam(name [, value])", +" Wrapper for JS_[GS]etGCParameter. The name is either maxBytes,\n" +" maxMallocBytes, gcBytes, gcNumber, or sliceTimeBudget."), + + JS_FN_HELP("countHeap", CountHeap, 0, 0, +"countHeap([start[, kind]])", +" Count the number of live GC things in the heap or things reachable from\n" +" start when it is given and is not null. kind is either 'all' (default) to\n" +" count all things or one of 'object', 'double', 'string', 'function',\n" +" 'qname', 'namespace', 'xml' to count only things of that kind."), + + JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0, +"makeFinalizeObserver()", +" Get a special object whose finalization increases the counter returned\n" +" by the finalizeCount function."), + + JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0, +"finalizeCount()", +" Return the current value of the finalization counter that is incremented\n" +" each time an object returned by the makeFinalizeObserver is finalized."), + +#ifdef JS_GC_ZEAL + JS_FN_HELP("gczeal", GCZeal, 2, 0, +"gczeal(level, [period], [compartmentGC?])", +" Specifies how zealous the garbage collector should be. Values for level:\n" +" 0: Normal amount of collection\n" +" 1: Collect when roots are added or removed\n" +" 2: Collect when memory is allocated\n" +" 3: Collect when the window paints (browser only)\n" +" 4: Verify write barriers between instructions\n" +" 5: Verify write barriers between paints\n" +" Period specifies that collection happens every n allocations.\n" +" If compartmentGC is true, the collections will be compartmental."), + + JS_FN_HELP("schedulegc", ScheduleGC, 1, 0, +"schedulegc(num, [compartmentGC?])", +" Schedule a GC to happen after num allocations."), + + JS_FN_HELP("verifybarriers", VerifyBarriers, 0, 0, +"verifybarriers()", +" Start or end a run of the write barrier verifier."), + + JS_FN_HELP("gcslice", GCSlice, 1, 0, +"gcslice(n)", +" Run an incremental GC slice that marks about n objects."), +#endif + + JS_FN_HELP("internalConst", InternalConst, 1, 0, +"internalConst(name)", +" Query an internal constant for the engine. See InternalConst source for\n" +" the list of constant names."), + +#ifdef JS_METHODJIT + JS_FN_HELP("mjitcodestats", MJitCodeStats, 0, 0, +"mjitcodestats()", +"Return stats on mjit code memory usage."), +#endif + + JS_FN_HELP("mjitChunkLimit", MJitChunkLimit, 1, 0, +"mjitChunkLimit(N)", +" Specify limit on compiled chunk size during mjit compilation."), + + JS_FN_HELP("terminate", Terminate, 0, 0, +"terminate()", +" Terminate JavaScript execution, as if we had run out of\n" +" memory or been terminated by the slow script dialog."), + + JS_FS_END +}; + +namespace js { + +bool +DefineTestingFunctions(JSContext *cx, JSObject *obj) +{ + return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions); +} + +} /* namespace js */ diff --git a/js/src/builtin/TestingFunctions.h b/js/src/builtin/TestingFunctions.h new file mode 100644 index 000000000000..186c64c11587 --- /dev/null +++ b/js/src/builtin/TestingFunctions.h @@ -0,0 +1,16 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef TestingFunctions_h__ +#define TestingFunctions_h__ + +namespace js { + +bool +DefineTestingFunctions(JSContext *cx, JSObject *obj); + +} /* namespace js */ + +#endif /* TestingFunctions_h__ */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 80fe3078f2e9..414ed5b67d0d 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -504,6 +504,37 @@ js_ReportErrorVA(JSContext *cx, unsigned flags, const char *format, va_list ap) return warning; } +namespace js { + +/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ +void +ReportUsageError(JSContext *cx, JSObject *callee, const char *msg) +{ + const char *usageStr = "usage"; + JSAtom *usageAtom = js_Atomize(cx, usageStr, strlen(usageStr)); + DebugOnly shape = callee->nativeLookup(cx, ATOM_TO_JSID(usageAtom)); + JS_ASSERT(!shape->configurable()); + JS_ASSERT(!shape->writable()); + JS_ASSERT(shape->hasDefaultGetter()); + + jsval usage; + if (!JS_LookupProperty(cx, callee, "usage", &usage)) + return; + + if (JSVAL_IS_VOID(usage)) { + JS_ReportError(cx, "%s", msg); + } else { + JSString *str = JSVAL_TO_STRING(usage); + JS::Anchor a_str(str); + const jschar *chars = JS_GetStringCharsZ(cx, str); + if (!chars) + return; + JS_ReportError(cx, "%s. Usage: %hs", msg, chars); + } +} + +} /* namespace js */ + /* * The arguments from ap need to be packaged up into an array and stored * into the report struct. diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3604a2f3eed9..41a8645ff3d7 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -1418,6 +1418,14 @@ js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback, bool charArgs, va_list ap); #endif +namespace js { + +/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ +extern void +ReportUsageError(JSContext *cx, JSObject *callee, const char *msg); + +} /* namespace js */ + extern void js_ReportOutOfMemory(JSContext *cx); diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index f7d1423bd011..e9f8d0590fa1 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -47,6 +47,8 @@ #include "jsweakmap.h" #include "jswatchpoint.h" +#include "builtin/TestingFunctions.h" + #include "jsobjinlines.h" using namespace js; @@ -180,6 +182,51 @@ JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape) MarkCycleCollectorChildren(trc, (Shape *)shape); } +static bool +DefineHelpProperty(JSContext *cx, JSObject *obj, const char *prop, const char *value) +{ + JSAtom *atom = js_Atomize(cx, value, strlen(value)); + if (!atom) + return false; + jsval v = STRING_TO_JSVAL(atom); + return JS_DefineProperty(cx, obj, prop, v, + JS_PropertyStub, JS_StrictPropertyStub, + JSPROP_READONLY | JSPROP_PERMANENT); +} + +JS_FRIEND_API(bool) +JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs) +{ + RootObject objRoot(cx, &obj); + + JS_ASSERT(cx->compartment != cx->runtime->atomsCompartment); + + CHECK_REQUEST(cx); + assertSameCompartment(cx, obj); + for (; fs->name; fs++) { + JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name)); + if (!atom) + return false; + + JSFunction *fun = js_DefineFunction(cx, objRoot, + ATOM_TO_JSID(atom), fs->call, fs->nargs, fs->flags); + if (!fun) + return false; + + if (fs->usage) { + if (!DefineHelpProperty(cx, fun, "usage", fs->usage)) + return false; + } + + if (fs->help) { + if (!DefineHelpProperty(cx, fun, "help", fs->help)) + return false; + } + } + + return true; +} + AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT) : cx(cx), oldCompartment(cx->compartment) @@ -789,4 +836,17 @@ IncrementalValueBarrier(const Value &v) HeapValue::writeBarrierPre(v); } +JS_FRIEND_API(JSObject *) +GetTestingFunctions(JSContext *cx) +{ + JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL); + if (!obj) + return NULL; + + if (!DefineTestingFunctions(cx, obj)) + return NULL; + + return obj; +} + } // namespace js diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 048dc3375cc8..d0715468f4ac 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -166,6 +166,21 @@ JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc); extern JS_FRIEND_API(JSBool) JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, js::Value *statep, jsid *idp); +struct JSFunctionSpecWithHelp { + const char *name; + JSNative call; + uint16_t nargs; + uint16_t flags; + const char *usage; + const char *help; +}; + +#define JS_FN_HELP(name,call,nargs,flags,usage,help) \ + {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, usage, help} + +extern JS_FRIEND_API(bool) +JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); + #endif JS_END_EXTERN_C @@ -808,6 +823,9 @@ class ObjectPtr operator JSObject *() const { return value; } }; +extern JS_FRIEND_API(JSObject *) +GetTestingFunctions(JSContext *cx); + } /* namespace js */ #endif diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 655049804981..d2d06854a79e 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -76,6 +76,7 @@ #include "jsxml.h" #include "jsperf.h" +#include "builtin/TestingFunctions.h" #include "frontend/BytecodeEmitter.h" #include "frontend/Parser.h" #include "methodjit/MethodJIT.h" @@ -1245,417 +1246,6 @@ AssertJit(JSContext *cx, unsigned argc, jsval *vp) return JS_TRUE; } -static JSBool -GC(JSContext *cx, unsigned argc, jsval *vp) -{ - JSCompartment *comp = NULL; - if (argc == 1) { - Value arg = vp[2]; - if (arg.isObject()) - comp = UnwrapObject(&arg.toObject())->compartment(); - } - -#ifndef JS_MORE_DETERMINISTIC - size_t preBytes = cx->runtime->gcBytes; -#endif - JS_CompartmentGC(cx, comp); - - char buf[256]; -#ifdef JS_MORE_DETERMINISTIC - buf[0] = '\0'; -#else - JS_snprintf(buf, sizeof(buf), "before %lu, after %lu, break %08lx\n", - (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes, -#ifdef HAVE_SBRK - (unsigned long)sbrk(0) -#else - 0 -#endif - ); -#endif - JSString *str = JS_NewStringCopyZ(cx, buf); - if (!str) - return false; - *vp = STRING_TO_JSVAL(str); - return true; -} - -static const struct ParamPair { - const char *name; - JSGCParamKey param; -} paramMap[] = { - {"maxBytes", JSGC_MAX_BYTES }, - {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, - {"gcBytes", JSGC_BYTES}, - {"gcNumber", JSGC_NUMBER}, - {"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET}, - {"markStackLimit", JSGC_MARK_STACK_LIMIT} -}; - -static JSBool -GCParameter(JSContext *cx, unsigned argc, jsval *vp) -{ - JSString *str; - if (argc == 0) { - str = JS_ValueToString(cx, JSVAL_VOID); - JS_ASSERT(str); - } else { - str = JS_ValueToString(cx, vp[2]); - if (!str) - return JS_FALSE; - vp[2] = STRING_TO_JSVAL(str); - } - - JSFlatString *flatStr = JS_FlattenString(cx, str); - if (!flatStr) - return false; - - size_t paramIndex = 0; - for (;; paramIndex++) { - if (paramIndex == ArrayLength(paramMap)) { - JS_ReportError(cx, - "the first argument argument must be maxBytes, " - "maxMallocBytes, gcStackpoolLifespan, gcBytes or " - "gcNumber"); - return false; - } - if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name)) - break; - } - JSGCParamKey param = paramMap[paramIndex].param; - - if (argc == 1) { - uint32_t value = JS_GetGCParameter(cx->runtime, param); - return JS_NewNumberValue(cx, value, &vp[0]); - } - - if (param == JSGC_NUMBER || - param == JSGC_BYTES) { - JS_ReportError(cx, "Attempt to change read-only parameter %s", - paramMap[paramIndex].name); - return false; - } - - uint32_t value; - if (!JS_ValueToECMAUint32(cx, vp[3], &value)) { - JS_ReportError(cx, - "the second argument must be convertable to uint32_t " - "with non-zero value"); - return false; - } - - if (param == JSGC_MAX_BYTES) { - uint32_t gcBytes = JS_GetGCParameter(cx->runtime, JSGC_BYTES); - if (value < gcBytes) { - JS_ReportError(cx, - "attempt to set maxBytes to the value less than the current " - "gcBytes (%u)", - gcBytes); - return false; - } - } - - JS_SetGCParameter(cx->runtime, param, value); - *vp = JSVAL_VOID; - return true; -} - -static JSBool -InternalConst(JSContext *cx, unsigned argc, jsval *vp) -{ - if (argc != 1) { - JS_ReportError(cx, "the function takes exactly one argument"); - return false; - } - - JSString *str = JS_ValueToString(cx, vp[2]); - if (!str) - return false; - JSFlatString *flat = JS_FlattenString(cx, str); - if (!flat) - return false; - - if (JS_FlatStringEqualsAscii(flat, "MARK_STACK_LENGTH")) { - vp[0] = UINT_TO_JSVAL(js::MARK_STACK_LENGTH); - } else { - JS_ReportError(cx, "unknown const name"); - return false; - } - return true; -} - -#ifdef JS_GC_ZEAL -static JSBool -GCZeal(JSContext *cx, unsigned argc, jsval *vp) -{ - uint32_t zeal, frequency = JS_DEFAULT_ZEAL_FREQ; - JSBool compartment = JS_FALSE; - - if (argc > 3) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_TOO_MANY_ARGS, "gczeal"); - return JS_FALSE; - } - if (!JS_ValueToECMAUint32(cx, argc < 1 ? JSVAL_VOID : vp[2], &zeal)) - return JS_FALSE; - if (argc >= 2) - if (!JS_ValueToECMAUint32(cx, vp[3], &frequency)) - return JS_FALSE; - if (argc >= 3) - compartment = js_ValueToBoolean(vp[3]); - - JS_SetGCZeal(cx, (uint8_t)zeal, frequency, compartment); - *vp = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -ScheduleGC(JSContext *cx, unsigned argc, jsval *vp) -{ - uint32_t count; - bool compartment = false; - - if (argc != 1 && argc != 2) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - (argc < 1) - ? JSSMSG_NOT_ENOUGH_ARGS - : JSSMSG_TOO_MANY_ARGS, - "schedulegc"); - return JS_FALSE; - } - if (!JS_ValueToECMAUint32(cx, vp[2], &count)) - return JS_FALSE; - if (argc == 2) - compartment = js_ValueToBoolean(vp[3]); - - JS_ScheduleGC(cx, count, compartment); - *vp = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -VerifyBarriers(JSContext *cx, unsigned argc, jsval *vp) -{ - gc::VerifyBarriers(cx); - *vp = JSVAL_VOID; - return JS_TRUE; -} - -static JSBool -GCSlice(JSContext *cx, unsigned argc, jsval *vp) -{ - uint32_t budget; - - if (argc != 1) { - JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, - (argc < 1) - ? JSSMSG_NOT_ENOUGH_ARGS - : JSSMSG_TOO_MANY_ARGS, - "gcslice"); - return JS_FALSE; - } - if (!JS_ValueToECMAUint32(cx, vp[2], &budget)) - return JS_FALSE; - - GCDebugSlice(cx, budget); - *vp = JSVAL_VOID; - return JS_TRUE; -} -#endif /* JS_GC_ZEAL */ - -typedef struct JSCountHeapNode JSCountHeapNode; - -struct JSCountHeapNode { - void *thing; - JSGCTraceKind kind; - JSCountHeapNode *next; -}; - -typedef struct JSCountHeapTracer { - JSTracer base; - JSDHashTable visited; - bool ok; - JSCountHeapNode *traceList; - JSCountHeapNode *recycleList; -} JSCountHeapTracer; - -static void -CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind) -{ - JSCountHeapTracer *countTracer; - JSDHashEntryStub *entry; - JSCountHeapNode *node; - void *thing = *thingp; - - JS_ASSERT(trc->callback == CountHeapNotify); - countTracer = (JSCountHeapTracer *)trc; - if (!countTracer->ok) - return; - - entry = (JSDHashEntryStub *) - JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD); - if (!entry) { - countTracer->ok = false; - return; - } - if (entry->key) - return; - entry->key = thing; - - node = countTracer->recycleList; - if (node) { - countTracer->recycleList = node->next; - } else { - node = (JSCountHeapNode *) js_malloc(sizeof *node); - if (!node) { - countTracer->ok = false; - return; - } - } - node->thing = thing; - node->kind = kind; - node->next = countTracer->traceList; - countTracer->traceList = node; -} - -static const struct TraceKindPair { - const char *name; - int32_t kind; -} traceKindNames[] = { - { "all", -1 }, - { "object", JSTRACE_OBJECT }, - { "string", JSTRACE_STRING }, -#if JS_HAS_XML_SUPPORT - { "xml", JSTRACE_XML }, -#endif -}; - -static JSBool -CountHeap(JSContext *cx, unsigned argc, jsval *vp) -{ - void* startThing; - JSGCTraceKind startTraceKind; - jsval v; - int32_t traceKind; - JSString *str; - JSCountHeapTracer countTracer; - JSCountHeapNode *node; - size_t counter; - - startThing = NULL; - startTraceKind = JSTRACE_OBJECT; - if (argc > 0) { - v = JS_ARGV(cx, vp)[0]; - if (JSVAL_IS_TRACEABLE(v)) { - startThing = JSVAL_TO_TRACEABLE(v); - startTraceKind = JSVAL_TRACE_KIND(v); - } else if (!JSVAL_IS_NULL(v)) { - JS_ReportError(cx, - "the first argument is not null or a heap-allocated " - "thing"); - return JS_FALSE; - } - } - - traceKind = -1; - if (argc > 1) { - str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]); - if (!str) - return JS_FALSE; - JSFlatString *flatStr = JS_FlattenString(cx, str); - if (!flatStr) - return JS_FALSE; - for (size_t i = 0; ;) { - if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { - traceKind = traceKindNames[i].kind; - break; - } - if (++i == ArrayLength(traceKindNames)) { - JSAutoByteString bytes(cx, str); - if (!!bytes) - JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr()); - return JS_FALSE; - } - } - } - - JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify); - if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(), - NULL, sizeof(JSDHashEntryStub), - JS_DHASH_DEFAULT_CAPACITY(100))) { - JS_ReportOutOfMemory(cx); - return JS_FALSE; - } - countTracer.ok = true; - countTracer.traceList = NULL; - countTracer.recycleList = NULL; - - if (!startThing) { - JS_TraceRuntime(&countTracer.base); - } else { - JS_SET_TRACING_NAME(&countTracer.base, "root"); - JS_CallTracer(&countTracer.base, startThing, startTraceKind); - } - - counter = 0; - while ((node = countTracer.traceList) != NULL) { - if (traceKind == -1 || node->kind == traceKind) - counter++; - countTracer.traceList = node->next; - node->next = countTracer.recycleList; - countTracer.recycleList = node; - JS_TraceChildren(&countTracer.base, node->thing, node->kind); - } - while ((node = countTracer.recycleList) != NULL) { - countTracer.recycleList = node->next; - js_free(node); - } - JS_DHashTableFinish(&countTracer.visited); - if (!countTracer.ok) { - JS_ReportOutOfMemory(cx); - return false; - } - - return JS_NewNumberValue(cx, (double) counter, vp); -} - -static unsigned finalizeCount = 0; - -static void -finalize_counter_finalize(JSContext *cx, JSObject *obj) -{ - JS_ATOMIC_INCREMENT(&finalizeCount); -} - -static JSClass FinalizeCounterClass = { - "FinalizeCounter", JSCLASS_IS_ANONYMOUS, - JS_PropertyStub, /* addProperty */ - JS_PropertyStub, /* delProperty */ - JS_PropertyStub, /* getProperty */ - JS_StrictPropertyStub, /* setProperty */ - JS_EnumerateStub, - JS_ResolveStub, - JS_ConvertStub, - finalize_counter_finalize -}; - -static JSBool -MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp) -{ - JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, NULL, - JS_GetGlobalObject(cx)); - if (!obj) - return false; - *vp = OBJECT_TO_JSVAL(obj); - return true; -} - -static JSBool -FinalizeCount(JSContext *cx, unsigned argc, jsval *vp) -{ - *vp = INT_TO_JSVAL(finalizeCount); - return true; -} - static JSScript * ValueToScript(JSContext *cx, jsval v, JSFunction **funp = NULL) { @@ -3893,47 +3483,6 @@ Deserialize(JSContext *cx, unsigned argc, jsval *vp) return true; } -JSBool -MJitCodeStats(JSContext *cx, unsigned argc, jsval *vp) -{ -#ifdef JS_METHODJIT - JSRuntime *rt = cx->runtime; - AutoLockGC lock(rt); - size_t n = 0; - for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) { - n += (*c)->sizeOfMjitCode(); - } - JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n)); -#else - JS_SET_RVAL(cx, vp, JSVAL_VOID); -#endif - return true; -} - -JSBool -MJitChunkLimit(JSContext *cx, unsigned argc, jsval *vp) -{ - if (argc > 1 || argc == 0) { - JS_ReportError(cx, "Wrong number of arguments"); - return JS_FALSE; - } - - double t; - if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t)) - return JS_FALSE; - -#ifdef JS_METHODJIT - mjit::SetChunkLimit((uint32_t) t); -#endif - - // Clear out analysis information which might refer to code compiled with - // the previous chunk limit. - JS_GC(cx); - - vp->setUndefined(); - return true; -} - enum CompartmentKind { SAME_COMPARTMENT, NEW_COMPARTMENT }; static JSObject * @@ -4009,183 +3558,151 @@ GetMaxArgs(JSContext *cx, unsigned arg, jsval *vp) return JS_TRUE; } -static JSBool -Terminate(JSContext *cx, unsigned arg, jsval *vp) -{ - JS_ClearPendingException(cx); - return JS_FALSE; -} +static JSFunctionSpecWithHelp shell_functions[] = { + JS_FN_HELP("version", Version, 0, 0, +"version([number])", +" Get or force a script compilation version number."), -static JSFunctionSpec shell_functions[] = { - JS_FN("version", Version, 0,0), - JS_FN("revertVersion", RevertVersion, 0,0), - JS_FN("options", Options, 0,0), - JS_FN("load", Load, 1,0), - JS_FN("evaluate", Evaluate, 1,0), - JS_FN("evalWithLocation", EvaluateWithLocation, 3,0), - JS_FN("run", Run, 1,0), - JS_FN("readline", ReadLine, 0,0), - JS_FN("print", Print, 0,0), - JS_FN("printErr", PrintErr, 0,0), - JS_FN("putstr", PutStr, 0,0), - JS_FN("dateNow", Now, 0,0), - JS_FN("help", Help, 0,0), - JS_FN("quit", Quit, 0,0), - JS_FN("assertEq", AssertEq, 2,0), - JS_FN("assertJit", AssertJit, 0,0), - JS_FN("gc", ::GC, 0,0), - JS_FN("gcparam", GCParameter, 2,0), - JS_FN("countHeap", CountHeap, 0,0), - JS_FN("makeFinalizeObserver", MakeFinalizeObserver, 0,0), - JS_FN("finalizeCount", FinalizeCount, 0,0), -#ifdef JS_GC_ZEAL - JS_FN("gczeal", GCZeal, 2,0), - JS_FN("schedulegc", ScheduleGC, 1,0), - JS_FN("verifybarriers", VerifyBarriers, 0,0), - JS_FN("gcslice", GCSlice, 1,0), -#endif - JS_FN("internalConst", InternalConst, 1,0), - JS_FN("setDebug", SetDebug, 1,0), - JS_FN("setDebuggerHandler", SetDebuggerHandler, 1,0), - JS_FN("setThrowHook", SetThrowHook, 1,0), - JS_FN("trap", Trap, 3,0), - JS_FN("untrap", Untrap, 2,0), - JS_FN("line2pc", LineToPC, 0,0), - JS_FN("pc2line", PCToLine, 0,0), - JS_FN("stringsAreUTF8", StringsAreUTF8, 0,0), - JS_FN("testUTF8", TestUTF8, 1,0), - JS_FN("throwError", ThrowError, 0,0), -#ifdef DEBUG - JS_FN("disassemble", DisassembleToString, 1,0), - JS_FN("dis", Disassemble, 1,0), - JS_FN("disfile", DisassFile, 1,0), - JS_FN("dissrc", DisassWithSrc, 1,0), - JS_FN("dumpHeap", DumpHeap, 0,0), - JS_FN("dumpObject", DumpObject, 1,0), - JS_FN("notes", Notes, 1,0), - JS_FN("stats", DumpStats, 1,0), - JS_FN("findReferences", FindReferences, 1,0), -#endif - JS_FN("dumpStack", DumpStack, 1,0), -#ifdef TEST_CVTARGS - JS_FN("cvtargs", ConvertArgs, 0,0), -#endif - JS_FN("build", BuildDate, 0,0), - JS_FN("clear", Clear, 0,0), - JS_FN("intern", Intern, 1,0), - JS_FN("clone", Clone, 1,0), - JS_FN("getpda", GetPDA, 1,0), - JS_FN("getslx", GetSLX, 1,0), - JS_FN("toint32", ToInt32, 1,0), - JS_FN("evalcx", EvalInContext, 1,0), - JS_FN("evalInFrame", EvalInFrame, 2,0), - JS_FN("shapeOf", ShapeOf, 1,0), - JS_FN("resolver", Resolver, 1,0), -#ifdef DEBUG - JS_FN("arrayInfo", js_ArrayInfo, 1,0), -#endif -#ifdef JS_THREADSAFE - JS_FN("sleep", Sleep_fn, 1,0), -#endif - JS_FN("snarf", Snarf, 0,0), - JS_FN("read", Snarf, 0,0), - JS_FN("compile", Compile, 1,0), - JS_FN("parse", Parse, 1,0), - JS_FN("timeout", Timeout, 1,0), - JS_FN("elapsed", Elapsed, 0,0), - JS_FN("parent", Parent, 1,0), - JS_FN("wrap", Wrap, 1,0), - JS_FN("serialize", Serialize, 1,0), - JS_FN("deserialize", Deserialize, 1,0), -#ifdef JS_METHODJIT - JS_FN("mjitcodestats", MJitCodeStats, 0,0), -#endif - JS_FN("mjitChunkLimit", MJitChunkLimit, 1,0), - JS_FN("newGlobal", NewGlobal, 1,0), - JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0), - JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0), - JS_FN("getMaxArgs", GetMaxArgs, 0,0), - JS_FN("terminate", Terminate, 0,0), - JS_FS_END -}; + JS_FN_HELP("revertVersion", RevertVersion, 0, 0, +"revertVersion()", +" Revert previously set version number."), -static const char shell_help_header[] = -"Command Description\n" -"======= ===========\n"; + JS_FN_HELP("options", Options, 0, 0, +"options([option ...])", +" Get or toggle JavaScript options."), -static const char *const shell_help_messages[] = { -"version([number]) Get or force a script compilation version number", -"revertVersion() Revert previously set version number", -"options([option ...]) Get or toggle JavaScript options", -"load(['foo.js' ...]) Load files named by string arguments", -"evaluate(code) Evaluate code as though it were the contents of a file", -"evalWithLocation(code, filename, lineno)\n" -" Eval code as if loaded from the given filename and line number", -"run('foo.js')\n" + JS_FN_HELP("load", Load, 1, 0, +"load(['foo.js' ...])", +" Load files named by string arguments."), + + JS_FN_HELP("evaluate", Evaluate, 1, 0, +"evaluate(code)", +" Evaluate code as though it were the contents of a file."), + + JS_FN_HELP("evalWithLocation", EvaluateWithLocation, 3, 0, +"evalWithLocation(code, filename, lineno)", +" Eval code as if loaded from the given filename and line number."), + + JS_FN_HELP("run", Run, 1, 0, +"run('foo.js')", " Run the file named by the first argument, returning the number of\n" -" of milliseconds spent compiling and executing it", -"readline() Read a single line from stdin", -"print([exp ...]) Evaluate and print expressions to stdout", -"printErr([exp ...]) Evaluate and print expressions to stderr", -"putstr([exp]) Evaluate and print expression without newline", -"dateNow() Return the current time with sub-ms precision", -"help([name ...]) Display usage and help messages", -"quit() Quit the shell", -"assertEq(actual, expected[, msg])\n" +" of milliseconds spent compiling and executing it."), + + JS_FN_HELP("readline", ReadLine, 0, 0, +"readline()", +" Read a single line from stdin."), + + JS_FN_HELP("print", Print, 0, 0, +"print([exp ...])", +" Evaluate and print expressions to stdout."), + + JS_FN_HELP("printErr", PrintErr, 0, 0, +"printErr([exp ...])", +" Evaluate and print expressions to stderr."), + + JS_FN_HELP("putstr", PutStr, 0, 0, +"putstr([exp])", +" Evaluate and print expression without newline."), + + JS_FN_HELP("dateNow", Now, 0, 0, +"dateNow()", +" Return the current time with sub-ms precision."), + + JS_FN_HELP("help", Help, 0, 0, +"help([name ...])", +" Display usage and help messages."), + + JS_FN_HELP("quit", Quit, 0, 0, +"quit()", +" Quit the shell."), + + JS_FN_HELP("assertEq", AssertEq, 2, 0, +"assertEq(actual, expected[, msg])", " Throw if the first two arguments are not the same (both +0 or both -0,\n" -" both NaN, or non-zero and ===)", -"assertJit() Throw if the calling function failed to JIT", -"gc([obj]) Run the garbage collector\n" -" When obj is given, GC only the compartment it's in", -"gcparam(name, value)\n" -" Wrapper for JS_SetGCParameter. The name must be either 'maxBytes' or\n" -" 'maxMallocBytes' and the value must be convertable to a positive uint32", -"countHeap([start[, kind]])\n" -" Count the number of live GC things in the heap or things reachable from\n" -" start when it is given and is not null. kind is either 'all' (default) to\n" -" count all things or one of 'object', 'double', 'string', 'function',\n" -" 'qname', 'namespace', 'xml' to count only things of that kind", -"makeFinalizeObserver()\n" -" get a special object whose finalization increases the counter returned\n" -" by the finalizeCount function", -"finalizeCount()\n" -" return the current value of the finalization counter that is incremented\n" -" each time an object returned by the makeFinalizeObserver is finalized", -#ifdef JS_GC_ZEAL -"gczeal(level, [freq], [compartmentGC?])\n" -" How zealous the garbage collector should be", -"schedulegc(num, [compartmentGC?])\n" -" Schedule a GC to happen after num allocations", -"verifybarriers() Start or end a run of the write barrier verifier", -"gcslice(n) Run an incremental GC slice that marks ~n objects", -#endif -"internalConst(name)\n" -" Query an internal constant for the engine. See InternalConst source for the\n" -" list of constant names", -"setDebug(debug) Set debug mode", -"setDebuggerHandler(f) Set handler for debugger keyword to f", -"setThrowHook(f) Set throw hook to f", -"trap([fun, [pc,]] exp) Trap bytecode execution", -"untrap(fun[, pc]) Remove a trap", -"line2pc([fun,] line) Map line number to PC", -"pc2line(fun[, pc]) Map PC to line number", -"stringsAreUTF8() Check if strings are UTF-8 encoded", -"testUTF8(mode) Perform UTF-8 tests (modes are 1 to 4)", -"throwError() Throw an error from JS_ReportError", +" both NaN, or non-zero and ===)."), + + JS_FN_HELP("assertJit", AssertJit, 0, 0, +"assertJit()", +" Throw if the calling function failed to JIT."), + + JS_FN_HELP("setDebug", SetDebug, 1, 0, +"setDebug(debug)", +" Set debug mode."), + + JS_FN_HELP("setDebuggerHandler", SetDebuggerHandler, 1, 0, +"setDebuggerHandler(f)", +" Set handler for debugger keyword to f."), + + JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0, +"setThrowHook(f)", +" Set throw hook to f."), + + JS_FN_HELP("trap", Trap, 3, 0, +"trap([fun, [pc,]] exp)", +" Trap bytecode execution."), + + JS_FN_HELP("untrap", Untrap, 2, 0, +"untrap(fun[, pc])", +" Remove a trap."), + + JS_FN_HELP("line2pc", LineToPC, 0, 0, +"line2pc([fun,] line)", +" Map line number to PC."), + + JS_FN_HELP("pc2line", PCToLine, 0, 0, +"pc2line(fun[, pc])", +" Map PC to line number."), + + JS_FN_HELP("stringsAreUTF8", StringsAreUTF8, 0, 0, +"stringsAreUTF8()", +" Check if strings are UTF-8 encoded."), + + JS_FN_HELP("testUTF8", TestUTF8, 1, 0, +"testUTF8(mode)", +" Perform UTF-8 tests (modes are 1 to 4)."), + + JS_FN_HELP("throwError", ThrowError, 0, 0, +"throwError()", +" Throw an error from JS_ReportError."), + #ifdef DEBUG -"disassemble([fun]) Return the disassembly for the given function", -"dis([fun]) Disassemble functions into bytecodes", -"disfile('foo.js') Disassemble script file into bytecodes\n" -" dis and disfile take these options as preceeding string arguments\n" + JS_FN_HELP("disassemble", DisassembleToString, 1, 0, +"disassemble([fun])", +" Return the disassembly for the given function."), + + JS_FN_HELP("dis", Disassemble, 1, 0, +"dis([fun])", +" Disassemble functions into bytecodes."), + + JS_FN_HELP("disfile", DisassFile, 1, 0, +"disfile('foo.js')", +" Disassemble script file into bytecodes.\n" +" dis and disfile take these options as preceeding string arguments:\n" " \"-r\" (disassemble recursively)\n" -" \"-l\" (show line numbers)", -"dissrc([fun]) Disassemble functions with source lines", -"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])\n" -" Interface to JS_DumpHeap with output sent to file", -"dumpObject() Dump an internal representation of an object", -"notes([fun]) Show source notes for functions", -"stats([string ...]) Dump 'atom' or 'global' stats", -"findReferences(target)\n" +" \"-l\" (show line numbers)"), + + JS_FN_HELP("dissrc", DisassWithSrc, 1, 0, +"dissrc([fun])", +" Disassemble functions with source lines."), + + JS_FN_HELP("dumpHeap", DumpHeap, 0, 0, +"dumpHeap([fileName[, start[, toFind[, maxDepth[, toIgnore]]]]])", +" Interface to JS_DumpHeap with output sent to file."), + + JS_FN_HELP("dumpObject", DumpObject, 1, 0, +"dumpObject()", +" Dump an internal representation of an object."), + + JS_FN_HELP("notes", Notes, 1, 0, +"notes([fun])", +" Show source notes for functions."), + + JS_FN_HELP("stats", DumpStats, 1, 0, +"stats([string ...])", +" Dump 'atom' or 'global' stats."), + + JS_FN_HELP("findReferences", FindReferences, 1, 0, +"findReferences(target)", " Walk the entire heap, looking for references to |target|, and return a\n" " \"references object\" describing what we found.\n" "\n" @@ -4207,91 +3724,144 @@ static const char *const shell_help_messages[] = { "\n" " If any references are found by the conservative scanner, the references\n" " object will have a property named \"edge: machine stack\"; the referrers will\n" -" be 'null', because they are roots.", +" be 'null', because they are roots."), + #endif -"dumpStack() Dump the stack as an array of callees (youngest first)", + JS_FN_HELP("dumpStack", DumpStack, 1, 0, +"dumpStack()", +" Dump the stack as an array of callees (youngest first)."), + #ifdef TEST_CVTARGS -"cvtargs(arg1..., arg12) Test argument formatter", + JS_FN_HELP("cvtargs", ConvertArgs, 0, 0, +"cvtargs(arg1..., arg12)", +" Test argument formatter."), + #endif -"build() Show build date and time", -"clear([obj]) Clear properties of object", -"intern(str) Internalize str in the atom table", -"clone(fun[, scope]) Clone function object", -"getpda(obj) Get the property descriptors for obj", -"getslx(obj) Get script line extent", -"toint32(n) Testing hook for JS_ValueToInt32", -"evalcx(s[, o])\n" -" Evaluate s in optional sandbox object o\n" + JS_FN_HELP("build", BuildDate, 0, 0, +"build()", +" Show build date and time."), + + JS_FN_HELP("clear", Clear, 0, 0, +"clear([obj])", +" Clear properties of object."), + + JS_FN_HELP("intern", Intern, 1, 0, +"intern(str)", +" Internalize str in the atom table."), + + JS_FN_HELP("clone", Clone, 1, 0, +"clone(fun[, scope])", +" Clone function object."), + + JS_FN_HELP("getpda", GetPDA, 1, 0, +"getpda(obj)", +" Get the property descriptors for obj."), + + JS_FN_HELP("getslx", GetSLX, 1, 0, +"getslx(obj)", +" Get script line extent."), + + JS_FN_HELP("toint32", ToInt32, 1, 0, +"toint32(n)", +" Testing hook for JS_ValueToInt32."), + + JS_FN_HELP("evalcx", EvalInContext, 1, 0, +"evalcx(s[, o])", +" Evaluate s in optional sandbox object o.\n" " if (s == '' && !o) return new o with eager standard classes\n" -" if (s == 'lazy' && !o) return new o with lazy standard classes", -"evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n" -" If 'save' (default false), save the frame chain", -"shapeOf(obj) Get the shape of obj (an implementation detail)", -"resolver(src[, proto]) Create object with resolve hook that copies properties\n" -" from src. If proto is omitted, use Object.prototype.", +" if (s == 'lazy' && !o) return new o with lazy standard classes"), + + JS_FN_HELP("evalInFrame", EvalInFrame, 2, 0, +"evalInFrame(n,str,save)", +" Evaluate 'str' in the nth up frame.\n" +" If 'save' (default false), save the frame chain."), + + JS_FN_HELP("shapeOf", ShapeOf, 1, 0, +"shapeOf(obj)", +" Get the shape of obj (an implementation detail)."), + + JS_FN_HELP("resolver", Resolver, 1, 0, +"resolver(src[, proto])", +" Create object with resolve hook that copies properties\n" +" from src. If proto is omitted, use Object.prototype."), + #ifdef DEBUG -"arrayInfo(a1, a2, ...) Report statistics about arrays", + JS_FN_HELP("arrayInfo", js_ArrayInfo, 1, 0, +"arrayInfo(a1, a2, ...)", +" Report statistics about arrays."), + #endif #ifdef JS_THREADSAFE -"sleep(dt) Sleep for dt seconds", + JS_FN_HELP("sleep", Sleep_fn, 1, 0, +"sleep(dt)", +" Sleep for dt seconds."), + #endif -"snarf(filename) Read filename into returned string", -"read(filename) Synonym for snarf", -"compile(code) Compiles a string to bytecode, potentially throwing", -"parse(code) Parses a string, potentially throwing", -"timeout([seconds])\n" + JS_FN_HELP("snarf", Snarf, 0, 0, +"snarf(filename)", +" Read filename into returned string."), + + JS_FN_HELP("read", Snarf, 0, 0, +"read(filename)", +" Synonym for snarf."), + + JS_FN_HELP("compile", Compile, 1, 0, +"compile(code)", +" Compiles a string to bytecode, potentially throwing."), + + JS_FN_HELP("parse", Parse, 1, 0, +"parse(code)", +" Parses a string, potentially throwing."), + + JS_FN_HELP("timeout", Timeout, 1, 0, +"timeout([seconds])", " Get/Set the limit in seconds for the execution time for the current context.\n" -" A negative value (default) means that the execution time is unlimited.", -"elapsed() Execution time elapsed for the current context.", -"parent(obj) Returns the parent of obj.", -"wrap(obj) Wrap an object into a noop wrapper.", -"serialize(sd) Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.", -"deserialize(a) Deserialize data generated by serialize.", -#ifdef JS_METHODJIT -"mjitcodestats() Return stats on mjit code memory usage.", -#endif -"mjitChunkLimit(N) Specify limit on compiled chunk size during mjit compilation.", -"newGlobal(kind) Return a new global object, in the current\n" -" compartment if kind === 'same-compartment' or in a\n" -" new compartment if kind === 'new-compartment'", -"parseLegacyJSON(str) Parse str as legacy JSON, returning the result if the\n" -" parse succeeded and throwing a SyntaxError if not.", -"enableStackWalkingAssertion(enabled)\n" +" A negative value (default) means that the execution time is unlimited."), + + JS_FN_HELP("elapsed", Elapsed, 0, 0, +"elapsed()", +" Execution time elapsed for the current context.."), + + JS_FN_HELP("parent", Parent, 1, 0, +"parent(obj)", +" Returns the parent of obj.."), + + JS_FN_HELP("wrap", Wrap, 1, 0, +"wrap(obj)", +" Wrap an object into a noop wrapper.."), + + JS_FN_HELP("serialize", Serialize, 1, 0, +"serialize(sd)", +" Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.."), + + JS_FN_HELP("deserialize", Deserialize, 1, 0, +"deserialize(a)", +" Deserialize data generated by serialize.."), + + JS_FN_HELP("newGlobal", NewGlobal, 1, 0, +"newGlobal(kind)", +" Return a new global object, in the current\n" +" compartment if kind === 'same-compartment' or in a\n" +" new compartment if kind === 'new-compartment'."), + + JS_FN_HELP("parseLegacyJSON", ParseLegacyJSON, 1, 0, +"parseLegacyJSON(str)", +" Parse str as legacy JSON, returning the result if the\n" +" parse succeeded and throwing a SyntaxError if not."), + + JS_FN_HELP("enableStackWalkingAssertion", EnableStackWalkingAssertion, 1, 0, +"enableStackWalkingAssertion(enabled)", " Enables or disables a particularly expensive assertion in stack-walking\n" " code. If your test isn't ridiculously thorough, such that performing this\n" " assertion increases test duration by an order of magnitude, you shouldn't\n" -" use this.", -"getMaxArgs() Return the maximum number of supported args for a call.", -"terminate() Terminate JavaScript execution, as if we had run out of\n" -" memory or been terminated by the slow script dialog.", +" use this."), -/* Keep these last: see the static assertion below. */ -#ifdef MOZ_PROFILING -"startProfiling([profileName])\n" -" Start a profiling session\n" -" Profiler must be running with programatic sampling", -"stopProfiling([profileName])\n" -" Stop a running profiling session", -"pauseProfilers([profileName])\n" -" Pause a running profiling session", -"resumeProfilers([profileName])\n" -" Resume a paused profiling session", -"dumpProfile([outfile[, profileName]])\n" -" Dump out current profile info (only valid for callgrind)", -# ifdef MOZ_CALLGRIND -"startCallgrind() Start Callgrind instrumentation", -"stopCallgrind() Stop Callgrind instrumentation", -"dumpCallgrind([outfile]) Dump current Callgrind counters to file or stdout", -# endif -# ifdef MOZ_VTUNE -"startVtune() Start Vtune instrumentation", -"stopVtune() Stop Vtune instrumentation", -"pauseVtune() Pause Vtune collection", -"resumeVtune() Resume Vtune collection", -# endif -#endif + JS_FN_HELP("getMaxArgs", GetMaxArgs, 0, 0, +"getMaxArgs()", +" Return the maximum number of supported args for a call."), + + JS_FS_END }; - #ifdef MOZ_PROFILING # define PROFILING_FUNCTION_COUNT 5 # ifdef MOZ_CALLGRIND @@ -4309,99 +3879,71 @@ static const char *const shell_help_messages[] = { # define EXTERNAL_FUNCTION_COUNT 0 #endif -/* Help messages must match shell functions. */ -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - EXTERNAL_FUNCTION_COUNT == - JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); - -#ifdef DEBUG -static void -CheckHelpMessages() -{ - const char *const *m; - const char *lp; - - /* Messages begin with "function_name(" prefix and don't end with \n. */ - for (m = shell_help_messages; m < ArrayEnd(shell_help_messages) - EXTERNAL_FUNCTION_COUNT; ++m) { - lp = strchr(*m, '('); - JS_ASSERT(lp); - JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, - *m, lp - *m) == 0); - JS_ASSERT((*m)[strlen(*m) - 1] != '\n'); - } -} -#else -# define CheckHelpMessages() ((void) 0) -#endif - #undef PROFILING_FUNCTION_COUNT #undef CALLGRIND_FUNCTION_COUNT #undef VTUNE_FUNCTION_COUNT #undef EXTERNAL_FUNCTION_COUNT +static bool +PrintHelpString(JSContext *cx, jsval v) +{ + JSString *str = JSVAL_TO_STRING(v); + JS::Anchor a_str(str); + const jschar *chars = JS_GetStringCharsZ(cx, str); + if (!chars) + return false; + + for (const jschar *p = chars; *p; p++) + fprintf(gOutFile, "%c", char(*p)); + + fprintf(gOutFile, "\n"); + + return true; +} + +static bool +PrintHelp(JSContext *cx, JSObject *obj) +{ + jsval usage, help; + if (!JS_LookupProperty(cx, obj, "usage", &usage)) + return false; + if (!JS_LookupProperty(cx, obj, "help", &help)) + return false; + + if (JSVAL_IS_VOID(usage) || JSVAL_IS_VOID(help)) + return true; + + return PrintHelpString(cx, usage) && PrintHelpString(cx, help); +} + static JSBool Help(JSContext *cx, unsigned argc, jsval *vp) { - unsigned i, j; - int did_header, did_something; - JSType type; - JSFunction *fun; - JSString *str; - fprintf(gOutFile, "%s\n", JS_GetImplementationVersion()); + if (argc == 0) { - fputs(shell_help_header, gOutFile); - for (i = 0; i < ArrayLength(shell_help_messages); ++i) - fprintf(gOutFile, "%s\n", shell_help_messages[i]); + JSObject *global = JS_GetGlobalObject(cx); + AutoIdArray ida(cx, JS_Enumerate(cx, global)); + if (!ida) + return false; + + for (size_t i = 0; i < ida.length(); i++) { + jsval v; + if (!JS_LookupPropertyById(cx, global, ida[i], &v)) + return false; + if (JSVAL_IS_OBJECT(v) && !PrintHelp(cx, JSVAL_TO_OBJECT(v))) + return false; + } } else { - did_header = 0; jsval *argv = JS_ARGV(cx, vp); - for (i = 0; i < argc; i++) { - did_something = 0; - type = JS_TypeOfValue(cx, argv[i]); - if (type == JSTYPE_FUNCTION) { - fun = JS_ValueToFunction(cx, argv[i]); - str = fun->atom; - } else if (type == JSTYPE_STRING) { - str = JSVAL_TO_STRING(argv[i]); - } else { - str = NULL; - } - if (str) { - JSAutoByteString funcName(cx, str); - if (!funcName) - return JS_FALSE; - for (j = 0; j < ArrayLength(shell_help_messages); ++j) { - /* Help messages are required to be formatted "functionName(..." */ - const char *msg = shell_help_messages[j]; - const char *p = strchr(msg, '('); - JS_ASSERT(p); - - if (size_t(p - msg) != str->length()) - continue; - - if (strncmp(funcName.ptr(), msg, p - msg) == 0) { - if (!did_header) { - did_header = 1; - fputs(shell_help_header, gOutFile); - } - did_something = 1; - fprintf(gOutFile, "%s\n", shell_help_messages[j]); - break; - } - } - } - if (!did_something) { - str = JS_ValueToString(cx, argv[i]); - if (!str) - return JS_FALSE; - fputs("Sorry, no help for ", gErrFile); - JS_FileEscapedString(gErrFile, str, 0); - putc('\n', gErrFile); - } + for (unsigned i = 0; i < argc; i++) { + if (JSVAL_IS_OBJECT(argv[i]) && !PrintHelp(cx, JSVAL_TO_OBJECT(argv[i]))) + return false; } } + JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } /* @@ -5082,10 +4624,12 @@ NewGlobalObject(JSContext *cx, CompartmentKind compartment) return NULL; if (!JS::RegisterPerfMeasurement(cx, glob)) return NULL; - if (!JS_DefineFunctions(cx, glob, shell_functions) || + if (!JS_DefineFunctionsWithHelp(cx, glob, shell_functions) || !JS_DefineProfilingFunctions(cx, glob)) { return NULL; } + if (!js::DefineTestingFunctions(cx, glob)) + return NULL; JSObject *it = JS_DefineObject(cx, glob, "it", &its_class, NULL, 0); if (!it) @@ -5377,7 +4921,6 @@ main(int argc, char **argv, char **envp) } #endif - CheckHelpMessages(); #ifdef HAVE_SETLOCALE setlocale(LC_ALL, ""); #endif diff --git a/js/xpconnect/idl/xpccomponents.idl b/js/xpconnect/idl/xpccomponents.idl index 9610a89245ff..7602a44d3256 100644 --- a/js/xpconnect/idl/xpccomponents.idl +++ b/js/xpconnect/idl/xpccomponents.idl @@ -152,7 +152,7 @@ interface ScheduledGCCallback : nsISupports /** * interface of Components.utils */ -[scriptable, uuid(9e9ba9d6-a0fa-4767-9f49-9b74bb2368cd)] +[scriptable, uuid(9e43a260-5db2-11e1-b86c-0800200c9a66)] interface nsIXPCComponents_Utils : nsISupports { @@ -301,6 +301,9 @@ interface nsIXPCComponents_Utils : nsISupports [implicit_jscontext] jsval nondeterministicGetWeakMapKeys(in jsval aMap); + [implicit_jscontext] + jsval getJSTestingFunctions(); + /* * To be called from JS only. * diff --git a/js/xpconnect/src/XPCComponents.cpp b/js/xpconnect/src/XPCComponents.cpp index e34cf533c982..66c285f35cb2 100644 --- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -3704,6 +3704,18 @@ nsXPCComponents_Utils::NondeterministicGetWeakMapKeys(const jsval &aMap, return NS_OK; } +/* void getDebugObject(); */ +NS_IMETHODIMP +nsXPCComponents_Utils::GetJSTestingFunctions(JSContext *cx, + JS::Value *retval) +{ + JSObject *obj = js::GetTestingFunctions(cx); + if (!obj) + return NS_ERROR_XPC_JAVASCRIPT_ERROR; + *retval = OBJECT_TO_JSVAL(obj); + return NS_OK; +} + /* void getGlobalForObject(); */ NS_IMETHODIMP nsXPCComponents_Utils::GetGlobalForObject(const JS::Value& object, From 39a4df5abbff3fe5d43f4b67176f15bd73d65406 Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Tue, 6 Mar 2012 11:39:31 -0800 Subject: [PATCH 54/88] Bug 731094 - Expose a testing function that permits only deterministic GCs (r=igor) --- js/src/builtin/TestingFunctions.cpp | 18 +++++++++++++++ js/src/jsapi.cpp | 1 + js/src/jscntxt.h | 1 + js/src/jsfriendapi.h | 2 +- js/src/jsgc.cpp | 36 +++++++++++++++++++++++++---- js/src/jsgc.h | 3 +++ 6 files changed, 56 insertions(+), 5 deletions(-) diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 3bb74f032328..6de7947092c8 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -8,6 +8,7 @@ #include "jscntxt.h" #include "jscompartment.h" #include "jsfriendapi.h" +#include "jsgc.h" #include "jsobj.h" #include "jsprf.h" #include "jswrapper.h" @@ -221,6 +222,19 @@ GCSlice(JSContext *cx, unsigned argc, jsval *vp) *vp = JSVAL_VOID; return JS_TRUE; } + +static JSBool +DeterministicGC(JSContext *cx, unsigned argc, jsval *vp) +{ + if (argc != 1) { + ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments"); + return JS_FALSE; + } + + gc::SetDeterministicGC(cx, js_ValueToBoolean(vp[2])); + *vp = JSVAL_VOID; + return JS_TRUE; +} #endif /* JS_GC_ZEAL */ typedef struct JSCountHeapNode JSCountHeapNode; @@ -516,6 +530,10 @@ static JSFunctionSpecWithHelp TestingFunctions[] = { JS_FN_HELP("gcslice", GCSlice, 1, 0, "gcslice(n)", " Run an incremental GC slice that marks about n objects."), + + JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0, +"deterministicgc(true|false)", +" If true, only allow determinstic GCs to run."), #endif JS_FN_HELP("internalConst", InternalConst, 1, 0, diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 657363e15417..b675c9518dee 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -753,6 +753,7 @@ JSRuntime::JSRuntime() gcZealFrequency(0), gcNextScheduled(0), gcDebugCompartmentGC(false), + gcDeterministicOnly(false), #endif gcCallback(NULL), gcSliceCallback(NULL), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 41a8645ff3d7..def1e7b76bac 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -419,6 +419,7 @@ struct JSRuntime : js::RuntimeFriendFields int gcZealFrequency; int gcNextScheduled; bool gcDebugCompartmentGC; + bool gcDeterministicOnly; int gcZeal() { return gcZeal_; } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d0715468f4ac..592429712dab 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -673,7 +673,7 @@ SizeOfJSContext(); D(LAST_DITCH) \ D(TOO_MUCH_MALLOC) \ D(ALLOC_TRIGGER) \ - D(UNUSED1) /* was CHUNK */ \ + D(DEBUG_GC) \ D(UNUSED2) /* was SHAPE */ \ D(UNUSED3) /* was REFILL */ \ \ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 7c42ef86c37a..c516730af7f0 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1708,13 +1708,13 @@ ArenaLists::finalizeScripts(JSContext *cx) } static void -RunLastDitchGC(JSContext *cx) +RunLastDitchGC(JSContext *cx, gcreason::Reason reason) { JSRuntime *rt = cx->runtime; /* The last ditch GC preserves all atoms. */ AutoKeepAtoms keep(rt); - GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH); + GC(cx, rt->gcTriggerCompartment, GC_NORMAL, reason); } /* static */ void * @@ -1729,7 +1729,7 @@ ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind) bool runGC = rt->gcIncrementalState != NO_INCREMENTAL && comp->gcBytes > comp->gcTriggerBytes; for (;;) { if (JS_UNLIKELY(runGC)) { - RunLastDitchGC(cx); + RunLastDitchGC(cx, gcreason::LAST_DITCH); /* * The JSGC_END callback can legitimately allocate new GC @@ -3662,6 +3662,20 @@ GCCycle(JSContext *cx, JSCompartment *comp, int64_t budget, JSGCInvocationKind g #endif } +#ifdef JS_GC_ZEAL +static bool +IsDeterministicGCReason(gcreason::Reason reason) +{ + if (reason > gcreason::DEBUG_GC && reason != gcreason::CC_FORCED) + return false; + + if (reason == gcreason::MAYBEGC) + return false; + + return true; +} +#endif + static void Collect(JSContext *cx, JSCompartment *comp, int64_t budget, JSGCInvocationKind gckind, gcreason::Reason reason) @@ -3669,6 +3683,11 @@ Collect(JSContext *cx, JSCompartment *comp, int64_t budget, JSRuntime *rt = cx->runtime; JS_AbortIfWrongThread(rt); +#ifdef JS_GC_ZEAL + if (rt->gcDeterministicOnly && !IsDeterministicGCReason(reason)) + return; +#endif + JS_ASSERT_IF(budget != SliceBudget::Unlimited, JSGC_INCREMENTAL); #ifdef JS_GC_ZEAL @@ -3947,7 +3966,16 @@ RunDebugGC(JSContext *cx) if (rt->gcTriggerCompartment == rt->atomsCompartment) rt->gcTriggerCompartment = NULL; - RunLastDitchGC(cx); + RunLastDitchGC(cx, gcreason::DEBUG_GC); +#endif +} + +void +SetDeterministicGC(JSContext *cx, bool enabled) +{ +#ifdef JS_GC_ZEAL + JSRuntime *rt = cx->runtime; + rt->gcDeterministicOnly = enabled; #endif } diff --git a/js/src/jsgc.h b/js/src/jsgc.h index f59816951fdd..246c4e1a3ca1 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1968,6 +1968,9 @@ NewCompartment(JSContext *cx, JSPrincipals *principals); void RunDebugGC(JSContext *cx); +void +SetDeterministicGC(JSContext *cx, bool enabled); + #if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) && !defined(JS_THREADSAFE) /* Overwrites stack references to GC things which have not been rooted. */ void CheckStackRoots(JSContext *cx); From 9002eae61d455ab874d203b37add175741a8b1a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Tue, 6 Mar 2012 11:50:58 -0800 Subject: [PATCH 55/88] Bug 720415 - update navigator.mozApps m-c implementation - IDL [r=jst] --- .../apps/nsIDOMApplicationRegistry.idl | 117 +++++++----------- 1 file changed, 46 insertions(+), 71 deletions(-) diff --git a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl index 1f69cc5e4509..75f52ddaf41b 100644 --- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl +++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl @@ -36,104 +36,79 @@ * ***** END LICENSE BLOCK ***** */ #include "domstubs.idl" +#include "nsIArray.idl" +#include "nsIDOMEvent.idl" +#include "nsIDOMEventTarget.idl" -[scriptable, uuid(e0c271cb-266b-48c9-a7e4-96590b445c26)] -interface mozIDOMApplicationRegistryError : nsISupports -{ - const unsigned short DENIED = 1; - const unsigned short PERMISSION_DENIED = 2; - const unsigned short MANIFEST_URL_ERROR = 3; - const unsigned short NETWORK_ERROR = 4; - const unsigned short MANIFEST_PARSE_ERROR = 5; - const unsigned short INVALID_MANIFEST = 6; +interface nsIDOMDOMRequest; - readonly attribute short code; -}; - -[scriptable, uuid(a6856a3d-dece-43ce-89b9-72dba07f4246)] -interface mozIDOMApplication : nsISupports +[scriptable, uuid(b70b84f1-7ac9-4a92-bc32-8b6a7eb7879e)] +interface mozIDOMApplication : nsISupports { readonly attribute jsval manifest; - readonly attribute DOMString receipt; + readonly attribute DOMString manifestURL; + readonly attribute nsIArray receipts; /* an array of strings */ readonly attribute DOMString origin; readonly attribute DOMString installOrigin; readonly attribute unsigned long installTime; + + /* startPoint will be used when several launch_path exists for an app */ + nsIDOMDOMRequest launch([optional] in DOMString startPoint); + nsIDOMDOMRequest uninstall(); }; -[scriptable, function, uuid(be170df5-9154-463b-9197-10a6195eba52)] -interface mozIDOMApplicationRegistryEnumerateCallback : nsISupports +[scriptable, uuid(870bfbdc-3e13-4042-99dd-18e25720782d)] +interface mozIDOMApplicationEvent : nsIDOMEvent { - void handleEvent([array, size_is(count)] in mozIDOMApplication apps, - in unsigned long count); + readonly attribute mozIDOMApplication application; }; -[scriptable, function, uuid(ae0ed33d-35cf-443a-837b-a6cebf16bd49)] -interface mozIDOMApplicationRegistryErrorCallback : nsISupports +[scriptable, uuid(a82771f6-ba46-4073-9e6e-f1ad3f42b1f6)] +interface mozIDOMApplicationMgmt : nsIDOMEventTarget { - void handleEvent(in mozIDOMApplicationRegistryError error); + /** + * the request will return the all the applications installed. Only accessible + * to privileged callers. + */ + nsIDOMDOMRequest getAll(); + + /** + * event listener to get notified of application installs. Only settable by + * privileged callers. + * the event will be a mozIDOMApplicationEvent + */ + attribute nsIDOMEventListener oninstall; + + /** + * event listener to get notified of application uninstalls. Only settable by + * privileged callers. + * the event will be a mozIDOMApplicationEvent + */ + attribute nsIDOMEventListener onuninstall; }; -[scriptable, uuid(ac63c0ba-1f33-4e3e-b9aa-6a3243a9adba)] +[scriptable, uuid(f6929871-288b-4613-9a37-9a150760ac50)] interface mozIDOMApplicationRegistry : nsISupports { /** * Install a web app. onerror can be used to report errors, * and oninstall if the caller is privileged. * - * @param manifestUrl : the URL of the webapps manifest - * @param receipt : An opaque string used to track payment status + * @param manifestUrl : the URL of the webapps manifest. + * @param parameters : A structure with optional information. + * { receipts: ... } will be used to specify the payment receipts for this installation. */ - void install(in DOMString manifestUrl, - [optional] in DOMString receipt); + nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval parameters); /** - * This call is only accessible to privileged callers. - * - * @param origin : the origin of the application to uninstall. + * the request will return the application currently installed, or null. */ - void uninstall(in DOMString origin); + nsIDOMDOMRequest getSelf(); /** - * Enumerate apps : either return itself if caller is an app, or - * apps installed from a store if caller is a store - * - * @param success: the callback that will get the array of applications - * @param error: optional error callback + * the request will return the applications installed from this origin, or null. */ - void enumerate(in mozIDOMApplicationRegistryEnumerateCallback success, - [optional] in mozIDOMApplicationRegistryErrorCallback error); + nsIDOMDOMRequest getInstalled(); - /** - * Enumerate all apps installed from all stores. - * Only usable by privileged callers. - * - * @param success: the callback that will get the array of applications - * @param error: optional error callback - */ - void enumerateAll(in mozIDOMApplicationRegistryEnumerateCallback success, - [optional] in mozIDOMApplicationRegistryErrorCallback error); - - /** - * launch a webapp. Behavior is application dependant. - * - * @param origin : the origin of the application to launch - */ - void launch(in DOMString origin); - - /** - * event listener to get notified of application installs. Only settable by - * privileged callers - */ - attribute nsIDOMEventListener oninstall; - - /** - * event listener to get notified of application uninstalls. Only settable by - * privileged callers - */ - attribute nsIDOMEventListener onuninstall; - - /** - * event listener to get notified of errors. - */ - attribute nsIDOMEventListener onerror; + readonly attribute mozIDOMApplicationMgmt mgmt; }; From 85faecc9408462985f214d0405473376f969fb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Tue, 6 Mar 2012 11:50:58 -0800 Subject: [PATCH 56/88] Bug 720415 - update navigator.mozApps m-c implementation - DOM + repo [r=gwagner] --- dom/base/DOMRequestHelper.jsm | 80 ++++++ dom/base/Makefile.in | 1 + dom/base/Webapps.js | 456 +++++++++++++++++++--------------- dom/base/Webapps.jsm | 141 ++++------- dom/base/Webapps.manifest | 6 - 5 files changed, 386 insertions(+), 298 deletions(-) create mode 100644 dom/base/DOMRequestHelper.jsm diff --git a/dom/base/DOMRequestHelper.jsm b/dom/base/DOMRequestHelper.jsm new file mode 100644 index 000000000000..721f58ad57b9 --- /dev/null +++ b/dom/base/DOMRequestHelper.jsm @@ -0,0 +1,80 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * helper object for APIs that deal with DOMRequest and need to release them properly + * when the window goes out of scope + */ +const Cu = Components.utils; +const Cc = Components.classes; +const Ci = Components.interfaces; + +let EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"]; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyGetter(Services, "rs", function() { + return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService); +}); + +XPCOMUtils.defineLazyGetter(this, "cpmm", function() { + return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); +}); + +function DOMRequestIpcHelper() { +} + +DOMRequestIpcHelper.prototype = { + getRequestId: function(aRequest) { + let id = "id" + this._getRandomId(); + this._requests[id] = aRequest; + return id; + }, + + getRequest: function(aId) { + if (this._requests[aId]) + return this._requests[aId]; + }, + + removeRequest: function(aId) { + if (this._requests[aId]) + delete this._requests[aId]; + }, + + _getRandomId: function() { + return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); + }, + + observe: function(aSubject, aTopic, aData) { + let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (wId == this.innerWindowID) { + Services.obs.removeObserver(this, "inner-window-destroyed"); + this._requests = []; + this._window = null; + this._messages.forEach((function(msgName) { + cpmm.removeMessageListener(msgName, this); + }).bind(this)); + if(this.uninit) + this.uninit(); + } + }, + + initHelper: function(aWindow, aMessages) { + this._messages = aMessages; + this._requests = []; + this._window = aWindow; + let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); + this.innerWindowID = util.currentInnerWindowID; + this._id = this._getRandomId(); + Services.obs.addObserver(this, "inner-window-destroyed", false); + this._messages.forEach((function(msgName) { + cpmm.addMessageListener(msgName, this); + }).bind(this)); + }, + + createRequest: function() { + return Services.rs.createRequest(this._window); + } +} diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index 6537634d4ded..034e1dbab034 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -66,6 +66,7 @@ EXTRA_COMPONENTS = \ $(NULL) EXTRA_JS_MODULES += Webapps.jsm \ + DOMRequestHelper.jsm \ $(NULL) endif diff --git a/dom/base/Webapps.js b/dom/base/Webapps.js index 2c04c6aa8ab9..796e6c3ab49c 100644 --- a/dom/base/Webapps.js +++ b/dom/base/Webapps.js @@ -1,38 +1,6 @@ -/* ***** 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 Open Web Apps. - * - * 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): - * Fabrice Desré - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ const Cc = Components.classes; const Ci = Components.interfaces; @@ -41,27 +9,31 @@ const Cr = Components.results; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/DOMRequestHelper.jsm"); + +XPCOMUtils.defineLazyGetter(Services, "rs", function() { + return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService); +}); + +XPCOMUtils.defineLazyGetter(this, "cpmm", function() { + return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); +}); + +function convertAppsArray(aApps, aWindow) { + let apps = new Array(); + for (let i = 0; i < aApps.length; i++) { + let app = aApps[i]; + apps.push(new WebappsApplication(aWindow, app.origin, app.manifest, app.manifestURL, + app.receipts, app.installOrigin, app.installTime)); + } + return apps; +} function WebappsRegistry() { - this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO", - "Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO", - "Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"]; - - this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); - - this.messages.forEach((function(msgName) { - this.mm.addMessageListener(msgName, this); - }).bind(this)); - - this._window = null; - this._id = this._getRandomId(); - this._callbacks = []; } WebappsRegistry.prototype = { - _onerror: null, - _oninstall: null, - _onuninstall: null, + __proto__: DOMRequestIpcHelper.prototype, /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest * only the name property is mandatory @@ -70,7 +42,7 @@ WebappsRegistry.prototype = { // TODO : check for install_allowed_from if (aManifest.name == undefined) return false; - + if (aManifest.installs_allowed_from) { ok = false; aManifest.installs_allowed_from.forEach(function(aOrigin) { @@ -81,97 +53,41 @@ WebappsRegistry.prototype = { } return true; }, - - getCallbackId: function(aCallback) { - let id = "id" + this._getRandomId(); - this._callbacks[id] = aCallback; - return id; - }, - - getCallback: function(aId) { - return this._callbacks[aId]; - }, - - removeCallback: function(aId) { - if (this._callbacks[aId]) - delete this._callbacks[aId]; - }, - - _getRandomId: function() { - return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); - }, - - _convertAppsArray: function(aApps) { - let apps = new Array(); - for (let i = 0; i < aApps.length; i++) { - let app = aApps[i]; - apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime)); - } - return apps; - }, - - set oninstall(aCallback) { - if (this.hasPrivileges) - this._oninstall = aCallback; - else - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - set onuninstall(aCallback) { - if (this.hasPrivileges) - this._onuninstall = aCallback; - else - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - set onerror(aCallback) { - this._onerror = aCallback; - }, receiveMessage: function(aMessage) { let msg = aMessage.json; - if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK")) + if (msg.oid != this._id) return + let req = this.getRequest(msg.requestID); + if (!req) + return; let app = msg.app; - let cb; switch (aMessage.name) { case "Webapps:Install:Return:OK": - if (this._oninstall) - this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt, + Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts, app.installOrigin, app.installTime)); break; case "Webapps:Install:Return:KO": - if (this._onerror) - this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.DENIED)); + Services.rs.fireError(req, "DENIED"); break; - case "Webapps:Uninstall:Return:OK": - if (this._onuninstall) - this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0)); - break; - case "Webapps:Uninstall:Return:KO": - if (this._onerror) - this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED)); - break; - case "Webapps:Enumerate:Return:OK": - cb = this.getCallback(msg.callbackID); - if (cb.success) { - let apps = this._convertAppsArray(msg.apps); - cb.success.handleEvent(apps, apps.length); + case "Webapps:GetSelf:Return:OK": + if (msg.apps.length) { + app = msg.apps[0]; + Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts, + app.installOrigin, app.installTime)); + } else { + Services.rs.fireSuccess(req, null); } break; - case "Webapps:Enumerate:Return:KO": - cb = this.getCallback(msg.callbackID); - if (cb.error) - cb.error.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED)); + case "Webapps:GetInstalled:Return:OK": + Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window)); + break; + case "Webapps:GetSelf:Return:KO": + case "Webapps:GetInstalled:Return:KO": + Services.rs.fireError(req, "ERROR"); break; } - this.removeCallback(msg.callbackID); - }, - - _fireError: function(aCode) { - if (!this._onerror) - return; - this._onerror.handleEvent(new RegistryError(aCode)); + this.removeRequest(msg.requestID); }, _getOrigin: function(aURL) { @@ -181,7 +97,9 @@ WebappsRegistry.prototype = { // mozIDOMApplicationRegistry implementation - install: function(aURL, aReceipt) { + install: function(aURL, aParams) { + let request = this.createRequest(); + let requestID = this.getRequestId(request); let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest); xhr.open("GET", aURL, true); @@ -191,90 +109,70 @@ WebappsRegistry.prototype = { let installOrigin = this._getOrigin(this._window.location.href); let manifest = JSON.parse(xhr.responseText, installOrigin); if (!this.checkManifest(manifest, installOrigin)) { - this._fireError(Ci.mozIDOMApplicationRegistryError.INVALID_MANIFEST); + Services.rs.fireError(request, "INVALID_MANIFEST"); } else { - this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin, - origin: this._getOrigin(aURL), - manifest: manifest, - receipt: aReceipt }, - from: this._window.location.href, - oid: this._id }); + let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : []; + cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin, + origin: this._getOrigin(aURL), + manifestURL: aURL, + manifest: manifest, + receipts: receipts }, + from: this._window.location.href, + oid: this._id, + requestID: requestID }); } } catch(e) { - this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR); + Services.rs.fireError(request, "MANIFEST_PARSE_ERROR"); } } else { - this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_URL_ERROR); + Services.rs.fireError(request, "MANIFEST_URL_ERROR"); } }).bind(this), false); xhr.addEventListener("error", (function() { - this._fireError(Ci.mozIDOMApplicationRegistryError.NETWORK_ERROR); + Services.rs.fireError(request, "NETWORK_ERROR"); }).bind(this), false); xhr.send(null); + return request; }, - uninstall: function(aOrigin) { - if (this.hasPrivileges) - this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href, - origin: aOrigin, - oid: this._id }); - else - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; + getSelf: function() { + let request = this.createRequest(); + cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href), + oid: this._id, + requestID: this.getRequestId(request) }); + return request; }, - launch: function(aOrigin) { - this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin, - from: this._window.location.href}); - }, - - enumerate: function(aSuccess, aError) { - this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href, - origin: this._getOrigin(this._window.location.href), + getInstalled: function() { + let request = this.createRequest(); + cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href), oid: this._id, - callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); + requestID: this.getRequestId(request) }); + return request; }, - enumerateAll: function(aSuccess, aError) { - if (this.hasPrivileges) { - this.mm.sendAsyncMessage("Webapps:EnumerateAll", { from: this._window.location.href, - origin: this._getOrigin(this._window.location.href), - oid: this._id, - callbackID: this.getCallbackId({ success: aSuccess, error: aError }) }); - } else { - if (aError) - aError.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED)); - } + get mgmt() { + if (!this._mgmt) + this._mgmt = new WebappsApplicationMgmt(this._window); + return this._mgmt; }, - observe: function(aSubject, aTopic, aData) { - let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data; - if (wId == this.innerWindowID) { - Services.obs.removeObserver(this, "inner-window-destroyed"); - this._oninstall = null; - this._onuninstall = null; - this._onerror = null; - this._callbacks = []; - this._window = null; - } + uninit: function() { + this._mgmt = null; }, // nsIDOMGlobalPropertyInitializer implementation init: function(aWindow) { - dump("DOMApplicationRegistry::init() " + aWindow + "\n"); - this._window = aWindow; - this._window.appId = this._id; - let from = Services.io.newURI(this._window.location.href, null, null); - let perm = Services.perms.testExactPermission(from, "webapps-manage"); - - //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall - this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about"); + this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO", + "Webapps:GetInstalled:Return:OK", "Webapps:GetInstalled:Return:KO", + "Webapps:GetSelf:Return:OK", "Webapps:GetSelf:Return:KO"]); Services.obs.addObserver(this, "inner-window-destroyed", false); let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); - this.innerWindowID = util.currentInnerWindowID; + this._id = util.outerWindowID; }, classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"), @@ -288,18 +186,26 @@ WebappsRegistry.prototype = { classDescription: "Webapps Registry"}) } -function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) { +/** + * mozIDOMApplication object + */ +function WebappsApplication(aWindow, aOrigin, aManifest, aManifestURL, aReceipts, aInstallOrigin, aInstallTime) { this._origin = aOrigin; this._manifest = aManifest; - this._receipt = aReceipt; + this._manifestURL = aManifestURL; + this._receipts = aReceipts; this._installOrigin = aInstallOrigin; this._installTime = aInstallTime; + + this.initHelper(aWindow, ["Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO"]); } WebappsApplication.prototype = { + __proto__: DOMRequestIpcHelper.prototype, _origin: null, _manifest: null, - _receipt: null, + _manifestURL: null, + _receipts: [], _installOrigin: null, _installTime: 0, @@ -311,8 +217,12 @@ WebappsApplication.prototype = { return this._manifest; }, - get receipt() { - return this._receipt; + get manifestURL() { + return this._manifestURL; + }, + + get receipts() { + return this._receipts; }, get installOrigin() { @@ -323,6 +233,39 @@ WebappsApplication.prototype = { return this._installTime; }, + launch: function(aStartPoint) { + let request = this.createRequest(); + cpmm.sendAsyncMessage("Webapps:Launch", { origin: this._origin, + startPoint: aStartPoint, + oid: this._id, + requestID: this.getRequestId(request) }); + return request; + }, + + uninstall: function() { + let request = this.createRequest(); + cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: this._origin, + oid: this._id, + requestID: this.getRequestId(request) }); + return request; + }, + + receiveMessage: function(aMessage) { + var msg = aMessage.json; + let req = this.getRequest(msg.requestID); + if (msg.oid != this._id || !req) + return; + switch (aMessage.name) { + case "Webapps:Uninstall:Return:OK": + Services.rs.fireSuccess(req, msg.origin); + break; + case "Webapps:Uninstall:Return:KO": + Services.rs.fireError(req, msg.origin); + break; + } + this.removeRequest(msg.requestID); + }, + classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"), QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]), @@ -334,26 +277,131 @@ WebappsApplication.prototype = { classDescription: "Webapps Application"}) } -function RegistryError(aCode) { - this._code = aCode; +/** + * mozIDOMApplicationMgmt object + */ +function WebappsApplicationMgmt(aWindow) { + let principal = aWindow.document.nodePrincipal; + let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); + + let perm = principal == secMan.getSystemPrincipal() ? + Ci.nsIPermissionManager.ALLOW_ACTION : + Services.perms.testExactPermission(principal.URI, "webapps-manage"); + + //only pages with perm set can use some functions + this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION; + + this.initHelper(aWindow, ["Webapps:GetAll:Return:OK", "Webapps:GetAll:Return:KO", + "Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]); + + this._oninstall = null; + this._onuninstall = null; } -RegistryError.prototype = { - _code: null, - - get code() { - return this._code; +WebappsApplicationMgmt.prototype = { + __proto__: DOMRequestIpcHelper.prototype, + + uninit: function() { + this._oninstall = null; + this._onuninstall = null; }, - - classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"), - QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistryError]), + getAll: function() { + let request = this.createRequest(); + cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id, + requestID: this.getRequestId(request), + hasPrivileges: this.hasPrivileges }); + return request; + }, - classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"), - contractID: "@mozilla.org/webapps/error;1", - interfaces: [Ci.mozIDOMApplicationRegistryError], + get oninstall() { + return this._oninstall; + }, + + get onuninstall() { + this._onuninstall; + }, + + set oninstall(aCallback) { + if (this.hasPrivileges) + this._oninstall = aCallback; + else + + throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE); + }, + + set onuninstall(aCallback) { + if (this.hasPrivileges) + this._onuninstall = aCallback; + else + throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE); + }, + + receiveMessage: function(aMessage) { + var msg = aMessage.json; + let req = this.getRequest(msg.requestID); + // We want Webapps:Install:Return:OK and Webapps:Uninstall:Return:OK to be boradcasted + // to all instances of mozApps.mgmt + if (!((msg.oid == this._id && req) + || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK")) + return; + switch (aMessage.name) { + case "Webapps:GetAll:Return:OK": + Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window)); + break; + case "Webapps:GetAll:Return:KO": + Services.rs.fireError(req, "DENIED"); + break; + case "Webapps:Install:Return:OK": + if (this._oninstall) { + let app = msg.app; + let event = new WebappsApplicationEvent(new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts, + app.installOrigin, app.installTime)); + this._oninstall.handleEvent(event); + } + break; + case "Webapps:Uninstall:Return:OK": + if (this._onuninstall) { + let event = new WebappsApplicationEvent(new WebappsApplication(this._window, msg.origin, null, null, null, null, 0)); + this._onuninstall.handleEvent(event); + } + break; + } + this.removeRequest(msg.requestID); + }, + + classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt]), + + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), + contractID: "@mozilla.org/webapps/application-mgmt;1", + interfaces: [Ci.mozIDOMApplicationMgmt], flags: Ci.nsIClassInfo.DOM_OBJECT, - classDescription: "Webapps Registry Error"}) + classDescription: "Webapps Application Mgmt"}) } -const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]); +/** + * mozIDOMApplicationEvent object + */ +function WebappsApplicationEvent(aApp) { + this._app = aApp; +} + +WebappsApplicationEvent.prototype = { + get application() { + return this._app; + }, + + classID: Components.ID("{5bc42b2a-9acc-49d5-a336-c353c8125e48}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationEvent]), + + classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"), + contractID: "@mozilla.org/webapps/application-event;1", + interfaces: [Ci.mozIDOMApplicationEvent], + flags: Ci.nsIClassInfo.DOM_OBJECT, + classDescription: "Webapps Application Event"}) +} + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]); diff --git a/dom/base/Webapps.jsm b/dom/base/Webapps.jsm index dbb4e1fe4c19..56b211541a3e 100644 --- a/dom/base/Webapps.jsm +++ b/dom/base/Webapps.jsm @@ -1,39 +1,6 @@ -/* ***** 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 Mobile Browser. - * - * 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): - * Fabrice Desré - * Mark Finkle - * - * 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 ***** */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ const Cu = Components.utils; const Cc = Components.classes; @@ -50,17 +17,21 @@ XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { return NetUtil; }); +XPCOMUtils.defineLazyGetter(this, "ppmm", function() { + return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); +}); + let DOMApplicationRegistry = { appsFile: null, webapps: { }, init: function() { - this.mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager); let messages = ["Webapps:Install", "Webapps:Uninstall", - "Webapps:Enumerate", "Webapps:EnumerateAll", "Webapps:Launch"]; + "Webapps:GetSelf", "Webapps:GetInstalled", + "Webapps:Launch", "Webapps:GetAll"]; messages.forEach((function(msgName) { - this.mm.addMessageListener(msgName, this); + ppmm.addMessageListener(msgName, this); }).bind(this)); let appsDir = FileUtils.getDir("ProfD", ["webapps"], true, true); @@ -114,29 +85,29 @@ let DOMApplicationRegistry = { receiveMessage: function(aMessage) { let msg = aMessage.json; - let from = Services.io.newURI(msg.from, null, null); - let perm = Services.perms.testExactPermission(from, "webapps-manage"); - - //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall - let hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about"); switch (aMessage.name) { case "Webapps:Install": // always ask for UI to install Services.obs.notifyObservers(this, "webapps-ask-install", JSON.stringify(msg)); break; + case "Webapps:GetSelf": + this.getSelf(msg); + break; case "Webapps:Uninstall": - if (hasPrivileges) - this.uninstall(msg); + this.uninstall(msg); break; case "Webapps:Launch": Services.obs.notifyObservers(this, "webapps-launch", JSON.stringify(msg)); break; - case "Webapps:Enumerate": - this.enumerate(msg); + case "Webapps:GetInstalled": + this.getInstalled(msg); break; - case "Webapps:EnumerateAll": - this.enumerateAll(msg); + case "Webapps:GetAll": + if (msg.hasPrivileges) + this.getAll(msg); + else + ppmm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg); break; } }, @@ -162,14 +133,15 @@ let DOMApplicationRegistry = { let clone = { installOrigin: aApp.installOrigin, origin: aApp.origin, - receipt: aApp.receipt, - installTime: aApp.installTime + receipts: aApp.receipts, + installTime: aApp.installTime, + manifestURL: aApp.manifestURL }; return clone; }, denyInstall: function(aData) { - this.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData); + ppmm.sendAsyncMessage("Webapps:Install:Return:KO", aData); }, confirmInstall: function(aData, aFromSync) { @@ -202,7 +174,7 @@ let DOMApplicationRegistry = { if (!aFromSync) this._saveApps((function() { - this.mm.sendAsyncMessage("Webapps:Install:Return:OK", aData); + ppmm.sendAsyncMessage("Webapps:Install:Return:OK", aData); Services.obs.notifyObservers(this, "webapps-sync-install", id); }).bind(this)); }, @@ -244,9 +216,11 @@ let DOMApplicationRegistry = { }, uninstall: function(aData) { + let found = false; for (let id in this.webapps) { let app = this.webapps[id]; if (app.origin == aData.origin) { + found = true; delete this.webapps[id]; let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true); try { @@ -254,62 +228,53 @@ let DOMApplicationRegistry = { } catch (e) { } this._saveApps((function() { - this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData); + ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData); Services.obs.notifyObservers(this, "webapps-sync-uninstall", id); }).bind(this)); } } + if (!found) + ppmm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData); }, - enumerate: function(aData) { + getSelf: function(aData) { aData.apps = []; let tmp = []; - let selfId; - let id = this._appId(aData.origin); - // if it's an app, add itself to the result + if (id) { let app = this._cloneAppObject(this.webapps[id]); aData.apps.push(app); tmp.push({ id: id }); - selfId = id; } - // check if it's a store. - let isStore = false; + this._readManifests(tmp, (function(aResult) { + for (let i = 0; i < aResult.length; i++) + aData.apps[i].manifest = aResult[i].manifest; + ppmm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData); + }).bind(this)); + }, + + getInstalled: function(aData) { + aData.apps = []; + let tmp = []; + let id = this._appId(aData.origin); + for (id in this.webapps) { - let app = this.webapps[id]; - if (app.installOrigin == aData.origin) { - isStore = true; - break; - } - } - - // add all the apps from this store - if (isStore) { - for (id in this.webapps) { - if (id == selfId) - continue; - let app = this._cloneAppObject(this.webapps[id]); - if (app.installOrigin == aData.origin) { - aData.apps.push(app); - tmp.push({ id: id }); - } + if (this.webapps[id].installOrigin == aData.origin) { + aData.apps.push(this._cloneAppObject(this.webapps[id])); + tmp.push({ id: id }); } } this._readManifests(tmp, (function(aResult) { for (let i = 0; i < aResult.length; i++) aData.apps[i].manifest = aResult[i].manifest; - this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData); + ppmm.sendAsyncMessage("Webapps:GetInstalled:Return:OK", aData); }).bind(this)); }, - denyEnumerate: function(aData) { - this.mm.sendAsyncMessage("Webapps:Enumerate:Return:KO", aData); - }, - - enumerateAll: function(aData) { + getAll: function(aData) { aData.apps = []; let tmp = []; @@ -322,7 +287,7 @@ let DOMApplicationRegistry = { this._readManifests(tmp, (function(aResult) { for (let i = 0; i < aResult.length; i++) aData.apps[i].manifest = aResult[i].manifest; - this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData); + ppmm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData); }).bind(this)); }, @@ -368,7 +333,7 @@ let DOMApplicationRegistry = { dir.remove(true); } catch (e) { } - this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin }); + ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin }); } else { if (!!this.webapps[record.id]) { this.webapps[record.id] = record.value; @@ -377,7 +342,7 @@ let DOMApplicationRegistry = { else { let data = { app: record.value }; this.confirmInstall(data, true); - this.mm.sendAsyncMessage("Webapps:Install:Return:OK", data); + ppmm.sendAsyncMessage("Webapps:Install:Return:OK", data); } } } diff --git a/dom/base/Webapps.manifest b/dom/base/Webapps.manifest index e1bf05ae57c7..8699961c5390 100644 --- a/dom/base/Webapps.manifest +++ b/dom/base/Webapps.manifest @@ -2,9 +2,3 @@ component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270} category JavaScript-navigator-property mozApps @mozilla.org/webapps;1 - -component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js -contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22} - -component {b4937718-11a3-400b-a69f-ab442a418569} Webapps.js -contract @mozilla.org/webapps/error;1 {b4937718-11a3-400b-a69f-ab442a418569} From 75e2707dce60e9be419b15c532eefe3fda0da508 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:41 -0800 Subject: [PATCH 57/88] Bug 704879 - (1/6) Refactor AutoCompletePopup to FormAssistPopup. r=lucasr --HG-- rename : mobile/android/base/AutoCompletePopup.java => mobile/android/base/FormAssistPopup.java --- ...AutoCompletePopup.java => FormAssistPopup.java} | 14 +++++++------- mobile/android/base/GeckoApp.java | 12 ++++++------ mobile/android/base/Makefile.in | 2 +- mobile/android/base/Tabs.java | 2 +- .../base/resources/layout-v11/gecko_app.xml | 14 +++++++------- mobile/android/base/resources/layout/gecko_app.xml | 14 +++++++------- mobile/android/base/ui/PanZoomController.java | 12 ++++++------ 7 files changed, 35 insertions(+), 35 deletions(-) rename mobile/android/base/{AutoCompletePopup.java => FormAssistPopup.java} (93%) diff --git a/mobile/android/base/AutoCompletePopup.java b/mobile/android/base/FormAssistPopup.java similarity index 93% rename from mobile/android/base/AutoCompletePopup.java rename to mobile/android/base/FormAssistPopup.java index a88be38159c5..13606db20dc5 100644 --- a/mobile/android/base/AutoCompletePopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -56,7 +56,7 @@ import android.widget.TextView; import org.json.JSONArray; import org.json.JSONException; -public class AutoCompletePopup extends ListView { +public class FormAssistPopup extends ListView { private Context mContext; private RelativeLayout.LayoutParams mLayout; @@ -65,14 +65,14 @@ public class AutoCompletePopup extends ListView { private Animation mAnimation; - private static final String LOGTAG = "AutoCompletePopup"; + private static final String LOGTAG = "FormAssistPopup"; private static int sMinWidth = 0; private static int sRowHeight = 0; - private static final int AUTOCOMPLETE_MIN_WIDTH_IN_DPI = 200; - private static final int AUTOCOMPLETE_ROW_HEIGHT_IN_DPI = 32; + private static final int POPUP_MIN_WIDTH_IN_DPI = 200; + private static final int POPUP_ROW_HEIGHT_IN_DPI = 32; - public AutoCompletePopup(Context context, AttributeSet attrs) { + public FormAssistPopup(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; @@ -136,8 +136,8 @@ public class AutoCompletePopup extends ListView { if (sMinWidth == 0) { DisplayMetrics metrics = new DisplayMetrics(); GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - sMinWidth = (int) (AUTOCOMPLETE_MIN_WIDTH_IN_DPI * metrics.density); - sRowHeight = (int) (AUTOCOMPLETE_ROW_HEIGHT_IN_DPI * metrics.density); + sMinWidth = (int) (POPUP_MIN_WIDTH_IN_DPI * metrics.density); + sRowHeight = (int) (POPUP_ROW_HEIGHT_IN_DPI * metrics.density); } // If the textbox is smaller than the screen-width, diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 783dce941c11..0a4a3908ab49 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -136,7 +136,7 @@ abstract public class GeckoApp public static BrowserToolbar mBrowserToolbar; public static DoorHangerPopup mDoorHangerPopup; - public static AutoCompletePopup mAutoCompletePopup; + public static FormAssistPopup mFormAssistPopup; public Favicons mFavicons; private Geocoder mGeocoder; @@ -981,7 +981,7 @@ abstract public class GeckoApp if (suggestions.length() == 0) { mMainHandler.post(new Runnable() { public void run() { - mAutoCompletePopup.hide(); + mFormAssistPopup.hide(); } }); } else { @@ -993,7 +993,7 @@ abstract public class GeckoApp InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (!imm.isFullscreenMode()) - mAutoCompletePopup.show(suggestions, rect, zoom); + mFormAssistPopup.show(suggestions, rect, zoom); } }); } @@ -1108,7 +1108,7 @@ abstract public class GeckoApp } public void run() { - mAutoCompletePopup.hide(); + mFormAssistPopup.hide(); if (mShow) { if (mAboutHomeContent == null) { mAboutHomeContent = (AboutHomeContent) findViewById(R.id.abouthome_content); @@ -1757,7 +1757,7 @@ abstract public class GeckoApp mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container); mDoorHangerPopup = new DoorHangerPopup(this); - mAutoCompletePopup = (AutoCompletePopup) findViewById(R.id.autocomplete_popup); + mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - UI almost up"); @@ -2211,7 +2211,7 @@ abstract public class GeckoApp if (mOrientation != newConfig.orientation) { mOrientation = newConfig.orientation; - mAutoCompletePopup.hide(); + mFormAssistPopup.hide(); refreshActionBar(); } } diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 468921bc354b..a7c9f102770d 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -63,7 +63,6 @@ SYNC_PP_RES_XML=res/xml/sync_syncadapter.xml res/xml/sync_options.xml FENNEC_JAVA_FILES = \ AboutHomeContent.java \ AlertNotification.java \ - AutoCompletePopup.java \ AwesomeBar.java \ AwesomeBarTabs.java \ BrowserToolbar.java \ @@ -77,6 +76,7 @@ FENNEC_JAVA_FILES = \ DoorHangerPopup.java \ Favicons.java \ FloatUtils.java \ + FormAssistPopup.java \ GeckoActionBar.java \ GeckoApp.java \ GeckoAppShell.java \ diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index c06f58974fb8..1626b59dcd71 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -129,7 +129,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { - GeckoApp.mAutoCompletePopup.hide(); + GeckoApp.mFormAssistPopup.hide(); // Do we need to do this check? if (isSelectedTab(tab)) { String url = tab.getURL(); diff --git a/mobile/android/base/resources/layout-v11/gecko_app.xml b/mobile/android/base/resources/layout-v11/gecko_app.xml index cac2df390ca6..92506744144e 100644 --- a/mobile/android/base/resources/layout-v11/gecko_app.xml +++ b/mobile/android/base/resources/layout-v11/gecko_app.xml @@ -13,13 +13,13 @@ android:layout_width="fill_parent" android:layout_height="fill_parent"/> - + - + Date: Tue, 6 Mar 2012 11:56:42 -0800 Subject: [PATCH 58/88] Bug 704879 - (2/6) Move FormAssist:AutoComplete message handling to FormAssistPopup. r=lucasr --- mobile/android/base/FormAssistPopup.java | 31 +++++++++++++++++++++++- mobile/android/base/GeckoApp.java | 23 ------------------ 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 13606db20dc5..a938739576f4 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -47,6 +47,7 @@ import android.util.DisplayMetrics; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.AdapterView; import android.widget.RelativeLayout; @@ -55,8 +56,9 @@ import android.widget.TextView; import org.json.JSONArray; import org.json.JSONException; +import org.json.JSONObject; -public class FormAssistPopup extends ListView { +public class FormAssistPopup extends ListView implements GeckoEventListener { private Context mContext; private RelativeLayout.LayoutParams mLayout; @@ -88,6 +90,33 @@ public class FormAssistPopup extends ListView { hide(); } }); + + GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this); + } + + public void handleMessage(String event, JSONObject message) { + try { + if (event.equals("FormAssist:AutoComplete")) { + final JSONArray suggestions = message.getJSONArray("suggestions"); + if (suggestions.length() == 0) { + hide(); + } else { + final JSONArray rect = message.getJSONArray("rect"); + final double zoom = message.getDouble("zoom"); + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + // Don't show autocomplete popup when using fullscreen VKB + InputMethodManager imm = + (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (!imm.isFullscreenMode()) + show(suggestions, rect, zoom); + } + }); + } + } + } catch (Exception e) { + Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); + } } public void show(JSONArray suggestions, JSONArray rect, double zoom) { diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 0a4a3908ab49..a9fb20554eb9 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -976,27 +976,6 @@ abstract public class GeckoApp mDOMFullScreen = true; } else if (event.equals("DOMFullScreen:Stop")) { mDOMFullScreen = false; - } else if (event.equals("FormAssist:AutoComplete")) { - final JSONArray suggestions = message.getJSONArray("suggestions"); - if (suggestions.length() == 0) { - mMainHandler.post(new Runnable() { - public void run() { - mFormAssistPopup.hide(); - } - }); - } else { - final JSONArray rect = message.getJSONArray("rect"); - final double zoom = message.getDouble("zoom"); - mMainHandler.post(new Runnable() { - public void run() { - // Don't show autocomplete popup when using fullscreen VKB - InputMethodManager imm = - (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (!imm.isFullscreenMode()) - mFormAssistPopup.show(suggestions, rect, zoom); - } - }); - } } else if (event.equals("Permissions:Data")) { String host = message.getString("host"); JSONArray permissions = message.getJSONArray("permissions"); @@ -1797,7 +1776,6 @@ abstract public class GeckoApp GeckoAppShell.registerGeckoEventListener("DOMFullScreen:Stop", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext); - GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); @@ -2170,7 +2148,6 @@ abstract public class GeckoApp GeckoAppShell.unregisterGeckoEventListener("Toast:Show", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext); - GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); From 69d179916e79dc08641e09524001966522a3e6bc Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:42 -0800 Subject: [PATCH 59/88] Bug 704879 - (3/6) Refactor FormAssistant autocomplete logic. r=lucasr --- mobile/android/chrome/content/browser.js | 89 +++++++++++++++--------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 2c6e4559eb91..9cb91d03e5eb 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2869,29 +2869,12 @@ var FormAssistant = { switch (aEvent.type) { case "input": let currentElement = aEvent.target; - if (!this._isAutocomplete(currentElement)) - break; - - // Keep track of input element so we can fill it in if the user - // selects an autocomplete suggestion - this._currentInputElement = currentElement; - let suggestions = this._getAutocompleteSuggestions(currentElement.value, currentElement); - - let rect = ElementTouchHelper.getBoundingContentRect(currentElement); - let viewport = BrowserApp.selectedTab.viewport; - - sendMessageToJava({ - gecko: { - type: "FormAssist:AutoComplete", - suggestions: suggestions, - rect: [rect.x - (viewport.x / viewport.zoom), rect.y - (viewport.y / viewport.zoom), rect.w, rect.h], - zoom: viewport.zoom - } - }); + this._showAutoCompleteSuggestions(currentElement); } }, - _isAutocomplete: function (aElement) { + // We only want to show autocomplete suggestions for certain elements + _isAutoComplete: function _isAutoComplete(aElement) { if (!(aElement instanceof HTMLInputElement) || (aElement.getAttribute("type") == "password") || (aElement.hasAttribute("autocomplete") && @@ -2901,25 +2884,65 @@ var FormAssistant = { return true; }, - /** Retrieve the autocomplete list from the autocomplete service for an element */ - _getAutocompleteSuggestions: function(aSearchString, aElement) { - let results = Cc["@mozilla.org/satchel/form-autocomplete;1"]. - getService(Ci.nsIFormAutoComplete). - autoCompleteSearch(aElement.name || aElement.id, aSearchString, aElement, null); + // Retrieves autocomplete suggestions for an element from the form autocomplete service. + _getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement) { + // Cache the form autocomplete service for future use + if (!this._formAutoCompleteService) + this._formAutoCompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"]. + getService(Ci.nsIFormAutoComplete); + let results = this._formAutoCompleteService.autoCompleteSearch(aElement.name || aElement.id, + aSearchString, aElement, null); let suggestions = []; - if (results.matchCount > 0) { - for (let i = 0; i < results.matchCount; i++) { - let value = results.getValueAt(i); - // Do not show the value if it is the current one in the input field - if (value == aSearchString) - continue; + for (let i = 0; i < results.matchCount; i++) { + let value = results.getValueAt(i); - suggestions.push(value); - } + // Do not show the value if it is the current one in the input field + if (value == aSearchString) + continue; + + suggestions.push(value); } return suggestions; + }, + + // Gets the element position data necessary for the Java UI to position + // the form assist popup. + _getElementPositionData: function _getElementPositionData(aElement) { + let rect = ElementTouchHelper.getBoundingContentRect(aElement); + let viewport = BrowserApp.selectedTab.viewport; + + return { rect: [rect.x - (viewport.x / viewport.zoom), + rect.y - (viewport.y / viewport.zoom), + rect.w, rect.h], + zoom: viewport.zoom } + }, + + // Retrieves autocomplete suggestions for an element from the form autocomplete service + // and sends the suggestions to the Java UI, along with element position data. + // Returns true if there are suggestions to show, false otherwise. + _showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement) { + if (!this._isAutoComplete(aElement)) + return false; + + let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + + let positionData = this._getElementPositionData(aElement); + sendMessageToJava({ + gecko: { + type: "FormAssist:AutoComplete", + suggestions: suggestions, + rect: positionData.rect, + zoom: positionData.zoom + } + }); + + // Keep track of input element so we can fill it in if the user + // selects an autocomplete suggestion + this._currentInputElement = aElement; + + return true; } }; From 2108689eca780da4c03bb6389030724699389b6c Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:43 -0800 Subject: [PATCH 60/88] Bug 704879 - (4/6) Make FormAssistant in charge of hiding an empty popup. r=lucasr --- mobile/android/base/FormAssistPopup.java | 35 +++++++++++++----------- mobile/android/chrome/content/browser.js | 22 ++++++++++++--- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index a938739576f4..688c348a581f 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -92,27 +92,30 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { }); GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this); + GeckoAppShell.registerGeckoEventListener("FormAssist:Hide", this); } public void handleMessage(String event, JSONObject message) { try { if (event.equals("FormAssist:AutoComplete")) { final JSONArray suggestions = message.getJSONArray("suggestions"); - if (suggestions.length() == 0) { - hide(); - } else { - final JSONArray rect = message.getJSONArray("rect"); - final double zoom = message.getDouble("zoom"); - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { - public void run() { - // Don't show autocomplete popup when using fullscreen VKB - InputMethodManager imm = - (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (!imm.isFullscreenMode()) - show(suggestions, rect, zoom); - } - }); - } + final JSONArray rect = message.getJSONArray("rect"); + final double zoom = message.getDouble("zoom"); + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + // Don't show autocomplete popup when using fullscreen // VKB + InputMethodManager imm = + (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (!imm.isFullscreenMode()) + show(suggestions, rect, zoom); + } + }); + } else if (event.equals("FormAssist:Hide")) { + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + hide(); + } + }); } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); @@ -211,7 +214,7 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { public void hide() { if (isShown()) { setVisibility(View.GONE); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Closed", null)); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 9cb91d03e5eb..37a7e267120e 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2838,14 +2838,14 @@ var FormAssistant = { init: function() { Services.obs.addObserver(this, "FormAssist:AutoComplete", false); - Services.obs.addObserver(this, "FormAssist:Closed", false); + Services.obs.addObserver(this, "FormAssist:Hidden", false); BrowserApp.deck.addEventListener("input", this, false); }, uninit: function() { Services.obs.removeObserver(this, "FormAssist:AutoComplete"); - Services.obs.removeObserver(this, "FormAssist:Closed"); + Services.obs.removeObserver(this, "FormAssist:Hidden"); }, observe: function(aSubject, aTopic, aData) { @@ -2859,7 +2859,7 @@ var FormAssistant = { this._currentInputElement.value = aData; break; - case "FormAssist:Closed": + case "FormAssist:Hidden": this._currentInputElement = null; break; } @@ -2869,7 +2869,11 @@ var FormAssistant = { switch (aEvent.type) { case "input": let currentElement = aEvent.target; - this._showAutoCompleteSuggestions(currentElement); + if (this._showAutoCompleteSuggestions(currentElement)) + break; + + // If we're not showing autocomplete suggestions, hide the form assist popup + this._hideFormAssistPopup(); } }, @@ -2928,6 +2932,10 @@ var FormAssistant = { let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + // Return false if there are no suggestions to show + if (!suggestions.length) + return false; + let positionData = this._getElementPositionData(aElement); sendMessageToJava({ gecko: { @@ -2943,6 +2951,12 @@ var FormAssistant = { this._currentInputElement = aElement; return true; + }, + + _hideFormAssistPopup: function _hideFormAssistPopup() { + sendMessageToJava({ + gecko: { type: "FormAssist:Hide" } + }); } }; From c8b477e6c236c5005254cfefd8a93c7ae570e08d Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:43 -0800 Subject: [PATCH 61/88] Bug 704879 - (4.5/6) Refactor message handling code into separate private messages. r=lucasr --- mobile/android/base/FormAssistPopup.java | 42 ++++++++++++++---------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 688c348a581f..01a14ab9cd0f 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -98,30 +98,38 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { public void handleMessage(String event, JSONObject message) { try { if (event.equals("FormAssist:AutoComplete")) { - final JSONArray suggestions = message.getJSONArray("suggestions"); - final JSONArray rect = message.getJSONArray("rect"); - final double zoom = message.getDouble("zoom"); - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { - public void run() { - // Don't show autocomplete popup when using fullscreen // VKB - InputMethodManager imm = - (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (!imm.isFullscreenMode()) - show(suggestions, rect, zoom); - } - }); + handleAutoCompleteMessage(message); } else if (event.equals("FormAssist:Hide")) { - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { - public void run() { - hide(); - } - }); + handleHideMessage(message); } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); } } + private void handleAutoCompleteMessage(JSONObject message) throws JSONException { + final JSONArray suggestions = message.getJSONArray("suggestions"); + final JSONArray rect = message.getJSONArray("rect"); + final double zoom = message.getDouble("zoom"); + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + // Don't show autocomplete popup when using fullscreen VKB + InputMethodManager imm = + (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (!imm.isFullscreenMode()) + show(suggestions, rect, zoom); + } + }); + } + + private void handleHideMessage(JSONObject message) { + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + hide(); + } + }); + } + public void show(JSONArray suggestions, JSONArray rect, double zoom) { ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); for (int i = 0; i < suggestions.length(); i++) { From 3fe9ff3ff7291beb0c80e9fcb1217007ed66a149 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:44 -0800 Subject: [PATCH 62/88] Bug 704879 - (5/6) Add form validation messages. r=lucasr --- mobile/android/base/FormAssistPopup.java | 73 ++++++++++++++++++------ mobile/android/chrome/content/browser.js | 72 +++++++++++++++++++++++ 2 files changed, 129 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 01a14ab9cd0f..e4ce2f220112 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -74,6 +74,11 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { private static final int POPUP_MIN_WIDTH_IN_DPI = 200; private static final int POPUP_ROW_HEIGHT_IN_DPI = 32; + private static enum PopupType { NONE, AUTOCOMPLETE, VALIDATION }; + + // Keep track of the type of popup we're currently showing + private PopupType mTypeShowing = PopupType.NONE; + public FormAssistPopup(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; @@ -85,13 +90,17 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parentView, View view, int position, long id) { - String value = ((TextView) view).getText().toString(); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); - hide(); + if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) { + TextView textView = (TextView) view; + String value = textView.getText().toString(); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); + hide(); + } } }); GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this); + GeckoAppShell.registerGeckoEventListener("FormAssist:ValidationMessage", this); GeckoAppShell.registerGeckoEventListener("FormAssist:Hide", this); } @@ -99,6 +108,8 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { try { if (event.equals("FormAssist:AutoComplete")) { handleAutoCompleteMessage(message); + } else if (event.equals("FormAssist:ValidationMessage")) { + handleValidationMessage(message); } else if (event.equals("FormAssist:Hide")) { handleHideMessage(message); } @@ -113,15 +124,22 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { final double zoom = message.getDouble("zoom"); GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { - // Don't show autocomplete popup when using fullscreen VKB - InputMethodManager imm = - (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (!imm.isFullscreenMode()) - show(suggestions, rect, zoom); + showAutoCompleteSuggestions(suggestions, rect, zoom); } }); } + private void handleValidationMessage(JSONObject message) throws JSONException { + final String validationMessage = message.getString("validationMessage"); + final JSONArray rect = message.getJSONArray("rect"); + final double zoom = message.getDouble("zoom"); + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + showValidationMessage(validationMessage, rect, zoom); + } + }); + } + private void handleHideMessage(JSONObject message) { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { @@ -130,18 +148,38 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { }); } - public void show(JSONArray suggestions, JSONArray rect, double zoom) { + private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); - for (int i = 0; i < suggestions.length(); i++) { - try { + try { + for (int i = 0; i < suggestions.length(); i++) adapter.add(suggestions.get(i).toString()); - } catch (JSONException e) { - Log.i(LOGTAG, "JSONException: " + e); - } + } catch (JSONException e) { + Log.e(LOGTAG, "JSONException: " + e); } - setAdapter(adapter); + if (positionAndShowPopup(rect, zoom)) + mTypeShowing = PopupType.AUTOCOMPLETE; + } + + // TODO: style the validation message popup differently (bug 731654) + private void showValidationMessage(String validationMessage, JSONArray rect, double zoom) { + ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); + adapter.add(validationMessage); + setAdapter(adapter); + + if (positionAndShowPopup(rect, zoom)) + mTypeShowing = PopupType.VALIDATION; + } + + // Returns true if the popup is successfully shown, false otherwise + public boolean positionAndShowPopup(JSONArray rect, double zoom) { + // Don't show the form assist popup when using fullscreen VKB + InputMethodManager imm = + (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm.isFullscreenMode()) + return false; + if (!isShown()) { setVisibility(View.VISIBLE); startAnimation(mAnimation); @@ -193,7 +231,7 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { listLeft = (int) (viewport.width - listWidth); } - listHeight = sRowHeight * adapter.getCount(); + listHeight = sRowHeight * getAdapter().getCount(); // The text box doesnt fit below if ((listTop + listHeight) > viewport.height) { @@ -217,11 +255,14 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { mLayout.setMargins(listLeft, listTop, 0, 0); setLayoutParams(mLayout); requestLayout(); + + return true; } public void hide() { if (isShown()) { setVisibility(View.GONE); + mTypeShowing = PopupType.NONE; GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 37a7e267120e..410a2ed4b447 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2832,20 +2832,31 @@ var ErrorPageEventHandler = { }; var FormAssistant = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]), + // Used to keep track of the element that corresponds to the current // autocomplete suggestions _currentInputElement: null, + // Keep track of whether or not an invalid form has been submitted + _invalidSubmit: false, + init: function() { Services.obs.addObserver(this, "FormAssist:AutoComplete", false); Services.obs.addObserver(this, "FormAssist:Hidden", false); + Services.obs.addObserver(this, "invalidformsubmit", false); BrowserApp.deck.addEventListener("input", this, false); + BrowserApp.deck.addEventListener("pageshow", this, false); }, uninit: function() { Services.obs.removeObserver(this, "FormAssist:AutoComplete"); Services.obs.removeObserver(this, "FormAssist:Hidden"); + Services.obs.removeObserver(this, "invalidformsubmit"); + + BrowserApp.deck.removeEventListener("input", this); + BrowserApp.deck.removeEventListener("pageshow", this); }, observe: function(aSubject, aTopic, aData) { @@ -2865,15 +2876,43 @@ var FormAssistant = { } }, + notifyInvalidSubmit: function notifyInvalidSubmit(aFormElement, aInvalidElements) { + if (!aInvalidElements.length) + return; + + // Ignore this notificaiton if the current tab doesn't contain the invalid form + if (BrowserApp.selectedBrowser.contentDocument != + aFormElement.ownerDocument.defaultView.top.document) + return; + + this._invalidSubmit = true; + + let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports); + if (this._showValidationMessage(currentElement)) + currentElement.focus(); + }, + handleEvent: function(aEvent) { switch (aEvent.type) { case "input": let currentElement = aEvent.target; + + // Since we can only show one popup at a time, prioritze autocomplete + // suggestions over a form validation message if (this._showAutoCompleteSuggestions(currentElement)) break; + if (this._showValidationMessage(currentElement)) + break; // If we're not showing autocomplete suggestions, hide the form assist popup this._hideFormAssistPopup(); + break; + + // Reset invalid submit state on each pageshow + case "pageshow": + let target = aEvent.originalTarget; + if (target == content.document || target.ownerDocument == content.document) + this._invalidSubmit = false; } }, @@ -2953,6 +2992,39 @@ var FormAssistant = { return true; }, + // Only show a validation message if the user submitted an invalid form, + // there's a non-empty message string, and the element is the correct type + _isValidateable: function _isValidateable(aElement) { + if (!this._invalidSubmit || + !aElement.validationMessage || + !(aElement instanceof HTMLInputElement || + aElement instanceof HTMLTextAreaElement || + aElement instanceof HTMLSelectElement || + aElement instanceof HTMLButtonElement)) + return false; + + return true; + }, + + // Sends a validation message and position data for an element to the Java UI. + // Returns true if there's a validation message to show, false otherwise. + _showValidationMessage: function _sendValidationMessage(aElement) { + if (!this._isValidateable(aElement)) + return false; + + let positionData = this._getElementPositionData(aElement); + sendMessageToJava({ + gecko: { + type: "FormAssist:ValidationMessage", + validationMessage: aElement.validationMessage, + rect: positionData.rect, + zoom: positionData.zoom + } + }); + + return true; + }, + _hideFormAssistPopup: function _hideFormAssistPopup() { sendMessageToJava({ gecko: { type: "FormAssist:Hide" } From 202b1975a4b89c472180be6428d08616fc129b8d Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 11:56:44 -0800 Subject: [PATCH 63/88] Bug 704879 - (6/6) Show form validation message when invalid element is focused. r=lucasr --- mobile/android/chrome/content/browser.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 410a2ed4b447..4f3e8cf43e08 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2846,6 +2846,8 @@ var FormAssistant = { Services.obs.addObserver(this, "FormAssist:Hidden", false); Services.obs.addObserver(this, "invalidformsubmit", false); + // We need to use a capturing listener for focus events + BrowserApp.deck.addEventListener("focus", this, true); BrowserApp.deck.addEventListener("input", this, false); BrowserApp.deck.addEventListener("pageshow", this, false); }, @@ -2855,6 +2857,7 @@ var FormAssistant = { Services.obs.removeObserver(this, "FormAssist:Hidden"); Services.obs.removeObserver(this, "invalidformsubmit"); + BrowserApp.deck.removeEventListener("focus", this); BrowserApp.deck.removeEventListener("input", this); BrowserApp.deck.removeEventListener("pageshow", this); }, @@ -2887,15 +2890,20 @@ var FormAssistant = { this._invalidSubmit = true; + // Our focus listener will show the element's validation message let currentElement = aInvalidElements.queryElementAt(0, Ci.nsISupports); - if (this._showValidationMessage(currentElement)) - currentElement.focus(); + currentElement.focus(); }, handleEvent: function(aEvent) { switch (aEvent.type) { - case "input": + case "focus": let currentElement = aEvent.target; + this._showValidationMessage(currentElement); + break; + + case "input": + currentElement = aEvent.target; // Since we can only show one popup at a time, prioritze autocomplete // suggestions over a form validation message From b5b19edd39a33195ecdf8cbbefc839b68e4799d3 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Tue, 6 Mar 2012 15:08:45 -0500 Subject: [PATCH 64/88] Bug 731637 - robocop on tegras hit OOM for PixelTest due to getPaintedSurface() array creation. r=kats --- build/mobile/robocop/Driver.java.in | 2 +- .../mobile/robocop/FennecNativeDriver.java.in | 36 ++++++++--- build/mobile/robocop/Makefile.in | 1 + build/mobile/robocop/PaintedSurface.java.in | 62 +++++++++++++++++++ mobile/android/base/tests/PixelTest.java.in | 20 +++--- .../base/tests/testAxisLocking.java.in | 4 +- .../base/tests/testFlingCorrectness.java.in | 2 +- .../android/base/tests/testOverscroll.java.in | 2 +- .../base/tests/testPanCorrectness.java.in | 2 +- .../android/base/tests/test_bug720538.java.in | 16 ++--- 10 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 build/mobile/robocop/PaintedSurface.java.in diff --git a/build/mobile/robocop/Driver.java.in b/build/mobile/robocop/Driver.java.in index e358ec89a239..b05dedcda381 100644 --- a/build/mobile/robocop/Driver.java.in +++ b/build/mobile/robocop/Driver.java.in @@ -77,5 +77,5 @@ public interface Driver { * @return A 2-D array of pixels (indexed by y, then x). The pixels * are in ARGB-8888 format. */ - int[][] getPaintedSurface(); + PaintedSurface getPaintedSurface(); } diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in index eb6d048dcb54..93e65b095cb4 100644 --- a/build/mobile/robocop/FennecNativeDriver.java.in +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -48,6 +48,8 @@ import java.io.IOException; import java.nio.IntBuffer; import java.util.HashMap; import java.util.List; +import java.io.FileOutputStream; +import java.io.DataOutputStream; import java.lang.Class; import java.lang.reflect.InvocationTargetException; @@ -291,7 +293,7 @@ public class FennecNativeDriver implements Driver { return null; } - public int[][] getPaintedSurface() { + public PaintedSurface getPaintedSurface() { GLSurfaceView view = getSurfaceView(); if (view == null) { return null; @@ -309,14 +311,34 @@ public class FennecNativeDriver implements Driver { int w = view.getWidth(); int h = view.getHeight(); pixelBuffer.position(0); - int[][] pixels = new int[h][w]; - for (int y = h - 1; y >= 0; y--) { - for (int x = 0; x < w; x++) { - int agbr = pixelBuffer.get(); - pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000); + String mapFile = "/mnt/sdcard/pixels.map"; + + FileOutputStream fos = null; + DataOutputStream dos = null; + try { + fos = new FileOutputStream(mapFile); + dos = new DataOutputStream(fos); + + for (int y = h - 1; y >= 0; y--) { + for (int x = 0; x < w; x++) { + int agbr = pixelBuffer.get(); + dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000)); + } + } + return new PaintedSurface(mapFile, w, h); + } catch (IOException e) { + throw new RoboCopException("exception with pixel writer on file: " + mapFile); + } finally { + try { + if (dos != null && fos != null) { + dos.flush(); + dos.close(); + fos.close(); + } + } catch (IOException e) { + throw new RoboCopException("exception closing pixel writer on file: " + mapFile); } } - return pixels; } public int mHeight=0; diff --git a/build/mobile/robocop/Makefile.in b/build/mobile/robocop/Makefile.in index 65ab91fe300f..47e3aca33d8a 100644 --- a/build/mobile/robocop/Makefile.in +++ b/build/mobile/robocop/Makefile.in @@ -61,6 +61,7 @@ _JAVA_HARNESS = \ FennecNativeDriver.java \ FennecNativeElement.java \ RoboCopException.java \ + PaintedSurface.java \ $(NULL) _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in)) diff --git a/build/mobile/robocop/PaintedSurface.java.in b/build/mobile/robocop/PaintedSurface.java.in new file mode 100644 index 000000000000..98bc0d58bef4 --- /dev/null +++ b/build/mobile/robocop/PaintedSurface.java.in @@ -0,0 +1,62 @@ +#filter substitution +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package @ANDROID_PACKAGE_NAME@; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; + +public class PaintedSurface { + private String mFileName = null; + private int mWidth = -1; + private int mHeight = -1; + private MappedByteBuffer mPixelBuffer = null; + + public PaintedSurface(String filename, int width, int height) { + mFileName = filename; + mWidth = width; + mHeight = height; + + try { + File f = new File(filename); + int pixelSize = (int)f.length(); + + FileInputStream pixelFile = new FileInputStream(filename); + mPixelBuffer = pixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize); + } catch (java.io.FileNotFoundException e) { + e.printStackTrace(); + } catch (java.io.IOException e) { + e.printStackTrace(); + } + } + + public final int getPixelAt(int x, int y) { + if (mPixelBuffer == null) { + throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer"); + } + + if (x >= mWidth || x < 0) { + throw new RoboCopException("Trying to access PaintedSurface with invalid x value"); + } + + if (y >= mHeight || y < 0) { + throw new RoboCopException("Trying to access PaintedSurface with invalid y value"); + } + + // The rows are reversed so row 0 is at the end and we start with the last row. + // This is why we do mHeight-y; + int index = (x + ((mHeight - y - 1) * mWidth)) * 4; + int b1 = mPixelBuffer.get(index) & 0xFF; + int b2 = mPixelBuffer.get(index + 1) & 0xFF; + int b3 = mPixelBuffer.get(index + 2) & 0xFF; + int b4 = mPixelBuffer.get(index + 3) & 0xFF; + int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0); + return value; + } +} + diff --git a/mobile/android/base/tests/PixelTest.java.in b/mobile/android/base/tests/PixelTest.java.in index 0f88d3119ded..62bab34b0911 100644 --- a/mobile/android/base/tests/PixelTest.java.in +++ b/mobile/android/base/tests/PixelTest.java.in @@ -6,19 +6,19 @@ import @ANDROID_PACKAGE_NAME@.*; class PixelTest extends BaseTest { private static final long PAINT_CLEAR_DELAY = 500; // milliseconds - protected final int[][] loadAndPaint(String url) { + protected final PaintedSurface loadAndPaint(String url) { Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); loadUrl(url); paintExpecter.blockUntilClear(PAINT_CLEAR_DELAY); return mDriver.getPaintedSurface(); } - protected final int[][] waitForPaint(Actions.RepeatedEventExpecter expecter) { + protected final PaintedSurface waitForPaint(Actions.RepeatedEventExpecter expecter) { expecter.blockUntilClear(PAINT_CLEAR_DELAY); return mDriver.getPaintedSurface(); } - protected final int[][] waitWithNoPaint(Actions.RepeatedEventExpecter expecter) { + protected final PaintedSurface waitWithNoPaint(Actions.RepeatedEventExpecter expecter) { try { Thread.sleep(PAINT_CLEAR_DELAY); } catch (InterruptedException ie) { @@ -41,15 +41,15 @@ class PixelTest extends BaseTest { /** * Checks the top-left corner of the visible area of the page is at (x,y) of robocop_boxes.html. */ - protected final void checkScrollWithBoxes(int[][] painted, int x, int y) { + protected final void checkScrollWithBoxes(PaintedSurface painted, int x, int y) { int[] color = getBoxColorAt(x, y); - mAsserter.ispixel(painted[0][0], color[0], color[1], color[2], "Pixel at 0, 0"); + mAsserter.ispixel(painted.getPixelAt(0, 0), color[0], color[1], color[2], "Pixel at 0, 0"); color = getBoxColorAt(x + 100, y); - mAsserter.ispixel(painted[0][100], color[0], color[1], color[2], "Pixel at 100, 0"); + mAsserter.ispixel(painted.getPixelAt(100, 0), color[0], color[1], color[2], "Pixel at 100, 0"); color = getBoxColorAt(x, y + 100); - mAsserter.ispixel(painted[100][0], color[0], color[1], color[2], "Pixel at 0, 100"); + mAsserter.ispixel(painted.getPixelAt(0, 100), color[0], color[1], color[2], "Pixel at 0, 100"); color = getBoxColorAt(x + 100, y + 100); - mAsserter.ispixel(painted[100][100], color[0], color[1], color[2], "Pixel at 100, 100"); + mAsserter.ispixel(painted.getPixelAt(100, 100), color[0], color[1], color[2], "Pixel at 100, 100"); } /** @@ -57,8 +57,8 @@ class PixelTest extends BaseTest { * @param url URL of the robocop_boxes.html file. * @return The painted surface after rendering the file. */ - protected final int[][] loadAndVerifyBoxes(String url) { - int[][] painted = loadAndPaint(url); + protected final PaintedSurface loadAndVerifyBoxes(String url) { + PaintedSurface painted = loadAndPaint(url); checkScrollWithBoxes(painted, 0, 0); return painted; } diff --git a/mobile/android/base/tests/testAxisLocking.java.in b/mobile/android/base/tests/testAxisLocking.java.in index cf1ddc61453a..a8c54d09d1a9 100644 --- a/mobile/android/base/tests/testAxisLocking.java.in +++ b/mobile/android/base/tests/testAxisLocking.java.in @@ -28,12 +28,12 @@ public class testAxisLocking extends PixelTest { // axis locking prevents any horizontal scrolling Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); meh.dragSync(20, 150, 10, 50); - int[][] painted = waitForPaint(paintExpecter); + PaintedSurface painted = waitForPaint(paintExpecter); checkScrollWithBoxes(painted, 0, 100); // since checkScrollWithBoxes only checks 4 points, it may not pick up a // sub-100 pixel horizontal shift. so we check another point manually to make sure. int[] color = getBoxColorAt(0, 100); - mAsserter.ispixel(painted[0][99], color[0], color[1], color[2], "Pixel at 99, 0 indicates no horizontal scroll"); + mAsserter.ispixel(painted.getPixelAt(99, 0), color[0], color[1], color[2], "Pixel at 99, 0 indicates no horizontal scroll"); // now drag at a 45-degree angle to ensure we break the axis lock, and // verify that we have both horizontal and vertical scrolling diff --git a/mobile/android/base/tests/testFlingCorrectness.java.in b/mobile/android/base/tests/testFlingCorrectness.java.in index 5d2ff67eb65d..8cbfcd26f7f6 100644 --- a/mobile/android/base/tests/testFlingCorrectness.java.in +++ b/mobile/android/base/tests/testFlingCorrectness.java.in @@ -27,7 +27,7 @@ public class testFlingCorrectness extends PixelTest { Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); meh.dragSync(10, 150, 10, 50); meh.dragSync(10, 150, 10, 50); - int[][] painted = waitForPaint(paintExpecter); + PaintedSurface painted = waitForPaint(paintExpecter); checkScrollWithBoxes(painted, 0, 200); // now fling page downwards using a 100-pixel drag but a velocity of 15px/sec, so that diff --git a/mobile/android/base/tests/testOverscroll.java.in b/mobile/android/base/tests/testOverscroll.java.in index 205d06531b88..349312fcb4c8 100644 --- a/mobile/android/base/tests/testOverscroll.java.in +++ b/mobile/android/base/tests/testOverscroll.java.in @@ -28,7 +28,7 @@ public class testOverscroll extends PixelTest { // and back this should NOT trigger a gecko-paint Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); meh.dragSync(10, 50, 10, 150); - int[][] painted = waitWithNoPaint(paintExpecter); + PaintedSurface painted = waitWithNoPaint(paintExpecter); checkScrollWithBoxes(painted, 0, 0); // drag page rightwards to go into overscroll on the left. let it bounce and verify. diff --git a/mobile/android/base/tests/testPanCorrectness.java.in b/mobile/android/base/tests/testPanCorrectness.java.in index 131c82c45084..8657069b6944 100644 --- a/mobile/android/base/tests/testPanCorrectness.java.in +++ b/mobile/android/base/tests/testPanCorrectness.java.in @@ -25,7 +25,7 @@ public class testPanCorrectness extends PixelTest { // drag page upwards by 100 pixels Actions.RepeatedEventExpecter paintExpecter = mActions.expectPaint(); meh.dragSync(10, 150, 10, 50); - int[][] painted = waitForPaint(paintExpecter); + PaintedSurface painted = waitForPaint(paintExpecter); checkScrollWithBoxes(painted, 0, 100); // drag page leftwards by 100 pixels diff --git a/mobile/android/base/tests/test_bug720538.java.in b/mobile/android/base/tests/test_bug720538.java.in index f02976ec582e..3ef959089800 100644 --- a/mobile/android/base/tests/test_bug720538.java.in +++ b/mobile/android/base/tests/test_bug720538.java.in @@ -24,10 +24,10 @@ public class test_bug720538 extends PixelTest { * the gray shades of the checkerboard. */ - int[][] painted = loadAndPaint(url); + PaintedSurface painted = loadAndPaint(url); // first we check that the point we want to double-tap (100, 100) is blue, indicating it's inside the iframe - mAsserter.ispixel(painted[100][100], 0, 0, 0xFF, "Ensuring double-tap point is in the iframe"); + mAsserter.ispixel(painted.getPixelAt(100, 100), 0, 0, 0xFF, "Ensuring double-tap point is in the iframe"); // do the double tap and wait for the double-tap animation to finish. we assume the animation is done // when we find a 500ms period with no paint events that occurs after at least one paint event. @@ -39,10 +39,10 @@ public class test_bug720538 extends PixelTest { // check a few points to ensure that we did a good zoom-to-block on the iframe. this checks that // the background color is visible on the left and right edges of the viewport, but the iframe is // visible in between those edges - mAsserter.ispixel(painted[100][0], 0, 0x80, 0, "Checking page background to the left of the iframe"); - mAsserter.ispixel(painted[100][50], 0, 0, 0xFF, "Checking for iframe a few pixels from the left edge"); - mAsserter.ispixel(painted[100][mDriver.getGeckoWidth() - 51], 0, 0, 0xFF, "Checking for iframe a few pixels from the right edge"); - mAsserter.ispixel(painted[100][mDriver.getGeckoWidth() - 1], 0, 0x80, 0, "Checking page background the right of the iframe"); + mAsserter.ispixel(painted.getPixelAt(0, 100), 0, 0x80, 0, "Checking page background to the left of the iframe"); + mAsserter.ispixel(painted.getPixelAt(50, 100), 0, 0, 0xFF, "Checking for iframe a few pixels from the left edge"); + mAsserter.ispixel(painted.getPixelAt(mDriver.getGeckoWidth() - 51, 100), 0, 0, 0xFF, "Checking for iframe a few pixels from the right edge"); + mAsserter.ispixel(painted.getPixelAt(mDriver.getGeckoWidth() - 1, 100), 0, 0x80, 0, "Checking page background the right of the iframe"); // now we do double-tap again to zoom out and wait for the animation to finish, as before paintExpecter = mActions.expectPaint(); @@ -54,9 +54,9 @@ public class test_bug720538 extends PixelTest { // the last row because the last row is subject to rounding and clipping errors for (int y = 2; y < 10; y++) { for (int x = 0; x < 10; x++) { - mAsserter.dumpLog("Pixel at " + x + ", " + (mDriver.getGeckoHeight() - y) + ": " + Integer.toHexString(painted[mDriver.getGeckoHeight() - y][x])); + mAsserter.dumpLog("Pixel at " + x + ", " + (mDriver.getGeckoHeight() - y) + ": " + Integer.toHexString(painted.getPixelAt(x, mDriver.getGeckoHeight() - y))); } } - mAsserter.ispixel(painted[mDriver.getGeckoHeight() - 2][0], 0, 0x80, 0, "Checking bottom-left corner of viewport"); + mAsserter.ispixel(painted.getPixelAt(0, mDriver.getGeckoHeight() - 2), 0, 0x80, 0, "Checking bottom-left corner of viewport"); } } From 2e2902f6b9d05696c84de14622f39ed27a11940e Mon Sep 17 00:00:00 2001 From: Malen Sok Date: Tue, 6 Mar 2012 15:42:53 -0500 Subject: [PATCH 65/88] Bug 722163 - Convert JumpListBuilder to use LazyIdleThread. r=khuey --- widget/windows/JumpListBuilder.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/widget/windows/JumpListBuilder.cpp b/widget/windows/JumpListBuilder.cpp index 52b5f0840953..6894402bf7c9 100644 --- a/widget/windows/JumpListBuilder.cpp +++ b/widget/windows/JumpListBuilder.cpp @@ -56,6 +56,10 @@ #include "nsStringStream.h" #include "nsNetUtil.h" #include "nsThreadUtils.h" +#include "mozilla/LazyIdleThread.h" + +// The amount of time, in milliseconds, that our IO thread will stay alive after the last event it processes. +#define DEFAULT_THREAD_TIMEOUT_MS 30000 namespace mozilla { namespace widget { @@ -85,7 +89,8 @@ JumpListBuilder::JumpListBuilder() : CoCreateInstance(CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, IID_ICustomDestinationList, getter_AddRefs(mJumpListMgr)); - NS_NewThread(getter_AddRefs(mIOThread)); + // Make a lazy thread for any IO + mIOThread = new LazyIdleThread(DEFAULT_THREAD_TIMEOUT_MS, LazyIdleThread::ManualShutdown); Preferences::AddStrongObserver(this, kPrefTaskbarEnabled); } From 4cafa7113845b3a30916ca6074dde6cd68398371 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Tue, 6 Mar 2012 13:28:25 -0800 Subject: [PATCH 66/88] Back out a2d511a124d7 (bug 664163) for causing failures in test_traceable_channel_wrap.js --- netwerk/protocol/http/HttpChannelChild.cpp | 35 +++++++++++++++++++-- netwerk/protocol/http/HttpChannelChild.h | 4 +++ netwerk/test/unit/test_traceable_channel.js | 24 ++++++-------- 3 files changed, 46 insertions(+), 17 deletions(-) diff --git a/netwerk/protocol/http/HttpChannelChild.cpp b/netwerk/protocol/http/HttpChannelChild.cpp index dcaf4dcc2b03..4df4d51f61a5 100644 --- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -291,8 +291,6 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, mCacheEntryAvailable = cacheEntryAvailable; mCacheExpirationTime = cacheExpirationTime; mCachedCharset = cachedCharset; - mSelfAddr = selfAddr; - mPeerAddr = peerAddr; AutoEventEnqueuer ensureSerialDispatch(mEventQ); @@ -315,6 +313,9 @@ HttpChannelChild::OnStartRequest(const nsHttpResponseHead& responseHead, rv = ApplyContentConversions(); if (NS_FAILED(rv)) Cancel(rv); + + mSelfAddr = selfAddr; + mPeerAddr = peerAddr; } class TransportAndDataEvent : public ChannelEvent @@ -1112,6 +1113,36 @@ HttpChannelChild::SetupFallbackChannel(const char *aFallbackKey) DROP_DEAD(); } +// The next four _should_ be implemented, but we need to figure out how +// to transfer the data from the chrome process first. + +NS_IMETHODIMP +HttpChannelChild::GetRemoteAddress(nsACString & _result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetRemotePort(PRInt32 * _result) +{ + NS_ENSURE_ARG_POINTER(_result); + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetLocalAddress(nsACString & _result) +{ + return NS_ERROR_NOT_AVAILABLE; +} + +NS_IMETHODIMP +HttpChannelChild::GetLocalPort(PRInt32 * _result) +{ + NS_ENSURE_ARG_POINTER(_result); + return NS_ERROR_NOT_AVAILABLE; +} + + //----------------------------------------------------------------------------- // HttpChannelChild::nsICacheInfoChannel //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/HttpChannelChild.h b/netwerk/protocol/http/HttpChannelChild.h index f9f7c2e0003a..43617ef5589c 100644 --- a/netwerk/protocol/http/HttpChannelChild.h +++ b/netwerk/protocol/http/HttpChannelChild.h @@ -106,6 +106,10 @@ public: bool aMerge); // nsIHttpChannelInternal NS_IMETHOD SetupFallbackChannel(const char *aFallbackKey); + NS_IMETHOD GetLocalAddress(nsACString& addr); + NS_IMETHOD GetLocalPort(PRInt32* port); + NS_IMETHOD GetRemoteAddress(nsACString& addr); + NS_IMETHOD GetRemotePort(PRInt32* port); // nsISupportsPriority NS_IMETHOD SetPriority(PRInt32 value); // nsIResumableChannel diff --git a/netwerk/test/unit/test_traceable_channel.js b/netwerk/test/unit/test_traceable_channel.js index 8fb562ea57fa..a62b8a2c92b3 100644 --- a/netwerk/test/unit/test_traceable_channel.js +++ b/netwerk/test/unit/test_traceable_channel.js @@ -22,18 +22,14 @@ TracingListener.prototype = { request.QueryInterface(Components.interfaces.nsIHttpChannelInternal); - try { - do_check_eq(request.localAddress, "127.0.0.1"); - do_check_eq(request.localPort > 0, true); - do_check_neq(request.localPort, 4444); - do_check_eq(request.remoteAddress, "127.0.0.1"); - do_check_eq(request.remotePort, 4444); - } catch(e) { - do_throw("failed to get local/remote socket info"); - } - - request.QueryInterface(Components.interfaces.nsISupportsPriority); - request.priority = Ci.nsISupportsPriority.PRIORITY_LOW; +// local/remote addresses broken in e10s: disable for now +/* + do_check_eq(request.localAddress, "127.0.0.1"); + do_check_eq(request.localPort > 0, true); + do_check_neq(request.localPort, 4444); + do_check_eq(request.remoteAddress, "127.0.0.1"); + do_check_eq(request.remotePort, 4444); +*/ // Make sure listener can't be replaced after OnStartRequest was called. request.QueryInterface(Components.interfaces.nsITraceableChannel); @@ -142,10 +138,8 @@ function channel_finished(request, input, ctx) { httpserver.stop(do_test_finished); } -// needs to be global or it'll go out of scope before it observes request -var observer = new HttpResponseExaminer(); - function run_test() { + var observer = new HttpResponseExaminer(); observer.register(); httpserver = new nsHttpServer(); From 62e481793a42682458ba130f833bf6118e361e0b Mon Sep 17 00:00:00 2001 From: Stephen Perry Date: Tue, 6 Mar 2012 16:44:18 -0500 Subject: [PATCH 67/88] Bug 666664 - Patch to fix warnings in gfx/thebes/gfxContext.cpp. r=BenWa --- gfx/thebes/gfxContext.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gfx/thebes/gfxContext.cpp b/gfx/thebes/gfxContext.cpp index 7b2661e26c40..97e8d2dc9b3e 100644 --- a/gfx/thebes/gfxContext.cpp +++ b/gfx/thebes/gfxContext.cpp @@ -96,8 +96,8 @@ private: }; gfxContext::gfxContext(gfxASurface *surface) - : mSurface(surface) - , mRefCairo(NULL) + : mRefCairo(NULL) + , mSurface(surface) { MOZ_COUNT_CTOR(gfxContext); @@ -1266,8 +1266,6 @@ gfxContext::ClipContainsRect(const gfxRect& aRect) } } - bool result = true; - // Since we always return false when the clip list contains a // non-rectangular clip or a non-rectilinear transform, our 'total' clip // is always a rectangle if we hit the end of this function. From 4b1f919fc38acc28264ce8fa57074bd2e3e5a077 Mon Sep 17 00:00:00 2001 From: Stephen Perry Date: Tue, 6 Mar 2012 16:44:23 -0500 Subject: [PATCH 68/88] Bug 666664 - Patch to fix warnings in gfx/thebes/gfxUserFontSet.cpp. r=BenWa --- gfx/thebes/gfxUserFontSet.cpp | 8 +++++--- gfx/thebes/gfxUserFontSet.h | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/gfx/thebes/gfxUserFontSet.cpp b/gfx/thebes/gfxUserFontSet.cpp index 13563eb1f97d..dba646a706c1 100644 --- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -402,9 +402,11 @@ StoreUserFontData(gfxFontEntry* aFontEntry, gfxProxyFontEntry* aProxy, } } -static void -CopyWOFFMetadata(const PRUint8* aFontData, PRUint32 aLength, - nsTArray* aMetadata, PRUint32* aMetaOrigLen) +void +gfxUserFontSet::CopyWOFFMetadata(const PRUint8* aFontData, + PRUint32 aLength, + nsTArray* aMetadata, + PRUint32* aMetaOrigLen) { // This function may be called with arbitrary, unvalidated "font" data // from @font-face, so it needs to be careful to bounds-check, etc., diff --git a/gfx/thebes/gfxUserFontSet.h b/gfx/thebes/gfxUserFontSet.h index 376aa2a49349..f1b0ee60dd23 100644 --- a/gfx/thebes/gfxUserFontSet.h +++ b/gfx/thebes/gfxUserFontSet.h @@ -281,6 +281,12 @@ protected: PRUint64 mGeneration; static PRLogModuleInfo *sUserFontsLog; + +private: + static void CopyWOFFMetadata(const PRUint8* aFontData, + PRUint32 aLength, + nsTArray* aMetadata, + PRUint32* aMetaOrigLen); }; // acts a placeholder until the real font is downloaded From 0050396637e11a882d0b3c7cc588509f3457bce8 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 13:56:16 -0800 Subject: [PATCH 69/88] Bug 628616 - Make sure suggestions from are shown in Firefox Mobile UI. r=mbrubeck --- mobile/android/base/FormAssistPopup.java | 60 ++++++++++++++++++++---- mobile/android/chrome/content/browser.js | 43 ++++++++++++++++- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index e4ce2f220112..f8950dfce272 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -44,7 +44,10 @@ import android.content.Context; import android.util.Log; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Pair; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; @@ -91,8 +94,10 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parentView, View view, int position, long id) { if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) { + // Use the value stored with the autocomplete view, not the label text, + // since they can be different. TextView textView = (TextView) view; - String value = textView.getText().toString(); + String value = (String) textView.getTag(); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); hide(); } @@ -149,13 +154,8 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { } private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { - ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); - try { - for (int i = 0; i < suggestions.length(); i++) - adapter.add(suggestions.get(i).toString()); - } catch (JSONException e) { - Log.e(LOGTAG, "JSONException: " + e); - } + AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item); + adapter.populateSuggestionsList(suggestions); setAdapter(adapter); if (positionAndShowPopup(rect, zoom)) @@ -266,4 +266,48 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } + + private class AutoCompleteListAdapter extends ArrayAdapter> { + private LayoutInflater mInflater; + private int mTextViewResourceId; + + public AutoCompleteListAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mTextViewResourceId = textViewResourceId; + } + + // This method takes an array of autocomplete suggestions with label/value properties + // and adds label/value Pair objects to the array that backs the adapter. + public void populateSuggestionsList(JSONArray suggestions) { + try { + for (int i = 0; i < suggestions.length(); i++) { + JSONObject suggestion = (JSONObject) suggestions.get(i); + String label = suggestion.getString("label"); + String value = suggestion.getString("value"); + add(new Pair(label, value)); + } + } catch (JSONException e) { + Log.e(LOGTAG, "JSONException: " + e); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) + convertView = mInflater.inflate(mTextViewResourceId, null); + + Pair item = getItem(position); + TextView itemView = (TextView) convertView; + + // Set the text with the suggestion label + itemView.setText(item.first); + + // Set a tag with the suggestion value + itemView.setTag(item.second); + + return convertView; + } + } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 4f3e8cf43e08..1a731b910d50 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2952,7 +2952,41 @@ var FormAssistant = { if (value == aSearchString) continue; - suggestions.push(value); + // Supply a label and value, since they can differ for datalist suggestions + suggestions.push({ label: value, value: value }); + } + + return suggestions; + }, + + /** + * (Copied from mobile/xul/chrome/content/forms.js) + * This function is similar to getListSuggestions from + * components/satchel/src/nsInputListAutoComplete.js but sadly this one is + * used by the autocomplete.xml binding which is not in used in fennec + */ + _getListSuggestions: function _getListSuggestions(aElement) { + if (!(aElement instanceof HTMLInputElement) || !aElement.list) + return []; + + let suggestions = []; + let filter = !aElement.hasAttribute("mozNoFilter"); + let lowerFieldValue = aElement.value.toLowerCase(); + + let options = aElement.list.options; + let length = options.length; + for (let i = 0; i < length; i++) { + let item = options.item(i); + + let label = item.value; + if (item.label) + label = item.label; + else if (item.text) + label = item.text; + + if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1) + continue; + suggestions.push({ label: label, value: item.value }); } return suggestions; @@ -2977,7 +3011,12 @@ var FormAssistant = { if (!this._isAutoComplete(aElement)) return false; - let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + let listSuggestions = this._getListSuggestions(aElement); + + // On desktop, we show datalist suggestions below autocomplete suggestions, + // without duplicates removed. + let suggestions = autoCompleteSuggestions.concat(listSuggestions); // Return false if there are no suggestions to show if (!suggestions.length) From 2b821bbf017a995a87616c900ffc67858209b7c4 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 13:56:16 -0800 Subject: [PATCH 70/88] Bug 733233 - Create getTabForWindow helper function. r=wesj --- mobile/android/chrome/content/browser.js | 37 ++++++++++-------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 1a731b910d50..ed668aea58bb 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -472,6 +472,15 @@ var BrowserApp = { return null; }, + getTabForWindow: function getTabForWindow(aWindow) { + let tabs = this._tabs; + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].browser.contentWindow == aWindow) + return tabs[i]; + } + return null; + }, + getBrowserForWindow: function getBrowserForWindow(aWindow) { let tabs = this._tabs; for (let i = 0; i < tabs.length; i++) { @@ -1445,7 +1454,7 @@ nsBrowserAccess.prototype = { if (newTab) { let parentId = -1; if (!isExternal) { - let parent = BrowserApp.getTabForBrowser(BrowserApp.getBrowserForWindow(aOpener.top)); + let parent = BrowserApp.getTabForWindow(aOpener.top); if (parent) parentId = parent.id; } @@ -2309,11 +2318,7 @@ var BrowserEventHandler = { this._cancelTapHighlight(); this.onDoubleTap(aData); } else if (aTopic == "dom-touch-listener-added") { - let browser = BrowserApp.getBrowserForWindow(aSubject); - if (!browser) - return; - - let tab = BrowserApp.getTabForBrowser(browser); + let tab = BrowserApp.getTabForWindow(aSubject); if (!tab) return; @@ -2556,11 +2561,7 @@ const ElementTouchHelper = { if (!aWindow) throw "Must provide a window"; - let browser = BrowserApp.getBrowserForWindow(aWindow.top); - if (!browser) - throw "Unable to find a browser"; - - let tab = BrowserApp.getTabForBrowser(browser); + let tab = BrowserApp.getTabForWindow(aWindow.top); if (!tab) throw "Unable to find a tab"; @@ -2574,12 +2575,8 @@ const ElementTouchHelper = { toScreenCoords: function(aWindow, aX, aY) { if (!aWindow) throw "Must provide a window"; - - let browser = BrowserApp.getBrowserForWindow(aWindow.top); - if (!browser) - throw "Unable to find a browser"; - let tab = BrowserApp.getTabForBrowser(browser); + let tab = BrowserApp.getTabForWindow(aWindow.top); if (!tab) throw "Unable to find a tab"; @@ -3511,8 +3508,7 @@ var OfflineApps = { if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) return; - let browser = BrowserApp.getBrowserForWindow(aContentWindow); - let tab = BrowserApp.getTabForBrowser(browser); + let tab = BrowserApp.getTabForWindow(aContentWindow); let currentURI = aContentWindow.document.documentURIObject; // Don't bother showing UI if the user has already made a decision @@ -3611,8 +3607,8 @@ var IndexedDB = { let contentWindow = requestor.getInterface(Ci.nsIDOMWindow); let contentDocument = contentWindow.document; - let browser = BrowserApp.getBrowserForWindow(contentWindow); - if (!browser) + let tab = BrowserApp.getTabForWindow(contentWindow); + if (!tab) return; let host = contentDocument.documentURIObject.asciiHost; @@ -3631,7 +3627,6 @@ var IndexedDB = { } let notificationID = responseTopic + host; - let tab = BrowserApp.getTabForBrowser(browser); let observer = requestor.getInterface(Ci.nsIObserver); if (topic == this._quotaCancel) { From ecd2a3a0e7a0411c417f78b8eafbf697ed43d4d8 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 13:56:16 -0800 Subject: [PATCH 71/88] Bug 711624 - JS prompt dialog will appear in the foreground when loading the page with the code in the background. r=wesj --- mobile/android/chrome/content/browser.js | 17 +++++++++++++++++ mobile/android/components/PromptService.js | 9 +++++++++ 2 files changed, 26 insertions(+) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index ed668aea58bb..513dc11c1159 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -594,6 +594,10 @@ var BrowserApp = { return; } + // There's nothing to do if the tab is already selected + if (aTab == this.selectedTab) + return; + let message = { gecko: { type: "Tab:Select", @@ -1569,6 +1573,7 @@ Tab.prototype = { this.browser.addEventListener("DOMLinkAdded", this, true); this.browser.addEventListener("DOMTitleChanged", this, true); this.browser.addEventListener("DOMWindowClose", this, true); + this.browser.addEventListener("DOMWillOpenModalDialog", this, true); this.browser.addEventListener("scroll", this, true); this.browser.addEventListener("PluginClickToPlay", this, true); this.browser.addEventListener("pagehide", this, true); @@ -1611,6 +1616,7 @@ Tab.prototype = { this.browser.removeEventListener("DOMLinkAdded", this, true); this.browser.removeEventListener("DOMTitleChanged", this, true); this.browser.removeEventListener("DOMWindowClose", this, true); + this.browser.removeEventListener("DOMWillOpenModalDialog", this, true); this.browser.removeEventListener("scroll", this, true); this.browser.removeEventListener("PluginClickToPlay", this, true); this.browser.removeEventListener("pagehide", this, true); @@ -1888,6 +1894,17 @@ Tab.prototype = { break; } + case "DOMWillOpenModalDialog": { + if (!aEvent.isTrusted) + return; + + // We're about to open a modal dialog, make sure the opening + // tab is brought to the front. + let tab = BrowserApp.getTabForWindow(aEvent.target.top); + BrowserApp.selectTab(tab); + break; + } + case "scroll": { let win = this.browser.contentWindow; if (this.userScrollPos.x != win.scrollX || this.userScrollPos.y != win.scrollY) { diff --git a/mobile/android/components/PromptService.js b/mobile/android/components/PromptService.js index afc562566f53..fe832a8ff37a 100644 --- a/mobile/android/components/PromptService.js +++ b/mobile/android/components/PromptService.js @@ -156,6 +156,8 @@ Prompt.prototype = { if (aCheckMsg) aInputs.push({ type: "checkbox", label: PromptUtils.cleanUpLabel(aCheckMsg), checked: aCheckState.value }); + PromptUtils.fireDialogEvent(this._domWin, "DOMWillOpenModalDialog"); + let msg = { type: "Prompt:Show" }; if (aTitle) msg.title = aTitle; if (aText) msg.text = aText; @@ -765,9 +767,16 @@ let PromptUtils = { } return hostname; }, + sendMessageToJava: function(aMsg) { let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify({ gecko: aMsg })); return JSON.parse(data); + }, + + fireDialogEvent: function(aDomWin, aEventName) { + let event = aDomWin.document.createEvent("Events"); + event.initEvent(aEventName, true, true); + aDomWin.dispatchEvent(event); } }; From 1c20ff00624dffed1805ab76f23cf598a1b08805 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 14:01:02 -0800 Subject: [PATCH 72/88] backout 1d4397ee1e9a for wrong bug number --- mobile/android/base/FormAssistPopup.java | 60 ++++-------------------- mobile/android/chrome/content/browser.js | 43 +---------------- 2 files changed, 10 insertions(+), 93 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index f8950dfce272..e4ce2f220112 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -44,10 +44,7 @@ import android.content.Context; import android.util.Log; import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.util.Pair; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; @@ -94,10 +91,8 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parentView, View view, int position, long id) { if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) { - // Use the value stored with the autocomplete view, not the label text, - // since they can be different. TextView textView = (TextView) view; - String value = (String) textView.getTag(); + String value = textView.getText().toString(); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); hide(); } @@ -154,8 +149,13 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { } private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { - AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item); - adapter.populateSuggestionsList(suggestions); + ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); + try { + for (int i = 0; i < suggestions.length(); i++) + adapter.add(suggestions.get(i).toString()); + } catch (JSONException e) { + Log.e(LOGTAG, "JSONException: " + e); + } setAdapter(adapter); if (positionAndShowPopup(rect, zoom)) @@ -266,48 +266,4 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } - - private class AutoCompleteListAdapter extends ArrayAdapter> { - private LayoutInflater mInflater; - private int mTextViewResourceId; - - public AutoCompleteListAdapter(Context context, int textViewResourceId) { - super(context, textViewResourceId); - - mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - mTextViewResourceId = textViewResourceId; - } - - // This method takes an array of autocomplete suggestions with label/value properties - // and adds label/value Pair objects to the array that backs the adapter. - public void populateSuggestionsList(JSONArray suggestions) { - try { - for (int i = 0; i < suggestions.length(); i++) { - JSONObject suggestion = (JSONObject) suggestions.get(i); - String label = suggestion.getString("label"); - String value = suggestion.getString("value"); - add(new Pair(label, value)); - } - } catch (JSONException e) { - Log.e(LOGTAG, "JSONException: " + e); - } - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) - convertView = mInflater.inflate(mTextViewResourceId, null); - - Pair item = getItem(position); - TextView itemView = (TextView) convertView; - - // Set the text with the suggestion label - itemView.setText(item.first); - - // Set a tag with the suggestion value - itemView.setTag(item.second); - - return convertView; - } - } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 513dc11c1159..c0d5821c6cc0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2966,41 +2966,7 @@ var FormAssistant = { if (value == aSearchString) continue; - // Supply a label and value, since they can differ for datalist suggestions - suggestions.push({ label: value, value: value }); - } - - return suggestions; - }, - - /** - * (Copied from mobile/xul/chrome/content/forms.js) - * This function is similar to getListSuggestions from - * components/satchel/src/nsInputListAutoComplete.js but sadly this one is - * used by the autocomplete.xml binding which is not in used in fennec - */ - _getListSuggestions: function _getListSuggestions(aElement) { - if (!(aElement instanceof HTMLInputElement) || !aElement.list) - return []; - - let suggestions = []; - let filter = !aElement.hasAttribute("mozNoFilter"); - let lowerFieldValue = aElement.value.toLowerCase(); - - let options = aElement.list.options; - let length = options.length; - for (let i = 0; i < length; i++) { - let item = options.item(i); - - let label = item.value; - if (item.label) - label = item.label; - else if (item.text) - label = item.text; - - if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1) - continue; - suggestions.push({ label: label, value: item.value }); + suggestions.push(value); } return suggestions; @@ -3025,12 +2991,7 @@ var FormAssistant = { if (!this._isAutoComplete(aElement)) return false; - let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); - let listSuggestions = this._getListSuggestions(aElement); - - // On desktop, we show datalist suggestions below autocomplete suggestions, - // without duplicates removed. - let suggestions = autoCompleteSuggestions.concat(listSuggestions); + let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); // Return false if there are no suggestions to show if (!suggestions.length) From 4613384604dac5dc8f76f3a6e61b2fa4417f2a22 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 6 Mar 2012 13:56:16 -0800 Subject: [PATCH 73/88] Bug 717787 - Suggestions from are not shown in Native Fennec. r=mbrubeck --- mobile/android/base/FormAssistPopup.java | 60 ++++++++++++++++++++---- mobile/android/chrome/content/browser.js | 43 ++++++++++++++++- 2 files changed, 93 insertions(+), 10 deletions(-) diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index e4ce2f220112..f8950dfce272 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -44,7 +44,10 @@ import android.content.Context; import android.util.Log; import android.util.AttributeSet; import android.util.DisplayMetrics; +import android.util.Pair; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; @@ -91,8 +94,10 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView parentView, View view, int position, long id) { if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) { + // Use the value stored with the autocomplete view, not the label text, + // since they can be different. TextView textView = (TextView) view; - String value = textView.getText().toString(); + String value = (String) textView.getTag(); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); hide(); } @@ -149,13 +154,8 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { } private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { - ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); - try { - for (int i = 0; i < suggestions.length(); i++) - adapter.add(suggestions.get(i).toString()); - } catch (JSONException e) { - Log.e(LOGTAG, "JSONException: " + e); - } + AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item); + adapter.populateSuggestionsList(suggestions); setAdapter(adapter); if (positionAndShowPopup(rect, zoom)) @@ -266,4 +266,48 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } + + private class AutoCompleteListAdapter extends ArrayAdapter> { + private LayoutInflater mInflater; + private int mTextViewResourceId; + + public AutoCompleteListAdapter(Context context, int textViewResourceId) { + super(context, textViewResourceId); + + mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mTextViewResourceId = textViewResourceId; + } + + // This method takes an array of autocomplete suggestions with label/value properties + // and adds label/value Pair objects to the array that backs the adapter. + public void populateSuggestionsList(JSONArray suggestions) { + try { + for (int i = 0; i < suggestions.length(); i++) { + JSONObject suggestion = (JSONObject) suggestions.get(i); + String label = suggestion.getString("label"); + String value = suggestion.getString("value"); + add(new Pair(label, value)); + } + } catch (JSONException e) { + Log.e(LOGTAG, "JSONException: " + e); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) + convertView = mInflater.inflate(mTextViewResourceId, null); + + Pair item = getItem(position); + TextView itemView = (TextView) convertView; + + // Set the text with the suggestion label + itemView.setText(item.first); + + // Set a tag with the suggestion value + itemView.setTag(item.second); + + return convertView; + } + } } diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index c0d5821c6cc0..513dc11c1159 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2966,7 +2966,41 @@ var FormAssistant = { if (value == aSearchString) continue; - suggestions.push(value); + // Supply a label and value, since they can differ for datalist suggestions + suggestions.push({ label: value, value: value }); + } + + return suggestions; + }, + + /** + * (Copied from mobile/xul/chrome/content/forms.js) + * This function is similar to getListSuggestions from + * components/satchel/src/nsInputListAutoComplete.js but sadly this one is + * used by the autocomplete.xml binding which is not in used in fennec + */ + _getListSuggestions: function _getListSuggestions(aElement) { + if (!(aElement instanceof HTMLInputElement) || !aElement.list) + return []; + + let suggestions = []; + let filter = !aElement.hasAttribute("mozNoFilter"); + let lowerFieldValue = aElement.value.toLowerCase(); + + let options = aElement.list.options; + let length = options.length; + for (let i = 0; i < length; i++) { + let item = options.item(i); + + let label = item.value; + if (item.label) + label = item.label; + else if (item.text) + label = item.text; + + if (filter && label.toLowerCase().indexOf(lowerFieldValue) == -1) + continue; + suggestions.push({ label: label, value: item.value }); } return suggestions; @@ -2991,7 +3025,12 @@ var FormAssistant = { if (!this._isAutoComplete(aElement)) return false; - let suggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement); + let listSuggestions = this._getListSuggestions(aElement); + + // On desktop, we show datalist suggestions below autocomplete suggestions, + // without duplicates removed. + let suggestions = autoCompleteSuggestions.concat(listSuggestions); // Return false if there are no suggestions to show if (!suggestions.length) From aef86e13cfece71632319ac59b43baf26e994e84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Tue, 6 Mar 2012 23:28:09 +0100 Subject: [PATCH 74/88] Bug 716334 - Avoid making the browser window visible before the layout is rendered. r=felip --- browser/base/content/browser.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 01331f058ecf..13872273e061 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5374,6 +5374,7 @@ function setToolbarVisibility(toolbar, isVisible) { var TabsOnTop = { init: function TabsOnTop_init() { + this._initialized = true; this.syncUI(); Services.prefs.addObserver(this._prefName, this, false); }, @@ -5387,6 +5388,9 @@ var TabsOnTop = { }, syncUI: function () { + if (!this._initialized) + return; + let userEnabled = Services.prefs.getBoolPref(this._prefName); let enabled = userEnabled && gBrowser.tabContainer.visible; From 10824569cc2c88225243373f083dc879bdfda20f Mon Sep 17 00:00:00 2001 From: Alastair Robertson Date: Tue, 6 Mar 2012 23:28:53 +0100 Subject: [PATCH 75/88] Bug 311605 - default browser checking should be done from nsBrowserGlue, not in delayedStartup. r=gavin --- browser/base/content/browser.js | 43 ----------------------------- browser/components/nsBrowserGlue.js | 43 +++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 13872273e061..d837e723ac1d 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1563,49 +1563,6 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) { gHomeButton.updateTooltip(homeButton); gHomeButton.updatePersonalToolbarStyle(homeButton); -#ifdef HAVE_SHELL_SERVICE - // Perform default browser checking (after window opens). - var shell = getShellService(); - if (shell) { -#ifdef DEBUG - var shouldCheck = false; -#else - var shouldCheck = shell.shouldCheckDefaultBrowser; -#endif - var willRecoverSession = false; - try { - var ss = Cc["@mozilla.org/browser/sessionstartup;1"]. - getService(Ci.nsISessionStartup); - willRecoverSession = - (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); - } - catch (ex) { /* never mind; suppose SessionStore is broken */ } - if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) { - // Delay the set-default-browser prompt so it doesn't block - // initialisation of the session store service. - setTimeout(function () { - var brandBundle = document.getElementById("bundle_brand"); - var shellBundle = document.getElementById("bundle_shell"); - - var brandShortName = brandBundle.getString("brandShortName"); - var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); - var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", - [brandShortName]); - var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", - [brandShortName]); - var checkEveryTime = { value: shouldCheck }; - var ps = Services.prompt; - var rv = ps.confirmEx(window, promptTitle, promptMessage, - ps.STD_YES_NO_BUTTONS, - null, null, null, checkboxLabel, checkEveryTime); - if (rv == 0) - shell.setDefaultBrowser(true, false); - shell.shouldCheckDefaultBrowser = checkEveryTime.value; - }, 0); - } - } -#endif - // BiDi UI gBidiUI = isBidiEnabled(); if (gBidiUI) { diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index d77532fd9ad9..18117ba39c98 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -426,6 +426,49 @@ BrowserGlue.prototype = { let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL"); Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet); + + // Perform default browser checking. + var shell; + try { + shell = Components.classes["@mozilla.org/browser/shell-service;1"] + .getService(Components.interfaces.nsIShellService); + } catch (e) { } + if (shell) { +#ifdef DEBUG + var shouldCheck = false; +#else + var shouldCheck = shell.shouldCheckDefaultBrowser; +#endif + var willRecoverSession = false; + try { + var ss = Cc["@mozilla.org/browser/sessionstartup;1"]. + getService(Ci.nsISessionStartup); + willRecoverSession = + (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION); + } + catch (ex) { /* never mind; suppose SessionStore is broken */ } + if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) { + Services.tm.mainThread.dispatch(function() { + var brandBundle = win.document.getElementById("bundle_brand"); + var shellBundle = win.document.getElementById("bundle_shell"); + + var brandShortName = brandBundle.getString("brandShortName"); + var promptTitle = shellBundle.getString("setDefaultBrowserTitle"); + var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage", + [brandShortName]); + var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk", + [brandShortName]); + var checkEveryTime = { value: shouldCheck }; + var ps = Services.prompt; + var rv = ps.confirmEx(win, promptTitle, promptMessage, + ps.STD_YES_NO_BUTTONS, + null, null, null, checkboxLabel, checkEveryTime); + if (rv == 0) + shell.setDefaultBrowser(true, false); + shell.shouldCheckDefaultBrowser = checkEveryTime.value; + }, Ci.nsIThread.DISPATCH_NORMAL); + } + } }, _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) { From 95f1f69f03d0798d331f3ff22000b91d16b8dacf Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Mon, 5 Mar 2012 19:36:47 -0500 Subject: [PATCH 76/88] Bug 733152 - add locale to telemetry metadata; r=taras --- toolkit/components/telemetry/TelemetryPing.js | 3 ++- toolkit/components/telemetry/tests/unit/test_TelemetryPing.js | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 7831c21ddcb0..6ff1661c92d2 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -293,7 +293,8 @@ TelemetryPing.prototype = { appName: ai.name, appBuildID: ai.appBuildID, appUpdateChannel: getUpdateChannel(), - platformBuildID: ai.platformBuildID + platformBuildID: ai.platformBuildID, + locale: getLocale() }; // sysinfo fields are not always available, get what we can. diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 5e2c5f6d055b..4b8c6aeef5c8 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -101,6 +101,9 @@ function checkHistograms(request, response) { do_check_eq(payload.info[f], expected_info[f]); } + do_check_true("appUpdateChannel" in payload.info); + do_check_true("locale" in payload.info); + try { // If we've not got nsIGfxInfoDebug, then this will throw and stop us doing // this test. From 4ed0326156d4d88b9d87b0201ecd3377ee810f6f Mon Sep 17 00:00:00 2001 From: Oleg Romashin Date: Tue, 6 Mar 2012 23:29:38 +0100 Subject: [PATCH 77/88] Bug 733506 - WebGL FBO offscreen texture not initialized properly. r=bjacob --- gfx/gl/GLContext.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 68dda711e2a1..9afcd48a362d 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1209,6 +1209,8 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c fBindTexture(LOCAL_GL_TEXTURE_2D, newOffscreenTexture); fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR); fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR); + fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE); + fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE); if (alpha) { fTexImage2D(LOCAL_GL_TEXTURE_2D, From f4abd10724399845d82a19ae45a7769e5e09c88b Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Tue, 6 Mar 2012 15:03:34 -0800 Subject: [PATCH 78/88] Bug 729098 - Part 1/2: Create xUnit XML results file when executing xpcshell tests; r=khuey --- config/rules.mk | 3 +++ js/src/config/rules.mk | 3 +++ testing/testsuite-targets.mk | 3 +++ testing/xpcshell/runxpcshelltests.py | 30 ++++++++++++++++++++++++++-- 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/config/rules.mk b/config/rules.mk index 6c4f7ecdace6..5e299925f5dd 100644 --- a/config/rules.mk +++ b/config/rules.mk @@ -148,6 +148,9 @@ xpcshell-tests: $(testxpcsrcdir)/runxpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ + --tests-root-dir=$(testxpcobjdir) \ + --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \ + --xunit-suite-name=xpcshell \ $(EXTRA_TEST_ARGS) \ $(LIBXUL_DIST)/bin/xpcshell \ $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) diff --git a/js/src/config/rules.mk b/js/src/config/rules.mk index 6c4f7ecdace6..5e299925f5dd 100644 --- a/js/src/config/rules.mk +++ b/js/src/config/rules.mk @@ -148,6 +148,9 @@ xpcshell-tests: $(testxpcsrcdir)/runxpcshelltests.py \ --symbols-path=$(DIST)/crashreporter-symbols \ --build-info-json=$(DEPTH)/mozinfo.json \ + --tests-root-dir=$(testxpcobjdir) \ + --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \ + --xunit-suite-name=xpcshell \ $(EXTRA_TEST_ARGS) \ $(LIBXUL_DIST)/bin/xpcshell \ $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir)) diff --git a/testing/testsuite-targets.mk b/testing/testsuite-targets.mk index 25462ab184e3..d0fad4ce767d 100644 --- a/testing/testsuite-targets.mk +++ b/testing/testsuite-targets.mk @@ -241,6 +241,9 @@ xpcshell-tests: --manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \ --build-info-json=$(DEPTH)/mozinfo.json \ --no-logfiles \ + --tests-root-dir=$(call core_abspath,_tests/xpcshell) \ + --xunit-file=$(call core_abspath,_tests/xpcshell/results.xml) \ + --xunit-suite-name=xpcshell \ $(SYMBOLS_PATH) \ $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) \ $(LIBXUL_DIST)/bin/xpcshell diff --git a/testing/xpcshell/runxpcshelltests.py b/testing/xpcshell/runxpcshelltests.py index 44ed98a837ae..ba6699e55f62 100644 --- a/testing/xpcshell/runxpcshelltests.py +++ b/testing/xpcshell/runxpcshelltests.py @@ -498,7 +498,8 @@ class XPCShellTests(object): thisChunk=1, totalChunks=1, debugger=None, debuggerArgs=None, debuggerInteractive=False, profileName=None, mozInfo=None, shuffle=False, - xunitFilename=None, xunitName=None, **otherOptions): + testsRootDir=None, xunitFilename=None, xunitName=None, + **otherOptions): """Run xpcshell tests. |xpcshell|, is the xpcshell executable to use to run the tests. @@ -523,6 +524,8 @@ class XPCShellTests(object): directory if running only a subset of tests. |mozInfo|, if set, specifies specifies build configuration information, either as a filename containing JSON, or a dict. |shuffle|, if True, execute tests in random order. + |testsRootDir|, absolute path to root directory of all tests. This is used + by xUnit generation to determine the package name of the tests. |xunitFilename|, if set, specifies the filename to which to write xUnit XML results. |xunitName|, if outputting an xUnit XML file, the str value to use for the @@ -535,6 +538,17 @@ class XPCShellTests(object): if testdirs is None: testdirs = [] + if xunitFilename is not None or xunitName is not None: + if not isinstance(testsRootDir, str): + raise Exception("testsRootDir must be a str when outputting xUnit.") + + if not os.path.isabs(testsRootDir): + testsRootDir = os.path.abspath(testsRootDir) + + if not os.path.exists(testsRootDir): + raise Exception("testsRootDir path does not exists: %s" % + testsRootDir) + self.xpcshell = xpcshell self.xrePath = xrePath self.appPath = appPath @@ -598,7 +612,16 @@ class XPCShellTests(object): self.testCount += 1 - xunitResult = {"classname": "xpcshell", "name": test["name"]} + xunitResult = {"name": test["name"], "classname": "xpcshell"} + # The xUnit package is defined as the path component between the root + # dir and the test with path characters replaced with '.' (using Java + # class notation). + if testsRootDir is not None: + if test["here"].find(testsRootDir) != 0: + raise Exception("testsRootDir is not a parent path of %s" % + test["here"]) + relpath = test["here"][len(testsRootDir):].lstrip("/\\") + xunitResult["classname"] = relpath.replace("/", ".").replace("\\", ".") # Check for skipped tests if 'disabled' in test: @@ -775,6 +798,9 @@ class XPCShellOptions(OptionParser): self.add_option("--test-path", type="string", dest="testPath", default=None, help="single path and/or test filename to test") + self.add_option("--tests-root-dir", + type="string", dest="testsRootDir", default=None, + help="absolute path to directory where all tests are located. this is typically $(objdir)/_tests") self.add_option("--total-chunks", type = "int", dest = "totalChunks", default=1, help = "how many chunks to split the tests up into") From 2d6be224565f4ad9d3e9d5df5351c20a299f8f51 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Tue, 6 Mar 2012 16:27:22 -0800 Subject: [PATCH 79/88] Bug 729098 - Part 1.01/2 - Fix xpcshell test bustage; r=bustage --- testing/xpcshell/selftest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/xpcshell/selftest.py b/testing/xpcshell/selftest.py index ef9b62bb4f52..174ee6651176 100644 --- a/testing/xpcshell/selftest.py +++ b/testing/xpcshell/selftest.py @@ -72,6 +72,7 @@ tail = manifest=self.manifest, mozInfo={}, shuffle=shuffle, + testsRootDir=self.tempdir, xunitFilename=xunitFilename), msg="""Tests should have %s, log: ======== From bcbd8548dc9218e8b4227bec07a6d69b0c27cd70 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Tue, 6 Mar 2012 15:08:55 -0800 Subject: [PATCH 80/88] Bug 725990 - Add link support to doorhangers. r=margaret --- mobile/android/base/DoorHanger.java | 26 +++++++++++++++++++ .../base/resources/layout/doorhanger.xml | 1 + .../android/base/resources/values/colors.xml | 1 + 3 files changed, 28 insertions(+) diff --git a/mobile/android/base/DoorHanger.java b/mobile/android/base/DoorHanger.java index 6650e8c48fec..372fba2f0906 100644 --- a/mobile/android/base/DoorHanger.java +++ b/mobile/android/base/DoorHanger.java @@ -39,6 +39,10 @@ package org.mozilla.gecko; import android.content.Context; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.style.ForegroundColorSpan; +import android.text.style.URLSpan; import android.view.LayoutInflater; import android.view.View; import android.widget.Button; @@ -140,6 +144,28 @@ public class DoorHanger extends LinearLayout implements Button.OnClickListener { try { mTimeout = options.getLong("timeout"); } catch (JSONException e) { } + + try { + JSONObject link = options.getJSONObject("link"); + String title = mTextView.getText().toString(); + String linkLabel = link.getString("label"); + String linkUrl = link.getString("url"); + SpannableString titleWithLink = new SpannableString(title + " " + linkLabel); + URLSpan linkSpan = new URLSpan(linkUrl) { + @Override + public void onClick(View view) { + GeckoApp.mAppContext.loadUrlInTab(this.getURL()); + } + }; + + // prevent text outside the link from flashing when clicked + ForegroundColorSpan colorSpan = new ForegroundColorSpan(mTextView.getCurrentTextColor()); + titleWithLink.setSpan(colorSpan, 0, title.length(), 0); + + titleWithLink.setSpan(linkSpan, title.length() + 1, titleWithLink.length(), 0); + mTextView.setText(titleWithLink); + mTextView.setMovementMethod(LinkMovementMethod.getInstance()); + } catch (JSONException e) { } } // This method checks with persistence and timeout options to see if diff --git a/mobile/android/base/resources/layout/doorhanger.xml b/mobile/android/base/resources/layout/doorhanger.xml index 46b81842ddd4..9df165dcdf4b 100644 --- a/mobile/android/base/resources/layout/doorhanger.xml +++ b/mobile/android/base/resources/layout/doorhanger.xml @@ -6,6 +6,7 @@ android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="?android:attr/textColorPrimary" + android:textColorLink="@color/doorhanger_link" android:padding="10dp"/> #ffffff #000000 #ffffff + #ACC4D5 From cbe0f99786a97593f120381b8f7d88a9e6b2cc7a Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 5 Mar 2012 16:58:59 -0800 Subject: [PATCH 81/88] Bug 731845 - XPCWrappedNative::ReparentWrapperIfFound should handle preserved wrappers. r=mrbkap --- content/base/src/nsNodeUtils.cpp | 19 ----------- js/xpconnect/src/XPCWrappedNative.cpp | 45 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/content/base/src/nsNodeUtils.cpp b/content/base/src/nsNodeUtils.cpp index f4a75a6c81a7..14e7f429d381 100644 --- a/content/base/src/nsNodeUtils.cpp +++ b/content/base/src/nsNodeUtils.cpp @@ -597,29 +597,10 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNode, bool aClone, bool aDeep, if (aCx && wrapper) { nsIXPConnect *xpc = nsContentUtils::XPConnect(); if (xpc) { - JSObject *preservedWrapper = nsnull; - - // If reparenting moves us to a new compartment, preserving causes - // problems. In that case, we release ourselves and re-preserve after - // reparenting so we're sure to have the right JS object preserved. - // We use a JSObject stack copy of the wrapper to protect it from GC - // under ReparentWrappedNativeIfFound. - if (aNode->PreservingWrapper()) { - preservedWrapper = wrapper; - nsContentUtils::ReleaseWrapper(aNode, aNode); - NS_ASSERTION(aNode->GetWrapper(), - "ReleaseWrapper cleared our wrapper, this code needs to " - "be changed to deal with that!"); - } - nsCOMPtr oldWrapper; rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode, getter_AddRefs(oldWrapper)); - if (preservedWrapper) { - nsContentUtils::PreserveWrapper(aNode, aNode); - } - if (NS_FAILED(rv)) { aNode->mNodeInfo.swap(nodeInfo); diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index 26cf95789f41..4372f9d2a4c9 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -53,6 +53,8 @@ #include "WrapperFactory.h" #include "dombindings.h" +#include "nsContentUtils.h" + #include "mozilla/Util.h" bool @@ -1479,6 +1481,43 @@ XPCWrappedNative::SystemIsBeingShutDown() /***************************************************************************/ +// If we have to transplant an object across compartments, we need to be +// careful if the underlying object implements nsWrapperCache and is preserving +// the wrapper. +// +// The class brackets a pair of Unpreserve/Preserve calls in the given scope. +// +// This class _must_ live on the stack, in part so that mPreservedWrapper is +// visible to the stack scanner. The caller wants the wrapper to be preserved, +// so we don't want it to get accidentally GCed. +class AutoWrapperChanger NS_STACK_CLASS { +public: + AutoWrapperChanger() : mCache(nsnull) + , mCOMObj(nsnull) + , mPreservedWrapper(nsnull) + {} + + void init(nsISupports* aCOMObj, nsWrapperCache* aWrapperCache) { + mCOMObj = aCOMObj; + mCache = aWrapperCache; + if (mCache->PreservingWrapper()) { + mPreservedWrapper = mCache->GetWrapper(); + MOZ_ASSERT(mPreservedWrapper); + nsContentUtils::ReleaseWrapper(mCOMObj, mCache); + } + } + + ~AutoWrapperChanger() { + if (mPreservedWrapper) + nsContentUtils::PreserveWrapper(mCOMObj, mCache); + } + +private: + nsWrapperCache* mCache; + nsISupports* mCOMObj; + JSObject* mPreservedWrapper; +}; + // static nsresult XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, @@ -1497,10 +1536,16 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx, nsresult rv; nsRefPtr wrapper; + AutoWrapperChanger wrapperChanger; JSObject *flat; nsWrapperCache* cache = nsnull; CallQueryInterface(aCOMObj, &cache); if (cache) { + + // There's a wrapper cache. Make sure we keep it sane no matter what + // happens. + wrapperChanger.init(aCOMObj, cache); + flat = cache->GetWrapper(); if (flat && !IS_SLIM_WRAPPER_OBJECT(flat)) { wrapper = static_cast(xpc_GetJSPrivate(flat)); From 0822e24e67d0acf7d7927c0f1ed3c980e604bbcb Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 6 Mar 2012 15:52:55 -0800 Subject: [PATCH 82/88] Bug 733260 followup: use uint32_t for array and string lengths, r=luke --HG-- extra : rebase_source : 1729b4b927fde0678872b9de62a7466381aa07c3 --- content/base/src/nsWebSocket.cpp | 2 +- content/canvas/src/CanvasUtils.cpp | 2 +- content/canvas/src/CanvasUtils.h | 4 +- dom/indexedDB/AsyncConnectionHelper.cpp | 2 +- dom/indexedDB/IDBDatabase.cpp | 8 +- dom/indexedDB/IDBIndex.cpp | 2 +- dom/indexedDB/IDBObjectStore.cpp | 8 +- dom/indexedDB/Key.cpp | 6 +- dom/sms/src/SmsFilter.cpp | 4 +- dom/sms/src/SmsManager.cpp | 4 +- js/src/ctypes/CTypes.cpp | 12 +-- .../jsapi-tests/testIsAboutToBeFinalized.cpp | 2 +- js/src/jsapi-tests/testNewObject.cpp | 2 +- js/src/jsapi.cpp | 8 +- js/src/jsapi.h | 8 +- js/src/jsarray.cpp | 86 +++++++++---------- js/src/jsarray.h | 8 +- js/src/jsatom.cpp | 6 +- js/src/jsfriendapi.h | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsgc.cpp | 2 +- js/src/jsinterp.cpp | 6 +- js/src/jsinterpinlines.h | 8 +- js/src/jsobj.cpp | 2 +- js/src/json.cpp | 6 +- js/src/jsopcode.cpp | 4 +- js/src/jsproxy.cpp | 4 +- js/src/jsscope.cpp | 4 +- js/src/jsstr.cpp | 42 ++++----- js/src/jstypedarray.cpp | 28 +++--- js/src/jstypedarray.h | 4 +- js/src/jsxml.cpp | 6 +- js/src/methodjit/MonoIC.cpp | 2 +- js/src/methodjit/StubCalls.cpp | 12 +-- js/src/shell/jsheaptools.cpp | 2 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 4 +- js/xpconnect/src/XPCVariant.cpp | 4 +- js/xpconnect/src/dombindings.cpp | 2 +- startupcache/test/TestStartupCache.cpp | 4 +- toolkit/components/places/History.cpp | 10 +-- 40 files changed, 167 insertions(+), 167 deletions(-) diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp index dc44ffebfe11..bf38a42ccac7 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/nsWebSocket.cpp @@ -602,7 +602,7 @@ nsWebSocket::Initialize(nsISupports* aOwner, if (JSVAL_IS_OBJECT(aArgv[1]) && (jsobj = JSVAL_TO_OBJECT(aArgv[1])) && JS_IsArrayObject(aContext, jsobj)) { - unsigned len; + uint32_t len; JS_GetArrayLength(aContext, jsobj, &len); for (PRUint32 index = 0; index < len; ++index) { diff --git a/content/canvas/src/CanvasUtils.cpp b/content/canvas/src/CanvasUtils.cpp index bfd7d3bcb9ba..1e4baf8acf62 100644 --- a/content/canvas/src/CanvasUtils.cpp +++ b/content/canvas/src/CanvasUtils.cpp @@ -122,7 +122,7 @@ JSValToMatrixElts(JSContext* cx, const jsval& val, double* (&elts)[N], nsresult* rv) { JSObject* obj; - unsigned length; + uint32_t length; if (JSVAL_IS_PRIMITIVE(val) || !(obj = JSVAL_TO_OBJECT(val)) || diff --git a/content/canvas/src/CanvasUtils.h b/content/canvas/src/CanvasUtils.h index 917fba8a59e0..ec3a9c581759 100644 --- a/content/canvas/src/CanvasUtils.h +++ b/content/canvas/src/CanvasUtils.h @@ -146,11 +146,11 @@ JSValToDashArray(JSContext* cx, const jsval& patternArray, { // The cap is pretty arbitrary. 16k should be enough for // anybody... - static const unsigned MAX_NUM_DASHES = 1 << 14; + static const uint32_t MAX_NUM_DASHES = 1 << 14; if (!JSVAL_IS_PRIMITIVE(patternArray)) { JSObject* obj = JSVAL_TO_OBJECT(patternArray); - unsigned length; + uint32_t length; if (!JS_GetArrayLength(cx, obj, &length)) { // Not an array-like thing return NS_ERROR_INVALID_ARG; diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 84c74f5db9fe..8adb60d292b1 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -90,7 +90,7 @@ ConvertCloneReadInfosToArrayInternal( } if (!aReadInfos.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, unsigned(aReadInfos.Length()))) { + if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) { NS_WARNING("Failed to set array length!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index 847a37be93a2..22bdcffdcf36 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -410,7 +410,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, JSObject* obj = JSVAL_TO_OBJECT(val); - unsigned length; + uint32_t length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -421,7 +421,7 @@ IDBDatabase::CreateObjectStore(const nsAString& aName, keyPathArray.SetCapacity(length); - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; @@ -566,7 +566,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames, // See if this is a JS array. if (JS_IsArrayObject(aCx, obj)) { - unsigned length; + uint32_t length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -577,7 +577,7 @@ IDBDatabase::Transaction(const jsval& aStoreNames, storesToOpen.SetCapacity(length); - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index 7bb52c4b2107..f2d3551e317e 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -904,7 +904,7 @@ GetAllKeysHelper::GetSuccessResult(JSContext* aCx, } if (!keys.IsEmpty()) { - if (!JS_SetArrayLength(aCx, array, unsigned(keys.Length()))) { + if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) { NS_WARNING("Failed to set array length!"); return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index e0f21bd211d8..b3704c7cf4b1 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -618,12 +618,12 @@ IDBObjectStore::AppendIndexUpdateInfo(PRInt64 aIndexID, if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) && JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) { JSObject* array = JSVAL_TO_OBJECT(key); - unsigned arrayLength; + uint32_t arrayLength; if (!JS_GetArrayLength(aCx, array, &arrayLength)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - for (unsigned arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { + for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) { jsval arrayItem; if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -1700,7 +1700,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, JSObject* obj = JSVAL_TO_OBJECT(aKeyPath); - unsigned length; + uint32_t length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } @@ -1711,7 +1711,7 @@ IDBObjectStore::CreateIndex(const nsAString& aName, keyPathArray.SetCapacity(length); - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { jsval val; JSString* jsstr; nsDependentJSString str; diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp index 93ed16a2ffcb..279cf8ee25f8 100644 --- a/dom/indexedDB/Key.cpp +++ b/dom/indexedDB/Key.cpp @@ -170,12 +170,12 @@ Key::EncodeJSVal(JSContext* aCx, const jsval aVal, PRUint8 aTypeOffset) aTypeOffset < (eMaxType * MaxArrayCollapse), "Wrong typeoffset"); - unsigned length; + uint32_t length; if (!JS_GetArrayLength(aCx, obj, &length)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; } - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { jsval val; if (!JS_GetElement(aCx, obj, index, &val)) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; @@ -220,7 +220,7 @@ Key::DecodeJSVal(const unsigned char*& aPos, const unsigned char* aEnd, aTypeOffset = 0; } - unsigned index = 0; + uint32_t index = 0; while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) { jsval val; nsresult rv = DecodeJSVal(aPos, aEnd, aCx, aTypeOffset, &val); diff --git a/dom/sms/src/SmsFilter.cpp b/dom/sms/src/SmsFilter.cpp index 3e5a5cd14814..f0fe9ee9841f 100644 --- a/dom/sms/src/SmsFilter.cpp +++ b/dom/sms/src/SmsFilter.cpp @@ -190,12 +190,12 @@ SmsFilter::SetNumbers(JSContext* aCx, const jsval& aNumbers) return NS_ERROR_INVALID_ARG; } - unsigned size; + uint32_t size; JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, &obj, &size)); nsTArray numbers; - for (unsigned i=0; iisArray()) { *lengthp = obj->getArrayLength(); @@ -168,7 +168,7 @@ js_GetLengthProperty(JSContext *cx, JSObject *obj, unsigned *lengthp) return false; if (tvr.value().isInt32()) { - *lengthp = unsigned(tvr.value().toInt32()); /* unsigned cast does ToUint32_t */ + *lengthp = uint32_t(tvr.value().toInt32()); /* uint32_t cast does ToUint32_t */ return true; } @@ -198,7 +198,7 @@ namespace js { * */ JS_FRIEND_API(bool) -StringIsArrayIndex(JSLinearString *str, unsigned *indexp) +StringIsArrayIndex(JSLinearString *str, uint32_t *indexp) { const jschar *s = str->chars(); uint32_t length = str->length(); @@ -237,7 +237,7 @@ StringIsArrayIndex(JSLinearString *str, unsigned *indexp) } static JSBool -BigIndexToId(JSContext *cx, JSObject *obj, unsigned index, JSBool createAtom, +BigIndexToId(JSContext *cx, JSObject *obj, uint32_t index, JSBool createAtom, jsid *idp) { @@ -319,8 +319,8 @@ IndexToId(JSContext* cx, JSObject* obj, double index, JSBool* hole, jsid* idp, return JS_TRUE; } - if (index <= unsigned(-1)) { - if (!BigIndexToId(cx, obj, unsigned(index), createAtom, idp)) + if (index <= uint32_t(-1)) { + if (!BigIndexToId(cx, obj, uint32_t(index), createAtom, idp)) return JS_FALSE; if (hole && JSID_IS_VOID(*idp)) *hole = JS_TRUE; @@ -438,7 +438,7 @@ GetElementsSlow(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp) } bool -GetElements(JSContext *cx, JSObject *aobj, unsigned length, Value *vp) +GetElements(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp) { if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() && !js_PrototypeHasIndexedProperties(cx, aobj)) { @@ -476,9 +476,9 @@ SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v) /* Predicted/prefetched code should favor the remains-dense case. */ JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; do { - if (index > unsigned(-1)) + if (index > uint32_t(-1)) break; - unsigned idx = unsigned(index); + uint32_t idx = uint32_t(index); result = obj->ensureDenseArrayElements(cx, idx, 1); if (result != JSObject::ED_OK) break; @@ -630,8 +630,8 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value * us to disregard length when reading from arrays as long we are within * the initialized capacity. */ - unsigned oldcap = obj->getDenseArrayCapacity(); - unsigned oldinit = obj->getDenseArrayInitializedLength(); + uint32_t oldcap = obj->getDenseArrayCapacity(); + uint32_t oldinit = obj->getDenseArrayInitializedLength(); if (oldinit > newlen) obj->setDenseArrayInitializedLength(newlen); if (oldcap > newlen) @@ -661,13 +661,13 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value if (!iter) return false; - unsigned gap = oldlen - newlen; + uint32_t gap = oldlen - newlen; for (;;) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id)) return false; if (JSID_IS_VOID(id)) break; - unsigned index; + uint32_t index; Value junk; if (js_IdIsIndex(id, &index) && index - newlen < gap && !obj->deleteElement(cx, index, &junk, false)) { @@ -864,7 +864,7 @@ array_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Valu static JSBool slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp) { - unsigned index, length; + uint32_t index, length; if (!js_IdIsIndex(id, &index)) return JS_TRUE; @@ -1489,11 +1489,11 @@ array_toSource(JSContext *cx, unsigned argc, Value *vp) if (!sb.append('[')) return false; - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return false; - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { JSBool hole; Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || @@ -1609,7 +1609,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, return true; } - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return false; @@ -1650,7 +1650,7 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, } } } else { - for (unsigned index = 0; index < length; index++) { + for (uint32_t index = 0; index < length; index++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; @@ -1794,7 +1794,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, JS_ASSERT(result == JSObject::ED_SPARSE); break; } - unsigned newlen = start + count; + uint32_t newlen = start + count; if (newlen > obj->getArrayLength()) obj->setDenseArrayLength(newlen); @@ -1837,7 +1837,7 @@ InitArrayElements(JSContext *cx, JSObject *obj, uint32_t start, uint32_t count, #if 0 static JSBool -InitArrayObject(JSContext *cx, JSObject *obj, unsigned length, const Value *vector) +InitArrayObject(JSContext *cx, JSObject *obj, uint32_t length, const Value *vector) { JS_ASSERT(obj->isArray()); @@ -1856,7 +1856,7 @@ InitArrayObject(JSContext *cx, JSObject *obj, unsigned length, const Value *vect obj->setDenseArrayInitializedLength(length); bool hole = false; - for (unsigned i = 0; i < length; i++) { + for (uint32_t i = 0; i < length; i++) { obj->setDenseArrayElement(i, vector[i]); hole |= vector[i].isMagic(JS_ARRAY_HOLE); } @@ -1899,7 +1899,7 @@ array_reverse(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - unsigned len; + uint32_t len; if (!js_GetLengthProperty(cx, obj, &len)) return false; @@ -1961,7 +1961,7 @@ array_reverse(JSContext *cx, unsigned argc, Value *vp) } while (false); Value lowval, hival; - for (unsigned i = 0, half = len / 2; i < half; i++) { + for (uint32_t i = 0, half = len / 2; i < half; i++) { JSBool hole, hole2; if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, i, &hole, &lowval) || @@ -2203,7 +2203,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - unsigned len; + uint32_t len; if (!js_GetLengthProperty(cx, obj, &len)) return false; if (len == 0) { @@ -2250,7 +2250,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) undefs = 0; bool allStrings = true; bool allInts = true; - for (unsigned i = 0; i < len; i++) { + for (uint32_t i = 0; i < len; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; @@ -2341,7 +2341,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) } } - if (!InitArrayElements(cx, obj, 0, unsigned(n), result, DontUpdateTypes)) + if (!InitArrayElements(cx, obj, 0, uint32_t(n), result, DontUpdateTypes)) return false; } @@ -2367,7 +2367,7 @@ js::array_sort(JSContext *cx, unsigned argc, Value *vp) static bool array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args) { - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return false; @@ -2451,7 +2451,7 @@ js::array_push(JSContext *cx, unsigned argc, Value *vp) static JSBool array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) { - unsigned index; + uint32_t index; if (!js_GetLengthProperty(cx, obj, &index)) return false; @@ -2477,7 +2477,7 @@ array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args) static JSBool array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args) { - unsigned index = obj->getArrayLength(); + uint32_t index = obj->getArrayLength(); if (index == 0) { args.rval().setUndefined(); return JS_TRUE; @@ -2538,7 +2538,7 @@ js::array_shift(JSContext *cx, unsigned argc, Value *vp) if (!obj) return JS_FALSE; - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -2567,7 +2567,7 @@ js::array_shift(JSContext *cx, unsigned argc, Value *vp) /* Slide down the array above the first element. */ AutoValueRooter tvr(cx); - for (unsigned i = 0; i < length; i++) { + for (uint32_t i = 0; i < length; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, obj, i + 1, &hole, tvr.addr()) || !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) { @@ -2590,7 +2590,7 @@ array_unshift(JSContext *cx, unsigned argc, Value *vp) if (!obj) return false; - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -2936,11 +2936,11 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp) return false; JSObject *nobj; - unsigned length; + uint32_t length; if (aobj->isDenseArray()) { length = aobj->getArrayLength(); const Value *vector = aobj->getDenseArrayElements(); - unsigned initlen = aobj->getDenseArrayInitializedLength(); + uint32_t initlen = aobj->getDenseArrayInitializedLength(); nobj = NewDenseCopiedArray(cx, initlen, vector); if (!nobj) return JS_FALSE; @@ -2967,7 +2967,7 @@ js::array_concat(JSContext *cx, unsigned argc, Value *vp) if (v.isObject()) { JSObject &obj = v.toObject(); if (ObjectClassIs(obj, ESClass_Array, cx)) { - unsigned alength; + uint32_t alength; if (!js_GetLengthProperty(cx, &obj, &alength)) return false; for (uint32_t slot = 0; slot < alength; slot++) { @@ -3000,7 +3000,7 @@ static JSBool array_slice(JSContext *cx, unsigned argc, Value *vp) { JSObject *nobj; - unsigned length, begin, end, slot; + uint32_t length, begin, end, slot; JSBool hole; CallArgs args = CallArgsFromVp(argc, vp); @@ -3025,7 +3025,7 @@ array_slice(JSContext *cx, unsigned argc, Value *vp) } else if (d > length) { d = length; } - begin = (unsigned)d; + begin = (uint32_t)d; if (args.hasDefined(1)) { if (!ToInteger(cx, args[1], &d)) @@ -3037,7 +3037,7 @@ array_slice(JSContext *cx, unsigned argc, Value *vp) } else if (d > length) { d = length; } - end = (unsigned)d; + end = (uint32_t)d; } } @@ -3081,7 +3081,7 @@ enum IndexOfKind { static JSBool array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) { - unsigned length, i, stop; + uint32_t length, i, stop; Value tosearch; int direction; JSBool hole; @@ -3110,14 +3110,14 @@ array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) goto not_found; i = 0; } else { - i = (unsigned)start; + i = (uint32_t)start; } } else if (start >= length) { if (mode == IndexOf) goto not_found; i = length - 1; } else { - i = (unsigned)start; + i = (uint32_t)start; } } @@ -3132,7 +3132,7 @@ array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args) for (;;) { Value elt; if (!JS_CHECK_OPERATION_LIMIT(cx) || - !GetElement(cx, obj, (unsigned)i, &hole, &elt)) { + !GetElement(cx, obj, (uint32_t)i, &hole, &elt)) { return JS_FALSE; } if (!hole) { @@ -3736,7 +3736,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) namespace js { static inline bool -EnsureNewArrayElements(JSContext *cx, JSObject *obj, unsigned length) +EnsureNewArrayElements(JSContext *cx, JSObject *obj, uint32_t length) { /* * If ensureElements creates dynamically allocated slots, then having diff --git a/js/src/jsarray.h b/js/src/jsarray.h index 7706293d1458..ffb054fc3c06 100644 --- a/js/src/jsarray.h +++ b/js/src/jsarray.h @@ -57,13 +57,13 @@ const uint32_t MAX_ARRAY_INDEX = 4294967294u; } inline JSBool -js_IdIsIndex(jsid id, unsigned *indexp) +js_IdIsIndex(jsid id, uint32_t *indexp) { if (JSID_IS_INT(id)) { int32_t i = JSID_TO_INT(id); if (i < 0) return JS_FALSE; - *indexp = (unsigned)i; + *indexp = (uint32_t)i; return JS_TRUE; } @@ -137,7 +137,7 @@ NewSlowEmptyArray(JSContext *cx); } /* namespace js */ extern JSBool -js_GetLengthProperty(JSContext *cx, JSObject *obj, unsigned *lengthp); +js_GetLengthProperty(JSContext *cx, JSObject *obj, uint32_t *lengthp); extern JSBool js_SetLengthProperty(JSContext *cx, JSObject *obj, double length); @@ -158,7 +158,7 @@ array_deleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, J * js_GetLengthProperty on aobj. */ extern bool -GetElements(JSContext *cx, JSObject *aobj, unsigned length, js::Value *vp); +GetElements(JSContext *cx, JSObject *aobj, uint32_t length, js::Value *vp); /* Natives exposed for optimization by the interpreter and JITs. */ diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index e5f7b417c2a5..dab76542fd02 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -712,9 +712,9 @@ js_CheckForStringIndex(jsid id) const jschar *cp = s; const jschar *end = s + n; - unsigned index = JS7_UNDEC(*cp++); - unsigned oldIndex = 0; - unsigned c = 0; + uint32_t index = JS7_UNDEC(*cp++); + uint32_t oldIndex = 0; + uint32_t c = 0; if (index != 0) { while (JS7_ISDEC(*cp)) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 592429712dab..a2273d29929f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -513,7 +513,7 @@ JS_FRIEND_API(bool) GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector *props); JS_FRIEND_API(bool) -StringIsArrayIndex(JSLinearString *str, unsigned *indexp); +StringIsArrayIndex(JSLinearString *str, uint32_t *indexp); JS_FRIEND_API(void) SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback); diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 329dc839979d..8106c65d6115 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -1296,7 +1296,7 @@ js_fun_apply(JSContext *cx, unsigned argc, Value *vp) * original version of ES5). */ JSObject *aobj = &vp[3].toObject(); - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, aobj, &length)) return false; diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index c516730af7f0..ba36807a7354 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -724,7 +724,7 @@ Chunk::removeFromAvailableList() * it to the most recently freed arena when we free, and forcing it to * the last alloc + 1 when we allocate. */ -unsigned +uint32_t Chunk::findDecommittedArenaOffset() { /* Note: lastFreeArenaOffset can be past the end of the list. */ diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 19490be3838e..51ad26c38b34 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2905,7 +2905,7 @@ BEGIN_CASE(JSOP_TABLESWITCH) int32_t high = GET_JUMP_OFFSET(pc2); i -= low; - if ((unsigned)i < (unsigned)(high - low + 1)) { + if ((uint32_t)i < (uint32_t)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; int32_t off = (int32_t) GET_JUMP_OFFSET(pc2); if (off) @@ -3557,9 +3557,9 @@ BEGIN_CASE(JSOP_INITELEM) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(unsigned(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); + JS_ASSERT(uint32_t(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT && - !js_SetLengthProperty(cx, obj, (unsigned) (JSID_TO_INT(id) + 1))) { + !js_SetLengthProperty(cx, obj, (uint32_t) (JSID_TO_INT(id) + 1))) { goto error; } } else { diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index ef396a47ec9e..1a27c332dcd9 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -232,7 +232,7 @@ GetPropertyOperation(JSContext *cx, jsbytecode *pc, const Value &lval, Value *vp if (lval.isObject()) { JSObject *obj = &lval.toObject(); if (obj->isArray()) { - unsigned length = obj->getArrayLength(); + uint32_t length = obj->getArrayLength(); *vp = NumberValue(length); return true; } @@ -804,13 +804,13 @@ SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &va do { if (obj->isDenseArray() && JSID_IS_INT(id)) { - unsigned length = obj->getDenseArrayInitializedLength(); + uint32_t length = obj->getDenseArrayInitializedLength(); int32_t i = JSID_TO_INT(id); - if ((unsigned)i < length) { + if ((uint32_t)i < length) { if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { if (js_PrototypeHasIndexedProperties(cx, obj)) break; - if ((unsigned)i >= obj->getArrayLength()) + if ((uint32_t)i >= obj->getArrayLength()) obj->setArrayLength(cx, i + 1); } obj->setDenseArrayElementWithType(cx, i, value); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index d6d5e4a65ca1..f1eef3864995 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2194,7 +2194,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDe if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx)) return JS_FALSE; - unsigned oldLen = obj->getArrayLength(); + uint32_t oldLen = obj->getArrayLength(); if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) { /* diff --git a/js/src/json.cpp b/js/src/json.cpp index 80f5087438cc..5d8ba1e0475c 100644 --- a/js/src/json.cpp +++ b/js/src/json.cpp @@ -500,7 +500,7 @@ JA(JSContext *cx, JSObject *obj, StringifyContext *scx) return JS_FALSE; /* Step 6. */ - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; @@ -649,7 +649,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu */ /* Step 4b(ii). */ - unsigned len; + uint32_t len; JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len)); if (replacer->isDenseArray()) len = JS_MIN(len, replacer->getDenseArrayCapacity()); @@ -659,7 +659,7 @@ js_Stringify(JSContext *cx, Value *vp, JSObject *replacer, Value space, StringBu return false; /* Step 4b(iii). */ - unsigned i = 0; + uint32_t i = 0; /* Step 4b(iv). */ for (; i < len; i++) { diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index ac0e94313cd7..41d0d90ef600 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1825,7 +1825,7 @@ GetLocal(SprintStack *ss, int i) if (obj->isBlock()) { uint32_t depth = obj->asBlock().stackDepth(); uint32_t count = obj->asBlock().slotCount(); - if (unsigned(i - depth) < unsigned(count)) + if (uint32_t(i - depth) < uint32_t(count)) return GetLocalInSlot(ss, i, int(i - depth), obj); } } @@ -1838,7 +1838,7 @@ GetLocal(SprintStack *ss, int i) if (obj->isBlock()) { uint32_t depth = obj->asBlock().stackDepth(); uint32_t count = obj->asBlock().slotCount(); - if (unsigned(i - depth) < unsigned(count)) + if (uint32_t(i - depth) < uint32_t(count)) return GetLocalInSlot(ss, i, int(i - depth), obj); } } diff --git a/js/src/jsproxy.cpp b/js/src/jsproxy.cpp index fe49b0296c71..8878c2cacd9c 100644 --- a/js/src/jsproxy.cpp +++ b/js/src/jsproxy.cpp @@ -494,11 +494,11 @@ ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props) return true; JSObject *obj = &array.toObject(); - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, obj, &length)) return false; - for (unsigned n = 0; n < length; ++n) { + for (uint32_t n = 0; n < length; ++n) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; Value v; diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index ea20fb175133..53f381a56ede 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -644,7 +644,7 @@ JSObject::addPropertyInternal(JSContext *cx, jsid id, { shape = self->lastProperty(); - unsigned index; + uint32_t index; bool indexed = js_IdIsIndex(id, &index); UnownedBaseShape *nbase; if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) { @@ -758,7 +758,7 @@ JSObject::putProperty(JSContext *cx, jsid id, RootedVar nbase(cx); { - unsigned index; + uint32_t index; bool indexed = js_IdIsIndex(id, &index); StackBaseShape base(self->lastProperty()->base()); base.updateGetterSetter(attrs, getter, setter); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index b2f03bc4a460..ec570757ebc7 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -877,31 +877,31 @@ out_of_range: * * Return the index of pat in text, or -1 if not found. */ -static const unsigned sBMHCharSetSize = 256; /* ISO-Latin-1 */ -static const unsigned sBMHPatLenMax = 255; /* skip table element is uint8_t */ +static const uint32_t sBMHCharSetSize = 256; /* ISO-Latin-1 */ +static const uint32_t sBMHPatLenMax = 255; /* skip table element is uint8_t */ static const int sBMHBadPattern = -2; /* return value if pat is not ISO-Latin-1 */ int -js_BoyerMooreHorspool(const jschar *text, unsigned textlen, - const jschar *pat, unsigned patlen) +js_BoyerMooreHorspool(const jschar *text, uint32_t textlen, + const jschar *pat, uint32_t patlen) { uint8_t skip[sBMHCharSetSize]; JS_ASSERT(0 < patlen && patlen <= sBMHPatLenMax); - for (unsigned i = 0; i < sBMHCharSetSize; i++) + for (uint32_t i = 0; i < sBMHCharSetSize; i++) skip[i] = (uint8_t)patlen; - unsigned m = patlen - 1; - for (unsigned i = 0; i < m; i++) { + uint32_t m = patlen - 1; + for (uint32_t i = 0; i < m; i++) { jschar c = pat[i]; if (c >= sBMHCharSetSize) return sBMHBadPattern; skip[c] = (uint8_t)(m - i); } jschar c; - for (unsigned k = m; + for (uint32_t k = m; k < textlen; k += ((c = text[k]) >= sBMHCharSetSize) ? patlen : skip[c]) { - for (unsigned i = k, j = m; ; i--, j--) { + for (uint32_t i = k, j = m; ; i--, j--) { if (text[i] != pat[j]) break; if (j == 0) @@ -912,8 +912,8 @@ js_BoyerMooreHorspool(const jschar *text, unsigned textlen, } struct MemCmp { - typedef unsigned Extent; - static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, unsigned patlen) { + typedef uint32_t Extent; + static JS_ALWAYS_INLINE Extent computeExtent(const jschar *, uint32_t patlen) { return (patlen - 1) * sizeof(jschar); } static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) { @@ -923,7 +923,7 @@ struct MemCmp { struct ManualCmp { typedef const jschar *Extent; - static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, unsigned patlen) { + static JS_ALWAYS_INLINE Extent computeExtent(const jschar *pat, uint32_t patlen) { return pat + patlen; } static JS_ALWAYS_INLINE bool match(const jschar *p, const jschar *t, Extent extent) { @@ -937,7 +937,7 @@ struct ManualCmp { template static int -UnrolledMatch(const jschar *text, unsigned textlen, const jschar *pat, unsigned patlen) +UnrolledMatch(const jschar *text, uint32_t textlen, const jschar *pat, uint32_t patlen) { JS_ASSERT(patlen > 0 && textlen > 0); const jschar *textend = text + textlen - (patlen - 1); @@ -982,8 +982,8 @@ UnrolledMatch(const jschar *text, unsigned textlen, const jschar *pat, unsigned } static JS_ALWAYS_INLINE int -StringMatch(const jschar *text, unsigned textlen, - const jschar *pat, unsigned patlen) +StringMatch(const jschar *text, uint32_t textlen, + const jschar *pat, uint32_t patlen) { if (patlen == 0) return 0; @@ -1046,7 +1046,7 @@ static const size_t sRopeMatchThresholdRatioLog2 = 5; * the 'match' outparam (-1 for not found). */ static bool -RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, unsigned patlen, int *match) +RopeMatch(JSContext *cx, JSString *textstr, const jschar *pat, uint32_t patlen, int *match) { JS_ASSERT(textstr->isRope()); @@ -1157,21 +1157,21 @@ str_indexOf(JSContext *cx, unsigned argc, Value *vp) if (!patstr) return false; - unsigned textlen = str->length(); + uint32_t textlen = str->length(); const jschar *text = str->getChars(cx); if (!text) return false; - unsigned patlen = patstr->length(); + uint32_t patlen = patstr->length(); const jschar *pat = patstr->chars(); - unsigned start; + uint32_t start; if (args.length() > 1) { if (args[1].isInt32()) { int i = args[1].toInt32(); if (i <= 0) { start = 0; - } else if (unsigned(i) > textlen) { + } else if (uint32_t(i) > textlen) { start = textlen; textlen = 0; } else { @@ -3957,7 +3957,7 @@ Decode(JSContext *cx, JSString *str, const jschar *reservedSet, Value *rval) goto report_bad_uri; if (!JS7_ISHEX(chars[k+1]) || !JS7_ISHEX(chars[k+2])) goto report_bad_uri; - unsigned B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); + uint32_t B = JS7_UNHEX(chars[k+1]) * 16 + JS7_UNHEX(chars[k+2]); k += 2; if (!(B & 0x80)) { c = (jschar)B; diff --git a/js/src/jstypedarray.cpp b/js/src/jstypedarray.cpp index 6f599a60ea55..0e274733f91f 100644 --- a/js/src/jstypedarray.cpp +++ b/js/src/jstypedarray.cpp @@ -81,7 +81,7 @@ using namespace js::types; static const uint8_t ARRAYBUFFER_RESERVED_SLOTS = JSObject::MAX_FIXED_SLOTS - 1; static bool -ValueIsLength(JSContext *cx, const Value &v, unsigned *len) +ValueIsLength(JSContext *cx, const Value &v, uint32_t *len) { if (v.isInt32()) { int32_t i = v.toInt32(); @@ -96,7 +96,7 @@ ValueIsLength(JSContext *cx, const Value &v, unsigned *len) if (JSDOUBLE_IS_NaN(d)) return false; - unsigned length = unsigned(d); + uint32_t length = uint32_t(d); if (d != double(length)) return false; @@ -730,9 +730,9 @@ TypedArray::getTypedArray(JSObject *obj) } inline bool -TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, unsigned *ip) +TypedArray::isArrayIndex(JSContext *cx, JSObject *obj, jsid id, uint32_t *ip) { - unsigned index; + uint32_t index; if (js_IdIsIndex(id, &index) && index < getLength(obj)) { if (ip) *ip = index; @@ -1264,7 +1264,7 @@ class TypedArrayTemplate return true; } - unsigned index; + uint32_t index; // We can't just chain to js_SetPropertyHelper, because we're not a normal object. if (!isArrayIndex(cx, tarray, id, &index)) { // Silent ignore is better than an exception here, because @@ -1509,7 +1509,7 @@ class TypedArrayTemplate /* N.B. there may not be an argv[-2]/argv[-1]. */ /* () or (number) */ - unsigned len = 0; + uint32_t len = 0; if (argc == 0 || ValueIsLength(cx, argv[0], &len)) { JSObject *bufobj = createBufferWithSizeAndCount(cx, len); if (!bufobj) @@ -1663,7 +1663,7 @@ class TypedArrayTemplate if (!copyFromTypedArray(cx, obj, src, offset)) return false; } else { - unsigned len; + uint32_t len; if (!js_GetLengthProperty(cx, arg0, &len)) return false; @@ -1734,7 +1734,7 @@ class TypedArrayTemplate * Otherwise create a new typed array and copy len properties from the * object. */ - unsigned len; + uint32_t len; if (!js_GetLengthProperty(cx, other, &len)) return NULL; @@ -1824,7 +1824,7 @@ class TypedArrayTemplate static bool copyFromArray(JSContext *cx, JSObject *thisTypedArrayObj, - JSObject *ar, unsigned len, unsigned offset = 0) + JSObject *ar, uint32_t len, uint32_t offset = 0) { thisTypedArrayObj = getTypedArray(thisTypedArrayObj); JS_ASSERT(thisTypedArrayObj); @@ -1858,7 +1858,7 @@ class TypedArrayTemplate } static bool - copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, unsigned offset) + copyFromTypedArray(JSContext *cx, JSObject *thisTypedArrayObj, JSObject *tarray, uint32_t offset) { thisTypedArrayObj = getTypedArray(thisTypedArrayObj); JS_ASSERT(thisTypedArrayObj); @@ -1935,7 +1935,7 @@ class TypedArrayTemplate } static bool - copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, unsigned offset) + copyFromWithOverlap(JSContext *cx, JSObject *self, JSObject *tarray, uint32_t offset) { JS_ASSERT(offset <= getLength(self)); @@ -2532,13 +2532,13 @@ js_IsTypedArray(JSObject *obj) } JS_FRIEND_API(JSObject *) -js_CreateArrayBuffer(JSContext *cx, unsigned nbytes) +js_CreateArrayBuffer(JSContext *cx, uint32_t nbytes) { return ArrayBuffer::create(cx, nbytes); } JS_FRIEND_API(JSObject *) -JS_NewArrayBuffer(JSContext *cx, unsigned nbytes) +JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes) { return js_CreateArrayBuffer(cx, nbytes); } @@ -2581,7 +2581,7 @@ TypedArrayConstruct(JSContext *cx, int atype, unsigned argc, Value *argv) } JS_FRIEND_API(JSObject *) -js_CreateTypedArray(JSContext *cx, int atype, unsigned nelements) +js_CreateTypedArray(JSContext *cx, int atype, uint32_t nelements) { JS_ASSERT(atype >= 0 && atype < TypedArray::TYPE_MAX); diff --git a/js/src/jstypedarray.h b/js/src/jstypedarray.h index ecf65cbb176d..bfed3c2c73a7 100644 --- a/js/src/jstypedarray.h +++ b/js/src/jstypedarray.h @@ -252,7 +252,7 @@ struct JS_FRIEND_API(TypedArray) { public: static bool - isArrayIndex(JSContext *cx, JSObject *obj, jsid id, unsigned *ip = NULL); + isArrayIndex(JSContext *cx, JSObject *obj, jsid id, uint32_t *ip = NULL); static inline uint32_t slotWidth(int atype) { switch (atype) { @@ -335,7 +335,7 @@ JS_FRIEND_API(JSBool) JS_IsArrayBufferObject(JSObject *obj); JS_FRIEND_API(JSObject *) -JS_NewArrayBuffer(JSContext *cx, unsigned nbytes); +JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes); JS_FRIEND_API(uint32_t) JS_GetArrayBufferByteLength(JSObject *obj); diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index b2a548fe7282..97a0ffcf30f1 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4665,7 +4665,7 @@ HasFunctionProperty(JSContext *cx, JSObject *obj, jsid funid, JSBool *found) } static bool -IdValIsIndex(JSContext *cx, jsval id, unsigned *indexp, bool *isIndex) +IdValIsIndex(JSContext *cx, jsval id, uint32_t *indexp, bool *isIndex) { if (JSVAL_IS_INT(id)) { int32_t i = JSVAL_TO_INT(id); @@ -4673,7 +4673,7 @@ IdValIsIndex(JSContext *cx, jsval id, unsigned *indexp, bool *isIndex) *isIndex = false; return true; } - *indexp = (unsigned)i; + *indexp = (uint32_t)i; *isIndex = true; return true; } @@ -6198,7 +6198,7 @@ static JSBool xml_namespace(JSContext *cx, unsigned argc, jsval *vp) { JSLinearString *prefix, *nsprefix; - unsigned i, length; + uint32_t i, length; JSObject *ns; NON_LIST_XML_METHOD_PROLOG; diff --git a/js/src/methodjit/MonoIC.cpp b/js/src/methodjit/MonoIC.cpp index 5c6191d4d1bb..b5b1183ea41f 100644 --- a/js/src/methodjit/MonoIC.cpp +++ b/js/src/methodjit/MonoIC.cpp @@ -1143,7 +1143,7 @@ ic::SplatApplyArgs(VMFrame &f) /* Steps 4-5. */ JSObject *aobj = &vp[3].toObject(); - unsigned length; + uint32_t length; if (!js_GetLengthProperty(cx, aobj, &length)) THROWV(false); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 7daffd0a6e50..2607c5bed260 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -234,13 +234,13 @@ stubs::SetElem(VMFrame &f) do { if (obj->isDenseArray() && JSID_IS_INT(id)) { - unsigned length = obj->getDenseArrayInitializedLength(); + uint32_t length = obj->getDenseArrayInitializedLength(); int32_t i = JSID_TO_INT(id); - if ((unsigned)i < length) { + if ((uint32_t)i < length) { if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) { if (js_PrototypeHasIndexedProperties(cx, obj)) break; - if ((unsigned)i >= obj->getArrayLength()) + if ((uint32_t)i >= obj->getArrayLength()) obj->setArrayLength(cx, i + 1); } obj->setDenseArrayElementWithType(cx, i, rval); @@ -1034,8 +1034,8 @@ stubs::InitElem(VMFrame &f, uint32_t last) if (rref.isMagic(JS_ARRAY_HOLE)) { JS_ASSERT(obj->isArray()); JS_ASSERT(JSID_IS_INT(id)); - JS_ASSERT(unsigned(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); - if (last && !js_SetLengthProperty(cx, obj, (unsigned) (JSID_TO_INT(id) + 1))) + JS_ASSERT(uint32_t(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX); + if (last && !js_SetLengthProperty(cx, obj, (uint32_t) (JSID_TO_INT(id) + 1))) THROW(); } else { if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE)) @@ -1584,7 +1584,7 @@ stubs::TableSwitch(VMFrame &f, jsbytecode *origPc) pc += JUMP_OFFSET_LEN; tableIdx -= low; - if ((unsigned) tableIdx < (unsigned)(high - low + 1)) { + if ((uint32_t) tableIdx < (uint32_t)(high - low + 1)) { pc += JUMP_OFFSET_LEN * tableIdx; if (uint32_t candidateOffset = GET_JUMP_OFFSET(pc)) jumpOffset = candidateOffset; diff --git a/js/src/shell/jsheaptools.cpp b/js/src/shell/jsheaptools.cpp index 481578fe0a6c..b93288ab696c 100644 --- a/js/src/shell/jsheaptools.cpp +++ b/js/src/shell/jsheaptools.cpp @@ -532,7 +532,7 @@ ReferenceFinder::addReferrer(jsval referrer, Path *path) JS_ASSERT(JS_IsArrayObject(context, array)); /* Append our referrer to this array. */ - unsigned length; + uint32_t length; return JS_GetArrayLength(context, array, &length) && JS_SetElement(context, array, length, &referrer); } diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index bf6a880b7fa0..85eb682d27a0 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -1214,7 +1214,7 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation, // Iterate over symbols array, installing symbols on targetObj: - unsigned symbolCount = 0; + uint32_t symbolCount = 0; if (!JS_GetArrayLength(mContext, symbolsObj, &symbolCount)) { return ReportOnCaller(cxhelper, ERROR_GETTING_ARRAY_LENGTH, PromiseFlatCString(aLocation).get()); @@ -1224,7 +1224,7 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation, nsCAutoString logBuffer; #endif - for (unsigned i = 0; i < symbolCount; ++i) { + for (uint32_t i = 0; i < symbolCount; ++i) { jsval val; jsid symbolId; diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index b672e5bdbca1..bacd8429bcf7 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -190,7 +190,7 @@ private: public: static JSBool GetTypeForArray(XPCCallContext& ccx, JSObject* array, - unsigned length, + uint32_t length, nsXPTType* resultType, nsID* resultID); }; @@ -215,7 +215,7 @@ XPCArrayHomogenizer::StateTable[tTypeCount][tTypeCount-1] = { // static JSBool XPCArrayHomogenizer::GetTypeForArray(XPCCallContext& ccx, JSObject* array, - unsigned length, + uint32_t length, nsXPTType* resultType, nsID* resultID) { Type state = tUnk; diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index 098d9d88ac27..3cbb64efca33 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -607,7 +607,7 @@ GetArrayIndexFromId(JSContext *cx, jsid id) if (NS_LIKELY((unsigned)s >= 'a' && (unsigned)s <= 'z')) return -1; - unsigned i; + uint32_t i; JSLinearString *str = js::AtomToLinearString(JSID_TO_ATOM(id)); return js::StringIsArrayIndex(str, &i) ? i : -1; } diff --git a/startupcache/test/TestStartupCache.cpp b/startupcache/test/TestStartupCache.cpp index 90894fb2d43f..633318468555 100644 --- a/startupcache/test/TestStartupCache.cpp +++ b/startupcache/test/TestStartupCache.cpp @@ -359,7 +359,7 @@ GetHistogramCounts(const char *testmsg, JSContext *cx, jsval *counts) nsresult CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after) { - unsigned before_size, after_size; + uint32_t before_size, after_size; if (!(JS_GetArrayLength(cx, before, &before_size) && JS_GetArrayLength(cx, after, &after_size))) { return NS_ERROR_UNEXPECTED; @@ -369,7 +369,7 @@ CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after) return NS_ERROR_UNEXPECTED; } - for (unsigned i = 0; i < before_size; ++i) { + for (uint32_t i = 0; i < before_size; ++i) { jsval before_num, after_num; if (!(JS_GetElement(cx, before, i, &before_num) diff --git a/toolkit/components/places/History.cpp b/toolkit/components/places/History.cpp index dcf0ddf8720c..6072a6cf572f 100644 --- a/toolkit/components/places/History.cpp +++ b/toolkit/components/places/History.cpp @@ -318,7 +318,7 @@ GetIntFromJSObject(JSContext* aCtx, nsresult GetJSObjectFromArray(JSContext* aCtx, JSObject* aArray, - unsigned aIndex, + uint32_t aIndex, JSObject** _rooter) { NS_PRECONDITION(JS_IsArrayObject(aCtx, aArray), @@ -2104,7 +2104,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED); NS_ENSURE_TRUE(!JSVAL_IS_PRIMITIVE(aPlaceInfos), NS_ERROR_INVALID_ARG); - unsigned infosLength = 1; + uint32_t infosLength = 1; JSObject* infos; if (JS_IsArrayObject(aCtx, JSVAL_TO_OBJECT(aPlaceInfos))) { infos = JSVAL_TO_OBJECT(aPlaceInfos); @@ -2122,7 +2122,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, } nsTArray visitData; - for (unsigned i = 0; i < infosLength; i++) { + for (uint32_t i = 0; i < infosLength; i++) { JSObject* info; nsresult rv = GetJSObjectFromArray(aCtx, infos, i, &info); NS_ENSURE_SUCCESS(rv, rv); @@ -2168,7 +2168,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, } NS_ENSURE_ARG(visits); - unsigned visitsLength = 0; + uint32_t visitsLength = 0; if (visits) { (void)JS_GetArrayLength(aCtx, visits, &visitsLength); } @@ -2176,7 +2176,7 @@ History::UpdatePlaces(const jsval& aPlaceInfos, // Check each visit, and build our array of VisitData objects. visitData.SetCapacity(visitData.Length() + visitsLength); - for (unsigned j = 0; j < visitsLength; j++) { + for (uint32_t j = 0; j < visitsLength; j++) { JSObject* visit; rv = GetJSObjectFromArray(aCtx, visits, j, &visit); NS_ENSURE_SUCCESS(rv, rv); From f5e01f173789612b948c052dc9738b0dcf064f0f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 20 Feb 2012 21:02:24 -0800 Subject: [PATCH 83/88] Bug 729008 - Add memory reporter for FramePropertyTable. r=bz. --HG-- extra : rebase_source : e366cd84d50b5277f9949b340d44190371e753f8 --- layout/base/FramePropertyTable.cpp | 14 ++++++++ layout/base/FramePropertyTable.h | 19 +++++++++++ layout/base/nsPresContext.cpp | 27 +++++++++++++++ layout/base/nsPresContext.h | 53 +++++++++++------------------- xpcom/glue/nsTHashtable.h | 2 +- 5 files changed, 80 insertions(+), 35 deletions(-) diff --git a/layout/base/FramePropertyTable.cpp b/layout/base/FramePropertyTable.cpp index dbefe69d8a4d..9e8012db62a8 100644 --- a/layout/base/FramePropertyTable.cpp +++ b/layout/base/FramePropertyTable.cpp @@ -258,4 +258,18 @@ FramePropertyTable::DeleteAll() mEntries.EnumerateEntries(DeleteEnumerator, nsnull); } +size_t +FramePropertyTable::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return mEntries.SizeOfExcludingThis(SizeOfPropertyTableEntryExcludingThis, + aMallocSizeOf); +} + +/* static */ size_t +FramePropertyTable::SizeOfPropertyTableEntryExcludingThis(Entry* aEntry, + nsMallocSizeOfFun aMallocSizeOf, void *) +{ + return aEntry->mProp.SizeOfExcludingThis(aMallocSizeOf); +} + } diff --git a/layout/base/FramePropertyTable.h b/layout/base/FramePropertyTable.h index 89ab723078a1..71f9f8cbbcf3 100644 --- a/layout/base/FramePropertyTable.h +++ b/layout/base/FramePropertyTable.h @@ -154,6 +154,8 @@ public: */ void DeleteAll(); + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + protected: /** * Stores a property descriptor/value pair. It can also be used to @@ -179,6 +181,20 @@ protected: } } + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) { + size_t n = 0; + // We don't need to measure mProperty because it always points to static + // memory. As for mValue: if it's a single value we can't measure it, + // because the type is opaque; if it's an array, we measure the array + // storage, but we can't measure the individual values, again because + // their types are opaque. + if (IsArray()) { + nsTArray* array = ToArray(); + n += array->SizeOfExcludingThis(aMallocSizeOf); + } + return n; + } + const FramePropertyDescriptor* mProperty; void* mValue; }; @@ -217,6 +233,9 @@ protected: static void DeleteAllForEntry(Entry* aEntry); static PLDHashOperator DeleteEnumerator(Entry* aEntry, void* aArg); + static size_t SizeOfPropertyTableEntryExcludingThis(Entry* aEntry, + nsMallocSizeOfFun aMallocSizeOf, void *); + nsTHashtable mEntries; nsIFrame* mLastFrame; Entry* mLastEntry; diff --git a/layout/base/nsPresContext.cpp b/layout/base/nsPresContext.cpp index a89e07f38d1f..343debcaa4c6 100644 --- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -2357,6 +2357,16 @@ nsPresContext::CheckForInterrupt(nsIFrame* aFrame) return mHasPendingInterrupt; } +size_t +nsPresContext::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf); + mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of other members may be added later if DMD finds it is + // worthwhile. +} + bool nsPresContext::IsRootContentDocument() { @@ -2786,3 +2796,20 @@ nsRootPresContext::FlushWillPaintObservers() observers[i]->Run(); } } + +size_t +nsRootPresContext::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return nsPresContext::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mNotifyDidPaintTimer + // - mRegisteredPlugins + // - mWillPaintObservers + // - mWillPaintFallbackEvent + // + // The following member are not measured: + // - mUpdatePluginGeometryForFrame, because it is non-owning +} + diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index cd2ac2835c22..e367c5f95bc4 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -968,22 +968,8 @@ public: bool MayHaveFixedBackgroundFrames() { return mMayHaveFixedBackgroundFrames; } void SetHasFixedBackgroundFrame() { mMayHaveFixedBackgroundFrames = true; } - virtual NS_MUST_OVERRIDE size_t - SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const { - size_t n = 0; - LangGroupFontPrefs *langGroupfontPrefs = mLangGroupFontPrefs.mNext; - while (langGroupfontPrefs) { - // XXX this doesn't include allocations made by the nsFont members - n += sizeof(LangGroupFontPrefs); - langGroupfontPrefs = langGroupfontPrefs->mNext; - } - return n; - - // Measurement of other members may be added later if DMD finds it is - // worthwhile. - } - virtual NS_MUST_OVERRIDE size_t - SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { + virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); } @@ -1037,6 +1023,22 @@ protected: NS_FONT_STRETCH_NORMAL, 0, 0) {} + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const { + size_t n = 0; + LangGroupFontPrefs *curr = mNext; + while (curr) { + n += aMallocSizeOf(curr); + + // Measurement of the following members may be added later if DMD finds + // it is worthwhile: + // - mLangGroup + // - mDefault*Font + + curr = curr->mNext; + } + return n; + } + nsCOMPtr mLangGroup; nscoord mMinimumFontSize; nsFont mDefaultVariableFont; @@ -1375,24 +1377,7 @@ public: */ void FlushWillPaintObservers(); - virtual NS_MUST_OVERRIDE size_t - SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const MOZ_OVERRIDE { - return nsPresContext::SizeOfExcludingThis(aMallocSizeOf); - - // Measurement of the following members may be added later if DMD finds it is - // worthwhile: - // - mNotifyDidPaintTimer - // - mRegisteredPlugins - // - mWillPaintObservers - // - mWillPaintFallbackEvent - // - // The following member are not measured: - // - mUpdatePluginGeometryForFrame, because it is non-owning - } - virtual NS_MUST_OVERRIDE size_t - SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const MOZ_OVERRIDE { - return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); - } + virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const MOZ_OVERRIDE; protected: class RunWillPaintObservers : public nsRunnable { diff --git a/xpcom/glue/nsTHashtable.h b/xpcom/glue/nsTHashtable.h index f0404d5c2e84..762517fb0aa3 100644 --- a/xpcom/glue/nsTHashtable.h +++ b/xpcom/glue/nsTHashtable.h @@ -286,7 +286,7 @@ public: * @return the summed size of all the entries */ size_t SizeOfExcludingThis(SizeOfEntryExcludingThisFun sizeOfEntryExcludingThis, - nsMallocSizeOfFun mallocSizeOf, void *userArg = NULL) + nsMallocSizeOfFun mallocSizeOf, void *userArg = NULL) const { if (IsInitialized()) { s_SizeOfArgs args = { sizeOfEntryExcludingThis, userArg }; From 734f4d43a613d8454781e8dfb4dc08f28dec1df3 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 6 Mar 2012 00:43:45 -0800 Subject: [PATCH 84/88] Bug 732744 - rm AutoPreserveEnumerators (r=dvander) --HG-- extra : rebase_source : f28eebcf3a3e14f4db0299b07b28f8e7a783b84a --- js/src/jsinterp.cpp | 32 ++++++++++++++++++++++---------- js/src/jsinterpinlines.h | 15 --------------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 51ad26c38b34..d1bf19662f5b 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -440,6 +440,21 @@ js::RunScript(JSContext *cx, JSScript *script, StackFrame *fp) } } +#ifdef DEBUG + struct CheckStackBalance { + JSContext *cx; + StackFrame *fp; + JSObject *enumerators; + CheckStackBalance(JSContext *cx) + : cx(cx), fp(cx->fp()), enumerators(cx->enumerators) + {} + ~CheckStackBalance() { + JS_ASSERT(fp == cx->fp()); + JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators); + } + } check(cx); +#endif + #ifdef JS_METHODJIT mjit::CompileStatus status; status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(), @@ -511,12 +526,9 @@ js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct) return false; /* Run function until JSOP_STOP, JSOP_RETURN or error. */ - JSBool ok; - { - AutoPreserveEnumerators preserve(cx); - ok = RunScript(cx, fun->script(), fp); - } + JSBool ok = RunScript(cx, fun->script(), fp); + /* Propagate the return value out. */ args.rval() = fp->returnValue(); JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive()); return ok; @@ -653,17 +665,17 @@ js::ExecuteKernel(JSContext *cx, JSScript *script, JSObject &scopeChain, const V TypeScript::SetThis(cx, script, fp->thisValue()); - AutoPreserveEnumerators preserve(cx); - JSBool ok = RunScript(cx, script, fp); - if (result && ok) - *result = fp->returnValue(); + bool ok = RunScript(cx, script, fp); if (fp->isStrictEvalFrame()) js_PutCallObject(fp); Probes::stopExecution(cx, script); - return !!ok; + /* Propgate the return value out. */ + if (result) + *result = fp->returnValue(); + return ok; } bool diff --git a/js/src/jsinterpinlines.h b/js/src/jsinterpinlines.h index 1a27c332dcd9..9c11ef007a28 100644 --- a/js/src/jsinterpinlines.h +++ b/js/src/jsinterpinlines.h @@ -61,21 +61,6 @@ namespace js { -class AutoPreserveEnumerators { - JSContext *cx; - JSObject *enumerators; - - public: - AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators) - { - } - - ~AutoPreserveEnumerators() - { - cx->enumerators = enumerators; - } -}; - /* * Compute the implicit |this| parameter for a call expression where the callee * funval was resolved from an unqualified name reference to a property on obj From b620c9004b3468189327e170b2775585fb8f3003 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 6 Mar 2012 00:52:42 -0800 Subject: [PATCH 85/88] Bug 733310 - factor out inc/dec interpreter logic (r=bhackett) --HG-- extra : rebase_source : 10d8277f4446f97e9a158f740d1a398341ac2382 --- js/src/jsinterp.cpp | 108 +++++++++++++++----------------------------- 1 file changed, 36 insertions(+), 72 deletions(-) diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index d1bf19662f5b..96ed1a0b741c 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -989,31 +989,35 @@ js::UnwindScope(JSContext *cx, uint32_t stackDepth) } /* - * Find the results of incrementing or decrementing *vp. For pre-increments, - * both *vp and *vp2 will contain the result on return. For post-increments, - * vp will contain the original value converted to a number and vp2 will get - * the result. Both vp and vp2 must be roots. + * Increment/decrement the value 'v'. The resulting value is stored in *slot. + * The result of the expression (taking into account prefix/postfix) is stored + * in *expr. */ static bool -DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2) +DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value *slot, Value *expr) { - if (cs->format & JOF_POST) { - double d; - if (!ToNumber(cx, *vp, &d)) - return JS_FALSE; - vp->setNumber(d); - (cs->format & JOF_INC) ? ++d : --d; - vp2->setNumber(d); - return JS_TRUE; + const JSCodeSpec &cs = js_CodeSpec[*pc]; + + if (v.isInt32()) { + int32_t i = v.toInt32(); + if (i > JSVAL_INT_MIN && i < JSVAL_INT_MAX) { + int32_t sum = i + (cs.format & JOF_INC ? 1 : -1); + *slot = Int32Value(sum); + *expr = (cs.format & JOF_POST) ? Int32Value(i) : *slot; + return true; + } } double d; - if (!ToNumber(cx, *vp, &d)) - return JS_FALSE; - (cs->format & JOF_INC) ? ++d : --d; - vp->setNumber(d); - *vp2 = *vp; - return JS_TRUE; + if (!ToNumber(cx, *slot, &d)) + return false; + + double sum = d + (cs.format & JOF_INC ? 1 : -1); + *slot = NumberValue(sum); + *expr = (cs.format & JOF_POST) ? NumberValue(d) : *slot; + + TypeScript::MonitorOverflow(cx, script, pc); + return true; } const Value & @@ -1091,13 +1095,6 @@ js::FindUpvarFrame(JSContext *cx, unsigned targetLevel) goto error; \ JS_END_MACRO -/* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */ -static JS_ALWAYS_INLINE bool -CanIncDecWithoutOverflow(int32_t i) -{ - return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX); -} - /* * Threaded interpretation via computed goto appears to be well-supported by * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., @@ -2501,62 +2498,29 @@ BEGIN_CASE(JSOP_GNAMEDEC) /* No-op */ END_CASE(JSOP_INCPROP) -{ - int incr, incr2; - uint32_t slot; - Value *vp; - - /* Position cases so the most frequent i++ does not need a jump. */ BEGIN_CASE(JSOP_DECARG) - incr = -1; incr2 = -1; goto do_arg_incop; BEGIN_CASE(JSOP_ARGDEC) - incr = -1; incr2 = 0; goto do_arg_incop; BEGIN_CASE(JSOP_INCARG) - incr = 1; incr2 = 1; goto do_arg_incop; BEGIN_CASE(JSOP_ARGINC) - incr = 1; incr2 = 0; - - do_arg_incop: - slot = GET_ARGNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numFormalArgs()); - vp = argv + slot; - goto do_int_fast_incop; +{ + Value &arg = regs.fp()->formalArg(GET_ARGNO(regs.pc)); + if (!DoIncDec(cx, script, regs.pc, arg, &arg, ®s.sp[0])) + goto error; + regs.sp++; +} +END_CASE(JSOP_ARGINC); BEGIN_CASE(JSOP_DECLOCAL) - incr = -1; incr2 = -1; goto do_local_incop; BEGIN_CASE(JSOP_LOCALDEC) - incr = -1; incr2 = 0; goto do_local_incop; BEGIN_CASE(JSOP_INCLOCAL) - incr = 1; incr2 = 1; goto do_local_incop; BEGIN_CASE(JSOP_LOCALINC) - incr = 1; incr2 = 0; - - /* - * do_local_incop comes right before do_int_fast_incop as we want to - * avoid an extra jump for variable cases as local++ is more frequent - * than arg++. - */ - do_local_incop: - slot = GET_SLOTNO(regs.pc); - JS_ASSERT(slot < regs.fp()->numSlots()); - vp = regs.fp()->slots() + slot; - - do_int_fast_incop: - int32_t tmp; - if (JS_LIKELY(vp->isInt32() && CanIncDecWithoutOverflow(tmp = vp->toInt32()))) { - vp->getInt32Ref() = tmp + incr; - JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length); - PUSH_INT32(tmp + incr2); - } else { - PUSH_COPY(*vp); - if (!DoIncDec(cx, &js_CodeSpec[op], ®s.sp[-1], vp)) - goto error; - TypeScript::MonitorOverflow(cx, script, regs.pc); - } - len = JSOP_INCARG_LENGTH; - JS_ASSERT(len == js_CodeSpec[op].length); - DO_NEXT_OP(len); +{ + Value &local = regs.fp()->localSlot(GET_SLOTNO(regs.pc)); + if (!DoIncDec(cx, script, regs.pc, local, &local, ®s.sp[0])) + goto error; + regs.sp++; } +END_CASE(JSOP_LOCALINC) BEGIN_CASE(JSOP_THIS) if (!ComputeThis(cx, regs.fp())) From f4f11fbe7f747b95ab03601eb65ac5186ad449a0 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 6 Mar 2012 18:49:00 -0800 Subject: [PATCH 86/88] Bug 704259: part 1, refactor use of cx->generatingError without changing behavior, r=luke --- js/src/jscntxt.h | 13 +++++-------- js/src/jsexn.cpp | 44 ++++++++++++++------------------------------ 2 files changed, 19 insertions(+), 38 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index def1e7b76bac..4d1afcb45f05 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -830,11 +830,11 @@ struct JSContext : js::ContextFriendFields bool hasVersionOverride; /* Exception state -- the exception member is a GC root by definition. */ - JSBool throwing; /* is there a pending exception? */ - js::Value exception; /* most-recently-thrown exception */ + JSBool throwing; /* is there a pending exception? */ + js::Value exception; /* most-recently-thrown exception */ /* Per-context run options. */ - unsigned runOptions; /* see jsapi.h for JSOPTION_* */ + unsigned runOptions; /* see jsapi.h for JSOPTION_* */ public: int32_t reportGranularity; /* see jsprobes.h */ @@ -844,11 +844,8 @@ struct JSContext : js::ContextFriendFields js::AutoResolving *resolvingList; - /* - * True if generating an error, to prevent runaway recursion. - * NB: generatingError packs with throwing below. - */ - bool generatingError; + /* True if generating an error, to prevent runaway recursion. */ + bool generatingError; /* GC heap compartment. */ JSCompartment *compartment; diff --git a/js/src/jsexn.cpp b/js/src/jsexn.cpp index d05144b38071..c6180b2ce33f 100644 --- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -1084,7 +1084,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, */ JS_ASSERT(reportp); if (JSREPORT_IS_WARNING(reportp->flags)) - return JS_FALSE; + return false; /* Find the exception index associated with this error. */ errorNumber = (JSErrNum) reportp->errorNumber; @@ -1107,19 +1107,12 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, * with the given error number. */ if (exn == JSEXN_NONE) - return JS_FALSE; + return false; - /* - * Prevent runaway recursion, via cx->generatingError. If an out-of-memory - * error occurs, no exception object will be created, but we don't assume - * that OOM is the only kind of error that subroutines of this function - * called below might raise. - */ + /* Prevent infinite recursion. */ if (cx->generatingError) - return JS_FALSE; - - MUST_FLOW_THROUGH("out"); - cx->generatingError = JS_TRUE; + return false; + AutoScopedAssign asa(&cx->generatingError, false); /* Protect the newly-created strings below from nesting GCs. */ PodArrayZero(tv); @@ -1132,43 +1125,34 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp, */ ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto); if (!ok) - goto out; + return false; tv[0] = OBJECT_TO_JSVAL(errProto); errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL); - if (!errObject) { - ok = JS_FALSE; - goto out; - } + if (!errObject) + return false; tv[1] = OBJECT_TO_JSVAL(errObject); messageStr = JS_NewStringCopyZ(cx, message); - if (!messageStr) { - ok = JS_FALSE; - goto out; - } + if (!messageStr) + return false; tv[2] = STRING_TO_JSVAL(messageStr); filenameStr = JS_NewStringCopyZ(cx, reportp->filename); - if (!filenameStr) { - ok = JS_FALSE; - goto out; - } + if (!filenameStr) + return false; tv[3] = STRING_TO_JSVAL(filenameStr); ok = InitExnPrivate(cx, errObject, messageStr, filenameStr, reportp->lineno, reportp, exn); if (!ok) - goto out; + return false; JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject)); /* Flag the error report passed in to indicate an exception was raised. */ reportp->flags |= JSREPORT_EXCEPTION; - -out: - cx->generatingError = JS_FALSE; - return ok; + return true; } JSBool From 1165a003f2fda5764d483b587b59832ab437f291 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 6 Mar 2012 18:49:05 -0800 Subject: [PATCH 87/88] Bug 704259: part 2, don't set cx->generatingError in JS_ReportPendingException, r=luke --- js/src/jsapi.cpp | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index c72da6c5cf1a..04af605afd78 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6467,23 +6467,10 @@ JS_ClearPendingException(JSContext *cx) JS_PUBLIC_API(JSBool) JS_ReportPendingException(JSContext *cx) { - JSBool ok; - bool save; - AssertNoGC(cx); CHECK_REQUEST(cx); - /* - * Set cx->generatingError to suppress the standard error-to-exception - * conversion done by all {js,JS}_Report* functions except for OOM. The - * cx->generatingError flag was added to suppress recursive divergence - * under js_ErrorToException, but it serves for our purposes here too. - */ - save = cx->generatingError; - cx->generatingError = JS_TRUE; - ok = js_ReportUncaughtException(cx); - cx->generatingError = save; - return ok; + return js_ReportUncaughtException(cx); } struct JSExceptionState { From ed12e36678a9f42048232a3024cd5a87870b4852 Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 6 Mar 2012 18:54:42 -0800 Subject: [PATCH 88/88] Bug 733146: Add needed import to results.py, r=dmandelin --- js/src/tests/results.py | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/tests/results.py b/js/src/tests/results.py index c1cc8a251b9c..34042f957bdb 100644 --- a/js/src/tests/results.py +++ b/js/src/tests/results.py @@ -1,4 +1,5 @@ import re +from subprocess import list2cmdline class TestOutput: """Output from a test run."""