From 52cd2b3167896434e11f279d348898c8f0564649 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 01/49] Bug 682180 - Allocate executable trampoline buffers in darwin 10 and beyond. r=khuey --HG-- rename : js/src/ctypes/libffi.patch => js/src/ctypes/patches-libffi/00-base.patch --- js/src/ctypes/libffi/configure | 2 +- js/src/ctypes/libffi/configure.ac | 3 +- .../00-base.patch} | 0 .../ctypes/patches-libffi/01-bug-670719.patch | 28 +++++++++++++++++++ .../ctypes/patches-libffi/02-bug-682180.patch | 27 ++++++++++++++++++ 5 files changed, 58 insertions(+), 2 deletions(-) rename js/src/ctypes/{libffi.patch => patches-libffi/00-base.patch} (100%) create mode 100644 js/src/ctypes/patches-libffi/01-bug-670719.patch create mode 100644 js/src/ctypes/patches-libffi/02-bug-682180.patch diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure index 2c08e1bdb12d..37e305514501 100755 --- a/js/src/ctypes/libffi/configure +++ b/js/src/ctypes/libffi/configure @@ -12362,7 +12362,7 @@ $as_echo "#define HAVE_AS_STRING_PSEUDO_OP 1" >>confdefs.h fi case "$target" in - *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) $as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac index e85cff10cdcc..1db02ce004dd 100644 --- a/js/src/ctypes/libffi/configure.ac +++ b/js/src/ctypes/libffi/configure.ac @@ -316,7 +316,8 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64 fi case "$target" in - *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + # Darwin 10 (OSX 10.6) and beyond allocate non-executable pages + *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, [Cannot use malloc on this target, so, we revert to alternative means]) diff --git a/js/src/ctypes/libffi.patch b/js/src/ctypes/patches-libffi/00-base.patch similarity index 100% rename from js/src/ctypes/libffi.patch rename to js/src/ctypes/patches-libffi/00-base.patch diff --git a/js/src/ctypes/patches-libffi/01-bug-670719.patch b/js/src/ctypes/patches-libffi/01-bug-670719.patch new file mode 100644 index 000000000000..fa152e3e94d2 --- /dev/null +++ b/js/src/ctypes/patches-libffi/01-bug-670719.patch @@ -0,0 +1,28 @@ +# HG changeset patch +# Parent e357f3f732a0f3e98f8bd4661de03c9042c5c330 +# User Landry Breuil +treat powerpc-*-openbsd* as powerpc-*-freebsd* + + +diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure +--- a/js/src/ctypes/libffi/configure ++++ b/js/src/ctypes/libffi/configure +@@ -11272,17 +11272,17 @@ case "$host" in + TARGET=POWERPC; TARGETDIR=powerpc + ;; + powerpc-*-darwin*) + TARGET=POWERPC_DARWIN; TARGETDIR=powerpc + ;; + powerpc-*-aix* | rs6000-*-aix*) + TARGET=POWERPC_AIX; TARGETDIR=powerpc + ;; +- powerpc-*-freebsd*) ++ powerpc-*-freebsd* | powerpc-*-openbsd*) + TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc + ;; + powerpc*-*-rtems*) + TARGET=POWERPC; TARGETDIR=powerpc + ;; + + s390-*-* | s390x-*-*) + TARGET=S390; TARGETDIR=s390 diff --git a/js/src/ctypes/patches-libffi/02-bug-682180.patch b/js/src/ctypes/patches-libffi/02-bug-682180.patch new file mode 100644 index 000000000000..310836387cc5 --- /dev/null +++ b/js/src/ctypes/patches-libffi/02-bug-682180.patch @@ -0,0 +1,27 @@ +diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure +index 2c08e1b..37e3055 100755 +--- a/js/src/ctypes/libffi/configure ++++ b/js/src/ctypes/libffi/configure +@@ -12362,7 +12362,7 @@ $as_echo "#define HAVE_AS_STRING_PSEUDO_OP 1" >>confdefs.h + fi + + case "$target" in +- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) ++ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + + $as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h + +diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac +index e85cff1..1db02ce 100644 +--- a/js/src/ctypes/libffi/configure.ac ++++ b/js/src/ctypes/libffi/configure.ac +@@ -316,7 +316,8 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64 + fi + + case "$target" in +- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) ++ # Darwin 10 (OSX 10.6) and beyond allocate non-executable pages ++ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*) + AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1, + [Cannot use malloc on this target, so, we revert to + alternative means]) From ce266cd7680dd17a30be79ae49f3e4d92ad56afd Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 02/49] Bug 682504 - Don't try to ImplicitConvert a void return value. r=jorendorff --- js/src/ctypes/CTypes.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 45d098858cec..777c4a8efdb9 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5429,6 +5429,10 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) argv.begin(), &rval)) return; + // If the callback doesn't return anything, we're done. + if (cif->rtype == &ffi_type_void) + return; + // Convert the result. Note that we pass 'isArgument = false', such that // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char // type, which would require an allocation that we can't track. The JS From c369988b3630a42616c80ef91df26057bc6705d5 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 03/49] Bug 682504 - Tests. r=jorendorff --- toolkit/components/ctypes/tests/unit/test_jsctypes.js.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in index 5a56a908c643..26312963f97d 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in @@ -2289,6 +2289,10 @@ function run_single_closure_tests(library, abi, suffix) let closure2 = fn_t(closure_fn, thisobj); do_check_eq(closure2(-17), -17 + thisobj.a); do_check_eq(test_closure(-52, closure2), -52 + thisobj.a); + + // Test a callback that returns void (see bug 682504). + var fn_v_t = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr; + fn_v_t(function() {})(); // Don't crash } function run_variadic_tests(library) { From 862f944567ddec1750624b4509e834f3fd5ea4bd Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 04/49] Bug 599791 - part 1 - Remove unnecessary conditional logic. r=jorendorff --- js/src/ctypes/CTypes.cpp | 42 +++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 777c4a8efdb9..81e4eafb92ab 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -3304,30 +3304,28 @@ PointerType::ConstructData(JSContext* cx, } jsval* argv = JS_ARGV(cx, vp); - if (argc >= 1) { - JSObject* baseObj = PointerType::GetBaseType(cx, obj); - if (CType::GetTypeCode(cx, baseObj) == TYPE_function && - JSVAL_IS_OBJECT(argv[0]) && - JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) { - // Construct a FunctionType.ptr from a JS function, and allow an - // optional 'this' argument. - JSObject* thisObj = NULL; - if (argc == 2) { - if (JSVAL_IS_OBJECT(argv[1])) { - thisObj = JSVAL_TO_OBJECT(argv[1]); - } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { - return JS_FALSE; - } - } - - JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); - return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); - } - + JSObject* baseObj = PointerType::GetBaseType(cx, obj); + if (CType::GetTypeCode(cx, baseObj) == TYPE_function && + JSVAL_IS_OBJECT(argv[0]) && + JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) { + // Construct a FunctionType.ptr from a JS function, and allow an + // optional 'this' argument. + JSObject* thisObj = NULL; if (argc == 2) { - JS_ReportError(cx, "first argument must be a function"); - return JS_FALSE; + if (JSVAL_IS_OBJECT(argv[1])) { + thisObj = JSVAL_TO_OBJECT(argv[1]); + } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { + return JS_FALSE; + } } + + JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); + return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); + } + + if (argc == 2) { + JS_ReportError(cx, "first argument must be a function"); + return JS_FALSE; } // Construct from a raw pointer value. From a79483e98e2ebd21854d5a348655849243c53dbb Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 05/49] Bug 599791 - part 2 - restructure argument handling in PointerType::ConstructData. r=jorendorff --- js/src/ctypes/CTypes.cpp | 77 ++++++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 26 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 81e4eafb92ab..cd2c7bfe082a 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -3286,8 +3286,8 @@ PointerType::ConstructData(JSContext* cx, return JS_FALSE; } - if (argc > 2) { - JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments"); + if (argc > 3) { + JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments"); return JS_FALSE; } @@ -3298,38 +3298,63 @@ PointerType::ConstructData(JSContext* cx, // Set return value early, must not observe *vp after JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result)); - if (argc == 0) { - // Construct a null pointer. - return JS_TRUE; - } + // There are 3 things that we might be creating here: + // 1 - A null pointer (no arguments) + // 2 - An initialized pointer (1 argument) + // 3 - A closure (1-3 arguments) + // + // The API doesn't give us a perfect way to distinguish 2 and 3, but the + // heuristics we use should be fine. + // + // Case 1 - Null pointer + // + if (argc == 0) + return JS_TRUE; + + // Analyze the arguments a bit to decide what to do next. jsval* argv = JS_ARGV(cx, vp); JSObject* baseObj = PointerType::GetBaseType(cx, obj); - if (CType::GetTypeCode(cx, baseObj) == TYPE_function && - JSVAL_IS_OBJECT(argv[0]) && - JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) { - // Construct a FunctionType.ptr from a JS function, and allow an - // optional 'this' argument. - JSObject* thisObj = NULL; - if (argc == 2) { - if (JSVAL_IS_OBJECT(argv[1])) { - thisObj = JSVAL_TO_OBJECT(argv[1]); - } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { - return JS_FALSE; - } + bool looksLikeClosure = CType::GetTypeCode(cx, baseObj) == TYPE_function && + JSVAL_IS_OBJECT(argv[0]) && + JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0])); + + // + // Case 2 - Initialized pointer + // + if (!looksLikeClosure) { + if (argc != 1) { + JS_ReportError(cx, "first argument must be a function"); + return JS_FALSE; } - - JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); - return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); + return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)); } - if (argc == 2) { - JS_ReportError(cx, "first argument must be a function"); - return JS_FALSE; + // + // Case 3 - Closure + // + + // The second argument is an optional 'this' parameter with which to invoke + // the given js function. Callers may leave this blank, or pass null if they + // wish to pass the third argument. + JSObject* thisObj = NULL; + if (argc >= 2) { + if (JSVAL_IS_OBJECT(argv[1])) { + thisObj = JSVAL_TO_OBJECT(argv[1]); + } else if (!JS_ValueToObject(cx, argv[1], &thisObj)) { + return JS_FALSE; + } } - // Construct from a raw pointer value. - return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result)); + // The third argument is an optional error sentinel that js-ctypes will return + // if an exception is raised while executing the closure. The type must match + // the return type of the callback. + jsval errVal = JSVAL_VOID; + if (argc == 3) + errVal = argv[2]; + + JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); + return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); } JSObject* From f0fde856e242cf44543888d54254d5f02295e9b9 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 06/49] Bug 599791 - part 3 - Pass errVal down into the closure constructor. r=jorendorff --- js/src/ctypes/CTypes.cpp | 10 ++++++---- js/src/ctypes/CTypes.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index cd2c7bfe082a..d7fffe1cd0b3 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -144,7 +144,7 @@ namespace StructType { namespace FunctionType { static JSBool Create(JSContext* cx, uintN argc, jsval* vp); static JSBool ConstructData(JSContext* cx, JSObject* typeObj, - JSObject* dataObj, JSObject* fnObj, JSObject* thisObj); + JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal); static JSBool Call(JSContext* cx, uintN argc, jsval* vp); @@ -3354,7 +3354,7 @@ PointerType::ConstructData(JSContext* cx, errVal = argv[2]; JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]); - return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj); + return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal); } JSObject* @@ -4945,7 +4945,8 @@ FunctionType::ConstructData(JSContext* cx, JSObject* typeObj, JSObject* dataObj, JSObject* fnObj, - JSObject* thisObj) + JSObject* thisObj, + jsval errVal) { JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function); @@ -4962,7 +4963,7 @@ FunctionType::ConstructData(JSContext* cx, return JS_FALSE; } - JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data); + JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data); if (!closureObj) return JS_FALSE; js::AutoObjectRooter root(cx, closureObj); @@ -5243,6 +5244,7 @@ CClosure::Create(JSContext* cx, JSObject* typeObj, JSObject* fnObj, JSObject* thisObj, + jsval errVal, PRFuncPtr* fnptr) { JS_ASSERT(fnObj); diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index a7ca07bf30de..d2677b89ad30 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -491,7 +491,7 @@ namespace FunctionType { namespace CClosure { JSObject* Create(JSContext* cx, JSObject* typeObj, JSObject* fnObj, - JSObject* thisObj, PRFuncPtr* fnptr); + JSObject* thisObj, jsval errVal, PRFuncPtr* fnptr); } namespace CData { From d35215058ff5633cd121624d7ad29e425c4cd9dc Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 07/49] Bug 599791 - part 4 - Do ClosureInfo cleanup with a destructor. r=jorendorff --- js/src/ctypes/CTypes.cpp | 10 ++-------- js/src/ctypes/CTypes.h | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index d7fffe1cd0b3..00de6a35ff2a 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5259,7 +5259,7 @@ CClosure::Create(JSContext* cx, JS_ASSERT(!fninfo->mIsVariadic); JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI); - AutoPtr cinfo(cx->new_()); + AutoPtr cinfo(cx->new_(JS_GetRuntime(cx))); if (!cinfo) { JS_ReportOutOfMemory(cx); return NULL; @@ -5319,17 +5319,14 @@ CClosure::Create(JSContext* cx, ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF, CClosure::ClosureStub, cinfo.get(), code); if (status != FFI_OK) { - ffi_closure_free(cinfo->closure); JS_ReportError(cx, "couldn't create closure - libffi error"); return NULL; } // Stash the ClosureInfo struct on our new object. if (!JS_SetReservedSlot(cx, result, SLOT_CLOSUREINFO, - PRIVATE_TO_JSVAL(cinfo.get()))) { - ffi_closure_free(cinfo->closure); + PRIVATE_TO_JSVAL(cinfo.get()))) return NULL; - } cinfo.forget(); // Casting between void* and a function pointer is forbidden in C and C++. @@ -5369,9 +5366,6 @@ CClosure::Finalize(JSContext* cx, JSObject* obj) return; ClosureInfo* cinfo = static_cast(JSVAL_TO_PRIVATE(slot)); - if (cinfo->closure) - ffi_closure_free(cinfo->closure); - cx->delete_(cinfo); } diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index d2677b89ad30..4cf4fdf3192c 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -322,6 +322,8 @@ struct FunctionInfo struct ClosureInfo { JSContext* cx; // JSContext to use + JSRuntime* rt; // Used in the destructor, where cx might have already + // been GCed. JSObject* closureObj; // CClosure object JSObject* typeObj; // FunctionType describing the C function JSObject* thisObj; // 'this' object to use for the JS function call @@ -330,6 +332,18 @@ struct ClosureInfo #ifdef DEBUG jsword cxThread; // The thread on which the context may be used #endif + + // Anything conditionally freed in the destructor should be initialized to + // NULL here. + ClosureInfo(JSRuntime* runtime) + : rt(runtime) + , closure(NULL) + {} + + ~ClosureInfo() { + if (closure) + ffi_closure_free(closure); + }; }; bool IsCTypesGlobal(JSContext* cx, JSObject* obj); From 65e11eb11f36c41702e411d073a9b6f5f36c783c Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 08/49] Bug 599791 - part 5 - Prepare the sentinel value and store it in ClosureInfo. r=jorendorff --- js/src/ctypes/CTypes.cpp | 31 +++++++++++++++++++++++++++++++ js/src/ctypes/CTypes.h | 4 ++++ 2 files changed, 35 insertions(+) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 00de6a35ff2a..571e7e5022b1 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5302,6 +5302,37 @@ CClosure::Create(JSContext* cx, cinfo->cxThread = JS_GetContextThread(cx); #endif + // Prepare the error sentinel value. It's important to do this now, because + // we might be unable to convert the value to the proper type. If so, we want + // the caller to know about it _now_, rather than some uncertain time in the + // future when the error sentinel is actually needed. + if (!JSVAL_IS_VOID(errVal)) { + + // Make sure the callback returns something. + if (CType::GetTypeCode(cx, fninfo->mReturnType) == TYPE_void_t) { + JS_ReportError(cx, "A void callback can't pass an error sentinel"); + return NULL; + } + + // With the exception of void, the FunctionType constructor ensures that + // the return type has a defined size. + JS_ASSERT(CType::IsSizeDefined(cx, fninfo->mReturnType)); + + // Allocate a buffer for the return value. + size_t rvSize = CType::GetSize(cx, fninfo->mReturnType); + cinfo->errResult = cx->malloc_(rvSize); + if (!cinfo->errResult) + return NULL; + + // Do the value conversion. This might fail, in which case we throw. + if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult, + false, NULL)) + return NULL; + } else { + cinfo->errResult = NULL; + } + + // Copy the important bits of context into cinfo. cinfo->closureObj = result; cinfo->typeObj = typeObj; cinfo->thisObj = thisObj; diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 4cf4fdf3192c..3de1b758ab56 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -328,6 +328,7 @@ struct ClosureInfo JSObject* typeObj; // FunctionType describing the C function JSObject* thisObj; // 'this' object to use for the JS function call JSObject* jsfnObj; // JS function + void* errResult; // Result that will be returned if the closure throws ffi_closure* closure; // The C closure itself #ifdef DEBUG jsword cxThread; // The thread on which the context may be used @@ -337,12 +338,15 @@ struct ClosureInfo // NULL here. ClosureInfo(JSRuntime* runtime) : rt(runtime) + , errResult(NULL) , closure(NULL) {} ~ClosureInfo() { if (closure) ffi_closure_free(closure); + if (errResult) + rt->free_(errResult); }; }; From ad94db44876bac088b0c4617616d92dbfef1d6bf Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 09/49] Bug 599791 - part 6 - Return the sentinel when we fail in ClosureStub. r=jorendorff --- js/src/ctypes/CTypes.cpp | 55 +++++++++++++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index 571e7e5022b1..9e4ee62450dc 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -5435,8 +5435,9 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) // Initialize the result to zero, in case something fails. Small integer types // are promoted to a word-sized ffi_arg, so we must be careful to zero the // whole word. + size_t rvSize = 0; if (cif->rtype != &ffi_type_void) { - size_t size = cif->rtype->size; + rvSize = cif->rtype->size; switch (typeCode) { #define DEFINE_INT_TYPE(name, type, ffiType) \ case TYPE_##name: @@ -5445,12 +5446,12 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) #define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) #define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z) #include "typedefs.h" - size = Align(size, sizeof(ffi_arg)); + rvSize = Align(rvSize, sizeof(ffi_arg)); break; default: break; } - memset(result, 0, size); + memset(result, 0, rvSize); } // Get a death grip on 'closureObj'. @@ -5475,21 +5476,51 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData) // Call the JS function. 'thisObj' may be NULL, in which case the JS engine // will find an appropriate object to use. jsval rval; - if (!JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), cif->nargs, - argv.begin(), &rval)) - return; - - // If the callback doesn't return anything, we're done. - if (cif->rtype == &ffi_type_void) - return; + JSBool success = JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), + cif->nargs, argv.begin(), &rval); // Convert the result. Note that we pass 'isArgument = false', such that // ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char // type, which would require an allocation that we can't track. The JS // function must perform this conversion itself and return a PointerType // CData; thusly, the burden of freeing the data is left to the user. - if (!ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, NULL)) - return; + if (success && cif->rtype != &ffi_type_void) + success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, + NULL); + + if (!success) { + // Something failed. The callee may have thrown, or it may not have + // returned a value that ImplicitConvert() was happy with. Depending on how + // prudent the consumer has been, we may or may not have a recovery plan. + + // In any case, a JS exception cannot be passed to C code, so report the + // exception if any and clear it from the cx. + if (JS_IsExceptionPending(cx)) + JS_ReportPendingException(cx); + + if (cinfo->errResult) { + // Good case: we have a sentinel that we can return. Copy it in place of + // the actual return value, and then proceed. + + // The buffer we're returning might be larger than the size of the return + // type, due to libffi alignment issues (see above). But it should never + // be smaller. + size_t copySize = CType::GetSize(cx, fninfo->mReturnType); + JS_ASSERT(copySize <= rvSize); + memcpy(result, cinfo->errResult, copySize); + } else { + // Bad case: not much we can do here. The rv is already zeroed out, so we + // just report (another) error and hope for the best. JS_ReportError will + // actually throw an exception here, so then we have to report it. Again. + // Ugh. + JS_ReportError(cx, "JavaScript callback failed, and an error sentinel " + "was not specified."); + if (JS_IsExceptionPending(cx)) + JS_ReportPendingException(cx); + + return; + } + } // Small integer types must be returned as a word-sized ffi_arg. Coerce it // back into the size libffi expects. From c676b3afdf7bb89fb3bf33ea2523876d5549de55 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:21 -0400 Subject: [PATCH 10/49] Bug 599791 - part 7 - Tests. r=jorendorff --- .../ctypes/tests/unit/test_jsctypes.js.in | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in index 26312963f97d..40da21ce9d07 100644 --- a/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in +++ b/toolkit/components/ctypes/tests/unit/test_jsctypes.js.in @@ -2268,6 +2268,8 @@ function run_single_closure_tests(library, abi, suffix) function closure_fn(i) { + if (i == 42) + throw "7.5 million years for that?"; return "a" in this ? i + this.a : i + b; } @@ -2290,9 +2292,37 @@ function run_single_closure_tests(library, abi, suffix) do_check_eq(closure2(-17), -17 + thisobj.a); do_check_eq(test_closure(-52, closure2), -52 + thisobj.a); + // Specify an error sentinel, and have the JS code throw (see bug 599791). + let closure3 = fn_t(closure_fn, null, 54); + do_check_eq(closure3(42), 54); + do_check_eq(test_closure(42, closure3), 54); + + // Check what happens when the return type is bigger than a word. + var fn_64_t = ctypes.FunctionType(ctypes.default_abi, ctypes.uint64_t, [ctypes.bool]).ptr; + var bignum1 = ctypes.UInt64.join(0xDEADBEEF, 0xBADF00D); + var bignum2 = ctypes.UInt64.join(0xDEFEC8ED, 0xD15EA5E); + function closure_fn_64(fail) + { + if (fail) + throw "Just following orders, sir!"; + return bignum1; + }; + var closure64 = fn_64_t(closure_fn_64, null, bignum2); + do_check_eq(ctypes.UInt64.compare(closure64(false), bignum1), 0); + do_check_eq(ctypes.UInt64.compare(closure64(true), bignum2), 0); + // Test a callback that returns void (see bug 682504). var fn_v_t = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr; fn_v_t(function() {})(); // Don't crash + + // Make sure that a void callback can't return an error sentinel. + var sentinelThrew = false; + try { + fn_v_t(function() {}, null, -1); + } catch(e) { + sentinelThrew = true; + } + do_check_true(sentinelThrew); } function run_variadic_tests(library) { From c828917f16236910a6b45134ccee86f451b2931c Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 13:51:29 -0400 Subject: [PATCH 11/49] Bug 690362 - Fix incorrect parenthetical grouping of nsXPTType::T_PWSTRING_SIZE_IS in xpcwrappednative.cpp. r=peterv --- js/src/xpconnect/src/xpcwrappednative.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp index 36de5c59c43a..f77d090a261e 100644 --- a/js/src/xpconnect/src/xpcwrappednative.cpp +++ b/js/src/xpconnect/src/xpcwrappednative.cpp @@ -2979,8 +2979,8 @@ CallMethodHelper::ConvertDependentParams() if((datum_type.IsPointer() && (datum_type.TagPart() == nsXPTType::T_IID || - datum_type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS) || - datum_type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS) || + datum_type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS || + datum_type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS)) || (isArray && datum_type.TagPart() == nsXPTType::T_CHAR_STR)) { dp->SetValNeedsCleanup(); From 6f4ecbed2700508fe3b106b6558e0c8e396fc124 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 7 Oct 2011 11:12:34 -0700 Subject: [PATCH 12/49] Bug 692536 - Prevent sending mousemove events if preventDefault is called on touchevents. r=mbrubeck --- mobile/chrome/content/browser.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index a324212b2421..2b77c7cbe9f8 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -1978,7 +1978,8 @@ const ContentTouchHandler = { }, tapOver: function tapOver(aX, aY) { - this._dispatchMouseEvent("Browser:MouseOver", aX, aY); + if (!this.clickPrevented) + this._dispatchMouseEvent("Browser:MouseOver", aX, aY); }, tapUp: function tapUp(aX, aY) { From d5106b64102bc94638cfdc64f3370bf358722e5e Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 7 Oct 2011 11:14:38 -0700 Subject: [PATCH 13/49] Bug 692445 - Fix behavior of change language button on about:home. r=mfinkle --- mobile/chrome/content/aboutHome.xhtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/chrome/content/aboutHome.xhtml b/mobile/chrome/content/aboutHome.xhtml index 102cdcb2d188..e2a8a73fdcd3 100644 --- a/mobile/chrome/content/aboutHome.xhtml +++ b/mobile/chrome/content/aboutHome.xhtml @@ -158,7 +158,7 @@ function openLocalePicker() { let win = getChromeWin(); win.BrowserUI.showPanel("prefs-container"); - win.document.getElementById("prefs-languages").click(); + win.document.getElementById("prefs-uilanguage-button").click(); } function init() { From 13d317f8cbc1811fffb10b9a34acd2c4a9dda0e2 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 7 Oct 2011 11:46:17 -0700 Subject: [PATCH 14/49] (no bug): Fix Emacs/vi mode line at top of js/src/methodjit/StubCalls.cpp. r=only comments changed --- js/src/methodjit/StubCalls.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index e7ca455ec5cd..f9d9f1e02281 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -1,4 +1,4 @@ -/* -*- Mode: C++ tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=4 sw=4 et tw=99: * * ***** BEGIN LICENSE BLOCK ***** From 486891d3b264bb14dfbdc3b25ebc7ebb26eb2963 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 7 Oct 2011 21:10:42 +0200 Subject: [PATCH 15/49] Bug 689142 - Places telemetry gets lost, due to being collected on idle-daily. r=taras,dietrich --- .../places/PlacesCategoriesStarter.js | 23 ++++++++++++++-- toolkit/components/places/PlacesDBUtils.jsm | 9 ++++--- .../places/tests/unit/test_telemetry.js | 6 +++-- toolkit/components/telemetry/TelemetryPing.js | 27 +++++++++++++++++-- .../tests/unit/test_TelemetryPing_idle.js | 19 +++++++++++++ .../telemetry/tests/unit/xpcshell.ini | 1 + 6 files changed, 75 insertions(+), 10 deletions(-) create mode 100644 toolkit/components/telemetry/tests/unit/test_TelemetryPing_idle.js diff --git a/toolkit/components/places/PlacesCategoriesStarter.js b/toolkit/components/places/PlacesCategoriesStarter.js index ba714e6016cf..c0c7f93df81d 100644 --- a/toolkit/components/places/PlacesCategoriesStarter.js +++ b/toolkit/components/places/PlacesCategoriesStarter.js @@ -43,10 +43,20 @@ const Cc = Components.classes; const Ci = Components.interfaces; +// Fired by TelemetryPing when async telemetry data should be collected. +const TOPIC_GATHER_TELEMETRY = "gather-telemetry"; + // Seconds between maintenance runs. const MAINTENANCE_INTERVAL_SECONDS = 7 * 86400; +//////////////////////////////////////////////////////////////////////////////// +//// Imports + Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/PlacesUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils", + "resource://gre/modules/PlacesDBUtils.jsm"); /** * This component can be used as a starter for modules that have to run when @@ -54,6 +64,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); */ function PlacesCategoriesStarter() { + Services.obs.addObserver(this, TOPIC_GATHER_TELEMETRY, false); + Services.obs.addObserver(this, PlacesUtils.TOPIC_SHUTDOWN, false); } PlacesCategoriesStarter.prototype = { @@ -63,15 +75,22 @@ PlacesCategoriesStarter.prototype = { observe: function PCS_observe(aSubject, aTopic, aData) { switch (aTopic) { + case PlacesUtils.TOPIC_SHUTDOWN: + Services.obs.removeObserver(this, PlacesUtils.TOPIC_SHUTDOWN); + Services.obs.removeObserver(this, TOPIC_GATHER_TELEMETRY); + break; + case TOPIC_GATHER_TELEMETRY: + PlacesDBUtils.telemetry(); + break; case "idle-daily": // Once a week run places.sqlite maintenance tasks. let lastMaintenance = 0; try { - lastMaintenance = Services.prefs.getIntPref("places.database.lastMaintenance"); + lastMaintenance = + Services.prefs.getIntPref("places.database.lastMaintenance"); } catch (ex) {} let nowSeconds = parseInt(Date.now() / 1000); if (lastMaintenance < nowSeconds - MAINTENANCE_INTERVAL_SECONDS) { - Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm"); PlacesDBUtils.maintenanceOnIdle(); } break; diff --git a/toolkit/components/places/PlacesDBUtils.jsm b/toolkit/components/places/PlacesDBUtils.jsm index 945c580f9266..a61a5dfa9982 100644 --- a/toolkit/components/places/PlacesDBUtils.jsm +++ b/toolkit/components/places/PlacesDBUtils.jsm @@ -54,6 +54,8 @@ let EXPORTED_SYMBOLS = [ "PlacesDBUtils" ]; const FINISHED_MAINTENANCE_TOPIC = "places-maintenance-finished"; +const BYTES_PER_MEBIBYTE = 1048576; + //////////////////////////////////////////////////////////////////////////////// //// Smart getters @@ -115,7 +117,6 @@ let PlacesDBUtils = { this.checkIntegrity , this.checkCoherence , this._refreshUI - , this._telemetry ]); tasks.callback = aCallback; tasks.scope = aScope; @@ -861,7 +862,7 @@ let PlacesDBUtils = { * @param [optional] aTasks * Tasks object to execute. */ - _telemetry: function PDBU__telemetry(aTasks) + telemetry: function PDBU_telemetry(aTasks) { let tasks = new Tasks(aTasks); @@ -911,7 +912,7 @@ let PlacesDBUtils = { let DBFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile); DBFile.append("places.sqlite"); try { - return parseInt(DBFile.fileSize / 1024); + return parseInt(DBFile.fileSize / BYTES_PER_MEBIBYTE); } catch (ex) { return 0; } @@ -921,7 +922,7 @@ let PlacesDBUtils = { let DBFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile); DBFile.append("places.sqlite-wal"); try { - return parseInt(DBFile.fileSize / 1024); + return parseInt(DBFile.fileSize / BYTES_PER_MEBIBYTE); } catch (ex) { return 0; } diff --git a/toolkit/components/places/tests/unit/test_telemetry.js b/toolkit/components/places/tests/unit/test_telemetry.js index bc95a6b6e71e..dddabb34556f 100644 --- a/toolkit/components/places/tests/unit/test_telemetry.js +++ b/toolkit/components/places/tests/unit/test_telemetry.js @@ -64,8 +64,10 @@ function run_test() { PlacesUtils.tagging.tagURI(uri, ["tag"]); PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword"); - // Test generic database probes. - PlacesDBUtils._telemetry(); + // Request to gather telemetry data. + Cc["@mozilla.org/places/categoriesStarter;1"] + .getService(Ci.nsIObserver) + .observe(null, "gather-telemetry", null); waitForAsyncUpdates(continue_test); } diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index bffc285da324..d86a297665d0 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -62,6 +62,10 @@ const MEM_HISTOGRAMS = { "heap-allocated": "MEMORY_HEAP_ALLOCATED", "page-faults-hard": "PAGE_FAULTS_HARD" }; +// Seconds of idle time before pinging. +// On idle-daily a gather-telemetry notification is fired, during it probes can +// start asynchronous tasks to gather data. On the next idle the data is sent. +const IDLE_TIMEOUT_SECONDS = 5 * 60; var gLastMemoryPoll = null; @@ -341,6 +345,10 @@ TelemetryPing.prototype = { return; Services.obs.removeObserver(this, "idle-daily"); Services.obs.removeObserver(this, "cycle-collector-begin"); + if (this._isIdleObserver) { + idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS); + this._isIdleObserver = false; + } }, /** @@ -416,11 +424,26 @@ TelemetryPing.prototype = { this.attachObservers() } break; + case "idle-daily": + // Enqueue to main-thread, otherwise components may be inited by the + // idle-daily category and miss the gather-telemetry notification. + Services.tm.mainThread.dispatch((function() { + // Notify that data should be gathered now, since ping will happen soon. + Services.obs.notifyObservers(null, "gather-telemetry", null); + // The ping happens at the first idle of length IDLE_TIMEOUT_SECONDS. + idle.addIdleObserver(this, IDLE_TIMEOUT_SECONDS); + this._isIdleObserver = true; + }).bind(this), Ci.nsIThread.DISPATCH_NORMAL); + break; case "test-ping": server = aData; // fall through - case "idle-daily": - this.send(aTopic, server); + case "idle": + if (this._isIdleObserver) { + idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS); + this._isIdleObserver = false; + } + this.send(aTopic == "idle" ? "idle-daily" : aTopic, server); break; } }, diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing_idle.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing_idle.js new file mode 100644 index 000000000000..e4d476af854a --- /dev/null +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing_idle.js @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Check that TelemetryPing notifies correctly on idle. + +Components.utils.import("resource://gre/modules/Services.jsm"); + +function run_test() { + do_test_pending(); + + Services.obs.addObserver(function observeTelemetry() { + Services.obs.removeObserver(observeTelemetry, "gather-telemetry"); + do_test_finished(); + }, "gather-telemetry", false); + + Components.classes["@mozilla.org/base/telemetry-ping;1"] + .getService(Components.interfaces.nsIObserver) + .observe(null, "idle-daily", null); +} diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.ini b/toolkit/components/telemetry/tests/unit/xpcshell.ini index ee7c3a22a1c7..713d680b4077 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.ini +++ b/toolkit/components/telemetry/tests/unit/xpcshell.ini @@ -6,3 +6,4 @@ tail = [test_TelemetryPing.js] # Bug 676989: test fails consistently on Android # fail-if = os == "android" +[test_TelemetryPing_idle.js] From 41b3b7b10863b061531a4ff98bb1ab0401cd531d Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 7 Oct 2011 21:10:44 +0200 Subject: [PATCH 16/49] Bug 691509 - Run ANALYZE at each schema change (and force a schema change). Partly copied from a patch by Richard Newman. r=dietrich --HG-- rename : toolkit/components/places/tests/migration/test_v11_from_v10.js => toolkit/components/places/tests/migration/test_current_from_v10.js rename : toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js => toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js --- toolkit/components/places/nsNavHistory.cpp | 48 ++++++++++++++++++- .../components/places/nsPlacesExpiration.js | 2 + .../components/places/tests/head_common.js | 4 +- ...1_from_v10.js => test_current_from_v10.js} | 6 +-- ...est_current_from_v10_migrated_from_v11.js} | 4 +- .../places/tests/migration/xpcshell.ini | 4 +- .../places/tests/unit/test_analyze.js | 27 +++++++++++ .../components/places/tests/unit/xpcshell.ini | 1 + 8 files changed, 87 insertions(+), 9 deletions(-) rename toolkit/components/places/tests/migration/{test_v11_from_v10.js => test_current_from_v10.js} (98%) rename toolkit/components/places/tests/migration/{test_v11_from_v10_migrated_from_v11.js => test_current_from_v10_migrated_from_v11.js} (96%) create mode 100644 toolkit/components/places/tests/unit/test_analyze.js diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index b943bfedf66a..b68303a2d1a9 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -163,7 +163,7 @@ using namespace mozilla::places; // This is the schema version, update it at any schema change and add a // corresponding migrateVxx method below. -#define DATABASE_SCHEMA_VERSION 11 +#define DATABASE_SCHEMA_VERSION 12 // Filename of the database. #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite") @@ -313,6 +313,49 @@ namespace mozilla { _sqlFragment.AppendLiteral(" AS tags "); } + /** + * Updates sqlite_stat1 table through ANALYZE. + * Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables + * must be the same in both components. So ensure they are in sync. + */ + nsresult updateSQLiteStatistics(mozIStorageConnection* aDBConn) + { + nsCOMPtr analyzePlacesStmt; + nsresult rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "ANALYZE moz_places" + ), getter_AddRefs(analyzePlacesStmt)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr analyzeBookmarksStmt; + rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "ANALYZE moz_bookmarks" + ), getter_AddRefs(analyzeBookmarksStmt)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr analyzeVisitsStmt; + rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "ANALYZE moz_historyvisits" + ), getter_AddRefs(analyzeVisitsStmt)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr analyzeInputStmt; + rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING( + "ANALYZE moz_inputhistory" + ), getter_AddRefs(analyzeInputStmt)); + NS_ENSURE_SUCCESS(rv, rv); + + mozIStorageBaseStatement *stmts[] = { + analyzePlacesStmt, + analyzeBookmarksStmt, + analyzeVisitsStmt, + analyzeInputStmt + }; + + nsCOMPtr ps; + rv = aDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull, + getter_AddRefs(ps)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; + } + } // namespace places } // namespace mozilla @@ -928,6 +971,9 @@ nsNavHistory::InitDB() rv = UpdateSchemaVersion(); NS_ENSURE_SUCCESS(rv, rv); + rv = updateSQLiteStatistics(mDBConn); + NS_ENSURE_SUCCESS(rv, rv); + rv = transaction.Commit(); NS_ENSURE_SUCCESS(rv, rv); diff --git a/toolkit/components/places/nsPlacesExpiration.js b/toolkit/components/places/nsPlacesExpiration.js index 00297b7a0669..1c608348da5a 100644 --- a/toolkit/components/places/nsPlacesExpiration.js +++ b/toolkit/components/places/nsPlacesExpiration.js @@ -404,6 +404,8 @@ const EXPIRATION_QUERIES = { // The following queries are used to adjust the sqlite_stat1 table to help the // query planner create better queries. These should always be run LAST, and // are therefore at the end of the object. + // Since also nsNavHistory.cpp executes ANALYZE, the analyzed tables + // must be the same in both components. So ensure they are in sync. QUERY_ANALYZE_MOZ_PLACES: { sql: "ANALYZE moz_places", diff --git a/toolkit/components/places/tests/head_common.js b/toolkit/components/places/tests/head_common.js index 177fa405887d..9d406e816b26 100644 --- a/toolkit/components/places/tests/head_common.js +++ b/toolkit/components/places/tests/head_common.js @@ -35,6 +35,8 @@ * * ***** END LICENSE BLOCK ***** */ +const CURRENT_SCHEMA_VERSION = 12; + const NS_APP_USER_PROFILE_50_DIR = "ProfD"; const NS_APP_PROFILE_DIR_STARTUP = "ProfDS"; const NS_APP_BOOKMARKS_50_FILE = "BMarks"; @@ -145,7 +147,7 @@ function readInputStreamData(aStream) { bistream.setInputStream(aStream); let expectedData = []; let avail; - while (avail = bistream.available()) { + while ((avail = bistream.available())) { expectedData = expectedData.concat(bistream.readByteArray(avail)); } return expectedData; diff --git a/toolkit/components/places/tests/migration/test_v11_from_v10.js b/toolkit/components/places/tests/migration/test_current_from_v10.js similarity index 98% rename from toolkit/components/places/tests/migration/test_v11_from_v10.js rename to toolkit/components/places/tests/migration/test_current_from_v10.js index a27e03e9eca3..503554448f41 100644 --- a/toolkit/components/places/tests/migration/test_v11_from_v10.js +++ b/toolkit/components/places/tests/migration/test_current_from_v10.js @@ -2,8 +2,8 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ /** - * This file tests migration invariants from schema version 10 to schema version - * 11. + * This file tests migration invariants from schema version 10 to the current + * schema version. */ //////////////////////////////////////////////////////////////////////////////// @@ -296,7 +296,7 @@ function test_final_state() do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex")); do_check_true(db.indexExists("moz_places_guid_uniqueindex")); - do_check_eq(db.schemaVersion, 11); + do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); db.close(); run_next_test(); diff --git a/toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js b/toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js similarity index 96% rename from toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js rename to toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js index c9b281f34c50..6386ecc9d4ac 100644 --- a/toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js +++ b/toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js @@ -4,7 +4,7 @@ /** * This file tests migration invariants from a database with schema version 11 * that was then downgraded to a database with a schema version 10. Places - * should then migrate this database to one with a schema version of 11. + * should then migrate this database to one with the current schema version. */ //////////////////////////////////////////////////////////////////////////////// @@ -109,7 +109,7 @@ function test_final_state() dbFile.append(kDBName); let db = Services.storage.openUnsharedDatabase(dbFile); - do_check_eq(db.schemaVersion, 11); + do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION); db.close(); run_next_test(); diff --git a/toolkit/components/places/tests/migration/xpcshell.ini b/toolkit/components/places/tests/migration/xpcshell.ini index 11c352e6876c..0fe99e97da20 100644 --- a/toolkit/components/places/tests/migration/xpcshell.ini +++ b/toolkit/components/places/tests/migration/xpcshell.ini @@ -2,5 +2,5 @@ head = head_migration.js tail = -[test_v11_from_v10.js] -[test_v11_from_v10_migrated_from_v11.js] +[test_current_from_v10.js] +[test_current_from_v10_migrated_from_v11.js] diff --git a/toolkit/components/places/tests/unit/test_analyze.js b/toolkit/components/places/tests/unit/test_analyze.js new file mode 100644 index 000000000000..02ed64e14b38 --- /dev/null +++ b/toolkit/components/places/tests/unit/test_analyze.js @@ -0,0 +1,27 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +// Tests sqlite_sta1 table exists, it should be created by analyze, but since +// the tables are empty it won't contain any data. + +function run_test() { + do_test_pending(); + + let stmt = DBConn().createAsyncStatement( + "SELECT ROWID FROM sqlite_stat1" + ); + stmt.executeAsync({ + _gotResult: false, + handleResult: function(aResultSet) { + this._gotResult = true; + }, + handleError: function(aError) { + do_throw("Unexpected error (" + aError.result + "): " + aError.message); + }, + handleCompletion: function(aReason) { + do_check_false(this._gotResult); + do_test_finished(); + } + }); + stmt.finalize(); +} diff --git a/toolkit/components/places/tests/unit/xpcshell.ini b/toolkit/components/places/tests/unit/xpcshell.ini index 046223357401..fc3cab0c9158 100644 --- a/toolkit/components/places/tests/unit/xpcshell.ini +++ b/toolkit/components/places/tests/unit/xpcshell.ini @@ -48,6 +48,7 @@ skip-if = os == "android" # Bug 676989: test hangs consistently on Android skip-if = os == "android" [test_adaptive_bug527311.js] +[test_analyze.js] [test_annotations.js] [test_asyncExecuteLegacyQueries.js] # Bug 676989: test hangs consistently on Android From 62165e618ba1611eb562357f337b74ec1deb1bd8 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Fri, 7 Oct 2011 21:10:46 +0200 Subject: [PATCH 17/49] Bug 692119 - Don't init the autocomplete database connection till the first search. r=dietrich --- .../components/places/nsPlacesAutoComplete.js | 65 ++++++++++++++----- 1 file changed, 49 insertions(+), 16 deletions(-) diff --git a/toolkit/components/places/nsPlacesAutoComplete.js b/toolkit/components/places/nsPlacesAutoComplete.js index 06d1994ec7b1..15b405050659 100644 --- a/toolkit/components/places/nsPlacesAutoComplete.js +++ b/toolkit/components/places/nsPlacesAutoComplete.js @@ -254,6 +254,22 @@ function nsPlacesAutoComplete() // Create our in-memory tables for tab tracking. initTempTable(db); + // Populate the table with current open pages cache contents. + if (this._openPagesCache.length > 0) { + // Avoid getter re-entrance from the _registerOpenPageQuery lazy getter. + let stmt = this._registerOpenPageQuery = + db.createAsyncStatement(this._registerOpenPageQuerySQL); + let params = stmt.newBindingParamsArray(); + for (let i = 0; i < this._openPagesCache.length; i++) { + let bp = params.newBindingParams(); + bp.bindByName("page_url", this._openPagesCache[i]); + params.addParams(bp); + } + stmt.bindParameters(params); + stmt.executeAsync(); + delete this._openPagesCache; + } + return db; }); @@ -379,20 +395,20 @@ function nsPlacesAutoComplete() ); }); + this._registerOpenPageQuerySQL = "INSERT OR REPLACE INTO moz_openpages_temp " + + "(url, open_count) " + + "VALUES (:page_url, " + + "IFNULL(" + + "(" + + "SELECT open_count + 1 " + + "FROM moz_openpages_temp " + + "WHERE url = :page_url " + + "), " + + "1" + + ")" + + ")"; XPCOMUtils.defineLazyGetter(this, "_registerOpenPageQuery", function() { - return this._db.createAsyncStatement( - "INSERT OR REPLACE INTO moz_openpages_temp (url, open_count) " - + "VALUES (:page_url, " - + "IFNULL(" - + "(" - + "SELECT open_count + 1 " - + "FROM moz_openpages_temp " - + "WHERE url = :page_url " - + "), " - + "1" - + ")" - + ")" - ); + return this._db.createAsyncStatement(this._registerOpenPageQuerySQL); }); XPCOMUtils.defineLazyGetter(this, "_unregisterOpenPageQuery", function() { @@ -509,19 +525,33 @@ nsPlacesAutoComplete.prototype = { ////////////////////////////////////////////////////////////////////////////// //// mozIPlacesAutoComplete + // If the connection has not yet been started, use this local cache. This + // prevents autocomplete from initing the database till the first search. + _openPagesCache: [], registerOpenPage: function PAC_registerOpenPage(aURI) { + if (!this._databaseInitialized) { + this._openPagesCache.push(aURI.spec); + return; + } + let stmt = this._registerOpenPageQuery; stmt.params.page_url = aURI.spec; - stmt.executeAsync(); }, unregisterOpenPage: function PAC_unregisterOpenPage(aURI) { + if (!this._databaseInitialized) { + let index = this._openPagesCache.indexOf(aURI.spec); + if (index != -1) { + this._openPagesCache.splice(index, 1); + } + return; + } + let stmt = this._unregisterOpenPageQuery; stmt.params.page_url = aURI.spec; - stmt.executeAsync(); }, @@ -613,7 +643,7 @@ nsPlacesAutoComplete.prototype = { } } - if (Object.getOwnPropertyDescriptor(this, "_db").value !== undefined) { + if (this._databaseInitialized) { this._db.asyncClose(); } } @@ -625,6 +655,9 @@ nsPlacesAutoComplete.prototype = { ////////////////////////////////////////////////////////////////////////////// //// nsPlacesAutoComplete + get _databaseInitialized() + Object.getOwnPropertyDescriptor(this, "_db").value !== undefined, + /** * Used to unescape encoded URI strings, and drop information that we do not * care about for searching. From 2b770669a885b872a4c1816f96fd5ebc4d281efa Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Fri, 7 Oct 2011 15:16:48 -0400 Subject: [PATCH 18/49] Bug 692458 - Fix for GLContext currency error - r=bjacob --- gfx/layers/d3d10/CanvasLayerD3D10.cpp | 2 ++ gfx/layers/d3d9/CanvasLayerD3D9.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/gfx/layers/d3d10/CanvasLayerD3D10.cpp b/gfx/layers/d3d10/CanvasLayerD3D10.cpp index 90e71413ea3d..6010bb6301bc 100644 --- a/gfx/layers/d3d10/CanvasLayerD3D10.cpp +++ b/gfx/layers/d3d10/CanvasLayerD3D10.cpp @@ -179,6 +179,8 @@ CanvasLayerD3D10::UpdateSurface() destination = (PRUint8*)map.pData; } + mGLContext->MakeCurrent(); + // We have to flush to ensure that any buffered GL operations are // in the framebuffer before we read. mGLContext->fFlush(); diff --git a/gfx/layers/d3d9/CanvasLayerD3D9.cpp b/gfx/layers/d3d9/CanvasLayerD3D9.cpp index 78dab111c050..cf9b4ac47ce0 100644 --- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp +++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp @@ -113,6 +113,8 @@ CanvasLayerD3D9::UpdateSurface() destination = (PRUint8*)r.pBits; } + mGLContext->MakeCurrent(); + // We have to flush to ensure that any buffered GL operations are // in the framebuffer before we read. mGLContext->fFlush(); From ce7741a910deb26782bc65dcd77beed9ee6b6415 Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Fri, 7 Oct 2011 15:31:12 -0400 Subject: [PATCH 19/49] Bug 692782 - Bug 690670 broke clang builds; r=cjones --- xpcom/glue/nsTArray.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/xpcom/glue/nsTArray.h b/xpcom/glue/nsTArray.h index 3acaa4206ea8..e307709d8172 100644 --- a/xpcom/glue/nsTArray.h +++ b/xpcom/glue/nsTArray.h @@ -1289,12 +1289,14 @@ private: template class nsAutoTArray : public nsAutoArrayBase, N> { + typedef nsAutoArrayBase, N> Base; + public: nsAutoTArray() {} template nsAutoTArray(const nsTArray& other) { - AppendElements(other); + Base::AppendElements(other); } }; @@ -1317,12 +1319,14 @@ PR_STATIC_ASSERT(sizeof(nsAutoTArray) == template class AutoFallibleTArray : public nsAutoArrayBase, N> { + typedef nsAutoArrayBase, N> Base; + public: AutoFallibleTArray() {} template AutoFallibleTArray(const nsTArray& other) { - AppendElements(other); + Base::AppendElements(other); } }; @@ -1330,12 +1334,14 @@ public: template class AutoInfallibleTArray : public nsAutoArrayBase, N> { + typedef nsAutoArrayBase, N> Base; + public: AutoInfallibleTArray() {} template AutoInfallibleTArray(const nsTArray& other) { - AppendElements(other); + Base::AppendElements(other); } }; #endif From 7d7e76f593320145e69a59deb7a32d32a7414b3a Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Fri, 7 Oct 2011 15:35:41 -0400 Subject: [PATCH 20/49] Bug 692767 - about:home shows tabs from last time with wrong encoding, non-ASCII characters are broken [r=mbrubeck] --- mobile/chrome/content/aboutHome.xhtml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mobile/chrome/content/aboutHome.xhtml b/mobile/chrome/content/aboutHome.xhtml index e2a8a73fdcd3..64ed6d854fd0 100644 --- a/mobile/chrome/content/aboutHome.xhtml +++ b/mobile/chrome/content/aboutHome.xhtml @@ -193,10 +193,15 @@ return; } - let content = NetUtil.readInputStreamToString(aStream, aStream.available()) || ""; + let fileSize = aStream.available(); + let cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream); + cvstream.init(aStream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + let content = {}; + cvstream.readString(fileSize, content); + cvstream.close(); aStream.close(); - aCallback(content.replace(/\r\n?/g, "\n")); + aCallback(content.value.replace(/\r\n?/g, "\n")); }); } From 877a54532a3a9aecd9d43f2af2a156eaab6c24d6 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 7 Oct 2011 13:07:05 -0700 Subject: [PATCH 21/49] Bug 692243: Change the InferSpew for TypeObject::setFlags to print flags in hex. r=bhackett --- js/src/jsinfer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index af4dbf29a383..e2d1736ef185 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2907,7 +2907,7 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags) this->flags |= flags; - InferSpew(ISpewOps, "%s: setFlags %u", TypeObjectString(this), flags); + InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags); ObjectStateChange(cx, this, false, false); } From 9e0e0829b3a7da254eb6e480f475633232dd6890 Mon Sep 17 00:00:00 2001 From: Wesley Johnston Date: Fri, 7 Oct 2011 14:32:28 -0700 Subject: [PATCH 22/49] Backout 733ab06d6e09 for browser-chrome bustage --- mobile/chrome/content/browser.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index 2b77c7cbe9f8..a324212b2421 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -1978,8 +1978,7 @@ const ContentTouchHandler = { }, tapOver: function tapOver(aX, aY) { - if (!this.clickPrevented) - this._dispatchMouseEvent("Browser:MouseOver", aX, aY); + this._dispatchMouseEvent("Browser:MouseOver", aX, aY); }, tapUp: function tapUp(aX, aY) { From 7f1c16c84c14ed9297d269de3e69637d30527a59 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 7 Oct 2011 15:08:56 -0700 Subject: [PATCH 23/49] Bug 692911: Delete useless call to target->addType in js::types::TypeConstraintSubsetBarrier::newType. r=bhackett js::types::TypeConstraintSubsetBarrier::newType calls 'target->hasType(type)', and if that returns true, calls 'target->addType(cx, type)'. That second call should have no effect: it just adds to target a type that it already has. This patch deletes the call to target->addType. --- js/src/jsinfer.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index e2d1736ef185..0cf30a34d80d 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -874,12 +874,8 @@ public: void newType(JSContext *cx, TypeSet *source, Type type) { - if (!target->hasType(type)) { + if (!target->hasType(type)) script->analysis()->addTypeBarrier(cx, pc, target, type); - return; - } - - target->addType(cx, type); } }; From a2b44f3d2942b85bf2687aa1331e478a1bcc218b Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Fri, 7 Oct 2011 15:12:12 -0700 Subject: [PATCH 24/49] Bug 692903: Identify scripts more helpfully in InferSpew output. r=bhackett This delays assigning id numbers to scripts until the first time the id is actually retrieved, and at that point produces an InferSpew message identifying the script by id, address, url and line number. This means that we only identify scripts we actually mention in InferSpew output. If JSScript::id had users other than jsinfer, this wouldn't be appropriate. With this patch applied, we get output like this, the first time a given script is mentioned: [infer] script #2: 0x7ffff5906660 /home/jimb/moz/dbg/js/src/jit-test/lib/prolog.js:1 [infer] typeSet: T0x9ff748 bytecode0 #2 ... --- js/src/jsinfer.cpp | 9 +++++++++ js/src/jsscript.cpp | 2 +- js/src/jsscript.h | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 0cf30a34d80d..6e6cdbfedc04 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -254,6 +254,15 @@ types::TypeObjectString(TypeObject *type) return TypeString(Type::ObjectType(type)); } +unsigned JSScript::id() { + if (!id_) { + id_ = ++compartment()->types.scriptCount; + InferSpew(ISpewOps, "script #%u: %p %s:%d", + id_, this, filename ? filename : "", lineno); + } + return id_; +} + void types::InferSpew(SpewChannel channel, const char *fmt, ...) { diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 5eaec06f5300..efb21a031300 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1088,7 +1088,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size); #ifdef DEBUG - script->id_ = ++cx->compartment->types.scriptCount; + script->id_ = 0; #endif JS_ASSERT(script->getVersion() == version); diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 17e5d3908e7f..56c1056201bb 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -594,7 +594,7 @@ struct JSScript : public js::gc::Cell { */ uint32 id_; uint32 idpad; - unsigned id() { return id_; } + unsigned id(); #else unsigned id() { return 0; } #endif From 41aa0881f3a74bd6d1679deb8f87ff15d313a8ef Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Wed, 5 Oct 2011 11:12:59 -0700 Subject: [PATCH 25/49] Bug 692069: Regexp engine agnostic abstraction. (r=luke) --- js/src/vm/RegExpObject-inl.h | 82 +++++++++++++++++------- js/src/vm/RegExpObject.cpp | 98 ++++++++++++++-------------- js/src/vm/RegExpObject.h | 120 +++++++++++++++++++++++------------ 3 files changed, 182 insertions(+), 118 deletions(-) diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h index d1a46fcfd539..e1c41a69efd2 100644 --- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -324,13 +324,11 @@ RegExpPrivate::create(JSContext *cx, JSString *source, RegExpFlag flags, TokenSt return RetType(self); } -/* - * This function should be deleted once we can. See bug 604774. - */ -static inline bool -EnableYarrJIT(JSContext *cx) +/* This function should be deleted once bad Android platforms phase out. See bug 604774. */ +inline bool +RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx) { -#if defined ANDROID && defined(JS_TRACER) && defined(JS_METHODJIT) +#if defined(ANDROID) && defined(JS_TRACER) && defined(JS_METHODJIT) return cx->traceJitEnabled || cx->methodJitEnabled; #else return true; @@ -338,38 +336,45 @@ EnableYarrJIT(JSContext *cx) } inline bool -RegExpPrivate::compileHelper(JSContext *cx, JSLinearString &pattern, TokenStream *ts) +RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, + uintN *parenCount, RegExpFlag flags) { #if ENABLE_YARR_JIT - JSC::Yarr::ErrorCode yarrError; - JSC::Yarr::YarrPattern yarrPattern(pattern, ignoreCase(), multiline(), &yarrError); + /* Parse the pattern. */ + ErrorCode yarrError; + YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag), + &yarrError); if (yarrError) { reportYarrError(cx, ts, yarrError); return false; } - parenCount = yarrPattern.m_numSubpatterns; + *parenCount = yarrPattern.m_numSubpatterns; -#if defined(JS_METHODJIT) - if (EnableYarrJIT(cx) && !yarrPattern.m_containsBackreferences) { - bool ok = cx->compartment->ensureJaegerCompartmentExists(cx); - if (!ok) + /* + * The YARR JIT compiler attempts to compile the parsed pattern. If + * it cannot, it informs us via |codeBlock.isFallBack()|, in which + * case we have to bytecode compile it. + */ + + if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) { + if (!cx->compartment->ensureJaegerCompartmentExists(cx)) return false; - JSC::Yarr::JSGlobalData globalData(cx->compartment->jaegerCompartment()->execAlloc()); - JSC::Yarr::jitCompile(yarrPattern, &globalData, codeBlock); + + JSGlobalData globalData(cx->compartment->jaegerCompartment()->execAlloc()); + jitCompile(yarrPattern, &globalData, codeBlock); if (!codeBlock.isFallBack()) return true; } -#endif codeBlock.setFallBack(true); - byteCode = JSC::Yarr::byteCompile(yarrPattern, cx->compartment->regExpAllocator).get(); + byteCode = byteCompile(yarrPattern, cx->compartment->regExpAllocator).get(); return true; -#else +#else /* !defined(ENABLE_YARR_JIT) */ int error = 0; compiled = jsRegExpCompile(pattern.chars(), pattern.length(), - ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase, - multiline() ? JSRegExpMultiline : JSRegExpSingleLine, - &parenCount, &error); + ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase, + multiline() ? JSRegExpMultiline : JSRegExpSingleLine, + parenCount, &error); if (error) { reportPCREError(cx, error); return false; @@ -386,7 +391,7 @@ RegExpPrivate::compile(JSContext *cx, TokenStream *ts) return false; if (!sticky()) - return compileHelper(cx, *source, ts); + return code.compile(cx, *source, ts, &parenCount, getFlags()); /* * The sticky case we implement hackily by prepending a caret onto the front @@ -405,7 +410,36 @@ RegExpPrivate::compile(JSContext *cx, TokenStream *ts) JSLinearString *fakeySource = sb.finishString(); if (!fakeySource) return false; - return compileHelper(cx, *fakeySource, ts); + return code.compile(cx, *fakeySource, ts, &parenCount, getFlags()); +} + +inline RegExpPrivateCode::ExecuteResult +RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, size_t length, + int *output, size_t outputCount) +{ + int result; +#if ENABLE_YARR_JIT + (void) cx; /* Unused. */ + if (codeBlock.isFallBack()) + result = JSC::Yarr::interpret(byteCode, chars, start, length, output); + else + result = JSC::Yarr::execute(codeBlock, chars, start, length, output); +#else + result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount); +#endif + + if (result == -1) + return Success_NotFound; + +#if !ENABLE_YARR_JIT + if (result < 0) { + reportPCREError(cx, result); + return Error; + } +#endif + + JS_ASSERT(result >= 0); + return Success; } inline void diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index e0ec6c9d16d5..96104ec99006 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -64,8 +64,8 @@ RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inpu size_t *lastIndex, bool test, Value *rval) { const size_t pairCount = parenCount + 1; - const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */ const size_t matchItemCount = pairCount * 2; + const size_t bufCount = RegExpPrivateCode::getOutputSize(pairCount); LifoAllocScope las(&cx->tempLifoAlloc()); int *buf = cx->tempLifoAlloc().newArray(bufCount); @@ -100,27 +100,19 @@ RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inpu inputOffset = *lastIndex; } - int result; -#if ENABLE_YARR_JIT - if (!codeBlock.isFallBack()) - result = JSC::Yarr::execute(codeBlock, chars, *lastIndex - inputOffset, len, buf); - else - result = JSC::Yarr::interpret(byteCode, chars, *lastIndex - inputOffset, len, buf); -#else - result = jsRegExpExecute(cx, compiled, chars, len, *lastIndex - inputOffset, buf, bufCount); -#endif - if (result == -1) { + size_t start = *lastIndex - inputOffset; + RegExpPrivateCode::ExecuteResult result = code.execute(cx, chars, start, len, buf, bufCount); + + switch (result) { + case RegExpPrivateCode::Error: + return false; + case RegExpPrivateCode::Success_NotFound: *rval = NullValue(); return true; + default: + JS_ASSERT(result == RegExpPrivateCode::Success); } -#if !ENABLE_YARR_JIT - if (result < 0) { - reportPCREError(cx, result); - return false; - } -#endif - /* * Adjust buf for the inputOffset. Use of sticky is rare and the matchItemCount is small, so * just do another pass. @@ -260,9 +252,42 @@ Class js::RegExpClass = { NULL /* trace */ }; -#if !ENABLE_YARR_JIT +#if ENABLE_YARR_JIT void -RegExpPrivate::reportPCREError(JSContext *cx, int error) +RegExpPrivateCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error) +{ + switch (error) { + case JSC::Yarr::NoError: + JS_NOT_REACHED("Called reportYarrError with value for no error"); + return; +#define COMPILE_EMSG(__code, __msg) \ + case JSC::Yarr::__code: \ + if (ts) \ + ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, __msg); \ + else \ + JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \ + return + COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX); + COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN); + COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN); + COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */ + COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE); + COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE); + COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE); + COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER); + COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH); +#undef COMPILE_EMSG + default: + JS_NOT_REACHED("Unknown Yarr error code"); + } +} + +#else /* !ENABLE_YARR_JIT */ + +void +RegExpPrivateCode::reportPCREError(JSContext *cx, int error) { #define REPORT(msg_) \ JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \ @@ -292,38 +317,7 @@ RegExpPrivate::reportPCREError(JSContext *cx, int error) } #undef REPORT } -#endif - -void -RegExpPrivate::reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error) -{ - switch (error) { - case JSC::Yarr::NoError: - JS_NOT_REACHED("Called reportYarrError with value for no error"); - return; -#define COMPILE_EMSG(__code, __msg) \ - case JSC::Yarr::__code: \ - if (ts) \ - ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, __msg); \ - else \ - JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \ - return - COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX); - COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER); - COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER); - COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN); - COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN); - COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */ - COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE); - COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE); - COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE); - COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER); - COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH); -#undef COMPILE_EMSG - default: - JS_NOT_REACHED("Unknown Yarr error code"); - } -} +#endif /* ENABLE_YARR_JIT */ bool js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut) diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 8c112d4ed8d6..5d26304789ee 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -149,6 +149,77 @@ ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag inline bool ResetRegExpObject(JSContext *cx, AlreadyIncRefed rep); +/* + * Ensure that the presence of the YARR JIT implies the presence of the + * method JIT so that we can use jaegerCompartments and such. + */ +#if ENABLE_YARR_JIT && !defined(JS_METHODJIT) +# error "YARR JIT without method JIT is an unsupported configuration." +#endif + +/* Abstracts away the gross |RegExpPrivate| backend details. */ +class RegExpPrivateCode +{ +#if ENABLE_YARR_JIT + typedef JSC::Yarr::BytecodePattern BytecodePattern; + typedef JSC::Yarr::ErrorCode ErrorCode; + typedef JSC::Yarr::JSGlobalData JSGlobalData; + typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock; + typedef JSC::Yarr::YarrPattern YarrPattern; + + /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */ + YarrCodeBlock codeBlock; + BytecodePattern *byteCode; +#else + JSRegExp *compiled; +#endif + + public: + RegExpPrivateCode() + : +#if ENABLE_YARR_JIT + codeBlock(), + byteCode(NULL) +#else + compiled(NULL) +#endif + { } + + ~RegExpPrivateCode() { +#if ENABLE_YARR_JIT + codeBlock.release(); + if (byteCode) + Foreground::delete_(byteCode); +#else + if (compiled) + jsRegExpFree(compiled); +#endif + } + +#if ENABLE_YARR_JIT + static inline bool isJITRuntimeEnabled(JSContext *cx); + void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error); +#else + void reportPCREError(JSContext *cx, int error); +#endif + + inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount, + RegExpFlag flags); + + enum ExecuteResult { Error, Success, Success_NotFound }; + + inline ExecuteResult execute(JSContext *cx, const jschar *chars, size_t start, size_t length, + int *output, size_t outputCount); + + static size_t getOutputSize(size_t pairCount) { +#if ENABLE_YARR_JIT + return pairCount * 2; +#else + return pairCount * 3; /* Should be x2, but PCRE has... needs. */ +#endif + } +}; + /* * The "meat" of the builtin regular expression objects: it contains the * mini-program that represents the source of the regular expression. @@ -165,58 +236,23 @@ ResetRegExpObject(JSContext *cx, AlreadyIncRefed rep); */ class RegExpPrivate { -#if ENABLE_YARR_JIT - /* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */ - JSC::Yarr::YarrCodeBlock codeBlock; - JSC::Yarr::BytecodePattern *byteCode; -#else - JSRegExp *compiled; -#endif + RegExpPrivateCode code; + JSLinearString *source; + size_t refCount; + uintN parenCount; + RegExpFlag flags; - JSLinearString *source; - size_t refCount; - unsigned parenCount; /* Must be |unsigned| to interface with YARR. */ - RegExpFlag flags; -#ifdef DEBUG public: - JSCompartment *compartment; + DebugOnly compartment; private: -#endif - RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment) - : -#if ENABLE_YARR_JIT - codeBlock(), - byteCode(NULL), -#else - compiled(NULL), -#endif - source(source), refCount(1), parenCount(0), flags(flags) -#ifdef DEBUG - , compartment(compartment) -#endif + : source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment) { } JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR; - ~RegExpPrivate() { -#if ENABLE_YARR_JIT - codeBlock.release(); - if (byteCode) - Foreground::delete_(byteCode); -#else - if (compiled) - jsRegExpFree(compiled); -#endif - } - - bool compileHelper(JSContext *cx, JSLinearString &pattern, TokenStream *ts); bool compile(JSContext *cx, TokenStream *ts); -#if !ENABLE_YARR_JIT - void reportPCREError(JSContext *cx, int error); -#endif - void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error); static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount); static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount); bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input, From b0edb0e9285db37a547bcb863e7f4db1954c24b0 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Thu, 6 Oct 2011 16:09:43 -0700 Subject: [PATCH 26/49] Bug 683885 - 'Assertion failure: self, at dom/workers/EventTarget.cpp:170'. r=sicking. --HG-- extra : transplant_source : r%E0N%98%BA%DF%DA%A6%0F%CE%04%D9%E7%A3%BF%82%C9%04b%2B --- dom/workers/EventTarget.cpp | 12 +++-- dom/workers/Worker.cpp | 47 ++++++++++++++++++- dom/workers/Worker.h | 3 ++ dom/workers/WorkerPrivate.cpp | 19 ++++++-- dom/workers/WorkerPrivate.h | 2 +- dom/workers/test/test_terminate.html | 70 ++++++++++++++++++++++------ 6 files changed, 129 insertions(+), 24 deletions(-) diff --git a/dom/workers/EventTarget.cpp b/dom/workers/EventTarget.cpp index 69d136e191c4..9876c07790e2 100644 --- a/dom/workers/EventTarget.cpp +++ b/dom/workers/EventTarget.cpp @@ -141,7 +141,9 @@ EventTarget::AddEventListener(JSContext* aCx, uintN aArgc, jsval* aVp) JSObject* obj = JS_THIS_OBJECT(aCx, aVp); EventTarget* self = GetPrivate(aCx, obj); - JS_ASSERT(self); + if (!self) { + return true; + } JSString* type; JSObject* listener; @@ -167,7 +169,9 @@ EventTarget::RemoveEventListener(JSContext* aCx, uintN aArgc, jsval* aVp) JSObject* obj = JS_THIS_OBJECT(aCx, aVp); EventTarget* self = GetPrivate(aCx, obj); - JS_ASSERT(self); + if (!self) { + return true; + } JSString* type; JSObject* listener; @@ -193,7 +197,9 @@ EventTarget::DispatchEvent(JSContext* aCx, uintN aArgc, jsval* aVp) JSObject* obj = JS_THIS_OBJECT(aCx, aVp); EventTarget* self = GetPrivate(aCx, obj); - JS_ASSERT(self); + if (!self) { + return true; + } JSObject* event; if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &event)) { diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp index a99323db0b8d..41029178db77 100644 --- a/dom/workers/Worker.cpp +++ b/dom/workers/Worker.cpp @@ -112,6 +112,29 @@ public: return proto; } + static void + ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers) + { + JS_ASSERT(!JS_IsExceptionPending(aCx)); + + WorkerPrivate* worker = GetJSPrivateSafeish(aCx, aObj); + JS_ASSERT(worker); + + if (aSaveEventHandlers) { + for (int index = 0; index < STRING_COUNT; index++) { + const char* name = sEventStrings[index]; + jsval listener; + if (!worker->GetEventListenerOnEventTarget(aCx, name + 2, &listener) || + !JS_DefineProperty(aCx, aObj, name, listener, NULL, NULL, + (PROPERTY_FLAGS & ~JSPROP_SHARED))) { + JS_ClearPendingException(aCx); + } + } + } + + SetJSPrivateSafeish(aCx, aObj, NULL); + } + protected: static WorkerPrivate* GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName); @@ -225,7 +248,7 @@ private: JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); WorkerPrivate* worker = GetJSPrivateSafeish(aCx, aObj); if (worker) { - worker->FinalizeInstance(aCx); + worker->FinalizeInstance(aCx, true); } } @@ -337,6 +360,12 @@ public: return proto; } + static void + ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers) + { + Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers); + } + private: // No instance of this class should ever be created so these are explicitly // left without an implementation to prevent linking in case someone tries to @@ -369,7 +398,7 @@ private: JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); WorkerPrivate* worker = GetJSPrivateSafeish(aCx, aObj); if (worker) { - worker->FinalizeInstance(aCx); + worker->FinalizeInstance(aCx, true); } } @@ -425,6 +454,20 @@ InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto, return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime); } +void +ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers) +{ + JSClass* clasp = JS_GET_CLASS(aCx, aObj); + JS_ASSERT(clasp == Worker::Class() || clasp == ChromeWorker::Class()); + + if (clasp == ChromeWorker::Class()) { + ChromeWorker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers); + } + else { + Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers); + } +} + } // namespace worker namespace chromeworker { diff --git a/dom/workers/Worker.h b/dom/workers/Worker.h index 33f748d937cd..3a219f0ec93f 100644 --- a/dom/workers/Worker.h +++ b/dom/workers/Worker.h @@ -51,6 +51,9 @@ JSObject* InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto, bool aMainRuntime); +void +ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers); + } // namespace worker namespace chromeworker { diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index f3dba937847f..7546f356b2e6 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -76,6 +76,7 @@ #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" +#include "Worker.h" #include "WorkerFeature.h" #include "WorkerScope.h" @@ -646,7 +647,7 @@ public: NS_WARNING("Failed to dispatch, going to leak!"); } - mFinishedWorker->FinalizeInstance(aCx); + mFinishedWorker->FinalizeInstance(aCx, false); RuntimeService* runtime = RuntimeService::GetService(); NS_ASSERTION(runtime, "This should never be null!"); @@ -678,7 +679,7 @@ public: RuntimeService::AutoSafeJSContext cx; - mFinishedWorker->FinalizeInstance(cx); + mFinishedWorker->FinalizeInstance(cx, false); RuntimeService* runtime = RuntimeService::GetService(); NS_ASSERTION(runtime, "This should never be null!"); @@ -1772,7 +1773,7 @@ WorkerPrivateParent::Notify(JSContext* aCx, Status aStatus) mParentStatus = aStatus; } - FinalizeInstance(aCx); + FinalizeInstance(aCx, false); if (pending) { WorkerPrivate* self = ParentAsWorkerPrivate(); @@ -1867,13 +1868,21 @@ WorkerPrivateParent::Resume(JSContext* aCx) template void -WorkerPrivateParent::FinalizeInstance(JSContext* aCx) +WorkerPrivateParent::FinalizeInstance(JSContext* aCx, + bool aFromJSFinalizer) { AssertIsOnParentThread(); if (mJSObject) { + // Make sure we're in the right compartment. + JSAutoEnterCompartment ac; + if (!ac.enter(aCx, mJSObject)) { + NS_ERROR("How can this fail?!"); + return; + } + // Decouple the object from the private now. - SetJSPrivateSafeish(aCx, mJSObject, nsnull); + worker::ClearPrivateSlot(aCx, mJSObject, !aFromJSFinalizer); // Clear the JS object. mJSObject = nsnull; diff --git a/dom/workers/WorkerPrivate.h b/dom/workers/WorkerPrivate.h index 816d5dab1c2d..9812d2c580fe 100644 --- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -276,7 +276,7 @@ public: } void - FinalizeInstance(JSContext* aCx); + FinalizeInstance(JSContext* aCx, bool aFromJSFinalizer); bool Terminate(JSContext* aCx) diff --git a/dom/workers/test/test_terminate.html b/dom/workers/test/test_terminate.html index 6e30df3286c1..0d060dc58b42 100644 --- a/dom/workers/test/test_terminate.html +++ b/dom/workers/test/test_terminate.html @@ -26,6 +26,61 @@ Tests of DOM Worker terminate feature var interval; + var worker; + + function messageListener(event) { + is(event.data, "Still alive!", "Correct message!"); + if (messageCount++ == 20) { + ok(worker.onmessage === messageListener, + "Correct listener before terminate"); + + worker.terminate(); + + var exception = false; + try { + worker.addEventListener("message", messageListener, false); + } + catch (e) { + exception = true; + } + is(exception, false, "addEventListener didn't throw after terminate"); + + exception = false; + try { + worker.removeEventListener("message", messageListener, false); + } + catch (e) { + exception = true; + } + is(exception, false, "removeEventListener didn't throw after terminate"); + + exception = false; + try { + worker.postMessage("foo"); + } + catch (e) { + exception = true; + } + is(exception, false, "postMessage didn't throw after terminate"); + + exception = false; + try { + worker.terminate(); + } + catch (e) { + exception = true; + } + is(exception, false, "terminate didn't throw after terminate"); + + ok(worker.onmessage === messageListener, + "Correct listener after terminate"); + + worker.onmessage = function(event) { } + + interval = setInterval(testCount, 1000); + } + } + function testCount() { is(messageCount, 21, "Received another message after terminated!"); if (intervalCount++ == 5) { @@ -34,19 +89,8 @@ Tests of DOM Worker terminate feature } } - var worker = new Worker("terminate_worker.js"); - worker.onmessage = function(event) { - is(event.data, "Still alive!", "Bad message!"); - if (messageCount++ == 20) { - worker.terminate(); - interval = setInterval(testCount, 1000); - } - }; - - worker.onerror = function(event) { - ok(false, "Worker had an error: " + event.data); - SimpleTest.finish(); - } + worker = new Worker("terminate_worker.js"); + worker.onmessage = messageListener; SimpleTest.waitForExplicitFinish(); From e743cd9b82940df3878c1a1a9346df5a2543729d Mon Sep 17 00:00:00 2001 From: Martijn Wargers Date: Fri, 7 Oct 2011 16:06:58 -0700 Subject: [PATCH 27/49] Bug 691758 - [Tablet] Optgroup should not have checkbox in select popup helper, r=wjohnston --- mobile/themes/core/gingerbread/forms.css | 4 ++-- mobile/themes/core/honeycomb/forms.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/themes/core/gingerbread/forms.css b/mobile/themes/core/gingerbread/forms.css index a47c1394923f..1dccc43e39dd 100644 --- a/mobile/themes/core/gingerbread/forms.css +++ b/mobile/themes/core/gingerbread/forms.css @@ -250,11 +250,11 @@ -moz-box-ordinal-group: 2; /* put the image on the right side */ } -.option-command { +.option-command:not([disabled="true"]):not(.optgroup) { list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png"); } -.option-command.selected { +.option-command.selected:not(.optgroup) { list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png"); } diff --git a/mobile/themes/core/honeycomb/forms.css b/mobile/themes/core/honeycomb/forms.css index 4b59f3eb4416..c5125ac41ed9 100644 --- a/mobile/themes/core/honeycomb/forms.css +++ b/mobile/themes/core/honeycomb/forms.css @@ -258,11 +258,11 @@ border-bottom: none; } -.option-command { +.option-command:not([disabled="true"]):not(.optgroup) { list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png"); } -.option-command.selected { +.option-command.selected:not(.optgroup) { list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png"); } From 1736081a74a60514eff0ba8f6255d56e94ae4292 Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Fri, 7 Oct 2011 16:31:48 -0700 Subject: [PATCH 28/49] Bug 692069 followup: Unburn no-methodjit. --- js/src/vm/RegExpObject-inl.h | 2 ++ js/src/vm/RegExpObject.h | 8 -------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h index e1c41a69efd2..c57f84000933 100644 --- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -356,6 +356,7 @@ RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream * * case we have to bytecode compile it. */ +#ifdef JS_METHODJIT if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) { if (!cx->compartment->ensureJaegerCompartmentExists(cx)) return false; @@ -365,6 +366,7 @@ RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream * if (!codeBlock.isFallBack()) return true; } +#endif codeBlock.setFallBack(true); byteCode = byteCompile(yarrPattern, cx->compartment->regExpAllocator).get(); diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index 5d26304789ee..952f86fd8f05 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -149,14 +149,6 @@ ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag inline bool ResetRegExpObject(JSContext *cx, AlreadyIncRefed rep); -/* - * Ensure that the presence of the YARR JIT implies the presence of the - * method JIT so that we can use jaegerCompartments and such. - */ -#if ENABLE_YARR_JIT && !defined(JS_METHODJIT) -# error "YARR JIT without method JIT is an unsupported configuration." -#endif - /* Abstracts away the gross |RegExpPrivate| backend details. */ class RegExpPrivateCode { From 64652fec724f2f8827ec94fd7e9381ebf3081dde Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 19:34:28 -0400 Subject: [PATCH 29/49] Bug 657260 - Move CheckStringLength to JSString. r=Waldo --- js/src/jsatom.cpp | 4 ++-- js/src/jsstr.cpp | 2 +- js/src/jsstrinlines.h | 24 ++---------------------- js/src/vm/String-inl.h | 22 ++++++++++++++++++++++ js/src/vm/String.cpp | 12 ++---------- js/src/vm/String.h | 7 +++++++ 6 files changed, 36 insertions(+), 35 deletions(-) diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 79d74f3e445a..58cd5ca6cc07 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -555,7 +555,7 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, F { CHECK_REQUEST(cx); - if (!CheckStringLength(cx, length)) + if (!JSString::validateLength(cx, length)) return NULL; /* @@ -597,7 +597,7 @@ js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavio { CHECK_REQUEST(cx); - if (!CheckStringLength(cx, length)) + if (!JSString::validateLength(cx, length)) return NULL; return AtomizeInline(cx, &chars, length, ib); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 7cb38ad7382e..6179f017593f 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3033,7 +3033,7 @@ js_InitStringClass(JSContext *cx, JSObject *obj) JSFixedString * js_NewString(JSContext *cx, jschar *chars, size_t length) { - if (!CheckStringLength(cx, length)) + if (!JSString::validateLength(cx, length)) return NULL; JSFixedString *s = JSFixedString::new_(cx, chars, length); diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index 87998571dbf2..ef44b03efb32 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -45,30 +45,10 @@ #include "jscntxtinlines.h" #include "jsgcinlines.h" +#include "vm/String-inl.h" namespace js { -static inline bool -CheckStringLength(JSContext *cx, size_t length) -{ - if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) { - if (JS_ON_TRACE(cx)) { - /* - * If we can't leave the trace, signal OOM condition, otherwise - * exit from trace before throwing. - */ - if (!CanLeaveTrace(cx)) - return NULL; - - LeaveTrace(cx); - } - js_ReportAllocationOverflow(cx); - return false; - } - - return true; -} - /* * String builder that eagerly checks for over-allocation past the maximum * string length. @@ -242,7 +222,7 @@ StringBuffer::length() const inline bool StringBuffer::checkLength(size_t length) { - return CheckStringLength(context(), length); + return JSString::validateLength(context(), length); } extern bool diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index b14463901565..6e8bc9443ec5 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -43,8 +43,30 @@ #include "String.h" +#include "jscntxt.h" #include "jsgcinlines.h" +JS_ALWAYS_INLINE bool +JSString::validateLength(JSContext *cx, size_t length) +{ + if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) { + if (JS_ON_TRACE(cx)) { + /* + * If we can't leave the trace, signal OOM condition, otherwise + * exit from trace before throwing. + */ + if (!js::CanLeaveTrace(cx)) + return NULL; + + js::LeaveTrace(cx); + } + js_ReportAllocationOverflow(cx); + return false; + } + + return true; +} + JS_ALWAYS_INLINE void JSRope::init(JSString *left, JSString *right, size_t length) { diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index d77d8de4e9c8..a26f2fe77c44 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -268,6 +268,8 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) return left; size_t wholeLength = leftLen + rightLen; + if (!JSString::validateLength(cx, wholeLength)) + return NULL; if (JSShortString::lengthFits(wholeLength)) { JSShortString *str = js_NewGCShortString(cx); @@ -287,16 +289,6 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right) return str; } - if (wholeLength > JSString::MAX_LENGTH) { - if (JS_ON_TRACE(cx)) { - if (!CanLeaveTrace(cx)) - return NULL; - LeaveTrace(cx); - } - js_ReportAllocationOverflow(cx); - return NULL; - } - return JSRope::new_(cx, left, right, wholeLength); } diff --git a/js/src/vm/String.h b/js/src/vm/String.h index ee7d7308c331..224601fd53b1 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -261,6 +261,13 @@ class JSString : public js::gc::Cell return (length << LENGTH_SHIFT) | flags; } + /* + * Helper function to validate that a string of a given length is + * representable by a JSString. An allocation overflow is reported if false + * is returned. + */ + static inline bool validateLength(JSContext *cx, size_t length); + static void staticAsserts() { JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32); JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >> From 4c1e66e1e45e5faacb99ad7569578765fcfa88ea Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 19:34:28 -0400 Subject: [PATCH 30/49] Bug 657260 - Check JS string length against maximum in more places. r=Waldo --- js/src/jsstr.cpp | 6 ++---- js/src/vm/String-inl.h | 7 ++++++- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 6179f017593f..7c6f58d98069 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3033,11 +3033,9 @@ js_InitStringClass(JSContext *cx, JSObject *obj) JSFixedString * js_NewString(JSContext *cx, jschar *chars, size_t length) { - if (!JSString::validateLength(cx, length)) - return NULL; - JSFixedString *s = JSFixedString::new_(cx, chars, length); - Probes::createString(cx, s, length); + if (s) + Probes::createString(cx, s, length); return s; } diff --git a/js/src/vm/String-inl.h b/js/src/vm/String-inl.h index 6e8bc9443ec5..68c66a20d80a 100644 --- a/js/src/vm/String-inl.h +++ b/js/src/vm/String-inl.h @@ -78,6 +78,8 @@ JSRope::init(JSString *left, JSString *right, size_t length) JS_ALWAYS_INLINE JSRope * JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length) { + if (!validateLength(cx, length)) + return NULL; JSRope *str = (JSRope *)js_NewGCString(cx); if (!str) return NULL; @@ -136,9 +138,10 @@ JSFixedString::init(const jschar *chars, size_t length) JS_ALWAYS_INLINE JSFixedString * JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length) { - JS_ASSERT(length <= MAX_LENGTH); JS_ASSERT(chars[length] == jschar(0)); + if (!validateLength(cx, length)) + return NULL; JSFixedString *str = (JSFixedString *)js_NewGCString(cx); if (!str) return NULL; @@ -207,6 +210,8 @@ JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN t JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT); JS_ASSERT(chars[length] == 0); + if (!validateLength(cx, length)) + return NULL; JSExternalString *str = js_NewGCExternalString(cx); if (!str) return NULL; From cd61da6abcc3d786dcec48f753a5b32ade46ce41 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Fri, 7 Oct 2011 19:34:40 -0400 Subject: [PATCH 31/49] Bug 657260 - Handle NULL return from JS_NewExternalString in XPCStringConvert::ReadableToJSVal. r=mrbkap --- js/src/xpconnect/src/xpcstring.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/xpconnect/src/xpcstring.cpp b/js/src/xpconnect/src/xpcstring.cpp index 082d9a903c0f..352c1b3364e7 100644 --- a/js/src/xpconnect/src/xpcstring.cpp +++ b/js/src/xpconnect/src/xpcstring.cpp @@ -139,5 +139,5 @@ XPCStringConvert::ReadableToJSVal(JSContext *cx, if (!str) JS_free(cx, chars); } - return STRING_TO_JSVAL(str); + return str ? STRING_TO_JSVAL(str) : JSVAL_NULL; } From f5852ac0b8569c2561b612dce23d072739f595c1 Mon Sep 17 00:00:00 2001 From: Gavin Barraclough Date: Fri, 7 Oct 2011 17:52:50 -0700 Subject: [PATCH 32/49] Bug 683838: Fix return logic in backTrackParentheses, r=dmandelin --- js/src/jit-test/tests/basic/bug683838.js | 3 +++ js/src/yarr/YarrInterpreter.cpp | 21 ++++++++++----------- 2 files changed, 13 insertions(+), 11 deletions(-) create mode 100644 js/src/jit-test/tests/basic/bug683838.js diff --git a/js/src/jit-test/tests/basic/bug683838.js b/js/src/jit-test/tests/basic/bug683838.js new file mode 100644 index 000000000000..841376c03159 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug683838.js @@ -0,0 +1,3 @@ +var rg = /(X(?:.(?!X))*?Y)|(Y(?:.(?!Y))*?Z)/g; +var str = "Y aaa X Match1 Y aaa Y Match2 Z"; +assertEq(str.match(rg) + "", "X Match1 Y,Y Match2 Z"); diff --git a/js/src/yarr/YarrInterpreter.cpp b/js/src/yarr/YarrInterpreter.cpp index 7aa52b309e66..b85ddd74aa13 100644 --- a/js/src/yarr/YarrInterpreter.cpp +++ b/js/src/yarr/YarrInterpreter.cpp @@ -860,12 +860,11 @@ public: resetMatches(term, context); freeParenthesesDisjunctionContext(context); - if (result == JSRegExpNoMatch) { - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } else + if (result != JSRegExpNoMatch) return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; } } @@ -947,12 +946,11 @@ public: resetMatches(term, context); freeParenthesesDisjunctionContext(context); - if (result == JSRegExpNoMatch) { - JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); - if (backtrackResult != JSRegExpMatch) - return backtrackResult; - } else + if (result != JSRegExpNoMatch) return result; + JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack); + if (backtrackResult != JSRegExpMatch) + return backtrackResult; } } @@ -1036,7 +1034,8 @@ public: popParenthesesDisjunctionContext(backTrack); freeParenthesesDisjunctionContext(context); - return result; + if (result != JSRegExpNoMatch) + return result; } return JSRegExpNoMatch; From 44ead00caf066052272be79a2d88a546e5651eeb Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Fri, 7 Oct 2011 21:52:09 -0400 Subject: [PATCH 33/49] Bug 607251 - Aero broken with Basic/Aero theme and minimize/un-minimize. r=jimm --- widget/src/windows/nsWindow.cpp | 48 +++++++++++++++++++++------------ widget/src/windows/nsWindow.h | 5 +++- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 98dfa81adbe0..d89638bc0320 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -2651,32 +2651,46 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide) /************************************************************** * - * SECTION: nsIWidget::Invalidate + * SECTION: nsWindow::Invalidate * * Invalidate an area of the client for painting. * **************************************************************/ // Invalidate this component visible area -NS_METHOD nsWindow::Invalidate(bool aIsSynchronous) +NS_METHOD nsWindow::Invalidate(bool aIsSynchronous, + bool aEraseBackground, + bool aUpdateNCArea, + bool aIncludeChildren) { - if (mWnd) - { + if (!mWnd) { + return NS_OK; + } + #ifdef WIDGET_DEBUG_OUTPUT - debug_DumpInvalidate(stdout, - this, - nsnull, - aIsSynchronous, - nsCAutoString("noname"), - (PRInt32) mWnd); + debug_DumpInvalidate(stdout, + this, + nsnull, + aIsSynchronous, + nsCAutoString("noname"), + (PRInt32) mWnd); #endif // WIDGET_DEBUG_OUTPUT - VERIFY(::InvalidateRect(mWnd, NULL, FALSE)); - - if (aIsSynchronous) { - VERIFY(::UpdateWindow(mWnd)); - } + DWORD flags = RDW_INVALIDATE; + if (aEraseBackground) { + flags |= RDW_ERASE; } + if (aIsSynchronous) { + flags |= RDW_UPDATENOW; + } + if (aUpdateNCArea) { + flags |= RDW_FRAME; + } + if (aIncludeChildren) { + flags |= RDW_ALLCHILDREN; + } + + VERIFY(::RedrawWindow(mWnd, NULL, NULL, flags)); return NS_OK; } @@ -4687,7 +4701,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, // Invalidate the window so that the repaint will // pick up the new theme. - Invalidate(false); + Invalidate(true, true, true, true); } break; @@ -5366,7 +5380,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED); DispatchStandardEvent(NS_THEMECHANGED); UpdateGlass(); - Invalidate(false); + Invalidate(true, true, true, true); break; #endif diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index e46966b4efbe..947c5372ecd9 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -145,7 +145,10 @@ public: virtual nsresult ConfigureChildren(const nsTArray& aConfigurations); NS_IMETHOD MakeFullScreen(bool aFullScreen); NS_IMETHOD HideWindowChrome(bool aShouldHide); - NS_IMETHOD Invalidate(bool aIsSynchronous); + NS_IMETHOD Invalidate(bool aIsSynchronous, + bool aEraseBackground = false, + bool aUpdateNCArea = false, + bool aIncludeChildren = false); NS_IMETHOD Invalidate(const nsIntRect & aRect, bool aIsSynchronous); NS_IMETHOD Update(); virtual void* GetNativeData(PRUint32 aDataType); From 2a2d0d21c2bc2f164283bee06718b040d28fe540 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Sat, 8 Oct 2011 14:16:37 +0900 Subject: [PATCH 34/49] Bug 691734 - make sure scrolling start event is delivered after document focus, r=marcoz --- accessible/src/base/FocusManager.cpp | 15 +++ .../src/base/nsAccessibilityService.cpp | 2 +- accessible/src/base/nsDocAccessible.cpp | 16 --- accessible/src/base/nsDocAccessible.h | 22 ++-- .../tests/mochitest/actions/test_anchors.html | 1 + .../tests/mochitest/events/test_scroll.xul | 104 +++++++++++++++--- 6 files changed, 114 insertions(+), 46 deletions(-) diff --git a/accessible/src/base/FocusManager.cpp b/accessible/src/base/FocusManager.cpp index 3505ce7a6fea..c4195802d27b 100644 --- a/accessible/src/base/FocusManager.cpp +++ b/accessible/src/base/FocusManager.cpp @@ -335,6 +335,21 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent) nsRefPtr focusEvent = new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag); nsEventShell::FireEvent(focusEvent); + + // Fire scrolling_start event when the document receives the focus if it has + // an anchor jump. If an accessible within the document receive the focus + // then null out the anchor jump because it no longer applies. + nsDocAccessible* targetDocument = target->GetDocAccessible(); + nsAccessible* anchorJump = targetDocument->AnchorJump(); + if (anchorJump) { + if (target == targetDocument) { + // XXX: bug 625699, note in some cases the node could go away before we + // we receive focus event, for example if the node is removed from DOM. + nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START, + anchorJump, fromUserInputFlag); + } + targetDocument->SetAnchorJump(nsnull); + } } nsIContent* diff --git a/accessible/src/base/nsAccessibilityService.cpp b/accessible/src/base/nsAccessibilityService.cpp index 570147028ca1..03de99586c0d 100644 --- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -162,7 +162,7 @@ nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode) if (documentNode) { nsDocAccessible* document = GetDocAccessible(documentNode); if (document) - document->HandleAnchorJump(aTargetNode); + document->SetAnchorJump(aTargetNode); } } diff --git a/accessible/src/base/nsDocAccessible.cpp b/accessible/src/base/nsDocAccessible.cpp index 05320de38c54..3ba4ad65193e 100644 --- a/accessible/src/base/nsDocAccessible.cpp +++ b/accessible/src/base/nsDocAccessible.cpp @@ -1769,22 +1769,6 @@ nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent) } } -void -nsDocAccessible::ProcessAnchorJump(nsIContent* aTargetNode) -{ - // If the jump target is not accessible then fire an event for nearest - // accessible in parent chain. - nsAccessible* target = GetAccessibleOrContainer(aTargetNode); - if (!target) - return; - - // XXX: bug 625699, note in some cases the node could go away before we flush - // the queue, for example if the node becomes inaccessible, or is removed from - // the DOM. - FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SCROLLING_START, - target->GetNode()); -} - void nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer, const nsTArray >* aInsertedContent) diff --git a/accessible/src/base/nsDocAccessible.h b/accessible/src/base/nsDocAccessible.h index a03a1b5b11b3..8aae48925890 100644 --- a/accessible/src/base/nsDocAccessible.h +++ b/accessible/src/base/nsDocAccessible.h @@ -205,13 +205,13 @@ public: nsresult FireDelayedAccessibleEvent(AccEvent* aEvent); /** - * Handle anchor jump when page is loaded. + * Get/set the anchor jump. */ - inline void HandleAnchorJump(nsIContent* aTargetNode) - { - HandleNotification - (this, &nsDocAccessible::ProcessAnchorJump, aTargetNode); - } + inline nsAccessible* AnchorJump() + { return GetAccessibleOrContainer(mAnchorJumpElm); } + + inline void SetAnchorJump(nsIContent* aTargetNode) + { mAnchorJumpElm = aTargetNode; } /** * Bind the child document to the tree. @@ -467,11 +467,6 @@ protected: */ void ProcessPendingEvent(AccEvent* aEvent); - /** - * Process anchor jump notification and fire scrolling end event. - */ - void ProcessAnchorJump(nsIContent* aTargetNode); - /** * Update the accessible tree for inserted content. */ @@ -572,6 +567,11 @@ protected: */ PRUint32 mLoadEventType; + /** + * Reference to anchor jump element. + */ + nsCOMPtr mAnchorJumpElm; + /** * Keep the ARIA attribute old value that is initialized by * AttributeWillChange and used by AttributeChanged notifications. diff --git a/accessible/tests/mochitest/actions/test_anchors.html b/accessible/tests/mochitest/actions/test_anchors.html index 9ff19a3b0aab..b452b9c547fc 100644 --- a/accessible/tests/mochitest/actions/test_anchors.html +++ b/accessible/tests/mochitest/actions/test_anchors.html @@ -35,6 +35,7 @@ // Test // gA11yEventDumpID = "debug"; // debug stuff + //gA11yEventDumpToConsole = true; // debug stuff function doTest() { diff --git a/accessible/tests/mochitest/events/test_scroll.xul b/accessible/tests/mochitest/events/test_scroll.xul index 38f0cc8342fe..e58e21c50e7d 100644 --- a/accessible/tests/mochitest/events/test_scroll.xul +++ b/accessible/tests/mochitest/events/test_scroll.xul @@ -16,11 +16,12 @@ + + + + +
+selected shadowed text +
+ + diff --git a/layout/reftests/text-shadow/text-shadow-selected-1-ref.html b/layout/reftests/text-shadow/text-shadow-selected-1-ref.html new file mode 100644 index 000000000000..497c22f955c3 --- /dev/null +++ b/layout/reftests/text-shadow/text-shadow-selected-1-ref.html @@ -0,0 +1,36 @@ + + + + + + + + +
+                       +
+
+selected shadowed text +
+ + diff --git a/layout/reftests/text-shadow/text-shadow-selected-1.html b/layout/reftests/text-shadow/text-shadow-selected-1.html new file mode 100644 index 000000000000..5b0d79c380a7 --- /dev/null +++ b/layout/reftests/text-shadow/text-shadow-selected-1.html @@ -0,0 +1,32 @@ + + + + + + + + +
+selected shadowed text +
+ + From a53be3d180f4290e467d7c4860b083f73319238a Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Fri, 7 Oct 2011 17:25:19 +0100 Subject: [PATCH 44/49] bug 577911 - don't ignore the return value from SetGlyphsFromRun. r=jdaggett --- gfx/thebes/gfxHarfBuzzShaper.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index fe35150ff27a..b4983154ab70 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -891,15 +891,13 @@ gfxHarfBuzzShaper::InitTextRun(gfxContext *aContext, hb_buffer_reverse(buffer); } -#ifdef DEBUG nsresult rv = -#endif - SetGlyphsFromRun(aContext, aTextRun, buffer, aRunStart, aRunLength); + SetGlyphsFromRun(aContext, aTextRun, buffer, aRunStart, aRunLength); NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into textrun"); hb_buffer_destroy(buffer); hb_font_destroy(font); - return PR_TRUE; + return NS_SUCCEEDED(rv); } /** From 8517df3350da717c184930b0c2af6f68e78d4357 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Tue, 27 Sep 2011 15:35:00 -0700 Subject: [PATCH 45/49] Bug 677118 - Long tapping on link which has div inside does not show link context menu [r=mbrubeck,mfinkle] --- mobile/chrome/content/content.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/mobile/chrome/content/content.js b/mobile/chrome/content/content.js index c3b5ee340e29..53d986ad0f25 100644 --- a/mobile/chrome/content/content.js +++ b/mobile/chrome/content/content.js @@ -899,6 +899,7 @@ var ContextHandler = { } let elem = popupNode; + let isText = false; while (elem) { if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) { // Link? @@ -947,14 +948,16 @@ var ContextHandler = { elem instanceof Ci.nsIDOMHTMLPreElement || elem instanceof Ci.nsIDOMHTMLHeadingElement || elem instanceof Ci.nsIDOMHTMLTableCellElement) { - state.types.push("content-text"); - break; + isText = true; } } elem = elem.parentNode; } + if (isText) + state.types.push("content-text"); + for (let i = 0; i < this._types.length; i++) if (this._types[i].handler(state, popupNode)) state.types.push(this._types[i].name); From 64fb3f8b1e34897ea22690cbd4160b3883141000 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 7 Oct 2011 22:43:00 -0700 Subject: [PATCH 46/49] Bug 692970 - Fix intermittent 'browser_locationBarCommand.js | Urlbar is still focused after click'. r=gavin --- browser/base/content/test/browser_locationBarCommand.js | 1 - 1 file changed, 1 deletion(-) diff --git a/browser/base/content/test/browser_locationBarCommand.js b/browser/base/content/test/browser_locationBarCommand.js index c697e5c1a1a1..794d15d73d0f 100644 --- a/browser/base/content/test/browser_locationBarCommand.js +++ b/browser/base/content/test/browser_locationBarCommand.js @@ -73,7 +73,6 @@ let gTests = [ check: function(aTab) { // Right click should do nothing (context menu will be shown) is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered"); - ok(gURLBar.focused, "Urlbar is still focused after click"); } }, From 2ac67698f35f0a58373742f0ed820d919d97706b Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Sat, 8 Oct 2011 14:44:26 -0700 Subject: [PATCH 47/49] Bug 691959: Remove unused variable 'bool done' from nsCSSFrameConstructor.cpp. r=dbaron --- layout/base/nsCSSFrameConstructor.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 125ef881179d..8e41888efeef 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -9931,14 +9931,12 @@ FirstLetterCount(const nsTextFragment* aFragment) { PRInt32 count = 0; PRInt32 firstLetterLength = 0; - bool done = false; PRInt32 i, n = aFragment->GetLength(); for (i = 0; i < n; i++) { PRUnichar ch = aFragment->CharAt(i); if (XP_IS_SPACE(ch)) { if (firstLetterLength) { - done = PR_TRUE; break; } count++; @@ -9947,7 +9945,6 @@ FirstLetterCount(const nsTextFragment* aFragment) // XXX I18n if ((ch == '\'') || (ch == '\"')) { if (firstLetterLength) { - done = PR_TRUE; break; } // keep looping @@ -9955,7 +9952,6 @@ FirstLetterCount(const nsTextFragment* aFragment) } else { count++; - done = PR_TRUE; break; } } From 639e3cffcd67695f833934013bbd88a813f94f5a Mon Sep 17 00:00:00 2001 From: Benoit Girard Date: Thu, 6 Oct 2011 12:12:51 -0700 Subject: [PATCH 48/49] Bug 692548 - Properly print OGL Shader compile errors on Android. r=jmuizelaar --- gfx/layers/opengl/LayerManagerOGLProgram.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gfx/layers/opengl/LayerManagerOGLProgram.h b/gfx/layers/opengl/LayerManagerOGLProgram.h index c25179b0f23f..9f142577bd43 100644 --- a/gfx/layers/opengl/LayerManagerOGLProgram.h +++ b/gfx/layers/opengl/LayerManagerOGLProgram.h @@ -247,14 +247,14 @@ protected: log.SetLength(len); if (!success) { - fprintf (stderr, "=== SHADER COMPILATION FAILED ===\n"); + printf_stderr("=== SHADER COMPILATION FAILED ===\n"); } else { - fprintf (stderr, "=== SHADER COMPILATION WARNINGS ===\n"); + printf_stderr("=== SHADER COMPILATION WARNINGS ===\n"); } - fprintf (stderr, "=== Source:\n%s\n", aShaderSource); - fprintf (stderr, "=== Log:\n%s\n", log.get()); - fprintf (stderr, "============\n"); + printf_stderr("=== Source:\n%s\n", aShaderSource); + printf_stderr("=== Log:\n%s\n", log.get()); + printf_stderr("============\n"); if (!success) { mGL->fDeleteShader(sh); @@ -303,12 +303,12 @@ protected: log.SetLength(len); if (!success) { - fprintf (stderr, "=== PROGRAM LINKING FAILED ===\n"); + printf_stderr("=== PROGRAM LINKING FAILED ===\n"); } else { - fprintf (stderr, "=== PROGRAM LINKING WARNINGS ===\n"); + printf_stderr("=== PROGRAM LINKING WARNINGS ===\n"); } - fprintf (stderr, "=== Log:\n%s\n", log.get()); - fprintf (stderr, "============\n"); + printf_stderr("=== Log:\n%s\n", log.get()); + printf_stderr("============\n"); } // We can mark the shaders for deletion; they're attached to the program From 8e039fda6d4995e44918a3306182a08c16542b3e Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Sat, 8 Oct 2011 21:37:31 -0700 Subject: [PATCH 49/49] Bug 461820 - prevent accessing searchbar history from content r=dolske --- toolkit/components/satchel/formSubmitListener.js | 5 +++++ toolkit/components/satchel/nsFormAutoComplete.js | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/toolkit/components/satchel/formSubmitListener.js b/toolkit/components/satchel/formSubmitListener.js index 904fe53cc7b0..49716ee27a74 100644 --- a/toolkit/components/satchel/formSubmitListener.js +++ b/toolkit/components/satchel/formSubmitListener.js @@ -184,6 +184,11 @@ var satchelFormListener = { if (!name) continue; + if (name == 'searchbar-history') { + this.log('addEntry for input name "' + name + '" is denied') + continue; + } + // Limit stored data to 200 characters. if (name.length > 200 || value.length > 200) { this.log("skipping input that has a name/value too large"); diff --git a/toolkit/components/satchel/nsFormAutoComplete.js b/toolkit/components/satchel/nsFormAutoComplete.js index 91f00399d39b..c730d91400f7 100644 --- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -171,6 +171,12 @@ FormAutoComplete.prototype = { if (!this._enabled) return null; + // don't allow form inputs (aField != null) to get results from search bar history + if (aInputName == 'searchbar-history' && aField) { + this.log('autoCompleteSearch for input name "' + aInputName + '" is denied'); + return null; + } + this.log("AutoCompleteSearch invoked. Search is: " + aUntrimmedSearchString); let searchString = aUntrimmedSearchString.trim().toLowerCase(); let result = null;