From 880121715e8e7b8040acc40b8c5ef0d39fa5ba42 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Thu, 14 May 2009 12:35:23 +0200 Subject: [PATCH 01/46] bug 491126 - sharing object map for non-native objects. r=brendan --- js/src/jsapi.cpp | 2 +- js/src/jsarray.cpp | 69 ++--- js/src/jsbuiltins.cpp | 15 +- js/src/jsemit.cpp | 2 +- js/src/jsfun.cpp | 4 +- js/src/jsinterp.cpp | 18 +- js/src/jslock.cpp | 30 +- js/src/jslock.h | 10 +- js/src/jsobj.cpp | 259 +++++++----------- js/src/jsobj.h | 46 ++-- js/src/jsparse.cpp | 2 +- js/src/jsprvtd.h | 24 -- js/src/jsscope.cpp | 48 +++- js/src/jsscope.h | 16 +- js/src/jstracer.cpp | 27 +- js/src/jsxml.cpp | 12 +- js/src/liveconnect/jsj_JavaArray.c | 8 +- js/src/liveconnect/jsj_JavaClass.c | 8 +- js/src/liveconnect/jsj_JavaObject.c | 31 +-- js/src/liveconnect/jsj_private.h | 7 - .../xpconnect/src/xpcwrappednativejsops.cpp | 2 +- 21 files changed, 256 insertions(+), 384 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 96283c9d9e36..170a3daad729 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2953,7 +2953,7 @@ JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep) return JS_TRUE; /* Walk slots in obj and if any value is a non-null object, seal it. */ - nslots = scope->map.freeslot; + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); if (JSVAL_IS_PRIMITIVE(v)) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 5b12beeddef9..67e96f96eae5 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1199,35 +1199,12 @@ array_trace(JSTracer *trc, JSObject *obj) } } -static JSObjectMap * -array_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ -#ifdef DEBUG - extern JSClass js_ArrayClass; - extern JSObjectOps js_ArrayObjectOps; -#endif - JSObjectMap *map = (JSObjectMap *) JS_malloc(cx, sizeof(*map)); - if (!map) - return NULL; +extern JSObjectOps js_ArrayObjectOps; - map->nrefs = nrefs; - JS_ASSERT(ops == &js_ArrayObjectOps); - map->ops = ops; - JS_ASSERT(clasp == &js_ArrayClass); - map->freeslot = JSSLOT_FREE(clasp); - - return map; -} - -void -array_destroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_free(cx, map); -} +static const JSObjectMap SharedArrayMap = { &js_ArrayObjectOps }; JSObjectOps js_ArrayObjectOps = { - array_newObjectMap, array_destroyObjectMap, + &SharedArrayMap, array_lookupProperty, array_defineProperty, array_getProperty, array_setProperty, array_getAttributes, array_setAttributes, @@ -1271,27 +1248,24 @@ JSClass js_SlowArrayClass = { JSBool js_MakeArraySlow(JSContext *cx, JSObject *obj) { - JSObjectMap *map, *oldmap; - uint32 i, capacity; - JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass); /* Create a native scope. */ - map = js_NewObjectMap(cx, obj->map->nrefs, &js_SlowArrayObjectOps, - &js_SlowArrayClass, obj); - if (!map) + JSScope *scope = js_NewScope(cx, &js_SlowArrayObjectOps, + &js_SlowArrayClass, obj); + if (!scope) return JS_FALSE; - capacity = js_DenseArrayCapacity(obj); + uint32 capacity = js_DenseArrayCapacity(obj); if (capacity) { - map->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; + scope->freeslot = STOBJ_NSLOTS(obj) + JS_INITIAL_NSLOTS; obj->dslots[-1] = JS_INITIAL_NSLOTS + capacity; } else { - map->freeslot = STOBJ_NSLOTS(obj); + scope->freeslot = STOBJ_NSLOTS(obj); } /* Create new properties pointing to existing values in dslots */ - for (i = 0; i < capacity; i++) { + for (uint32 i = 0; i < capacity; i++) { jsid id; JSScopeProperty *sprop; @@ -1303,7 +1277,7 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) continue; } - sprop = js_AddScopeProperty(cx, (JSScope *)map, id, NULL, NULL, + sprop = js_AddScopeProperty(cx, scope, id, NULL, NULL, i + JS_INITIAL_NSLOTS, JSPROP_ENUMERATE, 0, 0); if (!sprop) @@ -1327,15 +1301,11 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) obj->classword ^= (jsuword) &js_ArrayClass; obj->classword |= (jsuword) &js_SlowArrayClass; - /* Swap in our new map. */ - oldmap = obj->map; - obj->map = map; - array_destroyObjectMap(cx, oldmap); - + obj->map = &scope->map; return JS_TRUE; -out_bad: - js_DestroyObjectMap(cx, map); + out_bad: + js_DestroyScope(cx, scope); return JS_FALSE; } @@ -3387,9 +3357,9 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto) if (!obj) return NULL; - JSClass* clasp = &js_ArrayClass; - obj->classword = jsuword(clasp); - + /* Initialize all fields of JSObject. */ + obj->map = const_cast(&SharedArrayMap); + obj->classword = jsuword(&js_ArrayClass); obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; @@ -3397,11 +3367,6 @@ js_NewEmptyArray(JSContext* cx, JSObject* proto) obj->fslots[JSSLOT_ARRAY_COUNT] = 0; for (unsigned i = JSSLOT_ARRAY_COUNT + 1; i != JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; - - JSObjectOps* ops = clasp->getObjectOps(cx, clasp); - obj->map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!obj->map) - return NULL; obj->dslots = NULL; return obj; } diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index aa4362ae8e7b..9673d89b9b18 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -258,10 +258,10 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) } slot = sprop->slot; - if (!scope->table && sprop->parent == scope->lastProp && slot == scope->map.freeslot) { + if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) { if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { - JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->map.freeslot))); - ++scope->map.freeslot; + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) goto exit_trace; @@ -398,26 +398,27 @@ js_Arguments(JSContext* cx) JS_DEFINE_CALLINFO_1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0) JSObject* FASTCALL -js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject *parent) +js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject* parent) { JS_ASSERT(HAS_FUNCTION_CLASS(funobj)); + JS_ASSERT(HAS_FUNCTION_CLASS(proto)); + JS_ASSERT(JS_ON_TRACE(cx)); JSFunction *fun = (JSFunction*) funobj; JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun); - JS_ASSERT(JS_ON_TRACE(cx)); JSObject* closure = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); if (!closure) return NULL; + js_HoldScope(OBJ_SCOPE(proto)); + closure->map = proto->map; closure->classword = jsuword(&js_FunctionClass); closure->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); closure->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent); closure->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); for (unsigned i = JSSLOT_PRIVATE + 1; i != JS_INITIAL_NSLOTS; ++i) closure->fslots[i] = JSVAL_VOID; - - closure->map = js_HoldObjectMap(cx, proto->map); closure->dslots = NULL; return closure; } diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index bc626dc66877..df325e8da929 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -1821,7 +1821,7 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg) #endif } - blockObj->map->freeslot = JSSLOT_FREE(&js_BlockClass); + OBJ_SCOPE(blockObj)->freeslot = JSSLOT_FREE(&js_BlockClass); js_ReallocSlots(cx, blockObj, JSSLOT_FREE(&js_BlockClass), JS_TRUE); return true; } diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 1f595ff63a7c..9027e69a724f 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -718,8 +718,8 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp) memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval)); memcpy(callobj->dslots + fun->nargs, fp->slots, fun->u.i.nvars * sizeof(jsval)); - if (scope->object == callobj && n > scope->map.freeslot) - scope->map.freeslot = n; + if (scope->object == callobj && n > scope->freeslot) + scope->freeslot = n; } JS_UNLOCK_SCOPE(cx, scope); } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index ea9001e2727a..eba3b72c1dc7 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -4164,7 +4164,7 @@ js_Interpret(JSContext *cx) ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry); if (obj == obj2 && PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj, slot); if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) { rtmp = rval; @@ -4420,7 +4420,7 @@ js_Interpret(JSContext *cx) rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); } else if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); } else { JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); @@ -4511,7 +4511,7 @@ js_Interpret(JSContext *cx) rval = PCVAL_OBJECT_TO_JSVAL(entry->vword); } else if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); } else { JS_ASSERT(PCVAL_IS_SPROP(entry->vword)); @@ -4656,7 +4656,7 @@ js_Interpret(JSContext *cx) if (checkForAdd && SPROP_HAS_STUB_SETTER(sprop) && - (slot = sprop->slot) == scope->map.freeslot) { + (slot = sprop->slot) == scope->freeslot) { /* * Fast path: adding a plain old property that * was once at the frontier of the property @@ -4681,7 +4681,7 @@ js_Interpret(JSContext *cx) */ if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) { - ++scope->map.freeslot; + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) { JS_UNLOCK_SCOPE(cx, scope); @@ -5262,7 +5262,7 @@ js_Interpret(JSContext *cx) if (PCVAL_IS_SLOT(entry->vword)) { slot = PCVAL_TO_SLOT(entry->vword); - JS_ASSERT(slot < obj2->map->freeslot); + JS_ASSERT(slot < OBJ_SCOPE(obj2)->freeslot); rval = LOCKED_OBJ_GET_SLOT(obj2, slot); JS_UNLOCK_OBJ(cx, obj2); goto do_push_rval; @@ -5742,7 +5742,7 @@ js_Interpret(JSContext *cx) index = GET_UINT16(regs.pc); JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1])); JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj, - JS_INITIAL_NSLOTS + index < obj->map->freeslot); + JS_INITIAL_NSLOTS + index < OBJ_SCOPE(obj)->freeslot); PUSH_OPND(obj->dslots[index]); if (op == JSOP_CALLDSLOT) @@ -6371,9 +6371,9 @@ js_Interpret(JSContext *cx) !SCOPE_HAS_PROPERTY(scope, sprop)); slot = sprop->slot; - JS_ASSERT(slot == scope->map.freeslot); + JS_ASSERT(slot == scope->freeslot); if (slot < STOBJ_NSLOTS(obj)) { - ++scope->map.freeslot; + ++scope->freeslot; } else { if (!js_AllocSlot(cx, obj, &slot)) { JS_UNLOCK_SCOPE(cx, scope); diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp index be2f66191b14..35c4fbdc3c41 100644 --- a/js/src/jslock.cpp +++ b/js/src/jslock.cpp @@ -57,11 +57,6 @@ #include "jsscope.h" #include "jsstr.h" -/* - * Check that we can cast the data after JSObjectMap as JSTitle. - */ -JS_STATIC_ASSERT(offsetof(JSScope, title) == sizeof(JSObjectMap)); - #define ReadWord(W) (W) /* Implement NativeCompareAndSwap. */ @@ -456,7 +451,6 @@ ShareTitle(JSContext *cx, JSTitle *title) static void FinishSharingTitle(JSContext *cx, JSTitle *title) { - JSObjectMap *map; JSScope *scope; JSObject *obj; uint32 nslots, i; @@ -464,14 +458,10 @@ FinishSharingTitle(JSContext *cx, JSTitle *title) js_InitLock(&title->lock); title->u.count = 0; /* NULL may not pun as 0 */ - map = TITLE_TO_MAP(title); - if (!MAP_IS_NATIVE(map)) - return; - scope = (JSScope *)map; - + scope = TITLE_TO_SCOPE(title); obj = scope->object; if (obj) { - nslots = scope->map.freeslot; + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); if (JSVAL_IS_STRING(v) && @@ -617,9 +607,9 @@ ClaimTitle(JSTitle *title, JSContext *cx) * non-null test, and avoid double-insertion bugs. */ if (!title->u.link) { + js_HoldScope(TITLE_TO_SCOPE(title)); title->u.link = rt->titleSharingTodo; rt->titleSharingTodo = title; - js_HoldObjectMap(cx, TITLE_TO_MAP(title)); } /* @@ -693,13 +683,13 @@ js_ShareWaitingTitles(JSContext *cx) title->u.link = NULL; /* null u.link for sanity ASAP */ /* - * If js_DropObjectMap returns null, we held the last ref to scope. - * The waiting thread(s) must have been killed, after which the GC + * If js_DropScope returns false, we held the last ref to scope. The + * waiting thread(s) must have been killed, after which the GC * collected the object that held this scope. Unlikely, because it * requires that the GC ran (e.g., from an operation callback) * during this request, but possible. */ - if (js_DropObjectMap(cx, TITLE_TO_MAP(title), NULL)) { + if (js_DropScope(cx, TITLE_TO_SCOPE(title), NULL)) { FinishSharingTitle(cx, title); /* set ownercx = NULL */ shared = true; } @@ -740,7 +730,7 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot) scope = OBJ_SCOPE(obj); title = &scope->title; JS_ASSERT(title->ownercx != cx); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < scope->freeslot); /* * Avoid locking if called from the GC. Also avoid locking an object @@ -835,7 +825,7 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v) scope = OBJ_SCOPE(obj); title = &scope->title; JS_ASSERT(title->ownercx != cx); - JS_ASSERT(slot < obj->map->freeslot); + JS_ASSERT(slot < scope->freeslot); /* * Avoid locking if called from the GC. Also avoid locking an object @@ -1478,9 +1468,7 @@ js_IsRuntimeLocked(JSRuntime *rt) JSBool js_IsObjLocked(JSContext *cx, JSObject *obj) { - JSScope *scope = OBJ_SCOPE(obj); - - return MAP_IS_NATIVE(&scope->map) && js_IsTitleLocked(cx, &scope->title); + return js_IsTitleLocked(cx, &OBJ_SCOPE(obj)->title); } JSBool diff --git a/js/src/jslock.h b/js/src/jslock.h index 275e2c1e5a20..bfa0c637d7be 100644 --- a/js/src/jslock.h +++ b/js/src/jslock.h @@ -103,14 +103,10 @@ struct JSTitle { }; /* - * Title structures must be immediately preceded by JSObjectMap structures for - * maps that use titles for threadsafety. This is enforced by assertion in - * jsscope.h; see bug 408416 for future remedies to this somewhat fragile - * architecture. + * Title structure is always allocated as a field of JSScope. */ - -#define TITLE_TO_MAP(title) \ - ((JSObjectMap *)((char *)(title) - sizeof(JSObjectMap))) +#define TITLE_TO_SCOPE(title) \ + ((JSScope *)((uint8 *) (title) - offsetof(JSScope, title))) /* * Atomic increment and decrement for a reference counter, given jsrefcount *p. diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 517272d619e2..8808b8333837 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -102,7 +102,7 @@ js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop); #endif JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, js_LookupProperty, js_DefineProperty, js_GetProperty, js_SetProperty, js_GetAttributes, js_SetAttributes, @@ -2033,9 +2033,12 @@ js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } static inline bool -CreateMapForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* ops) +InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, + JSObjectOps* ops) { - JSObjectMap* map; + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(proto == OBJ_GET_PROTO(cx, obj)); + JSClass* protoclasp; JSClass* clasp = OBJ_GET_CLASS(cx, obj); @@ -2048,33 +2051,36 @@ CreateMapForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o * object classes must have the same (null or not) reserveSlots hook. */ if (proto && - ((map = proto->map)->ops == ops && - ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || - (!((protoclasp->flags ^ clasp->flags) & - (JSCLASS_HAS_PRIVATE | - (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && - protoclasp->reserveSlots == clasp->reserveSlots)))) + proto->map->ops == ops && + ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp || + (!((protoclasp->flags ^ clasp->flags) & + (JSCLASS_HAS_PRIVATE | + (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) && + protoclasp->reserveSlots == clasp->reserveSlots))) { - /* Share the given prototype's map. */ - obj->map = js_HoldObjectMap(cx, map); + js_HoldScope(OBJ_SCOPE(proto)); + obj->map = proto->map; return true; } - map = ops->newObjectMap(cx, 1, ops, clasp, obj); - if (!map) - return false; - obj->map = map; + JSScope *scope = js_NewScope(cx, ops, clasp, obj); + if (!scope) + goto bad; - /* Let ops->newObjectMap set freeslot so as to reserve slots. */ - uint32 nslots = map->freeslot; - JS_ASSERT(nslots >= JSSLOT_PRIVATE); - if (nslots > JS_INITIAL_NSLOTS && - !js_ReallocSlots(cx, obj, nslots, JS_TRUE)) { - js_DropObjectMap(cx, map, obj); - return false; + /* Let js_NewScope set freeslot so as to reserve slots. */ + JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE); + if (scope->freeslot > JS_INITIAL_NSLOTS && + !js_ReallocSlots(cx, obj, scope->freeslot, JS_TRUE)) { + js_DestroyScope(cx, scope); + goto bad; } - + obj->map = &scope->map; return true; + + bad: + /* Ensure that the map field is initialized for GC. */ + obj->map = NULL; + return false; } #ifdef JS_TRACER @@ -2093,11 +2099,8 @@ NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto, JSObject *parent for (unsigned i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; - if (!CreateMapForObject(cx, obj, proto, &js_ObjectOps)) - return NULL; obj->dslots = NULL; - - return obj; + return InitScopeForObject(cx, obj, proto, &js_ObjectOps) ? obj : NULL; } JSObject* FASTCALL @@ -2363,7 +2366,7 @@ with_ThisObject(JSContext *cx, JSObject *obj) } JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, with_LookupProperty, js_DefineProperty, with_GetProperty, with_SetProperty, with_GetAttributes, with_SetAttributes, @@ -2455,7 +2458,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind) /* * Block objects should never be exposed to scripts. Thus the clone should * not own the property map and rather always share it with the prototype - * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after + * object. This allows us to skip updating OBJ_SCOPE(obj)->freeslot after * we copy the stack slots into reserved slots. */ JS_ASSERT(OBJ_SCOPE(obj)->object != obj); @@ -2845,50 +2848,6 @@ bad: goto out; } -void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp) -{ - map->nrefs = nrefs; - map->ops = ops; - map->freeslot = JSSLOT_FREE(clasp); -} - -JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ - return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj); -} - -void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - js_DestroyScope(cx, (JSScope *)map); -} - -JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_ASSERT(map->nrefs >= 0); - JS_ATOMIC_INCREMENT(&map->nrefs); - return map; -} - -JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj) -{ - JS_ASSERT(map->nrefs > 0); - JS_ATOMIC_DECREMENT(&map->nrefs); - if (map->nrefs == 0) { - map->ops->destroyObjectMap(cx, map); - return NULL; - } - if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj) - ((JSScope *)map)->object = NULL; - return map; -} - static void FreeSlots(JSContext *cx, JSObject *obj) { @@ -3072,11 +3031,6 @@ JSObject * js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent, uintN objectSize) { - JSObject *obj; - JSObjectOps *ops; - uint32 i; - JSTempValueRooter tvr; - #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED()) jsdtrace_object_create_start(cx->fp, clasp); @@ -3097,16 +3051,18 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JS_ASSERT_IF(clasp->flags & JSCLASS_IS_EXTENDED, ((JSExtendedClass *)clasp)->equality); + /* Always call the class's getObjectOps hook if it has one. */ + JSObjectOps *ops = clasp->getObjectOps + ? clasp->getObjectOps(cx, clasp) + : &js_ObjectOps; + /* * Allocate an object from the GC heap and initialize all its fields before * doing any operation that can potentially trigger GC. */ - obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); + JSObject *obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize); if (!obj) - goto earlybad; - - obj->map = NULL; - obj->dslots = NULL; + goto out; /* * Set the class slot with the initial value of the system and delegate @@ -3117,55 +3073,54 @@ js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JS_ASSERT(!STOBJ_IS_DELEGATE(obj)); JS_ASSERT(!STOBJ_IS_SYSTEM(obj)); - /* Set the proto and parent properties. */ - STOBJ_SET_PROTO(obj, proto); - STOBJ_SET_PARENT(obj, parent); + obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); + + /* + * Default parent to the parent of the prototype, which was set from + * the parent of the prototype's constructor. + */ + obj->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL((!parent && proto) + ? OBJ_GET_PARENT(cx, proto) + : parent); /* Initialize the remaining fixed slots. */ - for (i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) + for (uint32 i = JSSLOT_PRIVATE; i < JS_INITIAL_NSLOTS; ++i) obj->fslots[i] = JSVAL_VOID; + obj->dslots = NULL; + + if (OPS_IS_NATIVE(ops)) { + if (!InitScopeForObject(cx, obj, proto, ops)) { + obj = NULL; + goto out; + } + } else { + JS_ASSERT(ops->objectMap->ops == ops); + obj->map = const_cast(ops->objectMap); + } + #ifdef DEBUG memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN, objectSize - sizeof(JSObject)); #endif - /* - * Root obj to prevent it from being collected out from under this call to - * js_NewObject. There's a possibilty of GC under the objectHook call-out - * further below. - */ - JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); - - /* Always call the class's getObjectOps hook if it has one. */ - ops = clasp->getObjectOps - ? clasp->getObjectOps(cx, clasp) - : &js_ObjectOps; - - /* - * Default parent to the parent of the prototype, which was set from - * the parent of the prototype's constructor. - */ - if (proto && !parent) - STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto)); - - if (!CreateMapForObject(cx, obj, proto, ops)) - goto bad; + /* Check that the newborn root still holds the object. */ + JS_ASSERT_IF(!cx->localRootStack, cx->weakRoots.newborn[GCX_OBJECT] == obj); /* * Do not call debug hooks on trace, because we might be in a non-_FAIL * builtin. See bug 481444. */ if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) { + JSAutoTempValueRooter tvr(cx, obj); JS_KEEP_ATOMS(cx->runtime); cx->debugHooks->objectHook(cx, obj, JS_TRUE, cx->debugHooks->objectHookData); JS_UNKEEP_ATOMS(cx->runtime); + cx->weakRoots.newborn[GCX_OBJECT] = obj; } out: - JS_POP_TEMP_ROOT(cx, &tvr); - cx->weakRoots.newborn[GCX_OBJECT] = obj; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) jsdtrace_object_create(cx, clasp, obj); @@ -3173,28 +3128,21 @@ out: jsdtrace_object_create_done(cx->fp, clasp); #endif return obj; - -bad: - obj = NULL; - goto out; - -earlybad: -#ifdef INCLUDE_MOZILLA_DTRACE - if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) - jsdtrace_object_create(cx, clasp, NULL); - if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED()) - jsdtrace_object_create_done(cx->fp, clasp); -#endif - return NULL; } JSObject* js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) { + JS_ASSERT(!clasp->getObjectOps); + JS_ASSERT(proto->map->ops == &js_ObjectOps); + JS_ASSERT(OBJ_GET_CLASS(cx, proto) == clasp); + JSObject* obj = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject)); if (!obj) return NULL; + js_HoldScope(OBJ_SCOPE(proto)); + obj->map = proto->map; obj->classword = jsuword(clasp); obj->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto); obj->fslots[JSSLOT_PARENT] = proto->fslots[JSSLOT_PARENT]; @@ -3203,9 +3151,6 @@ js_NewNativeObject(JSContext *cx, JSClass *clasp, JSObject *proto, uint32 slot) while (slot < JS_INITIAL_NSLOTS) obj->fslots[slot++] = JSVAL_VOID; - JS_ASSERT(!clasp->getObjectOps); - JS_ASSERT(proto->map->ops == &js_ObjectOps); - obj->map = js_HoldObjectMap(cx, proto->map); obj->dslots = NULL; return obj; } @@ -3456,11 +3401,8 @@ bad: void js_FinalizeObject(JSContext *cx, JSObject *obj) { - JSObjectMap *map; - /* Cope with stillborn objects that have no map. */ - map = obj->map; - if (!map) + if (!obj->map) return; if (cx->debugHooks->objectHook) { @@ -3476,8 +3418,8 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) jsdtrace_object_finalize(obj); #endif - /* Drop map and free slots. */ - js_DropObjectMap(cx, map, obj); + if (OBJ_IS_NATIVE(obj)) + js_DropScope(cx, OBJ_SCOPE(obj), obj); FreeSlots(cx, obj); } @@ -3486,38 +3428,35 @@ js_FinalizeObject(JSContext *cx, JSObject *obj) JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { - JSObjectMap *map; - JSClass *clasp; + JS_ASSERT(OBJ_IS_NATIVE(obj)); - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); - clasp = LOCKED_OBJ_GET_CLASS(obj); - if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { - /* Adjust map->freeslot to include computed reserved slots, if any. */ - map->freeslot += clasp->reserveSlots(cx, obj); + JSScope *scope = OBJ_SCOPE(obj); + JSClass *clasp = LOCKED_OBJ_GET_CLASS(obj); + if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { + /* Adjust scope->freeslot to include computed reserved slots, if any. */ + scope->freeslot += clasp->reserveSlots(cx, obj); } - if (map->freeslot >= STOBJ_NSLOTS(obj) && - !js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) { + if (scope->freeslot >= STOBJ_NSLOTS(obj) && + !js_ReallocSlots(cx, obj, scope->freeslot + 1, JS_FALSE)) { return JS_FALSE; } /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */ - JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot))); - *slotp = map->freeslot++; + JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot))); + *slotp = scope->freeslot++; return JS_TRUE; } void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) { - JSObjectMap *map; + JS_ASSERT(OBJ_IS_NATIVE(obj)); - map = obj->map; - JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj); + JSScope *scope = OBJ_SCOPE(obj); LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); - if (map->freeslot == slot + 1) { - map->freeslot = slot; + if (scope->freeslot == slot + 1) { + scope->freeslot = slot; /* When shrinking, js_ReallocSlots always returns true. */ js_ReallocSlots(cx, obj, slot, JS_FALSE); @@ -3914,8 +3853,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, proto = OBJ_GET_PROTO(cx, proto)) { protoIndex++; } - scope = OBJ_SCOPE(obj2); - if (!MAP_IS_NATIVE(&scope->map)) { + if (!OBJ_IS_NATIVE(obj2)) { /* Whoops, newresolve handed back a foreign obj2. */ JS_ASSERT(obj2 != obj); ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp); @@ -3931,6 +3869,7 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, * not on obj's proto chain. That last case is a * "too bad!" case. */ + scope = OBJ_SCOPE(obj2); if (scope->object == obj2) sprop = SCOPE_GET_PROPERTY(scope, id); } @@ -3953,8 +3892,8 @@ js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags, if (!ok) goto cleanup; JS_LOCK_OBJ(cx, obj); + JS_ASSERT(OBJ_IS_NATIVE(obj)); scope = OBJ_SCOPE(obj); - JS_ASSERT(MAP_IS_NATIVE(&scope->map)); if (scope->object == obj) sprop = SCOPE_GET_PROPERTY(scope, id); } @@ -5842,8 +5781,8 @@ js_TraceObject(JSTracer *trc, JSObject *obj) * above. */ nslots = STOBJ_NSLOTS(obj); - if (scope->object == obj && scope->map.freeslot < nslots) - nslots = scope->map.freeslot; + if (scope->object == obj && scope->freeslot < nslots) + nslots = scope->freeslot; for (i = 0; i != nslots; ++i) { v = STOBJ_GET_SLOT(obj, i); @@ -5877,7 +5816,7 @@ js_Clear(JSContext *cx, JSObject *obj) n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj)); while (--i >= n) STOBJ_SET_SLOT(obj, i, JSVAL_VOID); - scope->map.freeslot = n; + scope->freeslot = n; } JS_UNLOCK_OBJ(cx, obj); } @@ -5926,8 +5865,8 @@ js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) } /* Whether or not we grew nslots, we may need to advance freeslot. */ - if (scope->object == obj && slot >= scope->map.freeslot) - scope->map.freeslot = slot + 1; + if (scope->object == obj && slot >= scope->freeslot) + scope->freeslot = slot + 1; STOBJ_SET_SLOT(obj, slot, v); GC_POKE(cx, JS_NULL); @@ -6131,7 +6070,7 @@ js_DumpObject(JSObject *obj) uint32 i, slots; JSClass *clasp; jsuint reservedEnd; - JSBool sharesScope = JS_FALSE; + bool sharesScope = false; fprintf(stderr, "object %p\n", (void *) obj); clasp = STOBJ_GET_CLASS(obj); @@ -6186,7 +6125,9 @@ js_DumpObject(JSObject *obj) if (clasp->flags & JSCLASS_HAS_PRIVATE) reservedEnd++; reservedEnd += JSCLASS_RESERVED_SLOTS(clasp); - slots = sharesScope ? reservedEnd : obj->map->freeslot; + slots = (OBJ_IS_NATIVE(obj) && !sharesScope) + ? OBJ_SCOPE(obj)->freeslot + : STOBJ_NSLOTS(obj); for (i = 0; i < slots; i++) { fprintf(stderr, " %3d ", i); if (i == JSSLOT_PRIVATE && (clasp->flags & JSCLASS_HAS_PRIVATE)) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 4757c051dc3c..f77d71434124 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -56,9 +56,14 @@ JS_BEGIN_EXTERN_C /* For detailed comments on these function pointer types, see jsprvtd.h. */ struct JSObjectOps { + /* + * Custom shared object map for non-native objects. For native objects + * this should be null indicating, that JSObject.map is an instance of + * JSScope. + */ + const JSObjectMap *objectMap; + /* Mandatory non-null function pointer members. */ - JSNewObjectMapOp newObjectMap; - JSObjectMapOp destroyObjectMap; JSLookupPropOp lookupProperty; JSDefinePropOp defineProperty; JSPropertyIdOp getProperty; @@ -83,9 +88,7 @@ struct JSObjectOps { }; struct JSObjectMap { - jsrefcount nrefs; /* count of all referencing objects */ JSObjectOps *ops; /* high level object operation vtable */ - uint32 freeslot; /* index of next free slot in object */ }; /* Shorthand macros for frequently-made calls. */ @@ -206,8 +209,8 @@ struct JSObject { /* * STOBJ prefix means Single Threaded Object. Use the following fast macros to - * directly manipulate slots in obj when only one thread can access obj and - * when obj->map->freeslot can be inconsistent with slots. + * directly manipulate slots in obj when only one thread can access obj, or + * when accessing read-only slots within JS_INITIAL_NSLOTS. */ #define STOBJ_NSLOTS(obj) \ @@ -266,7 +269,7 @@ STOBJ_GET_CLASS(const JSObject* obj) JSVAL_TO_PRIVATE(STOBJ_GET_SLOT(obj, JSSLOT_PRIVATE))) #define OBJ_CHECK_SLOT(obj,slot) \ - JS_ASSERT(slot < (obj)->map->freeslot) + JS_ASSERT_IF(OBJ_IS_NATIVE(obj), slot < OBJ_SCOPE(obj)->freeslot) #define LOCKED_OBJ_GET_SLOT(obj,slot) \ (OBJ_CHECK_SLOT(obj, slot), STOBJ_GET_SLOT(obj, slot)) @@ -368,12 +371,14 @@ STOBJ_GET_CLASS(const JSObject* obj) #define OBJ_GET_CLASS(cx,obj) STOBJ_GET_CLASS(obj) #define OBJ_GET_PRIVATE(cx,obj) STOBJ_GET_PRIVATE(obj) -/* Test whether a map or object is native. */ -#define MAP_IS_NATIVE(map) \ - JS_LIKELY((map)->ops == &js_ObjectOps || \ - (map)->ops->newObjectMap == js_ObjectOps.newObjectMap) +/* + * Test whether the object is native. FIXME bug 492938: consider how it would + * affect the performance to do just the !ops->objectMap check. + */ +#define OPS_IS_NATIVE(ops) \ + JS_LIKELY((ops) == &js_ObjectOps || !(ops)->objectMap) -#define OBJ_IS_NATIVE(obj) MAP_IS_NATIVE((obj)->map) +#define OBJ_IS_NATIVE(obj) OPS_IS_NATIVE((obj)->map->ops) extern JS_FRIEND_DATA(JSObjectOps) js_ObjectOps; extern JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps; @@ -502,23 +507,6 @@ extern const char js_defineSetter_str[]; extern const char js_lookupGetter_str[]; extern const char js_lookupSetter_str[]; -extern void -js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp); - -extern JSObjectMap * -js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -extern void -js_DestroyObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_HoldObjectMap(JSContext *cx, JSObjectMap *map); - -extern JSObjectMap * -js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj); - extern JSBool js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp); diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 9fb82d70b464..d90cf81ddd40 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -2973,7 +2973,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) !js_ReallocSlots(cx, blockObj, slot + 1, JS_FALSE)) { return JS_FALSE; } - blockObj->map->freeslot = slot + 1; + OBJ_SCOPE(blockObj)->freeslot = slot + 1; STOBJ_SET_SLOT(blockObj, slot, PRIVATE_TO_JSVAL(pn)); return JS_TRUE; } diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index 517ecde41a82..377c68cb92f6 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -257,30 +257,6 @@ struct JSTempValueRooter { /* JSObjectOps function pointer typedefs. */ -/* - * Create a new subclass of JSObjectMap (see jsobj.h), with the nrefs and ops - * members initialized from the same-named parameters, and with the nslots and - * freeslot members initialized according to ops and clasp. Return null on - * error, non-null on success. - * - * JSObjectMaps are reference-counted by generic code in the engine. Usually, - * the nrefs parameter to JSObjectOps.newObjectMap will be 1, to count the ref - * returned to the caller on success. After a successful construction, some - * number of js_HoldObjectMap and js_DropObjectMap calls ensue. When nrefs - * reaches 0 due to a js_DropObjectMap call, JSObjectOps.destroyObjectMap will - * be called to dispose of the map. - */ -typedef JSObjectMap * -(* JSNewObjectMapOp)(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -/* - * Generic type for an infallible JSObjectMap operation, used currently by - * JSObjectOps.destroyObjectMap. - */ -typedef void -(* JSObjectMapOp)(JSContext *cx, JSObjectMap *map); - /* * Look for id in obj and its prototype chain, returning false on error or * exception, true on success. On success, return null in *propp if id was diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index 9496535ccb01..546579a397b2 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -77,23 +77,23 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) * birth, and runtime clone of a block objects are never mutated. */ JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); - newscope = js_NewScope(cx, 0, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), - obj); + newscope = js_NewScope(cx, scope->map.ops, LOCKED_OBJ_GET_CLASS(obj), obj); if (!newscope) return NULL; JS_LOCK_SCOPE(cx, newscope); - obj->map = js_HoldObjectMap(cx, &newscope->map); - JS_ASSERT(newscope->map.freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); + obj->map = &newscope->map; + + JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); clasp = STOBJ_GET_CLASS(obj); if (clasp->reserveSlots) { freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); if (freeslot > STOBJ_NSLOTS(obj)) freeslot = STOBJ_NSLOTS(obj); - if (newscope->map.freeslot < freeslot) - newscope->map.freeslot = freeslot; + if (newscope->freeslot < freeslot) + newscope->freeslot = freeslot; } - scope = (JSScope *) js_DropObjectMap(cx, &scope->map, obj); JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); + js_DropScope(cx, scope, obj); return newscope; } @@ -160,17 +160,19 @@ CreateScopeTable(JSContext *cx, JSScope *scope, JSBool report) } JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj) +js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj) { - JSScope *scope; + JS_ASSERT(OPS_IS_NATIVE(ops)); + JS_ASSERT(obj); - scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); + JSScope *scope = (JSScope *) JS_malloc(cx, sizeof(JSScope)); if (!scope) return NULL; - js_InitObjectMap(&scope->map, nrefs, ops, clasp); + scope->map.ops = ops; scope->object = obj; + scope->nrefs = 1; + scope->freeslot = JSSLOT_FREE(clasp); scope->flags = 0; InitMinimalScope(cx, scope); @@ -203,6 +205,28 @@ js_DestroyScope(JSContext *cx, JSScope *scope) JS_free(cx, scope); } +void +js_HoldScope(JSScope *scope) +{ + JS_ASSERT(scope->nrefs >= 0); + JS_ATOMIC_INCREMENT(&scope->nrefs); +} + +JSBool +js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj) +{ + JS_ASSERT(scope->nrefs > 0); + JS_ATOMIC_DECREMENT(&scope->nrefs); + + if (scope->nrefs == 0) { + js_DestroyScope(cx, scope); + return false; + } + if (scope->object == obj) + scope->object = NULL; + return true; +} + #ifdef JS_DUMP_PROPTREE_STATS typedef struct JSScopeStats { jsrefcount searches; diff --git a/js/src/jsscope.h b/js/src/jsscope.h index b15cb5f2a70c..31133cc5b650 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -201,6 +201,8 @@ struct JSScope { JSTitle title; /* lock state */ #endif JSObject *object; /* object that owns this scope */ + jsrefcount nrefs; /* count of all referencing objects */ + uint32 freeslot; /* index of next free slot in object */ uint32 shape; /* property cache shape identifier */ uint8 flags; /* flags, see below */ int8 hashShift; /* multiplicative hash shift */ @@ -213,7 +215,8 @@ struct JSScope { #define JS_IS_SCOPE_LOCKED(cx, scope) JS_IS_TITLE_LOCKED(cx, &(scope)->title) -#define OBJ_SCOPE(obj) ((JSScope *)(obj)->map) +#define OBJ_SCOPE(obj) (JS_ASSERT(OBJ_IS_NATIVE(obj)), \ + (JSScope *) (obj)->map) #define OBJ_SHAPE(obj) (OBJ_SCOPE(obj)->shape) /* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */ @@ -329,7 +332,7 @@ struct JSScopeProperty { #define SPROP_INVALID_SLOT 0xffffffff -#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->map.freeslot) +#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot) #define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope) #define SPROP_HAS_STUB_GETTER(sprop) (!(sprop)->getter) @@ -396,12 +399,17 @@ extern JSScope * js_GetMutableScope(JSContext *cx, JSObject *obj); extern JSScope * -js_NewScope(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, JSClass *clasp, - JSObject *obj); +js_NewScope(JSContext *cx, JSObjectOps *ops, JSClass *clasp, JSObject *obj); extern void js_DestroyScope(JSContext *cx, JSScope *scope); +extern void +js_HoldScope(JSScope *scope); + +extern JSBool +js_DropScope(JSContext *cx, JSScope *scope, JSObject *obj); + extern JS_FRIEND_API(JSScopeProperty **) js_SearchScope(JSScope *scope, jsid id, JSBool adding); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 04c448df2b59..e03d68a6f0a1 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6301,21 +6301,25 @@ TraceRecorder::binary(LOpcode op) return JSRS_STOP; } -JS_STATIC_ASSERT(offsetof(JSObjectOps, newObjectMap) == 0); +JS_STATIC_ASSERT(offsetof(JSObjectOps, objectMap) == 0); bool TraceRecorder::map_is_native(JSObjectMap* map, LIns* map_ins, LIns*& ops_ins, size_t op_offset) { -#define OP(ops) (*(JSObjectOp*) ((char*)(ops) + op_offset)) - if (OP(map->ops) != OP(&js_ObjectOps)) - return false; + JS_ASSERT(op_offset < sizeof(JSObjectOps)); + JS_ASSERT(op_offset % sizeof(void *) == 0); - ops_ins = addName(lir->insLoad(LIR_ldp, map_ins, offsetof(JSObjectMap, ops)), "ops"); +#define OP(ops) (*(void **) ((uint8 *) (ops) + op_offset)) + void* ptr = OP(map->ops); + if (ptr != OP(&js_ObjectOps)) + return false; +#undef OP + + ops_ins = addName(lir->insLoad(LIR_ldp, map_ins, int(offsetof(JSObjectMap, ops))), "ops"); LIns* n = lir->insLoad(LIR_ldp, ops_ins, op_offset); guard(true, - addName(lir->ins2(LIR_eq, n, INS_CONSTFUNPTR(OP(&js_ObjectOps))), "guard(native-map)"), + addName(lir->ins2(LIR_eq, n, INS_CONSTPTR(ptr)), "guard(native-map)"), BRANCH_EXIT); -#undef OP return true; } @@ -6340,10 +6344,9 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 LIns* ops_ins; // Interpreter calls to PROPERTY_CACHE_TEST guard on native object ops - // (newObjectMap == js_ObjectOps.newObjectMap) which is required to use - // native objects (those whose maps are scopes), or even more narrow - // conditions required because the cache miss case will call a particular - // object-op (js_GetProperty, js_SetProperty). + // which is required to use native objects (those whose maps are scopes), + // or even more narrow conditions required because the cache miss case + // will call a particular object-op (js_GetProperty, js_SetProperty). // // We parameterize using offsetof and guard on match against the hook at // the given offset in js_ObjectOps. TraceRecorder::record_JSOP_SETPROP @@ -6354,7 +6357,7 @@ TraceRecorder::test_property_cache(JSObject* obj, LIns* obj_ins, JSObject*& obj2 // No need to guard native-ness of global object. JS_ASSERT(OBJ_IS_NATIVE(globalObj)); if (aobj != globalObj) { - size_t op_offset = offsetof(JSObjectOps, newObjectMap); + size_t op_offset = offsetof(JSObjectOps, objectMap); if (mode == JOF_PROP || mode == JOF_VARPROP) { JS_ASSERT(!(format & JOF_SET)); op_offset = offsetof(JSObjectOps, getProperty); diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index e2529e64ce95..949350d64995 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -4912,10 +4912,10 @@ xml_trace_vector(JSTracer *trc, JSXML **vec, uint32 len) } /* - * js_XMLObjectOps.newObjectMap == js_NewObjectMap, so XML objects appear to - * be native. Thus xml_lookupProperty must return a valid JSScopeProperty - * pointer parameter via *propp to signify "property found". Since the only - * call to xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from + * js_XMLObjectOps.newObjectMap is null, so XML objects appear to be native. + * Thus xml_lookupProperty must return a valid JSScopeProperty pointer + * parameter via *propp to signify "property found". Since the only call to + * xml_lookupProperty is via OBJ_LOOKUP_PROPERTY, and then only from * js_FindProperty (in jsobj.c, called from jsinterp.c) or from JSOP_IN case * in the interpreter, the only time we add a JSScopeProperty here is when an * unqualified name is being accessed or when "name in xml" is called. @@ -5428,9 +5428,9 @@ out: return ok; } -/* Use js_NewObjectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ +/* Use NULL for objectMap so XML objects satisfy OBJ_IS_NATIVE tests. */ JS_FRIEND_DATA(JSObjectOps) js_XMLObjectOps = { - js_NewObjectMap, js_DestroyObjectMap, + NULL, xml_lookupProperty, xml_defineProperty, xml_getProperty, xml_setProperty, xml_getAttributes, xml_setAttributes, diff --git a/js/src/liveconnect/jsj_JavaArray.c b/js/src/liveconnect/jsj_JavaArray.c index b235cb151360..b67eb909ea1b 100644 --- a/js/src/liveconnect/jsj_JavaArray.c +++ b/js/src/liveconnect/jsj_JavaArray.c @@ -404,10 +404,14 @@ JavaArray_checkAccess(JSContext *cx, JSObject *obj, jsid id, } } +extern JSObjectOps JavaArray_ops; + +static const JSObjectMap JavaArrayMap = { &JavaArray_ops }; + JSObjectOps JavaArray_ops = { + &JavaArrayMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaArray_lookupProperty, JavaArray_defineProperty, JavaArray_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_JavaClass.c b/js/src/liveconnect/jsj_JavaClass.c index 2fe80c49476a..238fc0aad715 100644 --- a/js/src/liveconnect/jsj_JavaClass.c +++ b/js/src/liveconnect/jsj_JavaClass.c @@ -529,10 +529,14 @@ done: return JS_TRUE; } +extern JSObjectOps JavaClass_ops; + +static const JSObjectMap JavaClassMap = { &JavaClass_ops }; + JSObjectOps JavaClass_ops = { + &JavaClassMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaClass_lookupProperty, JavaClass_defineProperty, JavaClass_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_JavaObject.c b/js/src/liveconnect/jsj_JavaObject.c index ffa9bee15495..2d9a5fe32eb5 100644 --- a/js/src/liveconnect/jsj_JavaObject.c +++ b/js/src/liveconnect/jsj_JavaObject.c @@ -999,32 +999,10 @@ JavaObject_checkAccess(JSContext *cx, JSObject *obj, jsid id, #define JSJ_SLOT_COUNT (JSSLOT_PRIVATE+1) -JSObjectMap * -jsj_wrapper_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj) -{ - JSObjectMap * map; - - map = (JSObjectMap *) JS_malloc(cx, sizeof(JSObjectMap)); - if (map) { - map->nrefs = nrefs; - map->ops = ops; - map->freeslot = JSJ_SLOT_COUNT; - } - return map; -} - -void -jsj_wrapper_destroyObjectMap(JSContext *cx, JSObjectMap *map) -{ - JS_free(cx, map); -} - jsval jsj_wrapper_getRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot) { JS_ASSERT(slot < JSJ_SLOT_COUNT); - JS_ASSERT(obj->map->freeslot == JSJ_SLOT_COUNT); return STOBJ_GET_SLOT(obj, slot); } @@ -1032,15 +1010,18 @@ JSBool jsj_wrapper_setRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v) { JS_ASSERT(slot < JSJ_SLOT_COUNT); - JS_ASSERT(obj->map->freeslot == JSJ_SLOT_COUNT); STOBJ_SET_SLOT(obj, slot, v); return JS_TRUE; } +extern JSObjectOps JavaObject_ops; + +static const JSObjectMap JavaObjectMap = { &JavaObject_ops }; + JSObjectOps JavaObject_ops = { + &JavaObjectMap, /* objectMap */ + /* Mandatory non-null function pointer members. */ - jsj_wrapper_newObjectMap, /* newObjectMap */ - jsj_wrapper_destroyObjectMap, /* destroyObjectMap */ JavaObject_lookupProperty, JavaObject_defineProperty, JavaObject_getPropertyById, /* getProperty */ diff --git a/js/src/liveconnect/jsj_private.h b/js/src/liveconnect/jsj_private.h index a1bab8f113f7..7a17f52057cc 100644 --- a/js/src/liveconnect/jsj_private.h +++ b/js/src/liveconnect/jsj_private.h @@ -643,13 +643,6 @@ jsj_EnterJava(JSContext *cx, JNIEnv **envp); extern void jsj_ExitJava(JSJavaThreadState *jsj_env); -extern JSObjectMap * -jsj_wrapper_newObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops, - JSClass *clasp, JSObject *obj); - -extern void -jsj_wrapper_destroyObjectMap(JSContext *cx, JSObjectMap *map); - extern jsval jsj_wrapper_getRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot); diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp index dd3aa0db47e6..a680dbf565a0 100644 --- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp +++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp @@ -1399,7 +1399,7 @@ XPC_WN_GetObjectOpsWithCall(JSContext *cx, JSClass *clazz) JSBool xpc_InitWrappedNativeJSOps() { - if(!XPC_WN_NoCall_JSOps.newObjectMap) + if(!XPC_WN_NoCall_JSOps.lookupProperty) { memcpy(&XPC_WN_NoCall_JSOps, &js_ObjectOps, sizeof(JSObjectOps)); XPC_WN_NoCall_JSOps.enumerate = XPC_WN_JSOp_Enumerate; From dd7cc5cf3be9f4e783c40ec646de104ee830b937 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Wed, 13 May 2009 19:02:33 -0500 Subject: [PATCH 02/46] Bug 492714 - "Assertion failure: cg->staticLevel >= level, at ../jsemit.cpp" with genexp. r=brendan --- js/src/jsparse.cpp | 5 ++++- js/src/jsparse.h | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index d90cf81ddd40..b24163c7f20b 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -6091,7 +6091,10 @@ CompExprTransplanter::transplant(JSParseNode *pn) case PN_BINARY: transplant(pn->pn_left); - transplant(pn->pn_right); + + /* Binary TOK_COLON nodes can have left == right. See bug 492714. */ + if (pn->pn_right != pn->pn_left) + transplant(pn->pn_right); break; case PN_UNARY: diff --git a/js/src/jsparse.h b/js/src/jsparse.h index d62d55d0bf38..b49803054b1a 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -183,8 +183,10 @@ JS_BEGIN_EXTERN_C * TOK_RB list pn_head: list of pn_count array element exprs * [,,] holes are represented by TOK_COMMA nodes * pn_xflags: PN_ENDCOMMA if extra comma at end - * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where - * each has pn_left: property id, pn_right: value + * TOK_RC list pn_head: list of pn_count binary TOK_COLON nodes + * TOK_COLON binary key-value pair in object initializer or + * destructuring lhs + * pn_left: property id, pn_right: value * var {x} = object destructuring shorthand shares * PN_NAME node for x on left and right of TOK_COLON * node in TOK_RC's list, has PNX_DESTRUCT flag From 18ffcaaf54b2133a7840c429ff2dd2a308a411ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20B=C3=BCnzli?= Date: Thu, 14 May 2009 10:55:22 -0700 Subject: [PATCH 03/46] Bug 491810 - Geolocation cookie cleared at exit for network.cookie.lifetimePolicy==2 (cleaning up my mess) --- dom/src/geolocation/NetworkGeolocationProvider.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/dom/src/geolocation/NetworkGeolocationProvider.js b/dom/src/geolocation/NetworkGeolocationProvider.js index aa0a876d2794..c56b44d9bc28 100644 --- a/dom/src/geolocation/NetworkGeolocationProvider.js +++ b/dom/src/geolocation/NetworkGeolocationProvider.js @@ -166,16 +166,14 @@ WifiGeoPositionProvider.prototype = { this.timer = null; } - // Although we aren't using cookies, we should error on the side of not + // Although we aren't using cookies, we should err on the side of not // saving any access tokens if the user asked us not to save cookies or // has changed the lifetimePolicy. The access token in these cases is // used and valid for the life of this object (eg. between startup and // shutdown).e - let prefService = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); - if (prefService.getIntPref("network.cookie.lifetimePolicy") != 0) { - let branch = prefService.getBranch("geo.wifi.access_token."); - branch.deleteBranch(""); - } + let prefBranch = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch); + if (prefBranch.getIntPref("network.cookie.lifetimePolicy") != 0) + prefBranch.deleteBranch("geo.wifi.access_token."); let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService); os.removeObserver(this, "private-browsing"); From 5feb239461ae6c53fe6dedca96615de7ae19ba71 Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Thu, 14 May 2009 19:11:16 +0100 Subject: [PATCH 04/46] Bug 487717: browser_bug435788.js times out occasionally. r=robstrong --- .../plugins/tests/browser_bug435788.js | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/toolkit/mozapps/plugins/tests/browser_bug435788.js b/toolkit/mozapps/plugins/tests/browser_bug435788.js index 9608499c1a0f..201917ee6e0b 100644 --- a/toolkit/mozapps/plugins/tests/browser_bug435788.js +++ b/toolkit/mozapps/plugins/tests/browser_bug435788.js @@ -81,20 +81,21 @@ function prepare_test_1() { } function test_1_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_2, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_1_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_1_complete); + }, false); } function test_1_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test plugin 1", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_1_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -124,20 +125,21 @@ function prepare_test_2() { } function test_2_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_3, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_2_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_2_complete); + }, false); } function test_2_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test plugin 2", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_2_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -171,11 +173,15 @@ function prepare_test_3() { } function test_3_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_4, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_3_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_3_complete); + }, false); } function test_3_available() { @@ -183,9 +189,6 @@ function test_3_available() { ok(hasListItem("Test plugin 1", null), "Should have seen the right plugin name"); ok(hasListItem("Test plugin 2", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_3_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -218,20 +221,21 @@ function prepare_test_4() { } function test_4_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_5, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_4_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_4_complete); + }, false); } function test_4_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test plugin 3", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_4_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -262,20 +266,21 @@ function prepare_test_5() { } function test_5_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_6, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_5_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_5_complete); + }, false); } function test_5_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test extension 1", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_5_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -310,20 +315,21 @@ function prepare_test_6() { } function test_6_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_7, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_6_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_6_complete); + }, false); } function test_6_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test extension 2", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_6_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -357,11 +363,15 @@ function prepare_test_7() { } function test_7_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_8, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_7_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_7_complete); + }, false); } function test_7_available() { @@ -369,9 +379,6 @@ function test_7_available() { ok(hasListItem("Test extension 1", null), "Should have seen the right plugin name"); ok(hasListItem("Test extension 2", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_7_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -409,20 +416,21 @@ function prepare_test_8() { } function test_8_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_9, false); gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { executeSoon(test_8_available); }, false); + gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { + executeSoon(test_8_complete); + }, false); } function test_8_available() { is(getListCount(), 1, "Should have found 1 plugin to install"); ok(hasListItem("Test extension 3", null), "Should have seen the right plugin name"); - gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { - executeSoon(test_8_complete); - }, false); gPFS.document.documentElement.getButton("next").click(); } @@ -456,8 +464,12 @@ function prepare_test_9() { } function test_9_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_10, false); + gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { + ok(false, "Should not have found plugins to install"); + }, false); gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { executeSoon(test_9_complete); }, false); @@ -488,8 +500,12 @@ function prepare_test_10() { } function test_10_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", prepare_test_11, false); + gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { + ok(false, "Should not have found plugins to install"); + }, false); gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { executeSoon(test_10_complete); }, false); @@ -520,8 +536,12 @@ function prepare_test_11() { } function test_11_start() { + ok(true, "PFS loaded"); gPFS.addEventListener("unload", finishTest, false); + gPFS.document.documentElement.wizardPages[1].addEventListener("pageshow", function() { + ok(false, "Should not have found plugins to install"); + }, false); gPFS.document.documentElement.wizardPages[4].addEventListener("pageshow", function() { executeSoon(test_10_complete); }, false); From 1f95b73130eb681e58ca5c109c2f2764d3d7c4c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 14 May 2009 20:35:40 +0200 Subject: [PATCH 05/46] test and additional fix for bug 482801 --- browser/base/content/tabbrowser.xml | 2 ++ .../base/content/test/browser_bug462289.js | 23 +++++++++++++------ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index c871c825df3c..a9b709daad51 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3385,6 +3385,8 @@ var anonid = event.originalTarget.getAttribute("anonid"); if (anonid == "close-button") this.mOverCloseButton = false; + else + this.style.MozUserFocus = ""; this.style.MozUserFocus = ''; diff --git a/browser/base/content/test/browser_bug462289.js b/browser/base/content/test/browser_bug462289.js index e1fd9c7ab124..473e3d989856 100644 --- a/browser/base/content/test/browser_bug462289.js +++ b/browser/base/content/test/browser_bug462289.js @@ -22,23 +22,32 @@ function step3() { isnot(document.activeElement, tab1, "mouse on tab again activeElement"); - document.getElementById("searchbar").focus(); - EventUtils.synthesizeKey("VK_TAB", { }); - is(document.activeElement, tab1, "tab key to tab activeElement"); - - EventUtils.synthesizeMouse(tab1, 2, 2, {}); + EventUtils.synthesizeMouse(tab1, 2, 2, {button: 1, type: "mousedown"}); + EventUtils.synthesizeMouse(tab1, 0, 0, {button: 1, type: "mouseout"}); setTimeout(step4, 0); } function step4() { - is(document.activeElement, tab1, "mouse on tab while focused still activeElement"); + isnot(document.activeElement, tab1, "tab not focused via middle click"); - EventUtils.synthesizeMouse(tab2, 2, 2, {}); + document.getElementById("searchbar").focus(); + EventUtils.synthesizeKey("VK_TAB", { }); + is(document.activeElement, tab1, "tab key to tab activeElement"); + + EventUtils.synthesizeMouse(tab1, 2, 2, {}); setTimeout(step5, 0); } function step5() +{ + is(document.activeElement, tab1, "mouse on tab while focused still activeElement"); + + EventUtils.synthesizeMouse(tab2, 2, 2, {}); + setTimeout(step6, 0); +} + +function step6() { // The tabbox selects a tab within a setTimeout in a bubbling mousedown event // listener, and focuses the current tab if another tab previously had focus From 1ab5495ddef2efc62a25f43934558c752c25ce0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Thu, 14 May 2009 20:57:00 +0200 Subject: [PATCH 06/46] Backed out changeset 350e7f02ea89, since it might have issues, and I won't be there to watch the results :( --- browser/base/content/tabbrowser.xml | 2 -- browser/base/content/test/browser_bug462289.js | 17 ++++------------- 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index a9b709daad51..c871c825df3c 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3385,8 +3385,6 @@ var anonid = event.originalTarget.getAttribute("anonid"); if (anonid == "close-button") this.mOverCloseButton = false; - else - this.style.MozUserFocus = ""; this.style.MozUserFocus = ''; diff --git a/browser/base/content/test/browser_bug462289.js b/browser/base/content/test/browser_bug462289.js index 473e3d989856..e1fd9c7ab124 100644 --- a/browser/base/content/test/browser_bug462289.js +++ b/browser/base/content/test/browser_bug462289.js @@ -22,32 +22,23 @@ function step3() { isnot(document.activeElement, tab1, "mouse on tab again activeElement"); - EventUtils.synthesizeMouse(tab1, 2, 2, {button: 1, type: "mousedown"}); - EventUtils.synthesizeMouse(tab1, 0, 0, {button: 1, type: "mouseout"}); - setTimeout(step4, 0); -} - -function step4() -{ - isnot(document.activeElement, tab1, "tab not focused via middle click"); - document.getElementById("searchbar").focus(); EventUtils.synthesizeKey("VK_TAB", { }); is(document.activeElement, tab1, "tab key to tab activeElement"); EventUtils.synthesizeMouse(tab1, 2, 2, {}); - setTimeout(step5, 0); + setTimeout(step4, 0); } -function step5() +function step4() { is(document.activeElement, tab1, "mouse on tab while focused still activeElement"); EventUtils.synthesizeMouse(tab2, 2, 2, {}); - setTimeout(step6, 0); + setTimeout(step5, 0); } -function step6() +function step5() { // The tabbox selects a tab within a setTimeout in a bubbling mousedown event // listener, and focuses the current tab if another tab previously had focus From 7f46f44eb5fbf3e73519ec6cf3a8e999dd4c76ff Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 14 May 2009 13:02:13 -0700 Subject: [PATCH 07/46] Bug 490937 - Eagerly start prefetching URIs that we've found when we're done with our section of the document. Don't wait for the parser to tell us to go ahead. r+sr=jst --- parser/htmlparser/src/nsParser.cpp | 69 ++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/parser/htmlparser/src/nsParser.cpp b/parser/htmlparser/src/nsParser.cpp index 559365a8dcab..25efbaf08792 100644 --- a/parser/htmlparser/src/nsParser.cpp +++ b/parser/htmlparser/src/nsParser.cpp @@ -207,7 +207,6 @@ public: mCVar(PR_DestroyCondVar), mKeepParsing(PR_FALSE), mCurrentlyParsing(PR_FALSE), - mNumURIs(0), mNumConsumed(0), mContext(nsnull), mTerminated(PR_FALSE) { @@ -267,6 +266,8 @@ private: const nsAString &elementType, PrefetchType type); + void FlushURIs(); + // These members are only accessed on the speculatively parsing thread. nsTokenAllocator mTokenAllocator; @@ -282,7 +283,6 @@ private: enum { kBatchPrefetchURIs = 5 }; nsAutoTArray mURIs; - PRUint16 mNumURIs; // Number of characters consumed by the last speculative parse. PRUint32 mNumConsumed; @@ -381,13 +381,20 @@ nsPreloadURIs::PreloadURIs(const nsAutoTArrayScriptLoader()->PreloadURI(uri, pe.charset, pe.elementType); - break; - case nsSpeculativeScriptThread::STYLESHEET: + break; + case nsSpeculativeScriptThread::STYLESHEET: { nsCOMPtr obs = new nsDummyCSSLoaderObserver(); doc->CSSLoader()->LoadSheet(uri, doc->NodePrincipal(), NS_LossyConvertUTF16toASCII(pe.charset), obs); break; + } + case nsSpeculativeScriptThread::IMAGE: + NS_NOTREACHED("We don't scan these yet"); + break; + case nsSpeculativeScriptThread::NONE: + NS_NOTREACHED("Uninitialized preload entry?"); + break; } } } @@ -419,6 +426,15 @@ nsSpeculativeScriptThread::Run() } mTokenizer->DidTokenize(PR_FALSE); + if (mKeepParsing) { + // Ran out of room in this part of the document -- flush out the URIs we + // gathered so far so we don't end up waiting for the parser's current + // load to finish. + if (!mURIs.IsEmpty()) { + FlushURIs(); + } + } + { nsAutoLock al(mLock.get()); @@ -485,7 +501,7 @@ nsSpeculativeScriptThread::StartParsing(nsParser *aParser) if (mNumConsumed > context->mNumConsumed) { // We consumed more the last time we tried speculatively parsing than we - // did the last time we actually parsed. + // did the last time we actually parsed. PRUint32 distance = Distance(start, end); start.advance(PR_MIN(mNumConsumed - context->mNumConsumed, distance)); } @@ -550,10 +566,9 @@ nsSpeculativeScriptThread::StopParsing(PRBool /*aFromDocWrite*/) mDocument = nsnull; mTokenizer = nsnull; mScanner = nsnull; - } else if (mNumURIs) { + } else if (mURIs.Length()) { // Note: Don't do this if we're terminated. nsPreloadURIs::PreloadURIs(mURIs, this); - mNumURIs = 0; mURIs.Clear(); } @@ -598,12 +613,11 @@ nsSpeculativeScriptThread::ProcessToken(CToken *aToken) // // + + + + + +
+ diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 39a8055906dc..1b0745729a0e 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -228,7 +228,10 @@ load 471594-1.xhtml load 479114-1.html load 477333-1.xhtml load 481806-1.html +load 483604-1.xhtml load 488390-1.xhtml load 489691.html load 490376-1.xhtml load 490747.html +load 492112-1.xhtml +load 492163-1.xhtml diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 2aad48ac6f9a..59f478e5a1ca 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -5199,6 +5199,27 @@ nsCSSFrameConstructor::AddFrameConstructionItems(nsFrameConstructorState& aState aItems); } +/** + * Set aContent as undisplayed content with style context aStyleContext. This + * method enforces the invariant that all style contexts in the undisplayed + * content map must be non-pseudo contexts and also handles unbinding + * undisplayed generated content as needed. + */ +static void +SetAsUndisplayedContent(nsFrameManager* aFrameManager, nsIContent* aContent, + nsStyleContext* aStyleContext, + PRBool aIsGeneratedContent) +{ + if (aStyleContext->GetPseudoType()) { + if (aIsGeneratedContent) { + aContent->UnbindFromTree(); + } + return; + } + + NS_ASSERTION(!aIsGeneratedContent, "Should have had pseudo type"); + aFrameManager->SetUndisplayedContent(aContent, aStyleContext); +} void nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState& aState, @@ -5243,10 +5264,13 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState aTag = mDocument->BindingManager()->ResolveTag(aContent, &aNameSpaceID); } + PRBool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0); + // Pre-check for display "none" - if we find that, don't create // any frame at all if (NS_STYLE_DISPLAY_NONE == display->mDisplay) { - aState.mFrameManager->SetUndisplayedContent(aContent, styleContext); + SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext, + isGeneratedContent); return; } @@ -5270,6 +5294,8 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState aParentFrame->IsFrameOfType(nsIFrame::eSVG) && !aParentFrame->IsFrameOfType(nsIFrame::eSVGForeignObject) ) { + SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext, + isGeneratedContent); return; } #endif /* MOZ_SVG */ @@ -5304,7 +5330,8 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState NS_ASSERTION(data, "Should have frame construction data now"); if (data->mBits & FCDATA_SUPPRESS_FRAME) { - aState.mFrameManager->SetUndisplayedContent(aContent, styleContext); + SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext, + isGeneratedContent); return; } @@ -5314,7 +5341,8 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState aParentFrame->GetType() != nsGkAtoms::menuFrame)) { if (!aState.mPopupItems.containingBlock && !aState.mHavePendingPopupgroup) { - aState.mFrameManager->SetUndisplayedContent(aContent, styleContext); + SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext, + isGeneratedContent); return; } @@ -5330,7 +5358,8 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState aParentFrame->GetType() == nsGkAtoms::tableColGroupFrame && (!(bits & FCDATA_IS_TABLE_PART) || display->mDisplay != NS_STYLE_DISPLAY_TABLE_COLUMN)) { - aState.mFrameManager->SetUndisplayedContent(aContent, styleContext); + SetAsUndisplayedContent(aState.mFrameManager, aContent, styleContext, + isGeneratedContent); return; } @@ -5344,8 +5373,6 @@ nsCSSFrameConstructor::AddFrameConstructionItemsInternal(nsFrameConstructorState AddPageBreakItem(aContent, aStyleContext, aItems); } - PRBool isGeneratedContent = ((aFlags & ITEM_IS_GENERATED_CONTENT) != 0); - FrameConstructionItem* item = aItems.AppendItem(data, aContent, aTag, aNameSpaceID, styleContext.forget()); diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index 4d1040dbae83..289a5e02dfcb 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -581,6 +581,9 @@ void nsFrameManager::SetUndisplayedContent(nsIContent* aContent, nsStyleContext* aStyleContext) { + NS_PRECONDITION(!aStyleContext->GetPseudoType(), + "Should only have actual elements here"); + #ifdef DEBUG_UNDISPLAYED_MAP static int i = 0; printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent); @@ -1290,22 +1293,11 @@ nsFrameManager::ReResolveStyleContext(nsPresContext *aPresContext, undisplayed->mContent == mPresShell->GetDocument()->GetRootContent(), "undisplayed node child of null must be root"); - nsRefPtr undisplayedContext; - nsIAtom* const undisplayedPseudoTag = undisplayed->mStyle->GetPseudoType(); - if (!undisplayedPseudoTag) { // child content - undisplayedContext = styleSet->ResolveStyleFor(undisplayed->mContent, - newContext); - } - else if (undisplayedPseudoTag == nsCSSAnonBoxes::mozNonElement) { - undisplayedContext = styleSet->ResolveStyleForNonElement(newContext); - } - else { // pseudo element - NS_NOTREACHED("no pseudo elements in undisplayed map"); - NS_ASSERTION(undisplayedPseudoTag, "pseudo element without tag"); - undisplayedContext = styleSet->ResolvePseudoStyleFor(localContent, - undisplayedPseudoTag, - newContext); - } + NS_ASSERTION(!undisplayed->mStyle->GetPseudoType(), + "Shouldn't have random pseudo style contexts in the " + "undisplayed map"); + nsRefPtr undisplayedContext = + styleSet->ResolveStyleFor(undisplayed->mContent, newContext); if (undisplayedContext) { const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay(); if (display->mDisplay != NS_STYLE_DISPLAY_NONE) { From e923e46de82a4854551a7c59ebe39ad2fe541208 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 14 May 2009 21:40:28 -0400 Subject: [PATCH 18/46] Bug 492760. Don't try to do a best-effort scroll to never-been-laid-out frames. r+sr=roc --- layout/base/nsPresShell.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 4ca4df173b1c..9a75ba77af9e 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -4203,6 +4203,13 @@ PresShell::DoScrollContentIntoView(nsIContent* aContent, return; } + if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) { + // The reflow flush before this scroll got interrupted, and this frame's + // coords and size are all zero, and it has no content showing anyway. + // Don't bother scrolling to it. We'll try again when we finish up layout. + return; + } + // This is a two-step process. // Step 1: Find the bounds of the rect we want to scroll into view. For // example, for an inline frame we may want to scroll in the whole From 78b522fe6adcb2e674b527a17a455e96a3582385 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Thu, 14 May 2009 21:40:29 -0400 Subject: [PATCH 19/46] Bug 493025. Don't assert if we happen to do bitblit scroll analysis on some frames that are dirty because a reflow got interrupted. r+sr=roc --- layout/base/nsLayoutUtils.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 9cbf1f2be714..aae8e51d9b57 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1217,6 +1217,8 @@ nsLayoutUtils::ComputeRepaintRegionForCopy(nsIFrame* aRootFrame, NS_ASSERTION(aRootFrame != aMovingFrame, "The root frame shouldn't be the one that's moving, that makes no sense"); + nsAutoDisableGetUsedXAssertions disableAssert; + // Build the 'after' display list over the whole area of interest. // (We have to build the 'after' display list because the frame/view // hierarchy has already been updated for the move. From 9d77f0597a39badb8ac228254e75e345d3a99537 Mon Sep 17 00:00:00 2001 From: Chris Double Date: Fri, 15 May 2009 11:47:06 +1200 Subject: [PATCH 20/46] Bug 492436 - Fix for handling unknown Ogg streams - rs=roc --HG-- extra : rebase_source : 196ced84c53487b47a390fa043dd3e6de95d8bb3 --- media/liboggplay/README_MOZILLA | 4 +- media/liboggplay/bug492436.patch | 52 +++++++++++++++++++ .../include/oggplay/oggplay_enums.h | 3 ++ .../src/liboggplay/oggplay_callback.c | 1 + .../liboggplay/src/liboggplay/oggplay_query.c | 8 +++ media/liboggplay/update.sh | 1 + 6 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 media/liboggplay/bug492436.patch diff --git a/media/liboggplay/README_MOZILLA b/media/liboggplay/README_MOZILLA index 5f28e8e8f302..8f684d8ed7ba 100644 --- a/media/liboggplay/README_MOZILLA +++ b/media/liboggplay/README_MOZILLA @@ -14,4 +14,6 @@ bug485291_yuv_align: only use optimized YUV routines if video dimensions are a m endian: pick up NSPR's little/big endian defines in oggplay's config.h. -trac466: Fix for infinite loop in liboggplay when running decoder on its own thread. +trac466: Fix for infinite loop in liboggplay when running decoder on its own thread. Cherry picked from liboggplay git commit e6871f. + +bug492436: Fix for that bug cherry picked from liboggplay git commit 4b97ad. diff --git a/media/liboggplay/bug492436.patch b/media/liboggplay/bug492436.patch new file mode 100644 index 000000000000..631efbffc0a0 --- /dev/null +++ b/media/liboggplay/bug492436.patch @@ -0,0 +1,52 @@ +diff --git a/media/liboggplay/include/oggplay/oggplay_enums.h b/media/liboggplay/include/oggplay/oggplay_enums.h +index aff0e51..ffb7cb4 100644 +--- a/media/liboggplay/include/oggplay/oggplay_enums.h ++++ b/media/liboggplay/include/oggplay/oggplay_enums.h +@@ -64,6 +64,9 @@ typedef enum OggPlayErrorCode { + E_OGGPLAY_NO_KATE_SUPPORT = -19, + E_OGGPLAY_NO_TIGER_SUPPORT = -20, + E_OGGPLAY_OUT_OF_MEMORY = -21, ++ E_OGGPLAY_TYPE_OVERFLOW = -22, /**< Integer overflow detected */ ++ ++ E_OGGPLAY_TRACK_IS_UNKNOWN = -23, /**< The selected track's content type is UNKNOWN */ + E_OGGPLAY_NOTCHICKENPAYBACK = -777 + } OggPlayErrorCode; + +diff --git a/media/liboggplay/src/liboggplay/oggplay_callback.c b/media/liboggplay/src/liboggplay/oggplay_callback.c +index 05cf363..593691f 100644 +--- a/media/liboggplay/src/liboggplay/oggplay_callback.c ++++ b/media/liboggplay/src/liboggplay/oggplay_callback.c +@@ -508,6 +508,7 @@ OggPlayCallbackFunctions callbacks[] = { + {NULL, NULL, NULL, sizeof(OggPlayDecode)}, /* CELT */ + {oggplay_init_kate, oggplay_callback_kate, oggplay_shutdown_kate, + sizeof(OggPlayKateDecode)}, /* KATE */ ++ {NULL, NULL, NULL, sizeof(OggPlayDecode)}, /* DIRAC */ + {NULL, NULL, NULL, sizeof(OggPlayDecode)} /* UNKNOWN */ + }; + +diff --git a/media/liboggplay/src/liboggplay/oggplay_query.c b/media/liboggplay/src/liboggplay/oggplay_query.c +index 2a69beb..6344ec8 100644 +--- a/media/liboggplay/src/liboggplay/oggplay_query.c ++++ b/media/liboggplay/src/liboggplay/oggplay_query.c +@@ -131,6 +131,10 @@ oggplay_set_track_active(OggPlay *me, int track_num) { + return E_OGGPLAY_TRACK_IS_SKELETON; + } + ++ if (me->decode_data[track_num]->content_type == OGGZ_CONTENT_UNKNOWN) { ++ return E_OGGPLAY_TRACK_IS_UNKNOWN; ++ } ++ + if ((p = me->decode_data[track_num]->final_granulepos) != -1) { + if (p * me->decode_data[track_num]->granuleperiod > me->target) { + return E_OGGPLAY_TRACK_IS_OVER; +@@ -179,6 +183,10 @@ oggplay_set_track_inactive(OggPlay *me, int track_num) { + return E_OGGPLAY_TRACK_IS_SKELETON; + } + ++ if (me->decode_data[track_num]->content_type == OGGZ_CONTENT_UNKNOWN) { ++ return E_OGGPLAY_TRACK_IS_UNKNOWN; ++ } ++ + if (me->decode_data[track_num]->active == 1) { + me->decode_data[track_num]->active = 0; + diff --git a/media/liboggplay/include/oggplay/oggplay_enums.h b/media/liboggplay/include/oggplay/oggplay_enums.h index aff0e5104ac8..ffb7cb4a44b3 100644 --- a/media/liboggplay/include/oggplay/oggplay_enums.h +++ b/media/liboggplay/include/oggplay/oggplay_enums.h @@ -64,6 +64,9 @@ typedef enum OggPlayErrorCode { E_OGGPLAY_NO_KATE_SUPPORT = -19, E_OGGPLAY_NO_TIGER_SUPPORT = -20, E_OGGPLAY_OUT_OF_MEMORY = -21, + E_OGGPLAY_TYPE_OVERFLOW = -22, /**< Integer overflow detected */ + + E_OGGPLAY_TRACK_IS_UNKNOWN = -23, /**< The selected track's content type is UNKNOWN */ E_OGGPLAY_NOTCHICKENPAYBACK = -777 } OggPlayErrorCode; diff --git a/media/liboggplay/src/liboggplay/oggplay_callback.c b/media/liboggplay/src/liboggplay/oggplay_callback.c index 05cf363e73d5..593691fc7cca 100644 --- a/media/liboggplay/src/liboggplay/oggplay_callback.c +++ b/media/liboggplay/src/liboggplay/oggplay_callback.c @@ -508,6 +508,7 @@ OggPlayCallbackFunctions callbacks[] = { {NULL, NULL, NULL, sizeof(OggPlayDecode)}, /* CELT */ {oggplay_init_kate, oggplay_callback_kate, oggplay_shutdown_kate, sizeof(OggPlayKateDecode)}, /* KATE */ + {NULL, NULL, NULL, sizeof(OggPlayDecode)}, /* DIRAC */ {NULL, NULL, NULL, sizeof(OggPlayDecode)} /* UNKNOWN */ }; diff --git a/media/liboggplay/src/liboggplay/oggplay_query.c b/media/liboggplay/src/liboggplay/oggplay_query.c index 2a69beb9d84d..6344ec8e6ac9 100644 --- a/media/liboggplay/src/liboggplay/oggplay_query.c +++ b/media/liboggplay/src/liboggplay/oggplay_query.c @@ -131,6 +131,10 @@ oggplay_set_track_active(OggPlay *me, int track_num) { return E_OGGPLAY_TRACK_IS_SKELETON; } + if (me->decode_data[track_num]->content_type == OGGZ_CONTENT_UNKNOWN) { + return E_OGGPLAY_TRACK_IS_UNKNOWN; + } + if ((p = me->decode_data[track_num]->final_granulepos) != -1) { if (p * me->decode_data[track_num]->granuleperiod > me->target) { return E_OGGPLAY_TRACK_IS_OVER; @@ -179,6 +183,10 @@ oggplay_set_track_inactive(OggPlay *me, int track_num) { return E_OGGPLAY_TRACK_IS_SKELETON; } + if (me->decode_data[track_num]->content_type == OGGZ_CONTENT_UNKNOWN) { + return E_OGGPLAY_TRACK_IS_UNKNOWN; + } + if (me->decode_data[track_num]->active == 1) { me->decode_data[track_num]->active = 0; diff --git a/media/liboggplay/update.sh b/media/liboggplay/update.sh index fe62815d51ae..5318e518e39b 100644 --- a/media/liboggplay/update.sh +++ b/media/liboggplay/update.sh @@ -47,3 +47,4 @@ sed s/\#ifdef\ HAVE_INTTYPES_H/\#if\ HAVE_INTTYPES_H/g $1/src/liboggplay/oggplay patch -p3 < bug485291_yuv_align.patch patch -p3 < endian.patch patch -p3 < trac466.patch +patch -p3 < bug492436.patch From d82a5883f74bbe7495fdea20e8a8714362bbe024 Mon Sep 17 00:00:00 2001 From: Chris Double Date: Fri, 15 May 2009 13:29:05 +1200 Subject: [PATCH 21/46] Bug 466699 - Fix out of sync video and audio - r+sr=roc --HG-- extra : rebase_source : ee8eedf72add30627fdbeef7cf22bc70a9415362 --- content/media/video/public/nsAudioStream.h | 10 ++ content/media/video/src/nsAudioStream.cpp | 36 ++++- content/media/video/src/nsOggDecoder.cpp | 147 ++++++++++++++++----- 3 files changed, 160 insertions(+), 33 deletions(-) diff --git a/content/media/video/public/nsAudioStream.h b/content/media/video/public/nsAudioStream.h index d030c6cc20a9..5ba06b31419d 100644 --- a/content/media/video/public/nsAudioStream.h +++ b/content/media/video/public/nsAudioStream.h @@ -89,6 +89,16 @@ class nsAudioStream // Block until buffered audio data has been consumed. void Drain(); + // Pause audio playback + void Pause(); + + // Resume audio playback + void Resume(); + + // Return the position in seconds of the sample being played by the + // audio hardware. + float GetPosition(); + private: double mVolume; void* mAudioHandle; diff --git a/content/media/video/src/nsAudioStream.cpp b/content/media/video/src/nsAudioStream.cpp index 523726ed3942..c7a45d6710d9 100644 --- a/content/media/video/src/nsAudioStream.cpp +++ b/content/media/video/src/nsAudioStream.cpp @@ -190,7 +190,9 @@ PRInt32 nsAudioStream::Available() return FAKE_BUFFER_SIZE; size_t s = 0; - sa_stream_get_write_size(static_cast(mAudioHandle), &s); + if (sa_stream_get_write_size(static_cast(mAudioHandle), &s) != SA_SUCCESS) + return 0; + return s / sizeof(short); } @@ -218,3 +220,35 @@ void nsAudioStream::Drain() Shutdown(); } } + +void nsAudioStream::Pause() +{ + if (!mAudioHandle) + return; + + sa_stream_pause(static_cast(mAudioHandle)); +} + +void nsAudioStream::Resume() +{ + if (!mAudioHandle) + return; + + sa_stream_resume(static_cast(mAudioHandle)); +} + +float nsAudioStream::GetPosition() +{ + if (!mAudioHandle) + return -1.0; + + PRInt64 position = 0; + if (sa_stream_get_position(static_cast(mAudioHandle), + SA_POSITION_WRITE_SOFTWARE, + &position) == SA_SUCCESS) { + return (position / float(mRate) / mChannels / sizeof(short)); + } + + return -1.0; +} + diff --git a/content/media/video/src/nsOggDecoder.cpp b/content/media/video/src/nsOggDecoder.cpp index 497a0d85d128..8e5823cd8938 100644 --- a/content/media/video/src/nsOggDecoder.cpp +++ b/content/media/video/src/nsOggDecoder.cpp @@ -157,11 +157,8 @@ public: // Write the audio data from the frame to the Audio stream. void Write(nsAudioStream* aStream) { - PRUint32 length = mAudioData.Length(); - if (length == 0) - return; - - aStream->Write(mAudioData.Elements(), length); + aStream->Write(mAudioData.Elements(), mAudioData.Length()); + mAudioData.Clear(); } void SetVideoHeader(OggPlayDataHeader* aVideoHeader) @@ -230,6 +227,20 @@ public: return !mEmpty && mHead == mTail; } + float ResetTimes(float aPeriod) + { + float time = 0.0; + if (!mEmpty) { + PRInt32 current = mHead; + do { + mQueue[current]->mTime = time; + time += aPeriod; + current = (current + 1) % OGGPLAY_BUFFER_SIZE; + } while (current != mTail); + } + return time; + } + private: FrameData* mQueue[OGGPLAY_BUFFER_SIZE]; PRInt32 mHead; @@ -292,8 +303,11 @@ public: // must be locked when calling this method. void PlayVideo(FrameData* aFrame); - // Play the audio data from the given frame. The decode monitor must - // be locked when calling this method. + // Plays the audio for the frame, plus any outstanding audio data + // buffered by nsAudioStream and not yet written to the + // hardware. The audio data for the frame is cleared out so + // subsequent calls with the same frame do not re-write the data. + // The decode monitor must be locked when calling this method. void PlayAudio(FrameData* aFrame); // Called from the main thread to get the current frame time. The decoder @@ -365,11 +379,22 @@ protected: void StopAudio(); // Start playback of media. Must be called with the decode monitor held. + // This opens or re-opens the audio stream for playback to start. void StartPlayback(); // Stop playback of media. Must be called with the decode monitor held. + // This actually closes the audio stream and releases any OS resources. void StopPlayback(); + // Pause playback of media. Must be called with the decode monitor held. + // This does not close the OS based audio stream - it suspends it to be + // resumed later. + void PausePlayback(); + + // Resume playback of media. Must be called with the decode monitor held. + // This resumes a paused audio stream. + void ResumePlayback(); + // Update the playback position. This can result in a timeupdate event // and an invalidate of the frame being dispatched asynchronously if // there is no such event currently queued. @@ -377,6 +402,11 @@ protected: // the decode monitor held. void UpdatePlaybackPosition(float aTime); + // Takes decoded frames from liboggplay's internal buffer and + // places them in our frame queue. Must be called with the decode + // monitor held. + void QueueDecodedFrames(); + private: // ***** // The follow fields are only accessed by the decoder thread @@ -753,7 +783,7 @@ void nsOggDecodeStateMachine::PlayFrame() { if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) { if (!mPlaying) { - StartPlayback(); + ResumePlayback(); } if (!mDecodedFrames.IsEmpty()) { @@ -769,7 +799,14 @@ void nsOggDecodeStateMachine::PlayFrame() { double time; for (;;) { - time = (TimeStamp::Now() - mPlayStartTime - mPauseDuration).ToSeconds(); + // Even if the frame has had its audio data written we call + // PlayAudio to ensure that any data we have buffered in the + // nsAudioStream is written to the hardware. + PlayAudio(frame); + double hwtime = mAudioStream ? mAudioStream->GetPosition() : -1.0; + time = hwtime < 0.0 ? + (TimeStamp::Now() - mPlayStartTime - mPauseDuration).ToSeconds() : + hwtime; if (time < frame->mTime) { mon.Wait(PR_MillisecondsToInterval(PRInt64((frame->mTime - time)*1000))); if (mState == DECODER_STATE_SHUTDOWN) @@ -780,24 +817,33 @@ void nsOggDecodeStateMachine::PlayFrame() { } mDecodedFrames.Pop(); + QueueDecodedFrames(); // Skip frames up to the one we should be showing. while (!mDecodedFrames.IsEmpty() && time >= mDecodedFrames.Peek()->mTime) { LOG(PR_LOG_DEBUG, ("Skipping frame time %f with audio at time %f", mDecodedFrames.Peek()->mTime, time)); + PlayAudio(frame); delete frame; frame = mDecodedFrames.Peek(); mDecodedFrames.Pop(); } - PlayAudio(frame); - PlayVideo(frame); - mDecoder->mPlaybackPosition = frame->mEndStreamPosition; - UpdatePlaybackPosition(frame->mDecodedFrameTime); - delete frame; + if (time < frame->mTime + mCallbackPeriod) { + PlayAudio(frame); + PlayVideo(frame); + mDecoder->mPlaybackPosition = frame->mEndStreamPosition; + UpdatePlaybackPosition(frame->mDecodedFrameTime); + delete frame; + } + else { + PlayAudio(frame); + delete frame; + frame = 0; + } } } else { if (mPlaying) { - StopPlayback(); + PausePlayback(); } if (mState == DECODER_STATE_DECODING) { @@ -904,16 +950,52 @@ void nsOggDecodeStateMachine::StartPlayback() // Null out mPauseStartTime mPauseStartTime = TimeStamp(); } + mPlayStartTime = TimeStamp::Now(); + mPauseDuration = 0; + } void nsOggDecodeStateMachine::StopPlayback() { // NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "StopPlayback() called without acquiring decoder monitor"); + mLastFrameTime = mDecodedFrames.ResetTimes(mCallbackPeriod); StopAudio(); mPlaying = PR_FALSE; mPauseStartTime = TimeStamp::Now(); } +void nsOggDecodeStateMachine::PausePlayback() +{ + if (!mAudioStream) { + StopPlayback(); + return; + } + + mAudioStream->Pause(); + mPlaying = PR_FALSE; + mPauseStartTime = TimeStamp::Now(); +} + +void nsOggDecodeStateMachine::ResumePlayback() +{ + if (!mAudioStream) { + StartPlayback(); + return; + } + + mAudioStream->Resume(); + mPlaying = PR_TRUE; + + // Compute duration spent paused + if (!mPauseStartTime.IsNull()) { + mPauseDuration += TimeStamp::Now() - mPauseStartTime; + // Null out mPauseStartTime + mPauseStartTime = TimeStamp(); + } + mPlayStartTime = TimeStamp::Now(); + mPauseDuration = 0; +} + void nsOggDecodeStateMachine::UpdatePlaybackPosition(float aTime) { // NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "UpdatePlaybackPosition() called without acquiring decoder monitor"); @@ -926,6 +1008,15 @@ void nsOggDecodeStateMachine::UpdatePlaybackPosition(float aTime) } } +void nsOggDecodeStateMachine::QueueDecodedFrames() +{ + // NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "QueueDecodedFrames() called without acquiring decoder monitor"); + FrameData* frame; + while (!mDecodedFrames.IsFull() && (frame = NextFrame())) { + mDecodedFrames.Push(frame); + } +} + void nsOggDecodeStateMachine::ClearPositionChangeFlag() { // NS_ASSERTION(PR_InMonitor(mDecoder->GetMonitor()), "ClearPositionChangeFlag() called without acquiring decoder monitor"); @@ -1096,10 +1187,7 @@ nsresult nsOggDecodeStateMachine::Run() mon.Wait(PR_MillisecondsToInterval(PRInt64(mCallbackPeriod*500))); if (mState != DECODER_STATE_DECODING) break; - FrameData* frame = NextFrame(); - if (frame) { - mDecodedFrames.Push(frame); - } + QueueDecodedFrames(); } if (mState != DECODER_STATE_DECODING) @@ -1108,11 +1196,11 @@ nsresult nsOggDecodeStateMachine::Run() if (mDecodingCompleted) { LOG(PR_LOG_DEBUG, ("Changed state from DECODING to COMPLETED")); mState = DECODER_STATE_COMPLETED; - mStepDecodeThread->Shutdown(); - mStepDecodeThread = nsnull; mDecodingCompleted = PR_FALSE; mBufferExhausted = PR_FALSE; mon.NotifyAll(); + mStepDecodeThread->Shutdown(); + mStepDecodeThread = nsnull; continue; } @@ -1130,7 +1218,7 @@ nsresult nsOggDecodeStateMachine::Run() // more data to load. Let's buffer to make sure we can play a // decent amount of video in the future. if (mPlaying) { - StopPlayback(); + PausePlayback(); } // We need to tell the element that buffering has started. @@ -1153,7 +1241,7 @@ nsresult nsOggDecodeStateMachine::Run() BUFFERING_RATE(playbackRate) * BUFFERING_WAIT; mState = DECODER_STATE_BUFFERING; if (mPlaying) { - StopPlayback(); + PausePlayback(); } LOG(PR_LOG_DEBUG, ("Changed state from DECODING to BUFFERING")); } else { @@ -1284,7 +1372,7 @@ nsresult nsOggDecodeStateMachine::Run() NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL); if (mDecoder->GetState() == nsOggDecoder::PLAY_STATE_PLAYING) { if (!mPlaying) { - StartPlayback(); + ResumePlayback(); } } } @@ -1296,23 +1384,18 @@ nsresult nsOggDecodeStateMachine::Run() { // Get all the remaining decoded frames in the liboggplay buffer and // place them in the frame queue. - FrameData* frame; - do { - frame = NextFrame(); - if (frame) { - mDecodedFrames.Push(frame); - } - } while (frame); + QueueDecodedFrames(); // Play the remaining frames in the frame queue while (mState == DECODER_STATE_COMPLETED && !mDecodedFrames.IsEmpty()) { PlayFrame(); - if (mState != DECODER_STATE_SHUTDOWN) { + if (mState == DECODER_STATE_COMPLETED) { // Wait for the time of one frame so we don't tight loop // and we need to release the monitor so timeupdate and // invalidate's on the main thread can occur. mon.Wait(PR_MillisecondsToInterval(PRInt64(mCallbackPeriod*1000))); + QueueDecodedFrames(); } } From 0fa618a7c1355caf5e896baaa0f44d602a266956 Mon Sep 17 00:00:00 2001 From: Joe Drew Date: Thu, 14 May 2009 21:56:54 -0400 Subject: [PATCH 22/46] Bug 472590 - Always call OnStopFrame() from the GIF decoder, even if we didn't decode any rows (due to an invalid GIF). r=vlad --- modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp index 6c36e68dd2c6..4d5bae9ee4a1 100644 --- a/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp +++ b/modules/libpr0n/decoders/gif/nsGIFDecoder2.cpp @@ -415,11 +415,11 @@ void nsGIFDecoder2::EndImageFrame() mImageContainer->AppendFrame(mImageFrame); mImageContainer->EndFrameDecode(mGIFStruct.images_decoded); mGIFStruct.images_decoded++; - - if (mObserver) - mObserver->OnStopFrame(nsnull, mImageFrame); } + if (mObserver) + mObserver->OnStopFrame(nsnull, mImageFrame); + // Release reference to this frame mImageFrame = nsnull; From 94b6dbbe6113a451087191ba8331c527acf97177 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 14 May 2009 22:02:18 -0400 Subject: [PATCH 23/46] Bug 484954 - Fix a typo in the previous rgba text patch. r=vlad --- gfx/cairo/cairo/src/cairo-surface.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c index b542ba9b7945..e8e102313115 100644 --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -1319,7 +1319,7 @@ _wrap_image (cairo_surface_t *src, } pixman_image_set_component_alpha (surface->pixman_image, - pixman_image_get_component_alpha (surface->pixman_image)); + pixman_image_get_component_alpha (image->pixman_image)); *out = surface; return CAIRO_STATUS_SUCCESS; From b30669d3f7995062df3673c883c717e858f7a053 Mon Sep 17 00:00:00 2001 From: Jeff Muizelaar Date: Thu, 14 May 2009 22:03:26 -0400 Subject: [PATCH 24/46] Bug 493079 - Update cairo to 1ae2ddc1dd4c90d50b8c57c4de677f8ab96b1fa2 r=joe --- gfx/cairo/cairo/src/cairo-analysis-surface.c | 19 +- gfx/cairo/cairo/src/cairo-array.c | 3 + gfx/cairo/cairo/src/cairo-bentley-ottmann.c | 3 + gfx/cairo/cairo/src/cairo-clip-private.h | 4 +- gfx/cairo/cairo/src/cairo-clip.c | 141 ++--- gfx/cairo/cairo/src/cairo-compiler-private.h | 2 +- gfx/cairo/cairo/src/cairo-directfb-surface.c | 26 +- gfx/cairo/cairo/src/cairo-ft-font.c | 94 +-- gfx/cairo/cairo/src/cairo-gstate.c | 3 + gfx/cairo/cairo/src/cairo-hull.c | 3 + gfx/cairo/cairo/src/cairo-image-surface.c | 8 +- gfx/cairo/cairo/src/cairo-malloc-private.h | 7 + gfx/cairo/cairo/src/cairo-misc.c | 12 +- gfx/cairo/cairo/src/cairo-paginated-surface.c | 34 +- gfx/cairo/cairo/src/cairo-path-in-fill.c | 8 +- gfx/cairo/cairo/src/cairo-path-stroke.c | 2 + gfx/cairo/cairo/src/cairo-pattern.c | 33 +- gfx/cairo/cairo/src/cairo-pdf-surface.c | 38 +- gfx/cairo/cairo/src/cairo-pen.c | 9 + gfx/cairo/cairo/src/cairo-polygon.c | 5 + gfx/cairo/cairo/src/cairo-ps-surface.c | 5 + gfx/cairo/cairo/src/cairo-region.c | 589 +++++++++++++++--- .../cairo/src/cairo-scaled-font-subsets.c | 69 +- gfx/cairo/cairo/src/cairo-scaled-font.c | 3 + gfx/cairo/cairo/src/cairo-spans.c | 2 + gfx/cairo/cairo/src/cairo-spline.c | 2 +- gfx/cairo/cairo/src/cairo-stroke-style.c | 3 + gfx/cairo/cairo/src/cairo-surface-fallback.c | 67 +- gfx/cairo/cairo/src/cairo-surface.c | 71 +-- gfx/cairo/cairo/src/cairo-svg-surface.c | 2 + gfx/cairo/cairo/src/cairo-traps.c | 57 +- .../cairo/src/cairo-truetype-subset-private.h | 2 +- gfx/cairo/cairo/src/cairo-truetype-subset.c | 27 +- gfx/cairo/cairo/src/cairo-type1-fallback.c | 6 +- .../cairo/src/cairo-type3-glyph-surface.c | 36 +- gfx/cairo/cairo/src/cairo-types-private.h | 42 +- gfx/cairo/cairo/src/cairo-win32-surface.c | 50 +- gfx/cairo/cairo/src/cairo-xcb-surface.c | 24 +- gfx/cairo/cairo/src/cairo-xlib-surface.c | 77 ++- gfx/cairo/cairo/src/cairo.h | 78 +++ gfx/cairo/cairo/src/cairoint.h | 38 +- 41 files changed, 1146 insertions(+), 558 deletions(-) diff --git a/gfx/cairo/cairo/src/cairo-analysis-surface.c b/gfx/cairo/cairo/src/cairo-analysis-surface.c index 1be592e9ea02..c767d07c476c 100644 --- a/gfx/cairo/cairo/src/cairo-analysis-surface.c +++ b/gfx/cairo/cairo/src/cairo-analysis-surface.c @@ -38,7 +38,6 @@ #include "cairo-analysis-surface-private.h" #include "cairo-paginated-private.h" -#include "cairo-region-private.h" #include "cairo-meta-surface-private.h" typedef struct { @@ -215,7 +214,7 @@ _add_operation (cairo_analysis_surface_t *surface, * region there is no benefit in emitting a native operation as * the fallback image will be painted on top. */ - if (_cairo_region_contains_rectangle (&surface->fallback_region, rect) == PIXMAN_REGION_IN) + if (cairo_region_contains_rectangle (&surface->fallback_region, rect) == CAIRO_REGION_OVERLAP_IN) return CAIRO_INT_STATUS_IMAGE_FALLBACK; if (backend_status == CAIRO_INT_STATUS_FLATTEN_TRANSPARENCY) { @@ -226,7 +225,7 @@ _add_operation (cairo_analysis_surface_t *surface, * natively supported and the backend will blend the * transparency into the white background. */ - if (_cairo_region_contains_rectangle (&surface->supported_region, rect) == PIXMAN_REGION_OUT) + if (cairo_region_contains_rectangle (&surface->supported_region, rect) == CAIRO_REGION_OVERLAP_OUT) backend_status = CAIRO_STATUS_SUCCESS; } @@ -235,9 +234,7 @@ _add_operation (cairo_analysis_surface_t *surface, * this region will be emitted as native operations. */ surface->has_supported = TRUE; - status = _cairo_region_union_rect (&surface->supported_region, - &surface->supported_region, - rect); + status = cairo_region_union_rectangle (&surface->supported_region, rect); return status; } @@ -246,9 +243,7 @@ _add_operation (cairo_analysis_surface_t *surface, * emitted. */ surface->has_unsupported = TRUE; - status = _cairo_region_union_rect (&surface->fallback_region, - &surface->fallback_region, - rect); + status = cairo_region_union_rectangle (&surface->fallback_region, rect); /* The status CAIRO_INT_STATUS_IMAGE_FALLBACK is used to indicate * unsupported operations to the meta surface as using @@ -778,14 +773,14 @@ _cairo_analysis_surface_create (cairo_surface_t *target, surface->has_supported = FALSE; surface->has_unsupported = FALSE; + _cairo_region_init (&surface->supported_region); + _cairo_region_init (&surface->fallback_region); + surface->page_bbox.p1.x = 0; surface->page_bbox.p1.y = 0; surface->page_bbox.p2.x = 0; surface->page_bbox.p2.y = 0; - _cairo_region_init (&surface->supported_region); - _cairo_region_init (&surface->fallback_region); - if (width == -1 && height == -1) { surface->current_clip.x = CAIRO_RECT_INT_MIN; surface->current_clip.y = CAIRO_RECT_INT_MIN; diff --git a/gfx/cairo/cairo/src/cairo-array.c b/gfx/cairo/cairo/src/cairo-array.c index 318fd07e1b9d..77e575ff2e49 100644 --- a/gfx/cairo/cairo/src/cairo-array.c +++ b/gfx/cairo/cairo/src/cairo-array.c @@ -125,6 +125,9 @@ _cairo_array_grow_by (cairo_array_t *array, unsigned int additional) if (required_size > INT_MAX || required_size < array->num_elements) return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (required_size <= old_size) return CAIRO_STATUS_SUCCESS; diff --git a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c index 81eed1d67377..1d59d7038628 100644 --- a/gfx/cairo/cairo/src/cairo-bentley-ottmann.c +++ b/gfx/cairo/cairo/src/cairo-bentley-ottmann.c @@ -1660,6 +1660,9 @@ _cairo_bentley_ottmann_tessellate_polygon (cairo_traps_t *traps, if (0 == polygon->num_edges) return CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + has_limits = _cairo_traps_get_limit (traps, &limit); edges = stack_edges; diff --git a/gfx/cairo/cairo/src/cairo-clip-private.h b/gfx/cairo/cairo/src/cairo-clip-private.h index 36c0fbdcfb31..4229e4fb9268 100644 --- a/gfx/cairo/cairo/src/cairo-clip-private.h +++ b/gfx/cairo/cairo/src/cairo-clip-private.h @@ -40,7 +40,6 @@ #include "cairo-compiler-private.h" #include "cairo-path-fixed-private.h" #include "cairo-reference-count-private.h" -#include "cairo-region-private.h" extern const cairo_private cairo_rectangle_list_t _cairo_rectangles_nil; @@ -78,8 +77,7 @@ struct _cairo_clip { /* * A clip region that can be placed in the surface */ - cairo_region_t region; - cairo_bool_t has_region; + cairo_region_t *region; /* * If the surface supports path clipping, we store the list of * clipping paths that has been set here as a linked list. diff --git a/gfx/cairo/cairo/src/cairo-clip.c b/gfx/cairo/cairo/src/cairo-clip.c index 9aac115430d0..7066b932535c 100644 --- a/gfx/cairo/cairo/src/cairo-clip.c +++ b/gfx/cairo/cairo/src/cairo-clip.c @@ -64,8 +64,7 @@ _cairo_clip_init (cairo_clip_t *clip, cairo_surface_t *target) clip->serial = 0; - _cairo_region_init (&clip->region); - clip->has_region = FALSE; + clip->region = NULL; clip->path = NULL; } @@ -76,28 +75,29 @@ _cairo_clip_init_copy (cairo_clip_t *clip, cairo_clip_t *other) clip->mode = other->mode; clip->all_clipped = other->all_clipped; - + clip->surface = cairo_surface_reference (other->surface); clip->surface_rect = other->surface_rect; clip->serial = other->serial; - _cairo_region_init (&clip->region); - - if (other->has_region) { + if (other->region) { cairo_status_t status; + + clip->region = cairo_region_copy (other->region); - status = _cairo_region_copy (&clip->region, &other->region); + status = cairo_region_status (clip->region); if (unlikely (status)) { - _cairo_region_fini (&clip->region); cairo_surface_destroy (clip->surface); + cairo_region_destroy (clip->region); + clip->region = NULL; + return status; } - clip->has_region = TRUE; } else { - clip->has_region = FALSE; + clip->region = NULL; } - + clip->path = _cairo_clip_path_reference (other->path); return CAIRO_STATUS_SUCCESS; @@ -114,14 +114,10 @@ _cairo_clip_reset (cairo_clip_t *clip) clip->serial = 0; - if (clip->has_region) { - /* _cairo_region_fini just releases the resources used but - * doesn't bother with leaving the region in a valid state. - * So _cairo_region_init has to be called afterwards. */ - _cairo_region_fini (&clip->region); - _cairo_region_init (&clip->region); + if (clip->region) { + cairo_region_destroy (clip->region); - clip->has_region = FALSE; + clip->region = NULL; } _cairo_clip_path_destroy (clip->path); @@ -178,10 +174,10 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, return status; } - if (clip->has_region) { + if (clip->region) { cairo_rectangle_int_t extents; - _cairo_region_get_extents (&clip->region, &extents); + cairo_region_get_extents (clip->region, &extents); is_empty = _cairo_rectangle_intersect (rectangle, &extents); if (is_empty) return CAIRO_STATUS_SUCCESS; @@ -194,7 +190,7 @@ _cairo_clip_intersect_to_rectangle (cairo_clip_t *clip, } cairo_status_t -_cairo_clip_intersect_to_region (cairo_clip_t *clip, +_cairo_clip_intersect_to_region (cairo_clip_t *clip, cairo_region_t *region) { cairo_status_t status; @@ -202,40 +198,21 @@ _cairo_clip_intersect_to_region (cairo_clip_t *clip, if (!clip) return CAIRO_STATUS_SUCCESS; - if (clip->all_clipped) { - cairo_region_t clip_rect; - - _cairo_region_init_rect (&clip_rect, &clip->surface_rect); - - status = _cairo_region_intersect (region, &clip_rect, region); - - _cairo_region_fini (&clip_rect); - - return status; - } + if (clip->all_clipped) + return cairo_region_intersect_rectangle (region, &clip->surface_rect); if (clip->path) { /* Intersect clip path into region. */ } - if (clip->has_region) { - status = _cairo_region_intersect (region, &clip->region, region); + if (clip->region) { + status = cairo_region_intersect (region, clip->region); if (unlikely (status)) return status; } - if (clip->surface) { - cairo_region_t clip_rect; - - _cairo_region_init_rect (&clip_rect, &clip->surface_rect); - - status = _cairo_region_intersect (region, &clip_rect, region); - - _cairo_region_fini (&clip_rect); - - if (unlikely (status)) - return status; - } + if (clip->surface) + return cairo_region_intersect_rectangle (region, &clip->surface_rect); return CAIRO_STATUS_SUCCESS; } @@ -344,7 +321,7 @@ _cairo_clip_intersect_region (cairo_clip_t *clip, cairo_traps_t *traps, cairo_surface_t *target) { - cairo_region_t region; + cairo_region_t *region; cairo_int_status_t status; if (clip->all_clipped) @@ -357,29 +334,21 @@ _cairo_clip_intersect_region (cairo_clip_t *clip, if (status) return status; - if (!clip->has_region) { - status = _cairo_region_copy (&clip->region, ®ion); - if (status == CAIRO_STATUS_SUCCESS) - clip->has_region = TRUE; + if (clip->region) { + status = cairo_region_intersect (clip->region, region); } else { - cairo_region_t intersection; + clip->region = cairo_region_copy (region); - _cairo_region_init (&intersection); - - status = _cairo_region_intersect (&intersection, - &clip->region, - ®ion); - - if (status == CAIRO_STATUS_SUCCESS) - status = _cairo_region_copy (&clip->region, &intersection); - - _cairo_region_fini (&intersection); + assert (clip->region != NULL); + + if ((status = cairo_region_status (clip->region))) + clip->region = NULL; } clip->serial = _cairo_surface_allocate_clip_serial (target); - _cairo_region_fini (®ion); + cairo_region_destroy (region); - if (! _cairo_region_not_empty (&clip->region)) + if (!clip->region || cairo_region_is_empty (clip->region)) _cairo_clip_set_all_clipped (clip, target); return status; @@ -736,10 +705,10 @@ _cairo_clip_translate (cairo_clip_t *clip, if (clip->all_clipped) return; - if (clip->has_region) { - _cairo_region_translate (&clip->region, - _cairo_fixed_integer_part (tx), - _cairo_fixed_integer_part (ty)); + if (clip->region) { + cairo_region_translate (clip->region, + _cairo_fixed_integer_part (tx), + _cairo_fixed_integer_part (ty)); } if (clip->surface) { @@ -794,12 +763,10 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip, /* We should reapply the original clip path in this case, and let * whatever the right handling is happen */ } else { - if (other->has_region) { - status = _cairo_region_copy (&clip->region, &other->region); - if (unlikely (status)) + if (other->region) { + clip->region = cairo_region_copy (other->region); + if (unlikely ((status = cairo_region_status (clip->region)))) goto BAIL; - - clip->has_region = TRUE; } if (other->surface) { @@ -831,8 +798,8 @@ _cairo_clip_init_deep_copy (cairo_clip_t *clip, return CAIRO_STATUS_SUCCESS; BAIL: - if (clip->has_region) - _cairo_region_fini (&clip->region); + if (clip->region) + cairo_region_destroy (clip->region); if (clip->surface) cairo_surface_destroy (clip->surface); @@ -873,7 +840,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) { cairo_rectangle_list_t *list; cairo_rectangle_t *rectangles = NULL; - int n_boxes = 0; + int n_rects = 0; if (clip->all_clipped) goto DONE; @@ -883,28 +850,22 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) return (cairo_rectangle_list_t*) &_cairo_rectangles_not_representable; } - if (clip->has_region) { + if (clip->region) { int i; - n_boxes = _cairo_region_num_boxes (&clip->region); + n_rects = cairo_region_num_rectangles (clip->region); - if (n_boxes) { - rectangles = _cairo_malloc_ab (n_boxes, sizeof (cairo_rectangle_t)); + if (n_rects) { + rectangles = _cairo_malloc_ab (n_rects, sizeof (cairo_rectangle_t)); if (unlikely (rectangles == NULL)) { _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); return (cairo_rectangle_list_t*) &_cairo_rectangles_nil; } - for (i = 0; i < n_boxes; ++i) { - cairo_box_int_t box; + for (i = 0; i < n_rects; ++i) { cairo_rectangle_int_t clip_rect; - _cairo_region_get_box (&clip->region, i, &box); - - clip_rect.x = box.p1.x; - clip_rect.y = box.p1.y; - clip_rect.width = box.p2.x - box.p1.x; - clip_rect.height = box.p2.y - box.p1.y; + cairo_region_get_rectangle (clip->region, i, &clip_rect); if (!_cairo_clip_int_rect_to_user(gstate, &clip_rect, &rectangles[i])) { _cairo_error_throw (CAIRO_STATUS_CLIP_NOT_REPRESENTABLE); @@ -916,7 +877,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) } else { cairo_rectangle_int_t extents; - n_boxes = 1; + n_rects = 1; rectangles = malloc(sizeof (cairo_rectangle_t)); if (unlikely (rectangles == NULL)) { @@ -943,7 +904,7 @@ _cairo_clip_copy_rectangle_list (cairo_clip_t *clip, cairo_gstate_t *gstate) list->status = CAIRO_STATUS_SUCCESS; list->rectangles = rectangles; - list->num_rectangles = n_boxes; + list->num_rectangles = n_rects; return list; } diff --git a/gfx/cairo/cairo/src/cairo-compiler-private.h b/gfx/cairo/cairo/src/cairo-compiler-private.h index a5b60e4ccaa9..403c3f7a136e 100644 --- a/gfx/cairo/cairo/src/cairo-compiler-private.h +++ b/gfx/cairo/cairo/src/cairo-compiler-private.h @@ -173,7 +173,7 @@ #define inline __inline #endif -#if defined(_MSC_VER) && !defined(WINCE) +#if defined(_MSC_VER) && defined(_M_IX86) /* When compiling with /Gy and /OPT:ICF identical functions will be folded in together. The CAIRO_ENSURE_UNIQUE macro ensures that a function is always unique and will never be folded into another one. Something like this might eventually diff --git a/gfx/cairo/cairo/src/cairo-directfb-surface.c b/gfx/cairo/cairo/src/cairo-directfb-surface.c index b902cca165b1..3eee33ccd24f 100644 --- a/gfx/cairo/cairo/src/cairo-directfb-surface.c +++ b/gfx/cairo/cairo/src/cairo-directfb-surface.c @@ -1299,39 +1299,39 @@ _cairo_directfb_surface_set_clip_region (void *abstract_surface, __FUNCTION__, surface, region); if (region) { - int n_boxes; + int n_rects; cairo_status_t status; int i; surface->has_clip = TRUE; - n_boxes = _cairo_region_num_boxes (region); + n_rects = cairo_region_num_rectangles (region); - if (n_boxes == 0) + if (n_rects == 0) return CAIRO_STATUS_SUCCESS; - if (surface->n_clips != n_boxes) { + if (surface->n_clips != n_rects) { if (surface->clips) free (surface->clips); - surface->clips = _cairo_malloc_ab (n_boxes, sizeof (DFBRegion)); + surface->clips = _cairo_malloc_ab (n_rects, sizeof (DFBRegion)); if (!surface->clips) { surface->n_clips = 0; return _cairo_error (CAIRO_STATUS_NO_MEMORY); } - surface->n_clips = n_boxes; + surface->n_clips = n_rects; } - for (i = 0; i < n_boxes; i++) { - cairo_box_int_t box; + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_get_box (region, i, &box); + cairo_region_get_rectangle (region, i, &rect); - surface->clips[i].x1 = box.p1.x; - surface->clips[i].y1 = box.p1.y; - surface->clips[i].x2 = box.p2.x - 1; - surface->clips[i].y2 = box.p2.y - 1; + surface->clips[i].x1 = rect.x; + surface->clips[i].y1 = rect.y; + surface->clips[i].x2 = rect.x + rect.width - 1; + surface->clips[i].y2 = rect.y + rect.height - 1; } } else { surface->has_clip = FALSE; diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c index 934c23c93abf..2b513269a054 100644 --- a/gfx/cairo/cairo/src/cairo-ft-font.c +++ b/gfx/cairo/cairo/src/cairo-ft-font.c @@ -418,11 +418,12 @@ _cairo_ft_unscaled_font_keys_equal (const void *key_a, /* Finds or creates a #cairo_ft_unscaled_font_t for the filename/id from * pattern. Returns a new reference to the unscaled font. */ -static cairo_ft_unscaled_font_t * +static cairo_status_t _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, char *filename, int id, - FT_Face font_face) + FT_Face font_face, + cairo_ft_unscaled_font_t **out) { cairo_ft_unscaled_font_t key, *unscaled; cairo_ft_unscaled_font_map_t *font_map; @@ -430,7 +431,7 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, font_map = _cairo_ft_unscaled_font_map_lock (); if (unlikely (font_map == NULL)) - goto UNWIND; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); _cairo_ft_unscaled_font_init_key (&key, from_face, filename, id, font_face); @@ -439,14 +440,13 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, &key.base.hash_entry); if (unscaled != NULL) { _cairo_unscaled_font_reference (&unscaled->base); - _cairo_ft_unscaled_font_map_unlock (); - return unscaled; + goto DONE; } /* Otherwise create it and insert into hash table. */ unscaled = malloc (sizeof (cairo_ft_unscaled_font_t)); if (unlikely (unscaled == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + status = _cairo_error (CAIRO_STATUS_NO_MEMORY); goto UNWIND_FONT_MAP_LOCK; } @@ -460,9 +460,10 @@ _cairo_ft_unscaled_font_create_internal (cairo_bool_t from_face, if (unlikely (status)) goto UNWIND_UNSCALED_FONT_INIT; +DONE: _cairo_ft_unscaled_font_map_unlock (); - - return unscaled; + *out = unscaled; + return CAIRO_STATUS_SUCCESS; UNWIND_UNSCALED_FONT_INIT: _cairo_ft_unscaled_font_fini (unscaled); @@ -470,39 +471,52 @@ UNWIND_UNSCALED_MALLOC: free (unscaled); UNWIND_FONT_MAP_LOCK: _cairo_ft_unscaled_font_map_unlock (); -UNWIND: - return NULL; + return status; } #if CAIRO_HAS_FC_FONT -static cairo_ft_unscaled_font_t * -_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern) +static cairo_status_t +_cairo_ft_unscaled_font_create_for_pattern (FcPattern *pattern, + cairo_ft_unscaled_font_t **out) { FT_Face font_face = NULL; char *filename = NULL; int id = 0; + FcResult ret; - if (FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face) == FcResultMatch) - goto DONE; - - if (FcPatternGetString (pattern, FC_FILE, 0, (FcChar8 **) &filename) == FcResultMatch) { - /* If FC_INDEX is not set, we just use 0 */ - FcPatternGetInteger (pattern, FC_INDEX, 0, &id); + ret = FcPatternGetFTFace (pattern, FC_FT_FACE, 0, &font_face); + switch ((int) ret) { + case FcResultMatch: goto DONE; + case FcResultOutOfMemory: + break; + default: + if (FcPatternGetString (pattern, FC_FILE, 0, + (FcChar8 **) &filename) == FcResultMatch) + { + /* If FC_INDEX is not set, we just use 0 */ + if (FcPatternGetInteger (pattern, + FC_INDEX, 0, &id) != FcResultOutOfMemory) + goto DONE; + } + break; } - return NULL; + return _cairo_error (CAIRO_STATUS_NO_MEMORY); DONE: - return _cairo_ft_unscaled_font_create_internal (font_face != NULL, filename, id, font_face); + return _cairo_ft_unscaled_font_create_internal (font_face != NULL, + filename, id, font_face, + out); } #endif -static cairo_ft_unscaled_font_t * -_cairo_ft_unscaled_font_create_from_face (FT_Face face) +static cairo_status_t +_cairo_ft_unscaled_font_create_from_face (FT_Face face, + cairo_ft_unscaled_font_t **out) { - return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face); + return _cairo_ft_unscaled_font_create_internal (TRUE, NULL, 0, face, out); } static void @@ -2317,7 +2331,6 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, *scaled_font = _cairo_scaled_font_create_in_error (status); return CAIRO_STATUS_SUCCESS; } - } else #endif { @@ -2325,11 +2338,11 @@ _cairo_ft_font_face_scaled_font_create (void *abstract_face, ft_options = font_face->ft_options; } - return _cairo_ft_scaled_font_create (unscaled, - &font_face->base, - font_matrix, ctm, - options, ft_options, - scaled_font); + return _cairo_ft_scaled_font_create (unscaled, + &font_face->base, + font_matrix, ctm, + options, ft_options, + scaled_font); } const cairo_font_face_backend_t _cairo_ft_font_face_backend = { @@ -2358,7 +2371,7 @@ _cairo_ft_font_face_create_for_pattern (FcPattern *pattern, font_face->next = NULL; font_face->pattern = FcPatternDuplicate (pattern); - if (unlikely (pattern == NULL)) { + if (unlikely (font_face->pattern == NULL)) { free (font_face); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } @@ -2564,6 +2577,8 @@ _cairo_ft_resolve_pattern (FcPattern *pattern, return status; pattern = FcPatternDuplicate (pattern); + if (pattern == NULL) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (! FcPatternAddDouble (pattern, FC_PIXEL_SIZE, sf.y_scale)) { status = _cairo_error (CAIRO_STATUS_NO_MEMORY); @@ -2587,11 +2602,9 @@ _cairo_ft_resolve_pattern (FcPattern *pattern, goto FREE_PATTERN; } - *unscaled = _cairo_ft_unscaled_font_create_for_pattern (resolved); - if (!*unscaled) { - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + status = _cairo_ft_unscaled_font_create_for_pattern (resolved, unscaled); + if (unlikely (status)) goto FREE_RESOLVED; - } _get_pattern_ft_options (resolved, ft_options); @@ -2647,10 +2660,12 @@ cairo_ft_font_face_create_for_pattern (FcPattern *pattern) cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; + cairo_status_t status; - unscaled = _cairo_ft_unscaled_font_create_for_pattern (pattern); + status = _cairo_ft_unscaled_font_create_for_pattern (pattern, &unscaled); + if (unlikely (status)) + return (cairo_font_face_t *) &_cairo_font_face_nil; if (unlikely (unscaled == NULL)) { - cairo_status_t status; /* Store the pattern. We will resolve it and create unscaled * font when creating scaled fonts */ status = _cairo_ft_font_face_create_for_pattern (pattern, @@ -2722,12 +2737,11 @@ cairo_ft_font_face_create_for_ft_face (FT_Face face, cairo_ft_unscaled_font_t *unscaled; cairo_font_face_t *font_face; cairo_ft_options_t ft_options; + cairo_status_t status; - unscaled = _cairo_ft_unscaled_font_create_from_face (face); - if (unlikely (unscaled == NULL)) { - _cairo_error_throw (CAIRO_STATUS_NO_MEMORY); + status = _cairo_ft_unscaled_font_create_from_face (face, &unscaled); + if (unlikely (status)) return (cairo_font_face_t *)&_cairo_font_face_nil; - } ft_options.load_flags = load_flags; ft_options.extra_flags = 0; diff --git a/gfx/cairo/cairo/src/cairo-gstate.c b/gfx/cairo/cairo/src/cairo-gstate.c index 2445413b93db..2bb0e3cfc230 100644 --- a/gfx/cairo/cairo/src/cairo-gstate.c +++ b/gfx/cairo/cairo/src/cairo-gstate.c @@ -222,6 +222,9 @@ _cairo_gstate_save (cairo_gstate_t **gstate, cairo_gstate_t **freelist) cairo_gstate_t *top; cairo_status_t status; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + top = *freelist; if (top == NULL) { top = malloc (sizeof (cairo_gstate_t)); diff --git a/gfx/cairo/cairo/src/cairo-hull.c b/gfx/cairo/cairo/src/cairo-hull.c index a699a524bb82..1fa919bf321a 100644 --- a/gfx/cairo/cairo/src/cairo-hull.c +++ b/gfx/cairo/cairo/src/cairo-hull.c @@ -198,6 +198,9 @@ _cairo_hull_compute (cairo_pen_vertex_t *vertices, int *num_vertices) cairo_hull_t *hull; int num_hull = *num_vertices; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (num_hull > ARRAY_LENGTH (hull_stack)) { hull = _cairo_malloc_ab (num_hull, sizeof (cairo_hull_t)); if (unlikely (hull == NULL)) diff --git a/gfx/cairo/cairo/src/cairo-image-surface.c b/gfx/cairo/cairo/src/cairo-image-surface.c index cde68a108f67..09302d4aa588 100644 --- a/gfx/cairo/cairo/src/cairo-image-surface.c +++ b/gfx/cairo/cairo/src/cairo-image-surface.c @@ -1044,6 +1044,9 @@ _cairo_image_surface_fill_rectangles (void *abstract_surface, cairo_int_status_t status = CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + pixman_color.red = color->red_short; pixman_color.green = color->green_short; pixman_color.blue = color->blue_short; @@ -1112,6 +1115,9 @@ _cairo_image_surface_composite_trapezoids (cairo_operator_t op, if (height == 0 || width == 0) return CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + /* Convert traps to pixman traps */ if (num_traps > ARRAY_LENGTH (stack_traps)) { pixman_traps = _cairo_malloc_ab (num_traps, sizeof (pixman_trapezoid_t)); @@ -1427,7 +1433,7 @@ _cairo_image_surface_set_clip_region (void *abstract_surface, { cairo_image_surface_t *surface = (cairo_image_surface_t *) abstract_surface; - if (! pixman_image_set_clip_region32 (surface->pixman_image, ®ion->rgn)) + if (! pixman_image_set_clip_region32 (surface->pixman_image, region? ®ion->rgn : NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); surface->has_clip = region != NULL; diff --git a/gfx/cairo/cairo/src/cairo-malloc-private.h b/gfx/cairo/cairo/src/cairo-malloc-private.h index f8094f911936..e36f93b8da4b 100644 --- a/gfx/cairo/cairo/src/cairo-malloc-private.h +++ b/gfx/cairo/cairo/src/cairo-malloc-private.h @@ -39,6 +39,13 @@ #include "cairo-wideint-private.h" +#if HAVE_MEMFAULT +#include +#define CAIRO_INJECT_FAULT() VALGRIND_INJECT_FAULT() +#else +#define CAIRO_INJECT_FAULT() 0 +#endif + /** * _cairo_malloc: * @size: size in bytes diff --git a/gfx/cairo/cairo/src/cairo-misc.c b/gfx/cairo/cairo/src/cairo-misc.c index ab30327b4d61..d4b10e1c4e0f 100644 --- a/gfx/cairo/cairo/src/cairo-misc.c +++ b/gfx/cairo/cairo/src/cairo-misc.c @@ -123,9 +123,10 @@ cairo_status_to_string (cairo_status_t status) return "invalid value for an input #cairo_font_weight_t"; case CAIRO_STATUS_INVALID_SIZE: return "invalid value for the size of the input (surface, pattern, etc.)"; + default: + case CAIRO_STATUS_LAST_STATUS: + return ""; } - - return ""; } @@ -610,7 +611,7 @@ _cairo_lround (double d) #include #include -#if !WINCE +#if !_WIN32_WCE /* tmpfile() replacement for Windows. * * On Windows tmpfile() creates the file in the root directory. This @@ -660,7 +661,7 @@ _cairo_win32_tmpfile (void) return fp; } -#endif /* !WINCE */ +#endif /* !_WIN32_WCE */ #endif /* _WIN32 */ @@ -703,6 +704,9 @@ _cairo_intern_string (const char **str_inout, int len) cairo_intern_string_t tmpl, *istring; cairo_status_t status = CAIRO_STATUS_SUCCESS; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (len < 0) len = strlen (str); tmpl.hash_entry.hash = _intern_string_hash (str, len); diff --git a/gfx/cairo/cairo/src/cairo-paginated-surface.c b/gfx/cairo/cairo/src/cairo-paginated-surface.c index 8c62644ad5f3..b84fbffe2698 100644 --- a/gfx/cairo/cairo/src/cairo-paginated-surface.c +++ b/gfx/cairo/cairo/src/cairo-paginated-surface.c @@ -244,7 +244,7 @@ _cairo_paginated_surface_release_source_image (void *abstract_surface, static cairo_int_status_t _paint_fallback_image (cairo_paginated_surface_t *surface, - cairo_box_int_t *box) + cairo_rectangle_int_t *rect) { double x_scale = surface->base.x_fallback_resolution / surface->target->x_resolution; double y_scale = surface->base.y_fallback_resolution / surface->target->y_resolution; @@ -254,10 +254,10 @@ _paint_fallback_image (cairo_paginated_surface_t *surface, cairo_surface_t *image; cairo_surface_pattern_t pattern; - x = box->p1.x; - y = box->p1.y; - width = box->p2.x - x; - height = box->p2.y - y; + x = rect->x; + y = rect->y; + width = rect->width; + height = rect->height; image = _cairo_paginated_surface_create_image_surface (surface, ceil (width * x_scale), ceil (height * y_scale)); @@ -365,23 +365,23 @@ _paint_page (cairo_paginated_surface_t *surface) } if (has_page_fallback) { - cairo_box_int_t box; + cairo_rectangle_int_t rect; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); - box.p1.x = 0; - box.p1.y = 0; - box.p2.x = surface->width; - box.p2.y = surface->height; - status = _paint_fallback_image (surface, &box); + rect.x = 0; + rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + status = _paint_fallback_image (surface, &rect); if (unlikely (status)) goto FAIL; } if (has_finegrained_fallback) { cairo_region_t *region; - int num_boxes, i; + int num_rects, i; surface->backend->set_paginated_mode (surface->target, CAIRO_PAGINATED_MODE_FALLBACK); @@ -397,13 +397,13 @@ _paint_page (cairo_paginated_surface_t *surface) region = _cairo_analysis_surface_get_unsupported (analysis); - num_boxes = _cairo_region_num_boxes (region); - for (i = 0; i < num_boxes; i++) { - cairo_box_int_t box; + num_rects = cairo_region_num_rectangles (region); + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_get_box (region, i, &box); + cairo_region_get_rectangle (region, i, &rect); - status = _paint_fallback_image (surface, &box); + status = _paint_fallback_image (surface, &rect); if (unlikely (status)) goto FAIL; diff --git a/gfx/cairo/cairo/src/cairo-path-in-fill.c b/gfx/cairo/cairo/src/cairo-path-in-fill.c index e6416547f41e..d7b499ca0e54 100644 --- a/gfx/cairo/cairo/src/cairo-path-in-fill.c +++ b/gfx/cairo/cairo/src/cairo-path-in-fill.c @@ -201,15 +201,19 @@ _cairo_in_fill_curve_to (void *closure, if (c->y > bot) bot = c->y; if (d->y < top) top = d->y; if (d->y > bot) bot = d->y; - if (bot < in_fill->y || top > in_fill->y) + if (bot < in_fill->y || top > in_fill->y) { + in_fill->current_point = *d; return CAIRO_STATUS_SUCCESS; + } left = in_fill->current_point.x; if (b->x < left) left = b->x; if (c->x < left) left = c->x; if (d->x < left) left = d->x; - if (left > in_fill->x) + if (left > in_fill->x) { + in_fill->current_point = *d; return CAIRO_STATUS_SUCCESS; + } /* XXX Investigate direct inspection of the inflections? */ if (! _cairo_spline_init (&spline, diff --git a/gfx/cairo/cairo/src/cairo-path-stroke.c b/gfx/cairo/cairo/src/cairo-path-stroke.c index d6e5790c0b99..37ac79c7e0b2 100644 --- a/gfx/cairo/cairo/src/cairo-path-stroke.c +++ b/gfx/cairo/cairo/src/cairo-path-stroke.c @@ -1278,6 +1278,8 @@ _cairo_rectilinear_stroker_add_segment (cairo_rectilinear_stroker_t *stroker, cairo_bool_t is_horizontal, cairo_bool_t has_join) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); if (stroker->num_segments == stroker->segments_size) { int new_size = stroker->segments_size * 2; diff --git a/gfx/cairo/cairo/src/cairo-pattern.c b/gfx/cairo/cairo/src/cairo-pattern.c index bd7e07af5fb9..0fb36bfa7bc2 100644 --- a/gfx/cairo/cairo/src/cairo-pattern.c +++ b/gfx/cairo/cairo/src/cairo-pattern.c @@ -119,6 +119,9 @@ static cairo_status_t _cairo_gradient_pattern_init_copy (cairo_gradient_pattern_t *pattern, const cairo_gradient_pattern_t *other) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (other->base.type == CAIRO_PATTERN_TYPE_LINEAR) { cairo_linear_pattern_t *dst = (cairo_linear_pattern_t *) pattern; @@ -250,9 +253,10 @@ _cairo_pattern_fini (cairo_pattern_t *pattern) } cairo_status_t -_cairo_pattern_create_copy (cairo_pattern_t **pattern, +_cairo_pattern_create_copy (cairo_pattern_t **pattern_out, const cairo_pattern_t *other) { + cairo_pattern_t *pattern; cairo_status_t status; if (other->status) @@ -260,29 +264,32 @@ _cairo_pattern_create_copy (cairo_pattern_t **pattern, switch (other->type) { case CAIRO_PATTERN_TYPE_SOLID: - *pattern = malloc (sizeof (cairo_solid_pattern_t)); + pattern = malloc (sizeof (cairo_solid_pattern_t)); break; case CAIRO_PATTERN_TYPE_SURFACE: - *pattern = malloc (sizeof (cairo_surface_pattern_t)); + pattern = malloc (sizeof (cairo_surface_pattern_t)); break; case CAIRO_PATTERN_TYPE_LINEAR: - *pattern = malloc (sizeof (cairo_linear_pattern_t)); + pattern = malloc (sizeof (cairo_linear_pattern_t)); break; case CAIRO_PATTERN_TYPE_RADIAL: - *pattern = malloc (sizeof (cairo_radial_pattern_t)); + pattern = malloc (sizeof (cairo_radial_pattern_t)); break; + default: + ASSERT_NOT_REACHED; + return _cairo_error (CAIRO_STATUS_PATTERN_TYPE_MISMATCH); } - if (unlikely (*pattern == NULL)) + if (unlikely (pattern == NULL)) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - status = _cairo_pattern_init_copy (*pattern, other); + status = _cairo_pattern_init_copy (pattern, other); if (unlikely (status)) { - free (*pattern); + free (pattern); return status; } - CAIRO_REFERENCE_COUNT_INIT (&(*pattern)->ref_count, 1); - + CAIRO_REFERENCE_COUNT_INIT (&pattern->ref_count, 1); + *pattern_out = pattern; return CAIRO_STATUS_SUCCESS; } @@ -837,6 +844,9 @@ _cairo_pattern_gradient_grow (cairo_gradient_pattern_t *pattern) return CAIRO_STATUS_SUCCESS; } + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + assert (pattern->n_stops <= pattern->stops_size); if (pattern->stops == pattern->stops_embedded) { @@ -1253,6 +1263,9 @@ _cairo_pattern_acquire_surface_for_gradient (const cairo_gradient_pattern_t *pat int clone_offset_x, clone_offset_y; cairo_matrix_t matrix = pattern->base.matrix; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + if (pattern->n_stops > ARRAY_LENGTH(pixman_stops_static)) { pixman_stops = _cairo_malloc_ab (pattern->n_stops, sizeof(pixman_gradient_stop_t)); diff --git a/gfx/cairo/cairo/src/cairo-pdf-surface.c b/gfx/cairo/cairo/src/cairo-pdf-surface.c index f562e3fd8755..b1458d8e0d11 100644 --- a/gfx/cairo/cairo/src/cairo-pdf-surface.c +++ b/gfx/cairo/cairo/src/cairo-pdf-surface.c @@ -1015,18 +1015,21 @@ _cairo_pdf_surface_open_stream (cairo_pdf_surface_t *surface, static cairo_status_t _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) { - cairo_status_t status = CAIRO_STATUS_SUCCESS; + cairo_status_t status; long length; if (! surface->pdf_stream.active) return CAIRO_STATUS_SUCCESS; status = _cairo_pdf_operators_flush (&surface->pdf_operators); - if (unlikely (status)) - return status; if (surface->pdf_stream.compressed) { - status = _cairo_output_stream_destroy (surface->output); + cairo_status_t status2; + + status2 = _cairo_output_stream_destroy (surface->output); + if (likely (status == CAIRO_STATUS_SUCCESS)) + status = status2; + surface->output = surface->pdf_stream.old_output; _cairo_pdf_operators_set_stream (&surface->pdf_operators, surface->output); surface->pdf_stream.old_output = NULL; @@ -1051,7 +1054,7 @@ _cairo_pdf_surface_close_stream (cairo_pdf_surface_t *surface) surface->pdf_stream.active = FALSE; - if (status == CAIRO_STATUS_SUCCESS) + if (likely (status == CAIRO_STATUS_SUCCESS)) status = _cairo_output_stream_get_status (surface->output); return status; @@ -1324,10 +1327,12 @@ _cairo_pdf_surface_finish (void *abstract_surface) "%%%%EOF\n", offset); - status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); /* pdf_operators has already been flushed when the last stream was - * closed so we should never be writing anything here. */ - assert(status2 == CAIRO_STATUS_SUCCESS); + * closed so we should never be writing anything here - however, + * the stream may itself be in an error state. */ + status2 = _cairo_pdf_operators_fini (&surface->pdf_operators); + if (status == CAIRO_STATUS_SUCCESS) + status = status2; /* close any active streams still open due to fatal errors */ status2 = _cairo_pdf_surface_close_stream (surface); @@ -1723,6 +1728,8 @@ _cairo_pdf_surface_emit_jpeg_image (cairo_pdf_surface_t *surface, cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -1790,7 +1797,7 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, status = _cairo_surface_acquire_source_image (pattern->surface, &image, &image_extra); if (unlikely (status)) - goto BAIL; + return status; pad_image = &image->base; if (cairo_pattern_get_extend (&pattern->base) == CAIRO_EXTEND_PAD) { @@ -1840,10 +1847,10 @@ _cairo_pdf_surface_emit_image_surface (cairo_pdf_surface_t *surface, *origin_x = x; *origin_y = y; +BAIL: if (pad_image != &image->base) cairo_surface_destroy (pad_image); -BAIL: _cairo_surface_release_source_image (pattern->surface, image, image_extra); return status; @@ -3927,6 +3934,11 @@ _cairo_pdf_surface_analyze_user_font_subset (cairo_scaled_font_subset_t *font_su null_stream, _cairo_pdf_emit_imagemask, surface->font_subsets); + if (unlikely (type3_surface->status)) { + status2 = _cairo_output_stream_destroy (null_stream); + return type3_surface->status; + } + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); @@ -3983,6 +3995,12 @@ _cairo_pdf_surface_emit_type3_font_subset (cairo_pdf_surface_t *surface, NULL, _cairo_pdf_emit_imagemask, surface->font_subsets); + if (unlikely (type3_surface->status)) { + free (glyphs); + free (widths); + return type3_surface->status; + } + _cairo_type3_glyph_surface_set_font_subsets_callback (type3_surface, _cairo_pdf_surface_add_font, surface); diff --git a/gfx/cairo/cairo/src/cairo-pen.c b/gfx/cairo/cairo/src/cairo-pen.c index 9d5e8959405d..eb66b1a03cfc 100644 --- a/gfx/cairo/cairo/src/cairo-pen.c +++ b/gfx/cairo/cairo/src/cairo-pen.c @@ -55,6 +55,9 @@ _cairo_pen_init (cairo_pen_t *pen, int i; int reflect; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + pen->radius = radius; pen->tolerance = tolerance; @@ -109,6 +112,9 @@ _cairo_pen_init_copy (cairo_pen_t *pen, const cairo_pen_t *other) { *pen = *other; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + pen->vertices = pen->vertices_embedded; if (pen->num_vertices) { if (pen->num_vertices > ARRAY_LENGTH (pen->vertices_embedded)) { @@ -132,6 +138,9 @@ _cairo_pen_add_points (cairo_pen_t *pen, cairo_point_t *point, int num_points) int num_vertices; int i; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + num_vertices = pen->num_vertices + num_points; if (num_vertices > ARRAY_LENGTH (pen->vertices_embedded) || pen->vertices != pen->vertices_embedded) diff --git a/gfx/cairo/cairo/src/cairo-polygon.c b/gfx/cairo/cairo/src/cairo-polygon.c index 0b0fa991d594..d74d098d1ea6 100644 --- a/gfx/cairo/cairo/src/cairo-polygon.c +++ b/gfx/cairo/cairo/src/cairo-polygon.c @@ -64,6 +64,11 @@ _cairo_polygon_grow (cairo_polygon_t *polygon) int old_size = polygon->edges_size; int new_size = 4 * old_size; + if (CAIRO_INJECT_FAULT ()) { + polygon->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + if (polygon->edges == polygon->edges_embedded) { new_edges = _cairo_malloc_ab (new_size, sizeof (cairo_edge_t)); if (new_edges != NULL) diff --git a/gfx/cairo/cairo/src/cairo-ps-surface.c b/gfx/cairo/cairo/src/cairo-ps-surface.c index 346bb88c31a7..0037905a6ed7 100644 --- a/gfx/cairo/cairo/src/cairo-ps-surface.c +++ b/gfx/cairo/cairo/src/cairo-ps-surface.c @@ -2138,6 +2138,8 @@ _cairo_ps_surface_emit_jpeg_image (cairo_ps_surface_t *surface, cairo_surface_get_mime_data (source, CAIRO_MIME_TYPE_JPEG, &mime_data, &mime_data_length); + if (unlikely (source->status)) + return source->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; @@ -3315,6 +3317,9 @@ _cairo_ps_surface_stroke (void *abstract_surface, if (status == CAIRO_INT_STATUS_NOTHING_TO_DO) return CAIRO_STATUS_SUCCESS; + if (unlikely (status)) + return status; + return _cairo_pdf_operators_stroke (&surface->pdf_operators, path, style, diff --git a/gfx/cairo/cairo/src/cairo-region.c b/gfx/cairo/cairo/src/cairo-region.c index d32805e82ed6..5d1f2c5ff041 100644 --- a/gfx/cairo/cairo/src/cairo-region.c +++ b/gfx/cairo/cairo/src/cairo-region.c @@ -33,55 +33,64 @@ * Contributor(s): * Owen Taylor * Vladimir Vukicevic + * Søren Sandmann */ #include "cairoint.h" +static const cairo_region_t _cairo_region_nil = { + CAIRO_STATUS_NO_MEMORY, /* status */ +}; + +/** + * _cairo_region_set_error: + * @region: a region + * @status: a status value indicating an error + * + * Atomically sets region->status to @status and calls _cairo_error; + * Does nothing if status is %CAIRO_STATUS_SUCCESS or any of the internal + * status values. + * + * All assignments of an error status to region->status should happen + * through _cairo_region_set_error(). Note that due to the nature of + * the atomic operation, it is not safe to call this function on the + * nil objects. + * + * The purpose of this function is to allow the user to set a + * breakpoint in _cairo_error() to generate a stack trace for when the + * user causes cairo to detect an error. + * + * Return value: the error status. + **/ +static cairo_status_t +_cairo_region_set_error (cairo_region_t *region, + cairo_status_t status) +{ + if (! _cairo_status_is_error (status)) + return status; + + /* Don't overwrite an existing error. This preserves the first + * error, which is the most significant. */ + _cairo_status_set_error (®ion->status, status); + + return _cairo_error (status); +} + void _cairo_region_init (cairo_region_t *region) { + region->status = CAIRO_STATUS_SUCCESS; pixman_region32_init (®ion->rgn); } void -_cairo_region_init_rect (cairo_region_t *region, - cairo_rectangle_int_t *rect) +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) { + region->status = CAIRO_STATUS_SUCCESS; pixman_region32_init_rect (®ion->rgn, - rect->x, rect->y, - rect->width, rect->height); -} - -cairo_int_status_t -_cairo_region_init_boxes (cairo_region_t *region, - cairo_box_int_t *boxes, - int count) -{ - pixman_box32_t stack_pboxes[CAIRO_STACK_ARRAY_LENGTH (pixman_box32_t)]; - pixman_box32_t *pboxes = stack_pboxes; - cairo_int_status_t status = CAIRO_STATUS_SUCCESS; - int i; - - if (count > ARRAY_LENGTH (stack_pboxes)) { - pboxes = _cairo_malloc_ab (count, sizeof (pixman_box32_t)); - if (unlikely (pboxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - for (i = 0; i < count; i++) { - pboxes[i].x1 = boxes[i].p1.x; - pboxes[i].y1 = boxes[i].p1.y; - pboxes[i].x2 = boxes[i].p2.x; - pboxes[i].y2 = boxes[i].p2.y; - } - - if (! pixman_region32_init_rects (®ion->rgn, pboxes, count)) - status = _cairo_error (CAIRO_STATUS_NO_MEMORY); - - if (pboxes != stack_pboxes) - free (pboxes); - - return status; + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); } void @@ -90,108 +99,512 @@ _cairo_region_fini (cairo_region_t *region) pixman_region32_fini (®ion->rgn); } -cairo_int_status_t -_cairo_region_copy (cairo_region_t *dst, cairo_region_t *src) +/** + * cairo_region_create: + * + * Allocates a new empty region object. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create (void) { - if (!pixman_region32_copy (&dst->rgn, &src->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_region_t *region; - return CAIRO_STATUS_SUCCESS; + region = _cairo_malloc (sizeof (cairo_region_t)); + if (region == NULL) + return (cairo_region_t *) &_cairo_region_nil; + + region->status = CAIRO_STATUS_SUCCESS; + + pixman_region32_init (®ion->rgn); + + return region; } +slim_hidden_def (cairo_region_create); -int -_cairo_region_num_boxes (cairo_region_t *region) +/** + * cairo_region_create_rectangle: + * @rectangle: a #cairo_rectangle_int_t + * + * Allocates a new region object containing @rectangle. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle) { + cairo_region_t *region; + + region = _cairo_malloc (sizeof (cairo_region_t)); + if (region == NULL) + return (cairo_region_t *) &_cairo_region_nil; + + region->status = CAIRO_STATUS_SUCCESS; + + pixman_region32_init_rect (®ion->rgn, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + return region; +} +slim_hidden_def (cairo_region_create_rectangle); + +/** + * cairo_region_copy: + * @original: a #cairo_region_t + * + * Allocates a new region object copying the area from @original. + * + * Return value: A newly allocated #cairo_region_t. Free with + * cairo_region_destroy(). This function always returns a + * valid pointer; if memory cannot be allocated, then a special + * error object is returned where all operations on the object do nothing. + * You can check for this with cairo_region_status(). + * + * Since: 1.10 + **/ +cairo_region_t * +cairo_region_copy (cairo_region_t *original) +{ + cairo_region_t *copy; + + if (original->status) + return (cairo_region_t *) &_cairo_region_nil; + + copy = cairo_region_create (); + if (copy->status) + return copy; + + if (! pixman_region32_copy (©->rgn, &original->rgn)) { + cairo_region_destroy (copy); + return (cairo_region_t *) &_cairo_region_nil; + } + + return copy; +} +slim_hidden_def (cairo_region_copy); + +/** + * cairo_region_destroy: + * @region: a #cairo_region_t + * + * Destroys a #cairo_region_t object created with + * cairo_region_create(), cairo_region_copy(), or + * or cairo_region_create_rectangle(). + * + * Since: 1.10 + **/ +void +cairo_region_destroy (cairo_region_t *region) +{ + if (region == (cairo_region_t *) &_cairo_region_nil) + return; + + pixman_region32_fini (®ion->rgn); + free (region); +} +slim_hidden_def (cairo_region_destroy); + +/** + * cairo_region_num_rectangles: + * @region: a #cairo_region_t + * + * Returns the number of rectangles contained in @region. + * + * Return value: The number of rectangles contained in @region. + * + * Since: 1.10 + **/ +int +cairo_region_num_rectangles (cairo_region_t *region) +{ + if (region->status) + return 0; + return pixman_region32_n_rects (®ion->rgn); } +slim_hidden_def (cairo_region_num_rectangles); -cairo_private void -_cairo_region_get_box (cairo_region_t *region, - int nth_box, - cairo_box_int_t *box) +/** + * cairo_region_get_rectangle: + * @region: a #cairo_region_t + * @nth: a number indicating which rectangle should be returned + * @rectangle: return location for a #cairo_rectangle_int_t + * + * Stores the @nth rectangle from the region in @rectangle. + * + * Since: 1.10 + **/ +void +cairo_region_get_rectangle (cairo_region_t *region, + int nth, + cairo_rectangle_int_t *rectangle) { pixman_box32_t *pbox; - pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth_box; + if (region->status) { + rectangle->x = rectangle->y = 0; + rectangle->width = rectangle->height = 0; + return; + } - box->p1.x = pbox->x1; - box->p1.y = pbox->y1; - box->p2.x = pbox->x2; - box->p2.y = pbox->y2; + pbox = pixman_region32_rectangles (®ion->rgn, NULL) + nth; + + rectangle->x = pbox->x1; + rectangle->y = pbox->y1; + rectangle->width = pbox->x2 - pbox->x1; + rectangle->height = pbox->y2 - pbox->y1; } +slim_hidden_def (cairo_region_get_rectangle); /** - * _cairo_region_get_extents: + * cairo_region_get_extents: * @region: a #cairo_region_t - * @rect: rectangle into which to store the extents + * @rectangle: rectangle into which to store the extents * - * Gets the bounding box of a region as a #cairo_rectangle_int_t + * Gets the bounding rectangle of @region as a #cairo_rectangle_int_t + * + * Since: 1.10 **/ void -_cairo_region_get_extents (cairo_region_t *region, cairo_rectangle_int_t *extents) +cairo_region_get_extents (cairo_region_t *region, + cairo_rectangle_int_t *extents) { - pixman_box32_t *pextents = pixman_region32_extents (®ion->rgn); + pixman_box32_t *pextents; + + if (region->status) { + extents->x = extents->y = 0; + extents->width = extents->height = 0; + return; + } + + pextents = pixman_region32_extents (®ion->rgn); extents->x = pextents->x1; extents->y = pextents->y1; extents->width = pextents->x2 - pextents->x1; extents->height = pextents->y2 - pextents->y1; } +slim_hidden_def (cairo_region_get_extents); -cairo_int_status_t -_cairo_region_subtract (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b) +/** + * cairo_region_status: + * @region: a #cairo_region_t + * + * Checks whether an error has previous occured for this + * region object. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_status (cairo_region_t *region) { - if (!pixman_region32_subtract (&dst->rgn, &a->rgn, &b->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + return region->status; +} +slim_hidden_def (cairo_region_status); + +/** + * cairo_region_subtract: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Subtracts @other from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_subtract); -cairo_int_status_t -_cairo_region_intersect (cairo_region_t *dst, cairo_region_t *a, cairo_region_t *b) +/** + * cairo_region_subtract_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Subtracts @rectangle from @dst and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) { - if (!pixman_region32_intersect (&dst->rgn, &a->rgn, &b->rgn)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_subtract (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_subtract_rectangle); + +/** + * cairo_region_intersect: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the intersection of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect (cairo_region_t *dst, cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_intersect); -cairo_int_status_t -_cairo_region_union_rect (cairo_region_t *dst, - cairo_region_t *src, - cairo_rectangle_int_t *rect) +/** + * cairo_region_intersect_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the intersection of @dst with @rectangle and places the + * result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) { - if (!pixman_region32_union_rect (&dst->rgn, &src->rgn, - rect->x, rect->y, - rect->width, rect->height)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_intersect (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_intersect_rectangle); + +/** + * cairo_region_union: + * @dst: a #cairo_region_t + * @other: another #cairo_region_t + * + * Computes the union of @dst with @other and places the result in @dst + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union (cairo_region_t *dst, + cairo_region_t *other) +{ + if (dst->status) + return dst->status; + + if (other->status) + return _cairo_region_set_error (dst, other->status); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, &other->rgn)) + return _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_SUCCESS; } +slim_hidden_def (cairo_region_union); +/** + * cairo_region_union_rectangle: + * @dst: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Computes the union of @dst with @rectangle and places the result in @dst. + * + * Return value: %CAIRO_STATUS_SUCCESS or %CAIRO_STATUS_NO_MEMORY + * + * Since: 1.10 + **/ +cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle) +{ + cairo_status_t status = CAIRO_STATUS_SUCCESS; + pixman_region32_t region; + + if (dst->status) + return dst->status; + + pixman_region32_init_rect (®ion, + rectangle->x, rectangle->y, + rectangle->width, rectangle->height); + + if (! pixman_region32_union (&dst->rgn, &dst->rgn, ®ion)) + status = _cairo_region_set_error (dst, CAIRO_STATUS_NO_MEMORY); + + pixman_region32_fini (®ion); + + return status; +} +slim_hidden_def (cairo_region_union_rectangle); + +/** + * cairo_region_is_empty: + * @region: a #cairo_region_t + * + * Checks whether @region is empty. + * + * Return value: %TRUE if @region is empty, %FALSE if it isn't. + * + * Since: 1.10 + **/ cairo_bool_t -_cairo_region_not_empty (cairo_region_t *region) +cairo_region_is_empty (cairo_region_t *region) { - return (cairo_bool_t) pixman_region32_not_empty (®ion->rgn); -} + if (region->status) + return TRUE; + return ! pixman_region32_not_empty (®ion->rgn); +} +slim_hidden_def (cairo_region_is_empty); + +/** + * cairo_region_translate: + * @region: a #cairo_region_t + * @dx: Amount to translate in the x direction + * @dy: Amount to translate in the y direction + * + * Translates @region by (@dx, @dy). + * + * Since: 1.10 + **/ void -_cairo_region_translate (cairo_region_t *region, - int x, int y) +cairo_region_translate (cairo_region_t *region, + int dx, int dy) { - pixman_region32_translate (®ion->rgn, x, y); -} + if (region->status) + return; -pixman_region_overlap_t -_cairo_region_contains_rectangle (cairo_region_t *region, - const cairo_rectangle_int_t *rect) + pixman_region32_translate (®ion->rgn, dx, dy); +} +slim_hidden_def (cairo_region_translate); + +/** + * cairo_region_contains_rectangle: + * @region: a #cairo_region_t + * @rectangle: a #cairo_rectangle_int_t + * + * Checks whether @rectangle is inside, outside or partially contained + * in @region + * + * Return value: + * %CAIRO_REGION_OVERLAP_IN if @rectangle is entirely inside @region, + * %CAIRO_REGION_OVERLAP_OUT if @rectangle is entirely outside @region, or + * %CAIRO_REGION_OVERLAP_PART if @rectangle is partially inside and partially outside @region. + * + * Since: 1.10 + **/ +cairo_region_overlap_t +cairo_region_contains_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle) { pixman_box32_t pbox; + pixman_region_overlap_t poverlap; - pbox.x1 = rect->x; - pbox.y1 = rect->y; - pbox.x2 = rect->x + rect->width; - pbox.y2 = rect->y + rect->height; + if (region->status) + return CAIRO_REGION_OVERLAP_OUT; - return pixman_region32_contains_rectangle (®ion->rgn, &pbox); + pbox.x1 = rectangle->x; + pbox.y1 = rectangle->y; + pbox.x2 = rectangle->x + rectangle->width; + pbox.y2 = rectangle->y + rectangle->height; + + poverlap = pixman_region32_contains_rectangle (®ion->rgn, &pbox); + switch (poverlap) { + default: + case PIXMAN_REGION_OUT: return CAIRO_REGION_OVERLAP_OUT; + case PIXMAN_REGION_IN: return CAIRO_REGION_OVERLAP_IN; + case PIXMAN_REGION_PART: return CAIRO_REGION_OVERLAP_PART; + } } +slim_hidden_def (cairo_region_contains_rectangle); + +/** + * cairo_region_contains_point: + * @region: a #cairo_region_t + * @x: the x coordinate of a point + * @y: the y coordinate of a point + * + * Checks whether (@x, @y) is contained in @region. + * + * Return value: %TRUE if (@x, @y) is contained in @region, %FALSE if it is not. + * + * Since: 1.10 + **/ +cairo_bool_t +cairo_region_contains_point (cairo_region_t *region, + int x, int y) +{ + pixman_box32_t box; + + if (region->status) + return FALSE; + + return pixman_region32_contains_point (®ion->rgn, x, y, &box); +} +slim_hidden_def (cairo_region_contains_point); diff --git a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c index c802c31434ed..02b32aa7f5d8 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font-subsets.c @@ -372,13 +372,16 @@ _cairo_sub_font_glyph_lookup_unicode (cairo_sub_font_glyph_t *sub_font_glyph, return CAIRO_STATUS_SUCCESS; } -static cairo_bool_t +static cairo_status_t _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, const char *utf8, - int utf8_len) + int utf8_len, + cairo_bool_t *is_mapped) { + *is_mapped = FALSE; + if (utf8_len < 0) - return FALSE; + return CAIRO_STATUS_SUCCESS; if (utf8 != NULL && utf8_len != 0 && utf8[utf8_len - 1] == '\0') utf8_len--; @@ -389,28 +392,25 @@ _cairo_sub_font_glyph_map_to_unicode (cairo_sub_font_glyph_t *sub_font_glyph, memcmp (utf8, sub_font_glyph->utf8, utf8_len) == 0) { /* Requested utf8 mapping matches the existing mapping */ - return TRUE; - } - else - { - /* Requested utf8 mapping does not match the existing mapping */ - return FALSE; + *is_mapped = TRUE; } } else { /* No existing mapping. Use the requested mapping */ sub_font_glyph->utf8 = malloc (utf8_len + 1); + if (unlikely (sub_font_glyph->utf8 == NULL)) + return CAIRO_STATUS_NO_MEMORY; + memcpy (sub_font_glyph->utf8, utf8, utf8_len); sub_font_glyph->utf8[utf8_len] = 0; sub_font_glyph->utf8_len = utf8_len; - return TRUE; + *is_mapped = TRUE; } } - /* No mapping was requested. */ - return FALSE; + return CAIRO_STATUS_SUCCESS; } -static cairo_bool_t +static cairo_int_status_t _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, unsigned long scaled_font_glyph_index, const char *utf8, @@ -418,6 +418,7 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, cairo_scaled_font_subsets_glyph_t *subset_glyph) { cairo_sub_font_glyph_t key, *sub_font_glyph; + cairo_int_status_t status; _cairo_sub_font_glyph_init_key (&key, scaled_font_glyph_index); sub_font_glyph = _cairo_hash_table_lookup (sub_font->sub_font_glyphs, @@ -430,13 +431,15 @@ _cairo_sub_font_lookup_glyph (cairo_sub_font_t *sub_font, subset_glyph->is_composite = sub_font->is_composite; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; - subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len); + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; - return TRUE; + return status; } - return FALSE; + return CAIRO_INT_STATUS_UNSUPPORTED; } static cairo_status_t @@ -524,10 +527,12 @@ _cairo_sub_font_map_glyph (cairo_sub_font_t *sub_font, subset_glyph->is_composite = sub_font->is_composite; subset_glyph->x_advance = sub_font_glyph->x_advance; subset_glyph->y_advance = sub_font_glyph->y_advance; - subset_glyph->utf8_is_mapped = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, utf8, utf8_len); + status = _cairo_sub_font_glyph_map_to_unicode (sub_font_glyph, + utf8, utf8_len, + &subset_glyph->utf8_is_mapped); subset_glyph->unicode = sub_font_glyph->unicode; - return CAIRO_STATUS_SUCCESS; + return status; } static void @@ -539,6 +544,10 @@ _cairo_sub_font_collect (void *entry, void *closure) int i; unsigned int j; + if (collection->status) + return; + + collection->status = sub_font->scaled_font->status; if (collection->status) return; @@ -682,11 +691,12 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, sub_font = _cairo_hash_table_lookup (subsets->unscaled_sub_fonts, &key.base); if (sub_font != NULL) { - if (_cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph)) - return CAIRO_STATUS_SUCCESS; + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; } } @@ -696,11 +706,12 @@ _cairo_scaled_font_subsets_map_glyph (cairo_scaled_font_subsets_t *subsets, sub_font = _cairo_hash_table_lookup (subsets->scaled_sub_fonts, &key.base); if (sub_font != NULL) { - if (_cairo_sub_font_lookup_glyph (sub_font, - scaled_font_glyph_index, - utf8, utf8_len, - subset_glyph)) - return CAIRO_STATUS_SUCCESS; + status = _cairo_sub_font_lookup_glyph (sub_font, + scaled_font_glyph_index, + utf8, utf8_len, + subset_glyph); + if (status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; } /* Glyph not found. Determine whether the glyph is outline or @@ -1021,7 +1032,7 @@ _cairo_scaled_font_subset_create_glyph_names (cairo_scaled_font_subset_t *subset if (utf8 && *utf8) { status = _cairo_utf8_to_utf16 (utf8, -1, &utf16, &utf16_len); if (unlikely (status)) - return status; /* FIXME */ + goto CLEANUP_HASH; } if (utf16_len == 1) { diff --git a/gfx/cairo/cairo/src/cairo-scaled-font.c b/gfx/cairo/cairo/src/cairo-scaled-font.c index aa7fc11a39ba..d39f651581ae 100644 --- a/gfx/cairo/cairo/src/cairo-scaled-font.c +++ b/gfx/cairo/cairo/src/cairo-scaled-font.c @@ -2558,6 +2558,9 @@ _cairo_scaled_glyph_lookup (cairo_scaled_font_t *scaled_font, if (unlikely (scaled_font->status)) return scaled_font->status; + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + /* * Check cache for glyph */ diff --git a/gfx/cairo/cairo/src/cairo-spans.c b/gfx/cairo/cairo/src/cairo-spans.c index b6ac2c564260..625f83f1204f 100644 --- a/gfx/cairo/cairo/src/cairo-spans.c +++ b/gfx/cairo/cairo/src/cairo-spans.c @@ -255,6 +255,7 @@ _cairo_scan_converter_create_in_error (cairo_status_t status) } switch (status) { case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; break; case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; @@ -359,6 +360,7 @@ _cairo_span_renderer_create_in_error (cairo_status_t status) } switch (status) { case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; break; case CAIRO_STATUS_INVALID_RESTORE: RETURN_NIL; diff --git a/gfx/cairo/cairo/src/cairo-spline.c b/gfx/cairo/cairo/src/cairo-spline.c index 948516e1f0a0..45eedbd6aa1e 100644 --- a/gfx/cairo/cairo/src/cairo-spline.c +++ b/gfx/cairo/cairo/src/cairo-spline.c @@ -289,7 +289,7 @@ _cairo_spline_bound (cairo_spline_add_point_func_t add_point_func, * 0 < (-b±√delta)/a < 1 \ */ \ if (_2ab >= 0) \ - feasible = delta > b2 && delta < a*a + b2 - _2ab; \ + feasible = delta > b2 && delta < a*a + b2 + _2ab; \ else if (-b / a >= 1) \ feasible = delta < b2 && delta > a*a + b2 + _2ab; \ else \ diff --git a/gfx/cairo/cairo/src/cairo-stroke-style.c b/gfx/cairo/cairo/src/cairo-stroke-style.c index d808ad8bcdce..462b868a4f23 100644 --- a/gfx/cairo/cairo/src/cairo-stroke-style.c +++ b/gfx/cairo/cairo/src/cairo-stroke-style.c @@ -52,6 +52,9 @@ cairo_status_t _cairo_stroke_style_init_copy (cairo_stroke_style_t *style, cairo_stroke_style_t *other) { + if (CAIRO_INJECT_FAULT ()) + return _cairo_error (CAIRO_STATUS_NO_MEMORY); + style->line_width = other->line_width; style->line_cap = other->line_cap; style->line_join = other->line_join; diff --git a/gfx/cairo/cairo/src/cairo-surface-fallback.c b/gfx/cairo/cairo/src/cairo-surface-fallback.c index 7c38cbdfe7fc..a78c6ad7b7ee 100644 --- a/gfx/cairo/cairo/src/cairo-surface-fallback.c +++ b/gfx/cairo/cairo/src/cairo-surface-fallback.c @@ -417,7 +417,7 @@ _composite_trap_region (cairo_clip_t *clip, cairo_status_t status; cairo_solid_pattern_t solid_pattern; cairo_surface_pattern_t mask; - int num_rects = _cairo_region_num_boxes (trap_region); + int num_rects = cairo_region_num_rectangles (trap_region); unsigned int clip_serial; cairo_surface_t *clip_surface = clip ? clip->surface : NULL; @@ -520,10 +520,8 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, cairo_antialias_t antialias) { cairo_status_t status; - cairo_region_t trap_region; - cairo_region_t clear_region; - cairo_bool_t has_trap_region = FALSE; - cairo_bool_t has_clear_region = FALSE; + cairo_region_t *trap_region = NULL; + cairo_region_t *clear_region = NULL; cairo_rectangle_int_t extents; cairo_composite_traps_info_t traps_info; @@ -535,23 +533,18 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, return status; status = _cairo_traps_extract_region (traps, &trap_region); - if (CAIRO_INT_STATUS_UNSUPPORTED == status) { - has_trap_region = FALSE; - } else if (status) { - return status; - } else { - has_trap_region = TRUE; - } + if (status && status != CAIRO_INT_STATUS_UNSUPPORTED) + return status; if (_cairo_operator_bounded_by_mask (op)) { cairo_rectangle_int_t trap_extents; - if (has_trap_region) { - status = _cairo_clip_intersect_to_region (clip, &trap_region); + if (trap_region) { + status = _cairo_clip_intersect_to_region (clip, trap_region); if (unlikely (status)) goto out; - _cairo_region_get_extents (&trap_region, &trap_extents); + cairo_region_get_extents (trap_region, &trap_extents); } else { cairo_box_t trap_box; _cairo_traps_extents (traps, &trap_box); @@ -569,27 +562,30 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, } else { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; - if (has_trap_region && !clip_surface) { + if (trap_region && !clip_surface) { /* If we optimize drawing with an unbounded operator to * _cairo_surface_fill_rectangles() or to drawing with a * clip region, then we have an additional region to clear. */ - _cairo_region_init_rect (&clear_region, &extents); + clear_region = cairo_region_create_rectangle (&extents); - has_clear_region = TRUE; - status = _cairo_clip_intersect_to_region (clip, &clear_region); + status = cairo_region_status (clear_region); + if (unlikely (status)) + goto out; + + status = _cairo_clip_intersect_to_region (clip, clear_region); if (unlikely (status)) goto out; - _cairo_region_get_extents (&clear_region, &extents); + cairo_region_get_extents (clear_region, &extents); - status = _cairo_region_subtract (&clear_region, &clear_region, &trap_region); + status = cairo_region_subtract (clear_region, trap_region); if (unlikely (status)) goto out; - if (!_cairo_region_not_empty (&clear_region)) { - _cairo_region_fini (&clear_region); - has_clear_region = FALSE; + if (cairo_region_is_empty (clear_region)) { + cairo_region_destroy (clear_region); + clear_region = NULL; } } else { status = _cairo_clip_intersect_to_rectangle (clip, &extents); @@ -599,7 +595,7 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, if (unlikely (status)) goto out; - if (has_trap_region) { + if (trap_region) { cairo_surface_t *clip_surface = clip ? clip->surface : NULL; if ((src->type == CAIRO_PATTERN_TYPE_SOLID || @@ -613,12 +609,13 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, } /* Solid rectangles special case */ - status = _cairo_surface_fill_region (dst, op, color, &trap_region); + status = _cairo_surface_fill_region (dst, op, color, trap_region); - if (!status && has_clear_region) + if (!status && clear_region) { status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); + } goto out; } @@ -641,13 +638,13 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, * regions. In that case, we fall through. */ status = _composite_trap_region (clip, src, op, dst, - &trap_region, &extents); + trap_region, &extents); if (status != CAIRO_INT_STATUS_UNSUPPORTED) { - if (!status && has_clear_region) + if (!status && clear_region) status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_CLEAR, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); goto out; } } @@ -661,10 +658,10 @@ _clip_and_composite_trapezoids (const cairo_pattern_t *src, &traps_info, dst, &extents); out: - if (has_trap_region) - _cairo_region_fini (&trap_region); - if (has_clear_region) - _cairo_region_fini (&clear_region); + if (trap_region) + cairo_region_destroy (trap_region); + if (clear_region) + cairo_region_destroy (clear_region); return status; } diff --git a/gfx/cairo/cairo/src/cairo-surface.c b/gfx/cairo/cairo/src/cairo-surface.c index e8e102313115..0696a0b4de2f 100644 --- a/gfx/cairo/cairo/src/cairo-surface.c +++ b/gfx/cairo/cairo/src/cairo-surface.c @@ -1637,14 +1637,13 @@ _cairo_surface_fill_rectangle (cairo_surface_t *surface, * * Return value: %CAIRO_STATUS_SUCCESS or the error that occurred **/ -COMPILE_TIME_ASSERT (sizeof (cairo_box_int_t) <= sizeof (cairo_rectangle_int_t)); cairo_status_t _cairo_surface_fill_region (cairo_surface_t *surface, cairo_operator_t op, const cairo_color_t *color, cairo_region_t *region) { - int num_boxes; + int num_rects; cairo_rectangle_int_t stack_rects[CAIRO_STACK_ARRAY_LENGTH (cairo_rectangle_int_t)]; cairo_rectangle_int_t *rects = stack_rects; cairo_status_t status; @@ -1655,12 +1654,12 @@ _cairo_surface_fill_region (cairo_surface_t *surface, assert (! surface->is_snapshot); - num_boxes = _cairo_region_num_boxes (region); - if (num_boxes == 0) + num_rects = cairo_region_num_rectangles (region); + if (num_rects == 0) return CAIRO_STATUS_SUCCESS; - if (num_boxes > ARRAY_LENGTH (stack_rects)) { - rects = _cairo_malloc_ab (num_boxes, + if (num_rects > ARRAY_LENGTH (stack_rects)) { + rects = _cairo_malloc_ab (num_rects, sizeof (cairo_rectangle_int_t)); if (rects == NULL) { return _cairo_surface_set_error (surface, @@ -1668,19 +1667,11 @@ _cairo_surface_fill_region (cairo_surface_t *surface, } } - for (i = 0; i < num_boxes; i++) { - cairo_box_int_t box; - - _cairo_region_get_box (region, i, &box); - - rects[i].x = box.p1.x; - rects[i].y = box.p1.y; - rects[i].width = box.p2.x - rects[i].x; - rects[i].height = box.p2.y - rects[i].y; - } + for (i = 0; i < num_rects; i++) + cairo_region_get_rectangle (region, i, &rects[i]); status = _cairo_surface_fill_rectangles (surface, op, - color, rects, num_boxes); + color, rects, num_rects); if (rects != stack_rects) free (rects); @@ -2268,10 +2259,10 @@ _cairo_surface_reset_clip (cairo_surface_t *surface) cairo_status_t _cairo_surface_set_clip_region (cairo_surface_t *surface, cairo_region_t *region, - unsigned int serial) + unsigned int serial) { cairo_status_t status; - + if (surface->status) return surface->status; @@ -2448,7 +2439,7 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) if (surface->backend->set_clip_region != NULL) return _cairo_surface_set_clip_region (surface, - &clip->region, + clip->region, clip->serial); } else { if (clip->path) @@ -2456,9 +2447,9 @@ _cairo_surface_set_clip (cairo_surface_t *surface, cairo_clip_t *clip) clip->path, clip->serial); - if (clip->has_region) + if (clip->region) return _cairo_surface_set_clip_region (surface, - &clip->region, + clip->region, clip->serial); } } @@ -2754,53 +2745,44 @@ _cairo_surface_composite_fixup_unbounded_internal (cairo_surface_t *dst, unsigned int height) { cairo_rectangle_int_t dst_rectangle; - cairo_rectangle_int_t drawn_rectangle; - cairo_bool_t has_drawn_region = FALSE; - cairo_region_t drawn_region; - cairo_region_t clear_region; + cairo_region_t *clear_region; cairo_status_t status; - /* The area that was drawn is the area in the destination rectangle but not within - * the source or the mask. + /* The area that was drawn is the area in the destination rectangle but + * not within the source or the mask. */ dst_rectangle.x = dst_x; dst_rectangle.y = dst_y; dst_rectangle.width = width; dst_rectangle.height = height; - _cairo_region_init_rect (&clear_region, &dst_rectangle); - drawn_rectangle = dst_rectangle; + clear_region = cairo_region_create_rectangle (&dst_rectangle); + status = clear_region->status; + if (unlikely (status)) + goto CLEANUP_REGIONS; if (src_rectangle) { - if (! _cairo_rectangle_intersect (&drawn_rectangle, src_rectangle)) + if (! _cairo_rectangle_intersect (&dst_rectangle, src_rectangle)) goto EMPTY; } if (mask_rectangle) { - if (! _cairo_rectangle_intersect (&drawn_rectangle, mask_rectangle)) + if (! _cairo_rectangle_intersect (&dst_rectangle, mask_rectangle)) goto EMPTY; } - /* Now compute the area that is in dst_rectangle but not in drawn_rectangle - */ - _cairo_region_init_rect (&drawn_region, &drawn_rectangle); - has_drawn_region = TRUE; - - status = _cairo_region_subtract (&clear_region, - &clear_region, - &drawn_region); + /* Now compute the area that is in dst but not drawn */ + status = cairo_region_subtract_rectangle (clear_region, &dst_rectangle); if (unlikely (status)) goto CLEANUP_REGIONS; EMPTY: status = _cairo_surface_fill_region (dst, CAIRO_OPERATOR_SOURCE, CAIRO_COLOR_TRANSPARENT, - &clear_region); + clear_region); CLEANUP_REGIONS: - if (has_drawn_region) - _cairo_region_fini (&drawn_region); - _cairo_region_fini (&clear_region); + cairo_region_destroy (clear_region); return _cairo_surface_set_error (dst, status); } @@ -3034,6 +3016,7 @@ _cairo_surface_create_in_error (cairo_status_t status) case CAIRO_STATUS_INVALID_STRIDE: return (cairo_surface_t *) &_cairo_surface_nil_invalid_stride; case CAIRO_STATUS_SUCCESS: + case CAIRO_STATUS_LAST_STATUS: ASSERT_NOT_REACHED; /* fall-through */ case CAIRO_STATUS_INVALID_RESTORE: diff --git a/gfx/cairo/cairo/src/cairo-svg-surface.c b/gfx/cairo/cairo/src/cairo-svg-surface.c index db554a101d63..7a35fab9f3d1 100644 --- a/gfx/cairo/cairo/src/cairo-svg-surface.c +++ b/gfx/cairo/cairo/src/cairo-svg-surface.c @@ -1025,6 +1025,8 @@ _cairo_surface_base64_encode_png (cairo_surface_t *surface, cairo_surface_get_mime_data (surface, CAIRO_MIME_TYPE_PNG, &mime_data, &mime_data_length); + if (unlikely (surface->status)) + return surface->status; if (mime_data == NULL) return CAIRO_INT_STATUS_UNSUPPORTED; diff --git a/gfx/cairo/cairo/src/cairo-traps.c b/gfx/cairo/cairo/src/cairo-traps.c index 0afdce23aaf6..fed3f101c806 100644 --- a/gfx/cairo/cairo/src/cairo-traps.c +++ b/gfx/cairo/cairo/src/cairo-traps.c @@ -131,6 +131,11 @@ _cairo_traps_grow (cairo_traps_t *traps) cairo_trapezoid_t *new_traps; int new_size = 2 * MAX (traps->traps_size, 16); + if (CAIRO_INJECT_FAULT ()) { + traps->status = _cairo_error (CAIRO_STATUS_NO_MEMORY); + return FALSE; + } + if (traps->traps == traps->traps_embedded) { new_traps = _cairo_malloc_ab (new_size, sizeof (cairo_trapezoid_t)); if (new_traps != NULL) @@ -600,7 +605,7 @@ _cairo_traps_extents (const cairo_traps_t *traps, * Determines if a set of trapezoids are exactly representable as a * cairo region. If so, the passed-in region is initialized to * the area representing the given traps. It should be finalized - * with _cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED + * with cairo_region_fini(). If not, %CAIRO_INT_STATUS_UNSUPPORTED * is returned. * * Return value: %CAIRO_STATUS_SUCCESS, %CAIRO_INT_STATUS_UNSUPPORTED @@ -608,17 +613,11 @@ _cairo_traps_extents (const cairo_traps_t *traps, **/ cairo_int_status_t _cairo_traps_extract_region (const cairo_traps_t *traps, - cairo_region_t *region) + cairo_region_t **region) { - cairo_box_int_t stack_boxes[CAIRO_STACK_ARRAY_LENGTH (cairo_box_int_t)]; - cairo_box_int_t *boxes = stack_boxes; - int i, box_count; cairo_int_status_t status; - - if (traps->num_traps == 0) { - _cairo_region_init (region); - return CAIRO_STATUS_SUCCESS; - } + cairo_region_t *r; + int i; for (i = 0; i < traps->num_traps; i++) { if (traps->traps[i].left.p1.x != traps->traps[i].left.p2.x || @@ -632,16 +631,13 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, } } - if (traps->num_traps > ARRAY_LENGTH (stack_boxes)) { - boxes = _cairo_malloc_ab (traps->num_traps, sizeof (cairo_box_int_t)); - - if (unlikely (boxes == NULL)) - return _cairo_error (CAIRO_STATUS_NO_MEMORY); - } - - box_count = 0; + r = cairo_region_create (); + if (unlikely (r->status)) + return r->status; for (i = 0; i < traps->num_traps; i++) { + cairo_rectangle_int_t rect; + int x1 = _cairo_fixed_integer_part (traps->traps[i].left.p1.x); int y1 = _cairo_fixed_integer_part (traps->traps[i].top); int x2 = _cairo_fixed_integer_part (traps->traps[i].right.p1.x); @@ -653,23 +649,20 @@ _cairo_traps_extract_region (const cairo_traps_t *traps, if (x1 == x2 || y1 == y2) continue; - boxes[box_count].p1.x = x1; - boxes[box_count].p1.y = y1; - boxes[box_count].p2.x = x2; - boxes[box_count].p2.y = y2; + rect.x = x1; + rect.y = y1; + rect.width = x2 - x1; + rect.height = y2 - y1; - box_count++; + status = cairo_region_union_rectangle (r, &rect); + if (unlikely (status)) { + cairo_region_destroy (r); + return status; + } } - status = _cairo_region_init_boxes (region, boxes, box_count); - - if (boxes != stack_boxes) - free (boxes); - - if (unlikely (status)) - _cairo_region_fini (region); - - return status; + *region = r; + return CAIRO_STATUS_SUCCESS; } /* moves trap points such that they become the actual corners of the trapezoid */ diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset-private.h b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h index 397a9f389207..978256f6b45e 100644 --- a/gfx/cairo/cairo/src/cairo-truetype-subset-private.h +++ b/gfx/cairo/cairo/src/cairo-truetype-subset-private.h @@ -185,7 +185,7 @@ typedef struct _tt_name { typedef struct _tt_composite_glyph { uint16_t flags; uint16_t index; - uint16_t args[7]; /* 1 to 7 arguments depending on value of flags */ + uint16_t args[6]; /* 1 to 6 arguments depending on value of flags */ } tt_composite_glyph_t; typedef struct _tt_glyph_data { diff --git a/gfx/cairo/cairo/src/cairo-truetype-subset.c b/gfx/cairo/cairo/src/cairo-truetype-subset.c index 31e397109538..b3de972a9dd1 100644 --- a/gfx/cairo/cairo/src/cairo-truetype-subset.c +++ b/gfx/cairo/cairo/src/cairo-truetype-subset.c @@ -105,8 +105,8 @@ check (tt_maxp_t, 32); check (tt_name_record_t, 12); check (tt_name_t, 18); check (tt_name_t, 18); -check (tt_composite_glyph_t, 18); -check (tt_glyph_data_t, 28); +check (tt_composite_glyph_t, 16); +check (tt_glyph_data_t, 26); #undef check static cairo_status_t @@ -495,7 +495,7 @@ cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, composite_glyph = &glyph_data->glyph; do { - if ((unsigned char *)(&composite_glyph->args[1]) >= end) + if ((unsigned char *)(&composite_glyph->args[1]) > end) return CAIRO_INT_STATUS_UNSUPPORTED; flags = be16_to_cpu (composite_glyph->flags); @@ -508,13 +508,15 @@ cairo_truetype_font_remap_composite_glyph (cairo_truetype_font_t *font, num_args = 1; if (flags & TT_ARG_1_AND_2_ARE_WORDS) num_args += 1; - if (flags & TT_WE_HAVE_A_SCALE) + + if (flags & TT_WE_HAVE_A_SCALE) num_args += 1; else if (flags & TT_WE_HAVE_AN_X_AND_Y_SCALE) num_args += 2; else if (flags & TT_WE_HAVE_A_TWO_BY_TWO) - num_args += 3; - composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); + num_args += 4; + + composite_glyph = (tt_composite_glyph_t *) &(composite_glyph->args[num_args]); } while (has_more_components); return CAIRO_STATUS_SUCCESS; @@ -1329,8 +1331,8 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, tt_name_record_t *record; unsigned long size; int i, j; - char *ps_name; - char *font_name; + char *ps_name = NULL; + char *font_name = NULL; backend = scaled_font->backend; if (!backend->load_truetype_table) @@ -1360,8 +1362,6 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, * name. It should be extended to use any suitable font name in * the name table. */ - ps_name = NULL; - font_name = NULL; for (i = 0; i < be16_to_cpu(name->num_records); i++) { record = &(name->records[i]); if ((be16_to_cpu (record->platform) == 1) && @@ -1415,6 +1415,13 @@ _cairo_truetype_read_font_name (cairo_scaled_font_t *scaled_font, fail: free (name); + + if (ps_name != NULL) + free (ps_name); + + if (font_name != NULL) + free (font_name); + *ps_name_out = NULL; *font_name_out = NULL; diff --git a/gfx/cairo/cairo/src/cairo-type1-fallback.c b/gfx/cairo/cairo/src/cairo-type1-fallback.c index 2b6768dc00bd..1023bbdcb28e 100644 --- a/gfx/cairo/cairo/src/cairo-type1-fallback.c +++ b/gfx/cairo/cairo/src/cairo-type1-fallback.c @@ -449,14 +449,14 @@ cairo_type1_font_write_charstrings (cairo_type1_font_t *font, /* four "random" bytes required by encryption algorithm */ status = _cairo_array_append_multiple (&data, zeros, 4); if (unlikely (status)) - goto fail; + break; status = cairo_type1_font_create_charstring (font, i, font->scaled_font_subset->glyphs[i], CAIRO_CHARSTRING_TYPE1, &data); if (unlikely (status)) - goto fail; + break; charstring_encrypt (&data); length = _cairo_array_num_elements (&data); @@ -474,9 +474,9 @@ cairo_type1_font_write_charstrings (cairo_type1_font_t *font, length); _cairo_output_stream_printf (encrypted_output, " ND\n"); } + _cairo_scaled_font_thaw_cache (font->type1_scaled_font); fail: - _cairo_scaled_font_thaw_cache (font->type1_scaled_font); _cairo_array_fini (&data); return status; } diff --git a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c index 3ff5003bf3b1..104da53d8c69 100644 --- a/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c +++ b/gfx/cairo/cairo/src/cairo-type3-glyph-surface.c @@ -46,14 +46,17 @@ static const cairo_surface_backend_t cairo_type3_glyph_surface_backend; cairo_surface_t * -_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, - cairo_output_stream_t *stream, +_cairo_type3_glyph_surface_create (cairo_scaled_font_t *scaled_font, + cairo_output_stream_t *stream, cairo_type3_glyph_surface_emit_image_t emit_image, - cairo_scaled_font_subsets_t *font_subsets) + cairo_scaled_font_subsets_t *font_subsets) { cairo_type3_glyph_surface_t *surface; cairo_matrix_t invert_y_axis; + if (unlikely (stream != NULL && stream->status)) + return _cairo_surface_create_in_error (stream->status); + surface = malloc (sizeof (cairo_type3_glyph_surface_t)); if (unlikely (surface == NULL)) return _cairo_surface_create_in_error (_cairo_error (CAIRO_STATUS_NO_MEMORY)); @@ -88,6 +91,12 @@ _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, { cairo_status_t status; + /* The only image type supported by Type 3 fonts are 1-bit masks */ + image = _cairo_image_surface_coerce (image, CAIRO_FORMAT_A1); + status = image->base.status; + if (unlikely (status)) + return status; + _cairo_output_stream_printf (surface->stream, "q %f %f %f %f %f %f cm\n", image_matrix->xx, @@ -97,8 +106,6 @@ _cairo_type3_glyph_surface_emit_image (cairo_type3_glyph_surface_t *surface, image_matrix->x0, image_matrix->y0); - /* The only image type supported by Type 3 fonts are 1-bit masks */ - image = _cairo_image_surface_coerce (image, CAIRO_FORMAT_A1); status = surface->emit_image (image, surface->stream); cairo_surface_destroy (&image->base); @@ -278,6 +285,8 @@ _cairo_type3_glyph_surface_show_glyphs (void *abstract_surface, &scaled_font->font_matrix, &new_ctm, &scaled_font->options); + if (unlikely (font->status)) + return font->status; status = _cairo_pdf_operators_show_text_glyphs (&surface->pdf_operators, NULL, 0, @@ -375,6 +384,9 @@ _cairo_type3_glyph_surface_set_font_subsets_callback (void *abstract { cairo_type3_glyph_surface_t *surface = abstract_surface; + if (unlikely (surface->base.status)) + return; + _cairo_pdf_operators_set_font_subsets_callback (&surface->pdf_operators, use_font_subset, closure); @@ -389,7 +401,13 @@ _cairo_type3_glyph_surface_analyze_glyph (void *abstract_surface, cairo_status_t status, status2; cairo_output_stream_t *null_stream; + if (unlikely (surface->base.status)) + return surface->base.status; + null_stream = _cairo_null_stream_create (); + if (unlikely (null_stream->status)) + return null_stream->status; + _cairo_type3_glyph_surface_set_stream (surface, null_stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); @@ -442,6 +460,9 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, double x_advance, y_advance; cairo_matrix_t font_matrix_inverse; + if (unlikely (surface->base.status)) + return surface->base.status; + _cairo_type3_glyph_surface_set_stream (surface, stream); _cairo_scaled_font_freeze_cache (surface->scaled_font); @@ -492,6 +513,10 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, cairo_output_stream_t *mem_stream; mem_stream = _cairo_memory_stream_create (); + status = mem_stream->status; + if (unlikely (status)) + goto FAIL; + _cairo_type3_glyph_surface_set_stream (surface, mem_stream); _cairo_output_stream_printf (surface->stream, "q\n"); @@ -516,6 +541,7 @@ _cairo_type3_glyph_surface_emit_glyph (void *abstract_surface, if (status == CAIRO_INT_STATUS_IMAGE_FALLBACK) status = _cairo_type3_glyph_surface_emit_fallback_image (surface, glyph_index); + FAIL: _cairo_scaled_font_thaw_cache (surface->scaled_font); return status; diff --git a/gfx/cairo/cairo/src/cairo-types-private.h b/gfx/cairo/cairo/src/cairo-types-private.h index 0a3ec2e4fa11..bb62351dad8a 100644 --- a/gfx/cairo/cairo/src/cairo-types-private.h +++ b/gfx/cairo/cairo/src/cairo-types-private.h @@ -59,7 +59,6 @@ typedef struct _cairo_output_stream cairo_output_stream_t; typedef struct _cairo_paginated_surface_backend cairo_paginated_surface_backend_t; typedef struct _cairo_path_fixed cairo_path_fixed_t; typedef struct _cairo_rectangle_int16 cairo_glyph_size_t; -typedef struct _cairo_region cairo_region_t; typedef struct _cairo_scaled_font_backend cairo_scaled_font_backend_t; typedef struct _cairo_scaled_font_subsets cairo_scaled_font_subsets_t; typedef struct _cairo_solid_pattern cairo_solid_pattern_t; @@ -218,43 +217,12 @@ typedef struct _cairo_trapezoid { cairo_line_t left, right; } cairo_trapezoid_t; -struct _cairo_rectangle_int16 { - int16_t x, y; - uint16_t width, height; -}; - -struct _cairo_rectangle_int32 { - int32_t x, y; - uint32_t width, height; -}; - -struct _cairo_point_int16 { - int16_t x, y; -}; - -struct _cairo_point_int32 { - int32_t x, y; -}; - -#if CAIRO_FIXED_BITS == 32 && CAIRO_FIXED_FRAC_BITS >= 16 -typedef struct _cairo_rectangle_int16 cairo_rectangle_int_t; -typedef struct _cairo_point_int16 cairo_point_int_t; -#define CAIRO_RECT_INT_MIN (INT16_MIN >> (CAIRO_FIXED_FRAC_BITS - 16)) -#define CAIRO_RECT_INT_MAX (INT16_MAX >> (CAIRO_FIXED_FRAC_BITS - 16)) -#elif CAIRO_FIXED_BITS == 32 -typedef struct _cairo_rectangle_int32 cairo_rectangle_int_t; -typedef struct _cairo_point_int32 cairo_point_int_t; -#define CAIRO_RECT_INT_MIN (INT32_MIN >> CAIRO_FIXED_FRAC_BITS) -#define CAIRO_RECT_INT_MAX (INT32_MAX >> CAIRO_FIXED_FRAC_BITS) -#else -#error Not sure how to pick a cairo_rectangle_int_t and cairo_point_int_t for your CAIRO_FIXED_BITS! -#endif - -typedef struct _cairo_box_int { - cairo_point_int_t p1; - cairo_point_int_t p2; -} cairo_box_int_t; +typedef struct _cairo_point_int { + int x, y; +} cairo_point_int_t; +#define CAIRO_RECT_INT_MIN (INT_MIN >> CAIRO_FIXED_FRAC_BITS) +#define CAIRO_RECT_INT_MAX (INT_MAX >> CAIRO_FIXED_FRAC_BITS) /* Rectangles that take part in a composite operation. * diff --git a/gfx/cairo/cairo/src/cairo-win32-surface.c b/gfx/cairo/cairo/src/cairo-win32-surface.c index 7b398c686487..5fb02ab8bca2 100644 --- a/gfx/cairo/cairo/src/cairo-win32-surface.c +++ b/gfx/cairo/cairo/src/cairo-win32-surface.c @@ -1522,36 +1522,36 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, /* Then combine any new region with it */ if (region) { cairo_rectangle_int_t extents; - int num_boxes; + int num_rects; RGNDATA *data; size_t data_size; RECT *rects; int i; HRGN gdi_region; - cairo_box_int_t box0; + cairo_rectangle_int_t rect0; /* Create a GDI region for the cairo region */ - _cairo_region_get_extents (region, &extents); - num_boxes = _cairo_region_num_boxes (region); + cairo_region_get_extents (region, &extents); + num_rects = cairo_region_num_rectangles (region); - if (num_boxes == 1) - _cairo_region_get_box (region, 0, &box0); + if (num_rects == 1) + cairo_region_get_rectangle (region, 0, &rect0); - if (num_boxes == 1 && - box0.p1.x == 0 && - box0.p1.y == 0 && - box0.p2.x == surface->extents.width && - box0.p2.y == surface->extents.height) + if (num_rects == 1 && + rect0.x == 0 && + rect0.y == 0 && + rect0.width == surface->extents.width && + rect0.width == surface->extents.height) { gdi_region = NULL; SelectClipRgn (surface->dc, NULL); IntersectClipRect (surface->dc, - box0.p1.x, - box0.p1.y, - box0.p2.x, - box0.p2.y); + rect0.x, + rect0.y, + rect0.x + rect0.width, + rect0.y + rect0.height); } else { /* XXX see notes in _cairo_win32_save_initial_clip -- * this code will interact badly with a HDC which had an initial @@ -1560,7 +1560,7 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, * logical units (unlike IntersectClipRect). */ - data_size = sizeof (RGNDATAHEADER) + num_boxes * sizeof (RECT); + data_size = sizeof (RGNDATAHEADER) + num_rects * sizeof (RECT); data = malloc (data_size); if (!data) return _cairo_error(CAIRO_STATUS_NO_MEMORY); @@ -1568,22 +1568,22 @@ _cairo_win32_surface_set_clip_region (void *abstract_surface, data->rdh.dwSize = sizeof (RGNDATAHEADER); data->rdh.iType = RDH_RECTANGLES; - data->rdh.nCount = num_boxes; - data->rdh.nRgnSize = num_boxes * sizeof (RECT); + data->rdh.nCount = num_rects; + data->rdh.nRgnSize = num_rects * sizeof (RECT); data->rdh.rcBound.left = extents.x; data->rdh.rcBound.top = extents.y; data->rdh.rcBound.right = extents.x + extents.width; data->rdh.rcBound.bottom = extents.y + extents.height; - for (i = 0; i < num_boxes; i++) { - cairo_box_int_t box; + for (i = 0; i < num_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_get_box (region, i, &box); + cairo_region_get_rectangle (region, i, &rect); - rects[i].left = box.p1.x; - rects[i].top = box.p1.y; - rects[i].right = box.p2.x; - rects[i].bottom = box.p2.y; + rects[i].left = rect.x; + rects[i].top = rect.y; + rects[i].right = rect.x + rect.width; + rects[i].bottom = rect.y + rect.height; } gdi_region = ExtCreateRegion (NULL, data_size, data); diff --git a/gfx/cairo/cairo/src/cairo-xcb-surface.c b/gfx/cairo/cairo/src/cairo-xcb-surface.c index 17a09e3f105b..8d0090b055f7 100644 --- a/gfx/cairo/cairo/src/cairo-xcb-surface.c +++ b/gfx/cairo/cairo/src/cairo-xcb-surface.c @@ -1557,32 +1557,32 @@ _cairo_xcb_surface_set_clip_region (void *abstract_surface, } else { cairo_status_t status; xcb_rectangle_t *rects = NULL; - int n_boxes, i; + int n_rects, i; - n_boxes = _cairo_region_num_boxes (region); + n_rects = cairo_region_num_rectangles (region); - if (n_boxes > 0) { - rects = _cairo_malloc_ab (n_boxes, sizeof(xcb_rectangle_t)); + if (n_rects > 0) { + rects = _cairo_malloc_ab (n_rects, sizeof(xcb_rectangle_t)); if (rects == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); } else { rects = NULL; } - for (i = 0; i < n_boxes; i++) { - cairo_box_int_t box; + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_get_box (region, i, &box); + cairo_region_get_rectangle (region, i, &rect); - rects[i].x = box.p1.x; - rects[i].y = box.p1.y; - rects[i].width = box.p2.x - box.p1.x; - rects[i].height = box.p2.y - box.p1.y; + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; } surface->have_clip_rects = TRUE; surface->clip_rects = rects; - surface->num_clip_rects = n_boxes; + surface->num_clip_rects = n_rects; if (surface->gc) _cairo_xcb_surface_set_gc_clip_rects (surface); diff --git a/gfx/cairo/cairo/src/cairo-xlib-surface.c b/gfx/cairo/cairo/src/cairo-xlib-surface.c index ddcb60096cdd..cffd85e7516d 100644 --- a/gfx/cairo/cairo/src/cairo-xlib-surface.c +++ b/gfx/cairo/cairo/src/cairo-xlib-surface.c @@ -2271,7 +2271,32 @@ _cairo_xlib_surface_composite_trapezoids (cairo_operator_t op, return status; } -COMPILE_TIME_ASSERT (sizeof (XRectangle) <= sizeof (cairo_box_int_t)); +static cairo_region_t * +_surface_maybe_clip_region (cairo_xlib_surface_t *surface, + cairo_region_t *clip, + cairo_region_t *bounded) +{ + cairo_rectangle_int_t rect; + + cairo_region_get_extents (clip, &rect); + if (rect.x >= 0 && + rect.y >= 0 && + rect.x + rect.width <= surface->width && + rect.y + rect.height <= surface->height) + { + return clip; + } + + rect.x = rect.y = 0; + rect.width = surface->width; + rect.height = surface->height; + _cairo_region_init_rectangle (bounded, &rect); + + bounded->status = cairo_region_intersect (bounded, clip); + + return bounded; +} + static cairo_int_status_t _cairo_xlib_surface_set_clip_region (void *abstract_surface, cairo_region_t *region) @@ -2291,58 +2316,50 @@ _cairo_xlib_surface_set_clip_region (void *abstract_surface, surface->num_clip_rects = 0; if (region != NULL) { - cairo_status_t status; XRectangle *rects = NULL; - int n_boxes, i; - cairo_rectangle_int_t rect; + int n_rects, i; cairo_region_t bounded; - rect.x = rect.y = 0; - rect.width = surface->width; - rect.height = surface->height; - /* Intersect the region with the bounds of the surface. This * is necessary so we don't wrap around when we convert cairo's * 32 bit region into 16 bit rectangles. */ - _cairo_region_init_rect (&bounded, &rect); - status = _cairo_region_intersect (&bounded, &bounded, region); - if (unlikely (status)) { - _cairo_region_fini (&bounded); - return status; - } + region = _surface_maybe_clip_region (surface, region, &bounded); + if (unlikely (region->status)) + return region->status; - n_boxes = _cairo_region_num_boxes (&bounded); - - if (n_boxes > ARRAY_LENGTH (surface->embedded_clip_rects)) { - rects = _cairo_malloc_ab (n_boxes, sizeof (XRectangle)); + n_rects = cairo_region_num_rectangles (region); + if (n_rects > ARRAY_LENGTH (surface->embedded_clip_rects)) { + rects = _cairo_malloc_ab (n_rects, sizeof (XRectangle)); if (unlikely (rects == NULL)) { - _cairo_region_fini (&bounded); + if (unlikely (region == &bounded)) + _cairo_region_fini (&bounded); return _cairo_error (CAIRO_STATUS_NO_MEMORY); } } else { rects = surface->embedded_clip_rects; } - for (i = 0; i < n_boxes; i++) { - cairo_box_int_t box; + for (i = 0; i < n_rects; i++) { + cairo_rectangle_int_t rect; - _cairo_region_get_box (&bounded, i, &box); + cairo_region_get_rectangle (region, i, &rect); - rects[i].x = box.p1.x; - rects[i].y = box.p1.y; - rects[i].width = box.p2.x - rects[i].x; - rects[i].height = box.p2.y - rects[i].y; + rects[i].x = rect.x; + rects[i].y = rect.y; + rects[i].width = rect.width; + rects[i].height = rect.height; } - - _cairo_region_fini (&bounded); + + if (unlikely (region == &bounded)) + _cairo_region_fini (&bounded); surface->have_clip_rects = TRUE; surface->clip_rects = rects; - surface->num_clip_rects = n_boxes; + surface->num_clip_rects = n_rects; /* Discard the trivial clip rectangle that covers the entire surface */ - if (n_boxes == 1 && + if (n_rects == 1 && rects[0].x == 0 && rects[0].y == 0 && rects[0].width == surface->width && diff --git a/gfx/cairo/cairo/src/cairo.h b/gfx/cairo/cairo/src/cairo.h index 9a8cc5255b1e..51ebad4a5154 100644 --- a/gfx/cairo/cairo/src/cairo.h +++ b/gfx/cairo/cairo/src/cairo.h @@ -2353,10 +2353,88 @@ cairo_public void cairo_matrix_transform_point (const cairo_matrix_t *matrix, double *x, double *y); +/* Region functions */ + +typedef struct _cairo_region cairo_region_t; + +typedef struct _cairo_rectangle_int { + int x, y; + int width, height; +} cairo_rectangle_int_t; + +typedef enum _cairo_region_overlap { + CAIRO_REGION_OVERLAP_IN, /* completely inside region */ + CAIRO_REGION_OVERLAP_OUT, /* completely outside region */ + CAIRO_REGION_OVERLAP_PART /* partly inside region */ +} cairo_region_overlap_t; + +cairo_public cairo_region_t * +cairo_region_create (void); + +cairo_public cairo_region_t * +cairo_region_create_rectangle (const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_region_t * +cairo_region_copy (cairo_region_t *original); + +cairo_public void +cairo_region_destroy (cairo_region_t *region); + +cairo_public cairo_status_t +cairo_region_status (cairo_region_t *region); + +cairo_public void +cairo_region_get_extents (cairo_region_t *region, + cairo_rectangle_int_t *extents); + +cairo_public int +cairo_region_num_rectangles (cairo_region_t *region); + +cairo_public void +cairo_region_get_rectangle (cairo_region_t *region, + int nth_rectangle, + cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_is_empty (cairo_region_t *region); + +cairo_public cairo_region_overlap_t +cairo_region_contains_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_bool_t +cairo_region_contains_point (cairo_region_t *region, int x, int y); + +cairo_public void +cairo_region_translate (cairo_region_t *region, int dx, int dy); + +cairo_public cairo_status_t +cairo_region_subtract (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_subtract_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_intersect (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_intersect_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + +cairo_public cairo_status_t +cairo_region_union (cairo_region_t *dst, cairo_region_t *other); + +cairo_public cairo_status_t +cairo_region_union_rectangle (cairo_region_t *dst, + const cairo_rectangle_int_t *rectangle); + + /* Functions to be used while debugging (not intended for use in production code) */ cairo_public void cairo_debug_reset_static_data (void); + CAIRO_END_DECLS #endif /* CAIRO_H */ diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h index 083e1e916917..b0a2852061eb 100644 --- a/gfx/cairo/cairo/src/cairoint.h +++ b/gfx/cairo/cairo/src/cairoint.h @@ -85,7 +85,7 @@ CAIRO_BEGIN_DECLS -#if _WIN32 && !WINCE // we don't have to worry about permissions on WINCE +#if _WIN32 && !_WIN32_WCE // we don't have to worry about permissions on WinCE cairo_private FILE * _cairo_win32_tmpfile (void); #define tmpfile() _cairo_win32_tmpfile() @@ -2418,7 +2418,7 @@ _cairo_traps_extents (const cairo_traps_t *traps, cairo_private cairo_int_status_t _cairo_traps_extract_region (const cairo_traps_t *tr, - cairo_region_t *region); + cairo_region_t **region); cairo_private cairo_status_t _cairo_traps_path (const cairo_traps_t *traps, @@ -2538,7 +2538,21 @@ _cairo_pattern_reset_static_data (void); /* cairo-region.c */ -#include "cairo-region-private.h" +struct _cairo_region { + cairo_status_t status; + + pixman_region32_t rgn; +}; + +cairo_private void +_cairo_region_init (cairo_region_t *region); + +cairo_private void +_cairo_region_init_rectangle (cairo_region_t *region, + const cairo_rectangle_int_t *rectangle); + +cairo_private void +_cairo_region_fini (cairo_region_t *region); /* cairo-unicode.c */ @@ -2700,6 +2714,24 @@ slim_hidden_proto (cairo_user_font_face_set_unicode_to_glyph_func); slim_hidden_proto (cairo_user_to_device); slim_hidden_proto (cairo_user_to_device_distance); slim_hidden_proto (cairo_version_string); +slim_hidden_proto (cairo_region_create); +slim_hidden_proto (cairo_region_create_rectangle); +slim_hidden_proto (cairo_region_copy); +slim_hidden_proto (cairo_region_destroy); +slim_hidden_proto (cairo_region_status); +slim_hidden_proto (cairo_region_get_extents); +slim_hidden_proto (cairo_region_num_rectangles); +slim_hidden_proto (cairo_region_get_rectangle); +slim_hidden_proto (cairo_region_is_empty); +slim_hidden_proto (cairo_region_contains_rectangle); +slim_hidden_proto (cairo_region_contains_point); +slim_hidden_proto (cairo_region_translate); +slim_hidden_proto (cairo_region_subtract); +slim_hidden_proto (cairo_region_subtract_rectangle); +slim_hidden_proto (cairo_region_intersect); +slim_hidden_proto (cairo_region_intersect_rectangle); +slim_hidden_proto (cairo_region_union); +slim_hidden_proto (cairo_region_union_rectangle); #if CAIRO_HAS_PNG_FUNCTIONS From a60b90eae70704ae43cb89413d86e4c984074622 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 15 May 2009 14:14:45 +1200 Subject: [PATCH 25/46] b=485125 nsIWidget::SetParent(nsnull) for gtk. r=roc --- widget/src/gtk2/mozcontainer.c | 32 ++++- widget/src/gtk2/mozcontainer.h | 31 +++++ widget/src/gtk2/mozdrawingarea.c | 26 ---- widget/src/gtk2/mozdrawingarea.h | 2 + widget/src/gtk2/nsWindow.cpp | 211 ++++++++++++++++++++++++++----- widget/src/gtk2/nsWindow.h | 1 + 6 files changed, 245 insertions(+), 58 deletions(-) diff --git a/widget/src/gtk2/mozcontainer.c b/widget/src/gtk2/mozcontainer.c index 380cd3d5c11f..9fd7435252a5 100644 --- a/widget/src/gtk2/mozcontainer.c +++ b/widget/src/gtk2/mozcontainer.c @@ -375,7 +375,7 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget) { MozContainerChild *child; MozContainer *moz_container; - GList *tmp_list; + GdkWindow* parent_window; g_return_if_fail (IS_MOZ_CONTAINER(container)); g_return_if_fail (GTK_IS_WIDGET(child_widget)); @@ -385,8 +385,34 @@ moz_container_remove (GtkContainer *container, GtkWidget *child_widget) child = moz_container_get_child (moz_container, child_widget); g_return_if_fail (child); - if(child->widget == child_widget) { - gtk_widget_unparent(child_widget); + /* gtk_widget_unparent will remove the parent window (as well as the + * parent widget), but, in Mozilla's window hierarchy, the parent window + * may need to be kept because it may be part of a GdkWindow sub-hierarchy + * that is being moved to another MozContainer. + * + * (In a conventional GtkWidget hierarchy, GdkWindows being reparented + * would have their own GtkWidget and that widget would be the one being + * reparented. In Mozilla's hierarchy, the parent_window needs to be + * retained so that the GdkWindow sub-hierarchy is maintained.) + */ + parent_window = gtk_widget_get_parent_window(child_widget); + if (parent_window) + g_object_ref(parent_window); + + gtk_widget_unparent(child_widget); + + if (parent_window) { + /* The child_widget will always still exist because g_signal_emit, + * which invokes this function, holds a reference. + * + * If parent_window is the container's root window then it will not be + * the parent_window if the child_widget is placed in another + * container. + */ + if (parent_window != GTK_WIDGET(container)->window) + gtk_widget_set_parent_window(child_widget, parent_window); + + g_object_unref(parent_window); } moz_container->children = g_list_remove(moz_container->children, child); diff --git a/widget/src/gtk2/mozcontainer.h b/widget/src/gtk2/mozcontainer.h index b6cc29dd37c7..e96754d56698 100644 --- a/widget/src/gtk2/mozcontainer.h +++ b/widget/src/gtk2/mozcontainer.h @@ -45,6 +45,37 @@ extern "C" { #endif /* __cplusplus */ +/* + * MozContainer + * + * This class serves two purposes in the nsIWidget implementation. + * + * - It provides objects to receive signals from GTK for events on native + * windows. + * + * - It provides a container parent for GtkWidgets. The only GtkWidgets + * that need this in Mozilla are the GtkSockets for windowed plugins (Xt + * and XEmbed). + * + * Note that the window hierarchy in Mozilla differs from conventional + * GtkWidget hierarchies. + * + * Mozilla's hierarchy exists through the GdkWindow hierarchy, and all child + * GdkWindows (within a child nsIWidget hierarchy) belong to one MozContainer + * GtkWidget. If the MozContainer is unrealized or its GdkWindows are + * destroyed for some other reason, then the hierarchy no longer exists. (In + * conventional GTK clients, the hierarchy is recorded by the GtkWidgets, and + * so can be re-established after destruction of the GdkWindows.) + * + * One consequence of this is that the MozContainer does not know which of its + * GdkWindows should parent child GtkWidgets. (Conventional GtkContainers + * determine which GdkWindow to assign child GtkWidgets.) + * + * Therefore, when adding a child GtkWidget to a MozContainer, + * gtk_widget_set_parent_window should be called on the child GtkWidget before + * it is realized. + */ + #define MOZ_CONTAINER_TYPE (moz_container_get_type()) #define MOZ_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOZ_CONTAINER_TYPE, MozContainer)) #define MOZ_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOZ_CONTAINER_TYPE, MozContainerClass)) diff --git a/widget/src/gtk2/mozdrawingarea.c b/widget/src/gtk2/mozdrawingarea.c index 99f6c4e276b6..01f18d5aaa9a 100644 --- a/widget/src/gtk2/mozdrawingarea.c +++ b/widget/src/gtk2/mozdrawingarea.c @@ -124,24 +124,6 @@ moz_drawingarea_reparent (MozDrawingarea *drawingarea, GdkWindow *aNewParent) aNewParent, 0, 0); } -static void -nullify_widget_pointers (gpointer data, GObject *widget) -{ - MozDrawingarea *drawingarea = data; - -#ifdef DEBUG - gpointer user_data; - /* This function may get called twice before widget is destroyed, - so the user_data may have already been nullified. */ - gdk_window_get_user_data(drawingarea->inner_window, &user_data); - if (user_data && widget && user_data != widget) - g_critical("user_data does not match widget"); -#endif - - gdk_window_set_user_data(drawingarea->inner_window, NULL); - gdk_window_set_user_data(drawingarea->clip_window, NULL); -} - void moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent, GtkWidget *widget, GdkVisual *visual) @@ -189,8 +171,6 @@ moz_drawingarea_create_windows (MozDrawingarea *drawingarea, GdkWindow *parent, &attributes, attributes_mask); gdk_window_set_user_data(drawingarea->inner_window, widget); - g_object_weak_ref(G_OBJECT(widget), nullify_widget_pointers, drawingarea); - /* set the default pixmap to None so that you don't end up with the gtk default which is BlackPixel. */ gdk_window_set_back_pixmap(drawingarea->inner_window, NULL, FALSE); @@ -210,12 +190,6 @@ moz_drawingarea_finalize (GObject *object) drawingarea = MOZ_DRAWINGAREA(object); - gdk_window_get_user_data(drawingarea->inner_window, &user_data); - if (user_data) { - g_object_weak_unref(user_data, nullify_widget_pointers, drawingarea); - nullify_widget_pointers(drawingarea, NULL); - } - gdk_window_destroy(drawingarea->inner_window); gdk_window_destroy(drawingarea->clip_window); diff --git a/widget/src/gtk2/mozdrawingarea.h b/widget/src/gtk2/mozdrawingarea.h index 187c4a05e8ec..616618e52d62 100644 --- a/widget/src/gtk2/mozdrawingarea.h +++ b/widget/src/gtk2/mozdrawingarea.h @@ -65,6 +65,8 @@ typedef struct _MozDrawingareaClass MozDrawingareaClass; struct _MozDrawingarea { GObject parent_instance; + /* AFAIK this clip_window (and thus this whole class) exists solely to + * make gdk_window_scroll() smooth for nsIWidget::Scroll(). */ GdkWindow *clip_window; GdkWindow *inner_window; }; diff --git a/widget/src/gtk2/nsWindow.cpp b/widget/src/gtk2/nsWindow.cpp index 9fda721729e4..19d9b1d2a675 100644 --- a/widget/src/gtk2/nsWindow.cpp +++ b/widget/src/gtk2/nsWindow.cpp @@ -177,6 +177,7 @@ static gboolean expose_event_cb (GtkWidget *widget, GdkEventExpose *event); static gboolean configure_event_cb (GtkWidget *widget, GdkEventConfigure *event); +static void container_unrealize_cb (GtkWidget *widget); static void size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation); static gboolean delete_event_cb (GtkWidget *widget, @@ -355,6 +356,8 @@ PRBool gDisableNativeTheme = PR_FALSE; // created for exposes, even if the display has a different depth static PRBool gForce24bpp = PR_FALSE; +static GtkWidget *gInvisibleContainer = NULL; + nsWindow::nsWindow() { mIsTopLevel = PR_FALSE; @@ -642,6 +645,79 @@ nsWindow::Create(nsNativeWidget aParent, return rv; } +static GtkWidget* +EnsureInvisibleContainer() +{ + if (!gInvisibleContainer) { + // GtkWidgets need to be anchored to a GtkWindow to be realized (to + // have a window). Using GTK_WINDOW_POPUP rather than + // GTK_WINDOW_TOPLEVEL in the hope that POPUP results in less + // initialization and window manager interaction. + GtkWidget* window = gtk_window_new(GTK_WINDOW_POPUP); + gInvisibleContainer = moz_container_new(); + gtk_container_add(GTK_CONTAINER(window), gInvisibleContainer); + gtk_widget_realize(gInvisibleContainer); + + } + return gInvisibleContainer; +} + +static void +CheckDestroyInvisibleContainer() +{ + NS_PRECONDITION(gInvisibleContainer, "oh, no"); + + if (!gdk_window_peek_children(gInvisibleContainer->window)) { + // No children, so not in use. + // Make sure to destroy the GtkWindow also. + gtk_widget_destroy(gInvisibleContainer->parent); + gInvisibleContainer = NULL; + } +} + +// Change the containing GtkWidget on a sub-hierarchy of GdkWindows belonging +// to aOldWidget and rooted at aWindow, and reparent any child GtkWidgets of +// the GdkWindow hierarchy. If aNewWidget is NULL, the reference to +// aOldWidget is removed from its GdkWindows, and child GtkWidgets are +// destroyed. +static void +SetWidgetForHierarchy(GdkWindow *aWindow, + GtkWidget *aOldWidget, + GtkWidget *aNewWidget) +{ + gpointer data; + gdk_window_get_user_data(aWindow, &data); + + if (data != aOldWidget) { + if (!GTK_IS_WIDGET(data)) + return; + + GtkWidget* widget = static_cast(data); + if (widget->parent != aOldWidget) + return; + + // This window belongs to a child widget, which will no longer be a + // child of aOldWidget. + if (aNewWidget) { + gtk_widget_reparent(widget, aNewWidget); + } else { + // aNewWidget == NULL indicates that the window is about to be + // destroyed. + gtk_widget_destroy(widget); + } + + return; + } + + for (GList *list = gdk_window_peek_children(aWindow); + list; + list = list->next) { + SetWidgetForHierarchy(GDK_WINDOW(list->data), aOldWidget, aNewWidget); + } + + gdk_window_set_user_data(aWindow, aNewWidget); +} + NS_IMETHODIMP nsWindow::Destroy(void) { @@ -724,6 +800,24 @@ nsWindow::Destroy(void) mDragLeaveTimer = nsnull; } + GtkWidget *owningWidget = GetMozContainerWidget(); + if (mShell) { + gtk_widget_destroy(mShell); + mShell = nsnull; + mContainer = nsnull; + } + else if (mContainer) { + gtk_widget_destroy(GTK_WIDGET(mContainer)); + mContainer = nsnull; + } + else if (owningWidget) { + // Remove references from GdkWindows back to their container + // widget while the GdkWindow hierarchy is still available. + // (OnContainerUnrealize does this when the MozContainer widget is + // destroyed.) + SetWidgetForHierarchy(mDrawingarea->clip_window, owningWidget, NULL); + } + if (mDrawingarea) { g_object_set_data(G_OBJECT(mDrawingarea->clip_window), "nsWindow", NULL); @@ -735,18 +829,15 @@ nsWindow::Destroy(void) g_object_set_data(G_OBJECT(mDrawingarea->inner_window), "mozdrawingarea", NULL); + NS_ASSERTION(!get_gtk_widget_for_gdk_window(mDrawingarea->inner_window), + "widget reference not removed"); + g_object_unref(mDrawingarea); mDrawingarea = nsnull; } - if (mShell) { - gtk_widget_destroy(mShell); - mShell = nsnull; - mContainer = nsnull; - } - else if (mContainer) { - gtk_widget_destroy(GTK_WIDGET(mContainer)); - mContainer = nsnull; + if (gInvisibleContainer && owningWidget == gInvisibleContainer) { + CheckDestroyInvisibleContainer(); } OnDestroy(); @@ -769,29 +860,63 @@ nsWindow::GetParent(void) NS_IMETHODIMP nsWindow::SetParent(nsIWidget *aNewParent) { - NS_ENSURE_ARG_POINTER(aNewParent); - - GdkWindow* newParentWindow = - static_cast(aNewParent->GetNativeData(NS_NATIVE_WINDOW)); - NS_ASSERTION(newParentWindow, "Parent widget has a null native window handle"); - - if (!mShell && mDrawingarea) { -#ifdef DEBUG - if (!mContainer) { - // Check that the new Parent window has the same MozContainer - gpointer old_container; - gdk_window_get_user_data(mDrawingarea->inner_window, - &old_container); - gpointer new_container; - gdk_window_get_user_data(newParentWindow, &new_container); - NS_ASSERTION(old_container == new_container, - "FIXME: Wrong MozContainer on MozDrawingarea"); - } -#endif - moz_drawingarea_reparent(mDrawingarea, newParentWindow); - } else { + if (mContainer || !mDrawingarea || !mParent) { NS_NOTREACHED("nsWindow::SetParent - reparenting a non-child window"); + return NS_ERROR_NOT_IMPLEMENTED; } + + // nsBaseWidget::SetZIndex adds child widgets to the parent's list. + nsCOMPtr kungFuDeathGrip = this; + mParent->RemoveChild(this); + + mParent = aNewParent; + + GtkWidget* oldContainer = GetMozContainerWidget(); + if (!oldContainer) { + // The GdkWindows have been destroyed so there is nothing else to + // reparent. + NS_ABORT_IF_FALSE(GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed, + "live GdkWindow with no widget"); + return NS_OK; + } + + NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(mDrawingarea->inner_window)->destroyed, + "destroyed GdkWindow with widget"); + + GdkWindow* newParentWindow = NULL; + GtkWidget* newContainer = NULL; + if (aNewParent) { + newParentWindow = static_cast + (aNewParent->GetNativeData(NS_NATIVE_WINDOW)); + if (newParentWindow) { + newContainer = get_gtk_widget_for_gdk_window(newParentWindow); + } + } else { + // aNewParent is NULL, but reparent to a hidden window to avoid + // destroying the GdkWindow and its descendants. + // An invisible container widget is needed to hold descendant + // GtkWidgets. + newContainer = EnsureInvisibleContainer(); + newParentWindow = newContainer->window; + } + + if (!newContainer) { + // The new parent GdkWindow has been destroyed. + NS_ABORT_IF_FALSE(!newParentWindow || + GDK_WINDOW_OBJECT(newParentWindow)->destroyed, + "live GdkWindow with no widget"); + Destroy(); + } else { + if (newContainer != oldContainer) { + NS_ABORT_IF_FALSE(!GDK_WINDOW_OBJECT(newParentWindow)->destroyed, + "destroyed GdkWindow with widget"); + SetWidgetForHierarchy(mDrawingarea->clip_window, oldContainer, + newContainer); + } + + moz_drawingarea_reparent(mDrawingarea, newParentWindow); + } + return NS_OK; } @@ -2336,6 +2461,21 @@ nsWindow::OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent) return FALSE; } +void +nsWindow::OnContainerUnrealize(GtkWidget *aWidget) +{ + // The GdkWindows are about to be destroyed (but not deleted), so remove + // their references back to their container widget while the GdkWindow + // hierarchy is still available. + + NS_ASSERTION(mContainer == MOZ_CONTAINER(aWidget), + "unexpected \"unrealize\" signal"); + + if (mDrawingarea) { + SetWidgetForHierarchy(mDrawingarea->clip_window, aWidget, NULL); + } +} + void nsWindow::OnSizeAllocate(GtkWidget *aWidget, GtkAllocation *aAllocation) { @@ -3907,6 +4047,8 @@ nsWindow::NativeCreate(nsIWidget *aParent, } if (mContainer) { + g_signal_connect(G_OBJECT(mContainer), "unrealize", + G_CALLBACK(container_unrealize_cb), NULL); g_signal_connect_after(G_OBJECT(mContainer), "size_allocate", G_CALLBACK(size_allocate_cb), NULL); g_signal_connect(G_OBJECT(mContainer), "expose_event", @@ -5224,6 +5366,17 @@ configure_event_cb(GtkWidget *widget, return window->OnConfigureEvent(widget, event); } +/* static */ +void +container_unrealize_cb (GtkWidget *widget) +{ + nsRefPtr window = get_window_for_gtk_widget(widget); + if (!window) + return; + + window->OnContainerUnrealize(widget); +} + /* static */ void size_allocate_cb (GtkWidget *widget, GtkAllocation *allocation) diff --git a/widget/src/gtk2/nsWindow.h b/widget/src/gtk2/nsWindow.h index 492a8e3d9608..2d5a69118774 100644 --- a/widget/src/gtk2/nsWindow.h +++ b/widget/src/gtk2/nsWindow.h @@ -219,6 +219,7 @@ public: GdkEventExpose *aEvent); gboolean OnConfigureEvent(GtkWidget *aWidget, GdkEventConfigure *aEvent); + void OnContainerUnrealize(GtkWidget *aWidget); void OnSizeAllocate(GtkWidget *aWidget, GtkAllocation *aAllocation); void OnDeleteEvent(GtkWidget *aWidget, From 9b9b374144ca57e5809ef4f11e60e34601dd666d Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 15 May 2009 14:16:27 +1200 Subject: [PATCH 26/46] b=485125 reparent plugin widgets for delayed destruction. r=jst --- layout/generic/nsObjectFrame.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 7d03971a2281..3d4616ce058a 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1866,16 +1866,13 @@ static PRBool DoDelayedStop(nsPluginInstanceOwner *aInstanceOwner, PRBool aDelayedStop) { // Don't delay stopping QuickTime (bug 425157), Flip4Mac (bug 426524), - // XStandard (bug 430219), CMISS Zinc (bug 429604). ARM Flash (454756) + // XStandard (bug 430219), CMISS Zinc (bug 429604). if (aDelayedStop -#ifndef XP_WIN +#if !(defined XP_WIN || defined MOZ_X11) && !aInstanceOwner->MatchPluginName("QuickTime") && !aInstanceOwner->MatchPluginName("Flip4Mac") && !aInstanceOwner->MatchPluginName("XStandard plugin") && !aInstanceOwner->MatchPluginName("CMISS Zinc Plugin") -#endif -#if defined(XP_UNIX) && defined(__arm__) - && !aInstanceOwner->MatchPluginName("Shockwave Flash") #endif ) { nsCOMPtr evt = new nsStopPluginRunnable(aInstanceOwner); @@ -2036,7 +2033,7 @@ nsObjectFrame::StopPluginInternal(PRBool aDelayedStop) nsWeakFrame weakFrame(this); -#ifdef XP_WIN +#if defined(XP_WIN) || defined(MOZ_X11) if (aDelayedStop) { // If we're asked to do a delayed stop it means we're stopping the // plugin because we're destroying the frame. In that case, tell @@ -4276,7 +4273,7 @@ nsPluginInstanceOwner::Destroy() void nsPluginInstanceOwner::PrepareToStop(PRBool aDelayedStop) { -#ifdef XP_WIN +#if defined(XP_WIN) || defined(MOZ_X11) if (aDelayedStop && mWidget) { // To delay stopping a plugin we need to reparent the plugin // so that we can safely tear down the From e7ea7afe9fb297700c8d282cd9c2e7eb4f7bc32d Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 15 May 2009 14:19:10 +1200 Subject: [PATCH 27/46] remove unused moz_container_scroll_update. r=roc --- widget/src/gtk2/mozcontainer.c | 30 ------------------------------ widget/src/gtk2/mozcontainer.h | 4 ---- 2 files changed, 34 deletions(-) diff --git a/widget/src/gtk2/mozcontainer.c b/widget/src/gtk2/mozcontainer.c index 9fd7435252a5..b1156cadbdab 100644 --- a/widget/src/gtk2/mozcontainer.c +++ b/widget/src/gtk2/mozcontainer.c @@ -174,36 +174,6 @@ moz_container_move (MozContainer *container, GtkWidget *child_widget, gtk_widget_size_allocate(child_widget, &new_allocation); } -/* This function updates the allocation on a child widget without -causing a size_allocate event to be generated on that widget. This -should only be used for scrolling since it's assumed that the expose -event created by the scroll will update any widgets that come into view. */ - -void -moz_container_scroll_update (MozContainer *container, GtkWidget *child_widget, - gint x, gint y) -{ - MozContainerChild *child; - GtkAllocation new_allocation; - - child = moz_container_get_child (container, child_widget); - - child->x = x; - child->y = y; - - new_allocation.x = x; - new_allocation.y = y; - new_allocation.width = child_widget->allocation.width; - new_allocation.height = child_widget->allocation.height; - - /* printf("moz_container_update %p %p will allocate to %d %d %d %d\n", - (void *)container, (void *)child_widget, - new_allocation.x, new_allocation.y, - new_allocation.width, new_allocation.height); */ - - gtk_widget_size_allocate(child_widget, &new_allocation); -} - /* static methods */ void diff --git a/widget/src/gtk2/mozcontainer.h b/widget/src/gtk2/mozcontainer.h index e96754d56698..461aba58f5a3 100644 --- a/widget/src/gtk2/mozcontainer.h +++ b/widget/src/gtk2/mozcontainer.h @@ -109,10 +109,6 @@ void moz_container_move (MozContainer *container, gint y, gint width, gint height); -void moz_container_scroll_update (MozContainer *container, - GtkWidget *child_widget, - gint x, - gint y); #ifdef __cplusplus } From cdf16e275e64a500e93a961a3c4fe49297df28d3 Mon Sep 17 00:00:00 2001 From: Karl Tomlinson Date: Fri, 15 May 2009 14:26:10 +1200 Subject: [PATCH 28/46] b=491241 crash with windowless plugins when MOZ_COMPOSITED_PLUGINS is defined r=jrmuizel --- layout/generic/nsObjectFrame.cpp | 105 ++++++++++++++++--------------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 3d4616ce058a..ec73dd6f259b 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -3616,7 +3616,7 @@ static unsigned int XInputEventState(const nsInputEvent& anEvent) #endif #ifdef MOZ_COMPOSITED_PLUGINS -static void find_dest_id(XID top, XID *root, XID *dest, unsigned int target_x, unsigned int target_y) +static void find_dest_id(XID top, XID *root, XID *dest, int target_x, int target_y) { XID target_id = top; XID parent; @@ -3640,8 +3640,8 @@ loop: // XXX: we may need to be more careful here, i.e. if // this condition matches more than one child if (target_x >= x && target_y >= y && - target_x <= (x + width) && - target_y <= (y + height)) { + target_x <= x + int(width) && + target_y <= y + int(height)) { target_id = children[i]; // printf("found new target: %x\n", target_id); XFree(children); @@ -4562,10 +4562,13 @@ nsPluginInstanceOwner::Renderer::NativeDraw(QWidget * drawable, } #endif -#ifndef MOZ_COMPOSITED_PLUGINS - if (doupdatewindow) - mInstance->SetWindow(mWindow); +#ifdef MOZ_COMPOSITED_PLUGINS + if (mWindow->type == nsPluginWindowType_Drawable) #endif + { + if (doupdatewindow) + mInstance->SetWindow(mWindow); + } #ifdef MOZ_X11 // Translate the dirty rect to drawable coordinates. @@ -4575,57 +4578,61 @@ nsPluginInstanceOwner::Renderer::NativeDraw(QWidget * drawable, if (!dirtyRect.IntersectRect(dirtyRect, clipRect)) return NS_OK; -#ifndef MOZ_COMPOSITED_PLUGINS - nsPluginEvent pluginEvent; - XGraphicsExposeEvent& exposeEvent = pluginEvent.event.xgraphicsexpose; - // set the drawing info - exposeEvent.type = GraphicsExpose; - exposeEvent.display = DisplayOfScreen(screen); - exposeEvent.drawable = +#ifdef MOZ_COMPOSITED_PLUGINS + if (mWindow->type == nsPluginWindowType_Drawable) { +#endif + nsPluginEvent pluginEvent; + XGraphicsExposeEvent& exposeEvent = + pluginEvent.event.xgraphicsexpose; + // set the drawing info + exposeEvent.type = GraphicsExpose; + exposeEvent.display = DisplayOfScreen(screen); + exposeEvent.drawable = #if defined(MOZ_WIDGET_GTK2) GDK_DRAWABLE_XID(drawable); #elif defined(MOZ_WIDGET_QT) drawable->x11PictureHandle(); #endif - exposeEvent.x = mDirtyRect.x + offsetX; - exposeEvent.y = mDirtyRect.y + offsetY; - exposeEvent.width = mDirtyRect.width; - exposeEvent.height = mDirtyRect.height; - exposeEvent.count = 0; - // information not set: - exposeEvent.serial = 0; - exposeEvent.send_event = False; - exposeEvent.major_code = 0; - exposeEvent.minor_code = 0; - - PRBool eventHandled = PR_FALSE; - mInstance->HandleEvent(&pluginEvent, &eventHandled); -#endif -#endif + exposeEvent.x = mDirtyRect.x + offsetX; + exposeEvent.y = mDirtyRect.y + offsetY; + exposeEvent.width = mDirtyRect.width; + exposeEvent.height = mDirtyRect.height; + exposeEvent.count = 0; + // information not set: + exposeEvent.serial = 0; + exposeEvent.send_event = False; + exposeEvent.major_code = 0; + exposeEvent.minor_code = 0; + PRBool eventHandled = PR_FALSE; + mInstance->HandleEvent(&pluginEvent, &eventHandled); #ifdef MOZ_COMPOSITED_PLUGINS - /* XXX: this is very nasty. We need a better way of getting at mPlugWindow */ - GtkWidget *plug = (GtkWidget*)(((nsPluginNativeWindow*)mWindow)->mPlugWindow); - //GtkWidget *plug = (GtkWidget*)(((nsPluginNativeWindowGtk2*)mWindow)->mSocketWidget); + } + else { + /* XXX: this is very nasty. We need a better way of getting at mPlugWindow */ + GtkWidget *plug = (GtkWidget*)(((nsPluginNativeWindow*)mWindow)->mPlugWindow); + //GtkWidget *plug = (GtkWidget*)(((nsPluginNativeWindowGtk2*)mWindow)->mSocketWidget); - /* Cairo has bugs with IncludeInferiors when using paint - * so we use XCopyArea directly instead. */ - XGCValues gcv; - gcv.subwindow_mode = IncludeInferiors; - gcv.graphics_exposures = False; - GC gc = XCreateGC(GDK_DISPLAY(), gdk_x11_drawable_get_xid(drawable), GCGraphicsExposures | GCSubwindowMode, &gcv); - /* The source and destination appear to always line up, so src and dest - * coords should be the same */ - XCopyArea(GDK_DISPLAY(), gdk_x11_drawable_get_xid(plug->window), - gdk_x11_drawable_get_xid(drawable), - gc, - mDirtyRect.x, - mDirtyRect.y, - mDirtyRect.width, - mDirtyRect.height, - mDirtyRect.x, - mDirtyRect.y); - XFreeGC(GDK_DISPLAY(), gc); + /* Cairo has bugs with IncludeInferiors when using paint + * so we use XCopyArea directly instead. */ + XGCValues gcv; + gcv.subwindow_mode = IncludeInferiors; + gcv.graphics_exposures = False; + GC gc = XCreateGC(GDK_DISPLAY(), gdk_x11_drawable_get_xid(drawable), GCGraphicsExposures | GCSubwindowMode, &gcv); + /* The source and destination appear to always line up, so src and dest + * coords should be the same */ + XCopyArea(GDK_DISPLAY(), gdk_x11_drawable_get_xid(plug->window), + gdk_x11_drawable_get_xid(drawable), + gc, + mDirtyRect.x, + mDirtyRect.y, + mDirtyRect.width, + mDirtyRect.height, + mDirtyRect.x, + mDirtyRect.y); + XFreeGC(GDK_DISPLAY(), gc); + } +#endif #endif return NS_OK; } From 14352e6712ea731b3eae9c1cd4e687009fdfb4f4 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Thu, 14 May 2009 22:47:51 -0400 Subject: [PATCH 29/46] fixing gfx build bustage in wince r=joe --- gfx/cairo/cairo/src/cairo-ddraw-surface.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gfx/cairo/cairo/src/cairo-ddraw-surface.c b/gfx/cairo/cairo/src/cairo-ddraw-surface.c index 352166ff702a..bc5ccf9023a7 100644 --- a/gfx/cairo/cairo/src/cairo-ddraw-surface.c +++ b/gfx/cairo/cairo/src/cairo-ddraw-surface.c @@ -37,12 +37,11 @@ #define WIN32_LEAN_AND_MEAN #endif -#include "cairoint.h" - #if CAIRO_HAS_DDRAW_SURFACE #include "cairo-clip-private.h" #include "cairo-ddraw-private.h" +#include "cairo-region-private.h" #include #include From 0442e8caf5eddedc09afb68955f292cb86b95bc6 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Thu, 14 May 2009 09:52:50 +1200 Subject: [PATCH 30/46] Bug 489415. Handle media streams with data from different principals in the media cache. r=bzbarsky,doublec --- .../html/content/src/nsHTMLMediaElement.cpp | 29 ++++- content/media/video/public/nsMediaCache.h | 28 +++- content/media/video/public/nsMediaStream.h | 18 ++- content/media/video/src/nsMediaCache.cpp | 39 +++++- content/media/video/src/nsMediaStream.cpp | 123 +++++++++++++----- content/media/video/test/Makefile.in | 2 + content/media/video/test/dynamic_redirect.sjs | 39 ++++++ .../video/test/test_mixed_principals.html | 68 ++++++++++ 8 files changed, 301 insertions(+), 45 deletions(-) create mode 100644 content/media/video/test/dynamic_redirect.sjs create mode 100644 content/media/video/test/test_mixed_principals.html diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 5ff6bf01a144..c0e70daa7088 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -172,11 +172,15 @@ void nsHTMLMediaElement::QueueLoadFromSourceTask() NS_DispatchToMainThread(event); } -class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener +class nsHTMLMediaElement::MediaLoadListener : public nsIStreamListener, + public nsIChannelEventSink, + public nsIInterfaceRequestor { NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER + NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIINTERFACEREQUESTOR public: MediaLoadListener(nsHTMLMediaElement* aElement) @@ -190,7 +194,9 @@ private: nsCOMPtr mNextListener; }; -NS_IMPL_ISUPPORTS2(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, nsIStreamListener) +NS_IMPL_ISUPPORTS4(nsHTMLMediaElement::MediaLoadListener, nsIRequestObserver, + nsIStreamListener, nsIChannelEventSink, + nsIInterfaceRequestor) NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { @@ -252,6 +258,21 @@ NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnDataAvailable(nsIRequest* return mNextListener->OnDataAvailable(aRequest, aContext, aStream, aOffset, aCount); } +NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::OnChannelRedirect(nsIChannel* aOldChannel, + nsIChannel* aNewChannel, + PRUint32 aFlags) +{ + nsCOMPtr sink = do_QueryInterface(mNextListener); + if (sink) + return sink->OnChannelRedirect(aOldChannel, aNewChannel, aFlags); + return NS_OK; +} + +NS_IMETHODIMP nsHTMLMediaElement::MediaLoadListener::GetInterface(const nsIID & aIID, void **aResult) +{ + return QueryInterface(aIID, aResult); +} + NS_IMPL_ADDREF_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement) NS_IMPL_RELEASE_INHERITED(nsHTMLMediaElement, nsGenericHTMLElement) @@ -513,8 +534,10 @@ nsresult nsHTMLMediaElement::LoadResource(nsIURI* aURI) // The listener holds a strong reference to us. This creates a reference // cycle which is manually broken in the listener's OnStartRequest method // after it is finished with the element. - nsCOMPtr loadListener = new MediaLoadListener(this); + nsRefPtr loadListener = new MediaLoadListener(this); if (!loadListener) return NS_ERROR_OUT_OF_MEMORY; + + mChannel->SetNotificationCallbacks(loadListener); nsCOMPtr listener; if (ShouldCheckAllowOrigin()) { diff --git a/content/media/video/public/nsMediaCache.h b/content/media/video/public/nsMediaCache.h index c9546951a877..058c53b10eab 100644 --- a/content/media/video/public/nsMediaCache.h +++ b/content/media/video/public/nsMediaCache.h @@ -41,6 +41,8 @@ #include "nsTArray.h" #include "nsAutoLock.h" +#include "nsIPrincipal.h" +#include "nsCOMPtr.h" /** * Media applications want fast, "on demand" random access to media data, @@ -186,6 +188,13 @@ * we must not acquire any nsMediaDecoder locks or nsMediaStream locks * while holding the nsMediaCache lock. But it's OK to hold those locks * and then get the nsMediaCache lock. + * + * nsMediaCache associates a principal with each stream. CacheClientSeek + * can trigger new HTTP requests; due to redirects to other domains, + * each HTTP load can return data with a different principal. This + * principal must be passed to NotifyDataReceived, and nsMediaCache + * will detect when different principals are associated with data in the + * same stream, and replace them with a null principal. */ class nsMediaCache; // defined in nsMediaStream.h @@ -215,7 +224,8 @@ public: mStreamOffset(0), mStreamLength(-1), mPlaybackBytesPerSecond(10000), mPinCount(0), mCurrentMode(MODE_PLAYBACK), mClosed(PR_FALSE), mIsSeekable(PR_FALSE), mCacheSuspended(PR_FALSE), - mMetadataInPartialBlockBuffer(PR_FALSE) {} + mMetadataInPartialBlockBuffer(PR_FALSE), + mUsingNullPrincipal(PR_FALSE) {} ~nsMediaCacheStream(); // Set up this stream with the cache. Can fail on OOM. Must be called @@ -236,6 +246,8 @@ public: void Close(); // This returns true when the stream has been closed PRBool IsClosed() const { return mClosed; } + // Get the principal for this stream. + nsIPrincipal* GetCurrentPrincipal() { return mPrincipal; } // These callbacks are called on the main thread by the client // when data has been received via the channel. @@ -263,7 +275,9 @@ public: // the starting offset is known via NotifyDataStarted or because // the cache requested the offset in // nsMediaChannelStream::CacheClientSeek, or because it defaulted to 0. - void NotifyDataReceived(PRInt64 aSize, const char* aData); + // We pass in the principal that was used to load this data. + void NotifyDataReceived(PRInt64 aSize, const char* aData, + nsIPrincipal* aPrincipal); // Notifies the cache that the channel has closed with the given status. void NotifyDataEnded(nsresult aStatus); @@ -363,9 +377,12 @@ private: // This is used to NotifyAll to wake up threads that might be // blocked on reading from this stream. void CloseInternal(nsAutoMonitor* aMonitor); + // Update mPrincipal given that data has been received from aPrincipal + void UpdatePrincipal(nsIPrincipal* aPrincipal); - // This field is main-thread-only. - nsMediaChannelStream* mClient; + // These fields are main-thread-only. + nsMediaChannelStream* mClient; + nsCOMPtr mPrincipal; // All other fields are all protected by the cache's monitor and // can be accessed by by any thread. @@ -401,6 +418,9 @@ private: PRPackedBool mCacheSuspended; // true if some data in mPartialBlockBuffer has been read as metadata PRPackedBool mMetadataInPartialBlockBuffer; + // true if mPrincipal is a null principal because we saw data from + // multiple origins + PRPackedBool mUsingNullPrincipal; // Data received for the block containing mChannelOffset. Data needs // to wait here so we can write back a complete block. The first diff --git a/content/media/video/public/nsMediaStream.h b/content/media/video/public/nsMediaStream.h index a5a8eb592da9..bc16e85fdae4 100644 --- a/content/media/video/public/nsMediaStream.h +++ b/content/media/video/public/nsMediaStream.h @@ -44,6 +44,8 @@ #include "nsIPrincipal.h" #include "nsIURI.h" #include "nsIStreamListener.h" +#include "nsIChannelEventSink.h" +#include "nsIInterfaceRequestor.h" #include "prlock.h" #include "nsMediaCache.h" #include "nsTimeStamp.h" @@ -149,8 +151,6 @@ public: } // The following can be called on the main thread only: - // Get the current principal for the channel - already_AddRefed GetCurrentPrincipal(); // Get the decoder nsMediaDecoder* Decoder() { return mDecoder; } // Close the stream, stop any listeners, channels, etc. @@ -161,6 +161,8 @@ public: virtual void Suspend() = 0; // Resume any downloads that have been suspended. virtual void Resume() = 0; + // Get the current principal for the channel + virtual already_AddRefed GetCurrentPrincipal() = 0; // These methods are called off the main thread. // The mode is initially MODE_PLAYBACK. @@ -317,6 +319,7 @@ public: virtual nsresult Close(); virtual void Suspend(); virtual void Resume(); + virtual already_AddRefed GetCurrentPrincipal(); // Return PR_TRUE if the stream has been closed. PRBool IsClosed() const { return mCacheStream.IsClosed(); } @@ -337,13 +340,18 @@ public: virtual PRBool IsSuspendedByCache(); protected: - class Listener : public nsIStreamListener { + class Listener : public nsIStreamListener, + public nsIInterfaceRequestor, + public nsIChannelEventSink + { public: Listener(nsMediaChannelStream* aStream) : mStream(aStream) {} NS_DECL_ISUPPORTS NS_DECL_NSIREQUESTOBSERVER NS_DECL_NSISTREAMLISTENER + NS_DECL_NSICHANNELEVENTSINK + NS_DECL_NSIINTERFACEREQUESTOR void Revoke() { mStream = nsnull; } @@ -358,10 +366,12 @@ protected: nsresult OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream, PRUint32 aCount); + nsresult OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, PRUint32 aFlags); // Opens the channel, using an HTTP byte range request to start at aOffset // if possible. Main thread only. nsresult OpenChannel(nsIStreamListener** aStreamListener, PRInt64 aOffset); + void SetupChannelHeaders(); // Closes the channel. Main thread only. void CloseChannel(); @@ -373,9 +383,9 @@ protected: PRUint32 *aWriteCount); // Main thread access only + PRInt64 mLastSeekOffset; nsRefPtr mListener; PRUint32 mSuspendCount; - PRPackedBool mSeeking; // Any thread access nsMediaCacheStream mCacheStream; diff --git a/content/media/video/src/nsMediaCache.cpp b/content/media/video/src/nsMediaCache.cpp index eb110dd8b8fe..f697208898bf 100644 --- a/content/media/video/src/nsMediaCache.cpp +++ b/content/media/video/src/nsMediaCache.cpp @@ -1390,10 +1390,47 @@ nsMediaCacheStream::NotifyDataStarted(PRInt64 aOffset) } void -nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData) +nsMediaCacheStream::UpdatePrincipal(nsIPrincipal* aPrincipal) +{ + if (!mPrincipal) { + NS_ASSERTION(!mUsingNullPrincipal, "Are we using a null principal or not?"); + if (mUsingNullPrincipal) { + // Don't let mPrincipal be set to anything + return; + } + mPrincipal = aPrincipal; + return; + } + + if (mPrincipal == aPrincipal) { + // Common case + NS_ASSERTION(!mUsingNullPrincipal, "We can't receive data from a null principal"); + return; + } + if (mUsingNullPrincipal) { + // We've already fallen back to a null principal, so nothing more + // to do. + return; + } + + PRBool equal; + nsresult rv = mPrincipal->Equals(aPrincipal, &equal); + if (NS_SUCCEEDED(rv) && equal) + return; + + // Principals are not equal, so set mPrincipal to a null principal. + mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1"); + mUsingNullPrincipal = PR_TRUE; +} + +void +nsMediaCacheStream::NotifyDataReceived(PRInt64 aSize, const char* aData, + nsIPrincipal* aPrincipal) { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); + UpdatePrincipal(aPrincipal); + nsAutoMonitor mon(gMediaCache->Monitor()); PRInt64 size = aSize; const char* data = aData; diff --git a/content/media/video/src/nsMediaStream.cpp b/content/media/video/src/nsMediaStream.cpp index 08a0d26138e8..cb4c148c013f 100644 --- a/content/media/video/src/nsMediaStream.cpp +++ b/content/media/video/src/nsMediaStream.cpp @@ -65,7 +65,7 @@ using mozilla::TimeStamp; nsMediaChannelStream::nsMediaChannelStream(nsMediaDecoder* aDecoder, nsIChannel* aChannel, nsIURI* aURI) : nsMediaStream(aDecoder, aChannel, aURI), - mSuspendCount(0), mSeeking(PR_FALSE), + mLastSeekOffset(0), mSuspendCount(0), mCacheStream(this), mLock(nsAutoLock::NewLock("media.channel.stream")), mCacheSuspendCount(0) @@ -89,7 +89,9 @@ nsMediaChannelStream::~nsMediaChannelStream() // disconnect the old listener from the nsMediaChannelStream and hook up // a new listener, so notifications from the old channel are discarded // and don't confuse us. -NS_IMPL_ISUPPORTS2(nsMediaChannelStream::Listener, nsIRequestObserver, nsIStreamListener) +NS_IMPL_ISUPPORTS4(nsMediaChannelStream::Listener, + nsIRequestObserver, nsIStreamListener, nsIChannelEventSink, + nsIInterfaceRequestor) nsresult nsMediaChannelStream::Listener::OnStartRequest(nsIRequest* aRequest, @@ -122,6 +124,22 @@ nsMediaChannelStream::Listener::OnDataAvailable(nsIRequest* aRequest, return mStream->OnDataAvailable(aRequest, aStream, aCount); } +nsresult +nsMediaChannelStream::Listener::OnChannelRedirect(nsIChannel* aOldChannel, + nsIChannel* aNewChannel, + PRUint32 aFlags) +{ + if (!mStream) + return NS_OK; + return mStream->OnChannelRedirect(aOldChannel, aNewChannel, aFlags); +} + +nsresult +nsMediaChannelStream::Listener::GetInterface(const nsIID & aIID, void **aResult) +{ + return QueryInterface(aIID, aResult); +} + nsresult nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest) { @@ -148,7 +166,7 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest) ranges); PRBool acceptsRanges = ranges.EqualsLiteral("bytes"); - if (!mSeeking) { + if (mLastSeekOffset == 0) { // Look for duration headers from known Ogg content systems. In the case // of multiple options for obtaining the duration the order of precedence is; // 1) The Media resource metadata if possible (done by the decoder itself). @@ -172,12 +190,12 @@ nsMediaChannelStream::OnStartRequest(nsIRequest* aRequest) PRUint32 responseStatus = 0; hc->GetResponseStatus(&responseStatus); - if (mSeeking && responseStatus == HTTP_OK_CODE) { + if (mLastSeekOffset > 0 && responseStatus == HTTP_OK_CODE) { // If we get an OK response but we were seeking, we have to assume // that seeking doesn't work. We also need to tell the cache that // it's getting data for the start of the stream. mCacheStream.NotifyDataStarted(0); - } else if (!mSeeking && + } else if (mLastSeekOffset == 0 && (responseStatus == HTTP_OK_CODE || responseStatus == HTTP_PARTIAL_RESPONSE_CODE)) { // We weren't seeking and got a valid response status, @@ -243,6 +261,20 @@ nsMediaChannelStream::OnStopRequest(nsIRequest* aRequest, nsresult aStatus) return NS_OK; } +nsresult +nsMediaChannelStream::OnChannelRedirect(nsIChannel* aOld, nsIChannel* aNew, + PRUint32 aFlags) +{ + mChannel = aNew; + SetupChannelHeaders(); + return NS_OK; +} + +struct CopySegmentClosure { + nsCOMPtr mPrincipal; + nsMediaChannelStream* mStream; +}; + NS_METHOD nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream, void *aClosure, @@ -251,8 +283,9 @@ nsMediaChannelStream::CopySegmentToCache(nsIInputStream *aInStream, PRUint32 aCount, PRUint32 *aWriteCount) { - nsMediaChannelStream* stream = static_cast(aClosure); - stream->mCacheStream.NotifyDataReceived(aCount, aFromSegment); + CopySegmentClosure* closure = static_cast(aClosure); + closure->mStream->mCacheStream.NotifyDataReceived(aCount, aFromSegment, + closure->mPrincipal); *aWriteCount = aCount; return NS_OK; } @@ -269,10 +302,17 @@ nsMediaChannelStream::OnDataAvailable(nsIRequest* aRequest, mChannelStatistics.AddBytes(aCount); } + CopySegmentClosure closure; + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + if (secMan && mChannel) { + secMan->GetChannelPrincipal(mChannel, getter_AddRefs(closure.mPrincipal)); + } + closure.mStream = this; + PRUint32 count = aCount; while (count > 0) { PRUint32 read; - nsresult rv = aStream->ReadSegments(CopySegmentToCache, this, count, + nsresult rv = aStream->ReadSegments(CopySegmentToCache, &closure, count, &read); if (NS_FAILED(rv)) return rv; @@ -310,7 +350,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener, *aStreamListener = nsnull; } - mSeeking = aOffset != 0; + mLastSeekOffset = aOffset; mListener = new Listener(this); NS_ENSURE_TRUE(mListener, NS_ERROR_OUT_OF_MEMORY); @@ -319,6 +359,8 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener, *aStreamListener = mListener; NS_ADDREF(*aStreamListener); } else { + mChannel->SetNotificationCallbacks(mListener.get()); + nsCOMPtr listener = mListener.get(); // Ensure that if we're loading cross domain, that the server is sending @@ -342,18 +384,7 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener, NS_ENSURE_SUCCESS(rv, rv); } - // Use a byte range request from the start of the resource. - // This enables us to detect if the stream supports byte range - // requests, and therefore seeking, early. - nsCOMPtr hc = do_QueryInterface(mChannel); - if (hc) { - nsCAutoString rangeString("bytes="); - rangeString.AppendInt(aOffset); - rangeString.Append("-"); - hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE); - } else { - NS_ASSERTION(aOffset == 0, "Don't know how to seek on this channel type"); - } + SetupChannelHeaders(); nsresult rv = mChannel->AsyncOpen(listener, nsnull); NS_ENSURE_SUCCESS(rv, rv); @@ -362,6 +393,23 @@ nsresult nsMediaChannelStream::OpenChannel(nsIStreamListener** aStreamListener, return NS_OK; } +void nsMediaChannelStream::SetupChannelHeaders() +{ + // Always use a byte range request even if we're reading from the start + // of the resource. + // This enables us to detect if the stream supports byte range + // requests, and therefore seeking, early. + nsCOMPtr hc = do_QueryInterface(mChannel); + if (hc) { + nsCAutoString rangeString("bytes="); + rangeString.AppendInt(mLastSeekOffset); + rangeString.Append("-"); + hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, PR_FALSE); + } else { + NS_ASSERTION(mLastSeekOffset == 0, "Don't know how to seek on this channel type"); + } +} + nsresult nsMediaChannelStream::Close() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); @@ -371,6 +419,14 @@ nsresult nsMediaChannelStream::Close() return NS_OK; } +already_AddRefed nsMediaChannelStream::GetCurrentPrincipal() +{ + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); + + nsCOMPtr principal = mCacheStream.GetCurrentPrincipal(); + return principal.forget(); +} + void nsMediaChannelStream::CloseChannel() { NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); @@ -616,6 +672,7 @@ public: virtual nsresult Close(); virtual void Suspend() {} virtual void Resume() {} + virtual already_AddRefed GetCurrentPrincipal(); // These methods are called off the main thread. @@ -757,6 +814,18 @@ nsresult nsMediaFileStream::Close() return NS_OK; } +already_AddRefed nsMediaFileStream::GetCurrentPrincipal() +{ + NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); + + nsCOMPtr principal; + nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); + if (!secMan || !mChannel) + return nsnull; + secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); + return principal.forget(); +} + nsresult nsMediaFileStream::Read(char* aBuffer, PRUint32 aCount, PRUint32* aBytes) { nsAutoLock lock(mLock); @@ -831,18 +900,6 @@ nsMediaStream::Open(nsMediaDecoder* aDecoder, nsIURI* aURI, return NS_OK; } -already_AddRefed nsMediaStream::GetCurrentPrincipal() -{ - NS_ASSERTION(NS_IsMainThread(), "Only call on main thread"); - - nsCOMPtr principal; - nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); - if (!secMan || !mChannel) - return nsnull; - secMan->GetChannelPrincipal(mChannel, getter_AddRefs(principal)); - return principal.forget(); -} - void nsMediaStream::MoveLoadsToBackground() { NS_ASSERTION(!mLoadInBackground, "Why are you calling this more than once?"); mLoadInBackground = PR_TRUE; diff --git a/content/media/video/test/Makefile.in b/content/media/video/test/Makefile.in index 0416483936e7..ddfa1610a425 100644 --- a/content/media/video/test/Makefile.in +++ b/content/media/video/test/Makefile.in @@ -61,6 +61,7 @@ _TEST_FILES = \ ifdef MOZ_OGG _TEST_FILES += \ + dynamic_redirect.sjs \ test_access_control.html \ file_access_controls.html \ test_bug448534.html \ @@ -82,6 +83,7 @@ _TEST_FILES += \ test_info_leak.html \ test_onloadedmetadata.html \ test_load_candidates.html \ + test_mixed_principals.html \ test_play.html \ test_progress1.html \ test_progress3.html \ diff --git a/content/media/video/test/dynamic_redirect.sjs b/content/media/video/test/dynamic_redirect.sjs new file mode 100644 index 000000000000..69fb751d58e8 --- /dev/null +++ b/content/media/video/test/dynamic_redirect.sjs @@ -0,0 +1,39 @@ +// Return seek.ogv file content for the first request with a given key. +// All subsequent requests return a redirect to a different-origin resource. +function handleRequest(request, response) +{ + var key = request.queryString.match(/^key=(.*)$/); + + if (getState(key[1]) == "redirect") { + var origin = request.host == "localhost" ? "example.org" : "localhost:8888"; + response.setStatusLine(request.httpVersion, 303, "See Other"); + response.setHeader("Location", "http://" + origin + "/tests/content/media/video/test/seek.ogv"); + response.setHeader("Content-Type", "text/html"); + return; + } + + setState(key[1], "redirect"); + + var file = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("CurWorkD", Components.interfaces.nsILocalFile); + var fis = Components.classes['@mozilla.org/network/file-input-stream;1']. + createInstance(Components.interfaces.nsIFileInputStream); + var bis = Components.classes["@mozilla.org/binaryinputstream;1"]. + createInstance(Components.interfaces.nsIBinaryInputStream); + var paths = "tests/content/media/video/test/seek.ogv"; + var split = paths.split("/"); + for(var i = 0; i < split.length; ++i) { + file.append(split[i]); + } + fis.init(file, -1, -1, false); + dump("file=" + file + "\n"); + bis.setInputStream(fis); + var bytes = bis.readBytes(bis.available()); + response.setStatusLine(request.httpVersion, 206, "Partial Content"); + response.setHeader("Content-Range", "bytes 0-" + (bytes.length - 1) + "/" + bytes.length); + response.setHeader("Content-Length", ""+bytes.length, false); + response.setHeader("Content-Type", "video/ogg", false); + response.write(bytes, bytes.length); + bis.close(); +} diff --git a/content/media/video/test/test_mixed_principals.html b/content/media/video/test/test_mixed_principals.html new file mode 100644 index 000000000000..8178798d373c --- /dev/null +++ b/content/media/video/test/test_mixed_principals.html @@ -0,0 +1,68 @@ + + + + + Test for Bug 489415 + + + + + +Mozilla Bug 489415 +

+ + + + +
+
+
+ + + From 6724ca001274ca22deaa4f007f72e64c37c7a556 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Fri, 15 May 2009 15:08:41 +1200 Subject: [PATCH 31/46] Bug 491848. Ensure scripts are blocked during frame destruction. Also, don't dispatch paint events during a synchronous plugin message. r=roc --- layout/base/nsDocumentViewer.cpp | 1 + layout/base/nsPresShell.cpp | 3 +++ layout/generic/nsFrame.cpp | 3 +++ layout/printing/nsPrintObject.cpp | 2 ++ widget/src/windows/nsWindow.cpp | 9 ++++++++- 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/layout/base/nsDocumentViewer.cpp b/layout/base/nsDocumentViewer.cpp index dd096cabb54f..4d80313ad280 100644 --- a/layout/base/nsDocumentViewer.cpp +++ b/layout/base/nsDocumentViewer.cpp @@ -4251,6 +4251,7 @@ DocumentViewerImpl::DestroyPresShell() if (selPrivate && mSelectionListener) selPrivate->RemoveSelectionListener(mSelectionListener); + nsAutoScriptBlocker scriptBlocker; mPresShell->Destroy(); mPresShell = nsnull; } diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 9a75ba77af9e..11a75bbad698 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -1773,6 +1773,9 @@ PresShell::Init(nsIDocument* aDocument, NS_IMETHODIMP PresShell::Destroy() { + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), + "destroy called on presshell while scripts not blocked"); + #ifdef MOZ_REFLOW_PERF DumpReflows(); if (mReflowCountMgr) { diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 216de82b9e5f..721dbdc9ee34 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -442,6 +442,9 @@ nsFrame::RemoveFrame(nsIAtom* aListName, void nsFrame::Destroy() { + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), + "destroy called on frame while scripts not blocked"); + #ifdef MOZ_SVG nsSVGEffects::InvalidateDirectRenderingObservers(this); #endif diff --git a/layout/printing/nsPrintObject.cpp b/layout/printing/nsPrintObject.cpp index bb7e47ba99f8..5fa36ab59cbe 100644 --- a/layout/printing/nsPrintObject.cpp +++ b/layout/printing/nsPrintObject.cpp @@ -38,6 +38,7 @@ #include "nsPrintObject.h" #include "nsIContentViewer.h" #include "nsIDOMDocument.h" +#include "nsContentUtils.h" //--------------------------------------------------- //-- nsPrintObject Class Impl @@ -93,6 +94,7 @@ nsPrintObject::DestroyPresentation() mPresContext = nsnull; if (mPresShell) { mPresShell->EndObservingDocument(); + nsAutoScriptBlocker scriptBlocker; mPresShell->Destroy(); } mPresShell = nsnull; diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 72e24a21c899..530cc1e6244c 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -5380,6 +5380,7 @@ nsWindow::ProcessMessageForPlugin(const MSG &aMsg, aCallDefWndProc = PR_FALSE; PRBool fallBackToNonPluginProcess = PR_FALSE; PRBool eventDispatched = PR_FALSE; + PRBool dispatchPendingEvents = PR_TRUE; switch (aMsg.message) { case WM_INPUTLANGCHANGEREQUEST: case WM_INPUTLANGCHANGE: @@ -5422,7 +5423,12 @@ nsWindow::ProcessMessageForPlugin(const MSG &aMsg, case WM_IME_NOTIFY: case WM_IME_REQUEST: case WM_IME_SELECT: + break; + case WM_IME_SETCONTEXT: + // Don't synchronously dispatch when we receive WM_IME_SETCONTEXT + // because we get it during plugin destruction. (bug 491848) + dispatchPendingEvents = PR_FALSE; break; default: @@ -5431,7 +5437,8 @@ nsWindow::ProcessMessageForPlugin(const MSG &aMsg, if (!eventDispatched) aCallDefWndProc = !DispatchPluginEvent(aMsg); - DispatchPendingEvents(); + if (dispatchPendingEvents) + DispatchPendingEvents(); return PR_TRUE; } From 2d82c27e964ac5220809ae7952fa4f7c9f4264f0 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 15 May 2009 15:09:49 +1200 Subject: [PATCH 32/46] Bug 491960. Only reflow due to selection if we did or will have overflowing selection decorations. r=dbaron --- layout/generic/nsTextFrameThebes.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 7507594e1d26..c7732b1cc6c3 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -5032,11 +5032,12 @@ nsTextFrame::SetSelected(nsPresContext* aPresContext, // If the selection state is changed in this content, we need to reflow // to recompute the overflow area for underline of spellchecking or IME if // their underline is thicker than normal decoration line. - PRBool didHaveSelectionUnderline = - !!(mState & TEXT_SELECTION_UNDERLINE_OVERFLOWED); + PRBool didHaveOverflowingSelection = + (mState & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0; nsRect r(nsPoint(0, 0), GetSize()); - if (didHaveSelectionUnderline != aSelected || - (aSelected && CombineSelectionUnderlineRect(PresContext(), r))) { + PRBool willHaveOverflowingSelection = + aSelected && CombineSelectionUnderlineRect(PresContext(), r); + if (didHaveOverflowingSelection || willHaveOverflowingSelection) { PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); From d7d1f2450b7e5b887b17f161333654b6bde3c2e9 Mon Sep 17 00:00:00 2001 From: Timothy Nikkel Date: Fri, 15 May 2009 15:11:59 +1200 Subject: [PATCH 33/46] Bug 492014. Wrap the display list for a subdocument in a wrapper which associates it all with the subdocument's owning element, so sorting-by-content doesn't try to reorder it. r=roc --- layout/base/crashtests/492014.xhtml | 4 ++ layout/base/crashtests/crashtests.list | 1 + layout/generic/nsFrameFrame.cpp | 51 +++++++++++++++----------- 3 files changed, 34 insertions(+), 22 deletions(-) create mode 100644 layout/base/crashtests/492014.xhtml diff --git a/layout/base/crashtests/492014.xhtml b/layout/base/crashtests/492014.xhtml new file mode 100644 index 000000000000..b16622d6912c --- /dev/null +++ b/layout/base/crashtests/492014.xhtml @@ -0,0 +1,4 @@ + + + + diff --git a/layout/base/crashtests/crashtests.list b/layout/base/crashtests/crashtests.list index 1b0745729a0e..a53b260f17b1 100644 --- a/layout/base/crashtests/crashtests.list +++ b/layout/base/crashtests/crashtests.list @@ -233,5 +233,6 @@ load 488390-1.xhtml load 489691.html load 490376-1.xhtml load 490747.html +load 492014.xhtml load 492112-1.xhtml load 492163-1.xhtml diff --git a/layout/generic/nsFrameFrame.cpp b/layout/generic/nsFrameFrame.cpp index b2e86b058e94..45c60ccb1a4c 100644 --- a/layout/generic/nsFrameFrame.cpp +++ b/layout/generic/nsFrameFrame.cpp @@ -339,40 +339,47 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* f = static_cast(subdocView->GetClientData()); - if ((!f || suppressed) && !aBuilder->IsForEventDelivery()) { + nsDisplayList childItems; + + nsRect dirty; + if (f) { + dirty = aDirtyRect - f->GetOffsetTo(this); + aBuilder->EnterPresShell(f, dirty); + NS_ASSERTION(presShell == f->PresContext()->PresShell(), + "these presshells should be the same"); + + rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, &childItems); + } + + // Get the bounds of subdocView relative to the reference frame. + nsRect shellBounds = subdocView->GetBounds() + + mInnerView->GetPosition() + + GetOffsetTo(aBuilder->ReferenceFrame()); + + if (NS_SUCCEEDED(rv) && (!f || suppressed) && + !aBuilder->IsForEventDelivery()) { // If we don't have a frame or painting of the PresShell is suppressed, // try to draw the default background color. (Bug 485275) - - // Get the bounds of subdocView relative to the reference frame. - nsRect shellBounds = subdocView->GetBounds() + - mInnerView->GetPosition() + - GetOffsetTo(aBuilder->ReferenceFrame()); - rv = aLists.Content()->AppendNewToBottom( + rv = childItems.AppendNewToBottom( new (aBuilder) nsDisplaySolidColor( f ? f : this, shellBounds, presShell->GetCanvasBackground())); } - if (!f) - return NS_OK; - - nsRect dirty = aDirtyRect - f->GetOffsetTo(this); - - aBuilder->EnterPresShell(f, dirty); - - // Clip children to the child root frame's rectangle - nsDisplayList childItems; - rv = f->BuildDisplayListForStackingContext(aBuilder, dirty, &childItems); if (NS_SUCCEEDED(rv)) { + // Clip children to the child root frame's rectangle rv = aLists.Content()->AppendNewToTop( - new (aBuilder) nsDisplayClip(nsnull, this, &childItems, - nsRect(aBuilder->ToReferenceFrame(f), f->GetSize()))); - // delete childItems in case of OOM - childItems.DeleteAll(); + new (aBuilder) nsDisplayClip(this, this, &childItems, + shellBounds)); + } + // delete childItems in case of OOM + childItems.DeleteAll(); + + if (f) { + aBuilder->LeavePresShell(f, dirty); } - aBuilder->LeavePresShell(f, dirty); return rv; } From ee64697ca8b3d94adb27f45bb1a36b65abbfe6c8 Mon Sep 17 00:00:00 2001 From: Brandon Sterne Date: Fri, 15 May 2009 15:16:04 +1200 Subject: [PATCH 34/46] Bug 490760. Don't crash on changes to the child node list. r+sr=jst --- content/base/src/nsContentUtils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index e1b605b84beb..373d5668e485 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -3765,7 +3765,7 @@ nsContentUtils::SetNodeTextContent(nsIContent* aContent, // i is unsigned, so i >= is always true for (PRUint32 i = 0; i < childCount; ++i) { nsIContent* child = aContent->GetChildAt(removeIndex); - if (removeIndex == 0 && child->IsNodeOfType(nsINode::eTEXT)) { + if (removeIndex == 0 && child && child->IsNodeOfType(nsINode::eTEXT)) { nsresult rv = child->SetText(aValue, PR_TRUE); NS_ENSURE_SUCCESS(rv, rv); From 760a26b48655cdf40648013f6631399394ce123f Mon Sep 17 00:00:00 2001 From: Honza Bombas Date: Fri, 15 May 2009 15:17:55 +1200 Subject: [PATCH 35/46] Bug 492219. Rename globalStorage constructor to StorageObsolete and localStorage constuctor to Storage. r+sr=jst --- dom/base/nsDOMClassInfo.cpp | 8 ++-- dom/base/nsDOMClassInfoID.h | 2 +- dom/src/storage/nsDOMStorage.cpp | 4 +- .../mochitest/chrome/domstorage_global.js | 4 +- dom/tests/mochitest/localstorage/Makefile.in | 1 + .../localstorage/test_storageConstructor.html | 37 +++++++++++++++++++ 6 files changed, 47 insertions(+), 9 deletions(-) create mode 100644 dom/tests/mochitest/localstorage/test_storageConstructor.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index fdb32ef30d55..a74af84e9996 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1182,7 +1182,7 @@ static nsDOMClassInfoData sClassInfoData[] = { // since a call to addProperty() is always followed by a call to // setProperty(), except in the case when a getter or setter is set // for a property. But we don't care about getters or setters here. - NS_DEFINE_CLASSINFO_DATA(Storage, nsStorageSH, + NS_DEFINE_CLASSINFO_DATA(StorageObsolete, nsStorageSH, DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::WANT_NEWRESOLVE | nsIXPCScriptable::WANT_GETPROPERTY | @@ -1191,7 +1191,7 @@ static nsDOMClassInfoData sClassInfoData[] = { nsIXPCScriptable::DONT_ENUM_STATIC_PROPS | nsIXPCScriptable::WANT_NEWENUMERATE) - NS_DEFINE_CLASSINFO_DATA(Storage2, nsStorage2SH, + NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH, DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::WANT_NEWRESOLVE | nsIXPCScriptable::WANT_GETPROPERTY | @@ -3396,11 +3396,11 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage) + DOM_CLASSINFO_MAP_BEGIN(StorageObsolete, nsIDOMStorage) DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(Storage2, nsIDOMStorage2) + DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage2) DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage2) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoID.h b/dom/base/nsDOMClassInfoID.h index 6a9b220af3bc..fb36733db052 100644 --- a/dom/base/nsDOMClassInfoID.h +++ b/dom/base/nsDOMClassInfoID.h @@ -382,8 +382,8 @@ enum nsDOMClassInfoID { eDOMClassInfo_XPathResult_id, // WhatWG WebApps Objects + eDOMClassInfo_StorageObsolete_id, eDOMClassInfo_Storage_id, - eDOMClassInfo_Storage2_id, eDOMClassInfo_StorageList_id, eDOMClassInfo_StorageItem_id, eDOMClassInfo_StorageEvent_id, diff --git a/dom/src/storage/nsDOMStorage.cpp b/dom/src/storage/nsDOMStorage.cpp index 8454a1b67a5d..4d9029f2cd66 100644 --- a/dom/src/storage/nsDOMStorage.cpp +++ b/dom/src/storage/nsDOMStorage.cpp @@ -471,7 +471,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage) NS_INTERFACE_MAP_ENTRY(nsIDOMStorage) NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete) NS_INTERFACE_MAP_END NS_IMETHODIMP @@ -1342,7 +1342,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage2) NS_INTERFACE_MAP_ENTRY(nsIDOMStorage2) NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage2) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) NS_INTERFACE_MAP_END nsresult diff --git a/dom/tests/mochitest/chrome/domstorage_global.js b/dom/tests/mochitest/chrome/domstorage_global.js index eb741f389071..089307d37654 100644 --- a/dom/tests/mochitest/chrome/domstorage_global.js +++ b/dom/tests/mochitest/chrome/domstorage_global.js @@ -9,10 +9,10 @@ function test_DOMStorage_global() is(globalStorage instanceof StorageList, true, "globalStorage property"); var storage = globalStorage.namedItem(currentDomain); - is(storage instanceof Storage, true, "StorageList namedItem"); + is(storage instanceof StorageObsolete, true, "StorageList namedItem"); var storage2 = globalStorage[currentDomain]; - is(storage2 instanceof Storage, true, "StorageList property syntax"); + is(storage2 instanceof StorageObsolete, true, "StorageList property syntax"); is(storage, storage2, "StorageList namedItem and array return same value"); diff --git a/dom/tests/mochitest/localstorage/Makefile.in b/dom/tests/mochitest/localstorage/Makefile.in index 15bef5b4ae32..ab5caa2391a0 100644 --- a/dom/tests/mochitest/localstorage/Makefile.in +++ b/dom/tests/mochitest/localstorage/Makefile.in @@ -71,6 +71,7 @@ _TEST_FILES = \ test_localStorageQuota.html \ test_localStorageKeyOrder.html \ test_removeOwnersAPI.html \ + test_storageConstructor.html \ $(NULL) _CHROME_FILES = \ diff --git a/dom/tests/mochitest/localstorage/test_storageConstructor.html b/dom/tests/mochitest/localstorage/test_storageConstructor.html new file mode 100644 index 000000000000..8d5932dfb38b --- /dev/null +++ b/dom/tests/mochitest/localstorage/test_storageConstructor.html @@ -0,0 +1,37 @@ + + +localStorage different port numbers + + + + + + + + + + + + + From b8e092eedf646131ab88b63e5e0d87c8d0d89187 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Fri, 15 May 2009 17:31:45 +1200 Subject: [PATCH 36/46] Backing out bug 492219 to fix assertion failure on leak boxes --- dom/base/nsDOMClassInfo.cpp | 8 ++-- dom/base/nsDOMClassInfoID.h | 2 +- dom/src/storage/nsDOMStorage.cpp | 4 +- .../mochitest/chrome/domstorage_global.js | 4 +- dom/tests/mochitest/localstorage/Makefile.in | 1 - .../localstorage/test_storageConstructor.html | 37 ------------------- 6 files changed, 9 insertions(+), 47 deletions(-) delete mode 100644 dom/tests/mochitest/localstorage/test_storageConstructor.html diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index a74af84e9996..fdb32ef30d55 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -1182,7 +1182,7 @@ static nsDOMClassInfoData sClassInfoData[] = { // since a call to addProperty() is always followed by a call to // setProperty(), except in the case when a getter or setter is set // for a property. But we don't care about getters or setters here. - NS_DEFINE_CLASSINFO_DATA(StorageObsolete, nsStorageSH, + NS_DEFINE_CLASSINFO_DATA(Storage, nsStorageSH, DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::WANT_NEWRESOLVE | nsIXPCScriptable::WANT_GETPROPERTY | @@ -1191,7 +1191,7 @@ static nsDOMClassInfoData sClassInfoData[] = { nsIXPCScriptable::DONT_ENUM_STATIC_PROPS | nsIXPCScriptable::WANT_NEWENUMERATE) - NS_DEFINE_CLASSINFO_DATA(Storage, nsStorage2SH, + NS_DEFINE_CLASSINFO_DATA(Storage2, nsStorage2SH, DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::WANT_NEWRESOLVE | nsIXPCScriptable::WANT_GETPROPERTY | @@ -3396,11 +3396,11 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMXPathResult) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(StorageObsolete, nsIDOMStorage) + DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage) DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage) DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(Storage, nsIDOMStorage2) + DOM_CLASSINFO_MAP_BEGIN(Storage2, nsIDOMStorage2) DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorage2) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoID.h b/dom/base/nsDOMClassInfoID.h index fb36733db052..6a9b220af3bc 100644 --- a/dom/base/nsDOMClassInfoID.h +++ b/dom/base/nsDOMClassInfoID.h @@ -382,8 +382,8 @@ enum nsDOMClassInfoID { eDOMClassInfo_XPathResult_id, // WhatWG WebApps Objects - eDOMClassInfo_StorageObsolete_id, eDOMClassInfo_Storage_id, + eDOMClassInfo_Storage2_id, eDOMClassInfo_StorageList_id, eDOMClassInfo_StorageItem_id, eDOMClassInfo_StorageEvent_id, diff --git a/dom/src/storage/nsDOMStorage.cpp b/dom/src/storage/nsDOMStorage.cpp index 4d9029f2cd66..8454a1b67a5d 100644 --- a/dom/src/storage/nsDOMStorage.cpp +++ b/dom/src/storage/nsDOMStorage.cpp @@ -471,7 +471,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage) NS_INTERFACE_MAP_ENTRY(nsIDOMStorage) NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(StorageObsolete) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) NS_INTERFACE_MAP_END NS_IMETHODIMP @@ -1342,7 +1342,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMStorage2) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMStorage2) NS_INTERFACE_MAP_ENTRY(nsIDOMStorage2) NS_INTERFACE_MAP_ENTRY(nsPIDOMStorage) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Storage2) NS_INTERFACE_MAP_END nsresult diff --git a/dom/tests/mochitest/chrome/domstorage_global.js b/dom/tests/mochitest/chrome/domstorage_global.js index 089307d37654..eb741f389071 100644 --- a/dom/tests/mochitest/chrome/domstorage_global.js +++ b/dom/tests/mochitest/chrome/domstorage_global.js @@ -9,10 +9,10 @@ function test_DOMStorage_global() is(globalStorage instanceof StorageList, true, "globalStorage property"); var storage = globalStorage.namedItem(currentDomain); - is(storage instanceof StorageObsolete, true, "StorageList namedItem"); + is(storage instanceof Storage, true, "StorageList namedItem"); var storage2 = globalStorage[currentDomain]; - is(storage2 instanceof StorageObsolete, true, "StorageList property syntax"); + is(storage2 instanceof Storage, true, "StorageList property syntax"); is(storage, storage2, "StorageList namedItem and array return same value"); diff --git a/dom/tests/mochitest/localstorage/Makefile.in b/dom/tests/mochitest/localstorage/Makefile.in index ab5caa2391a0..15bef5b4ae32 100644 --- a/dom/tests/mochitest/localstorage/Makefile.in +++ b/dom/tests/mochitest/localstorage/Makefile.in @@ -71,7 +71,6 @@ _TEST_FILES = \ test_localStorageQuota.html \ test_localStorageKeyOrder.html \ test_removeOwnersAPI.html \ - test_storageConstructor.html \ $(NULL) _CHROME_FILES = \ diff --git a/dom/tests/mochitest/localstorage/test_storageConstructor.html b/dom/tests/mochitest/localstorage/test_storageConstructor.html deleted file mode 100644 index 8d5932dfb38b..000000000000 --- a/dom/tests/mochitest/localstorage/test_storageConstructor.html +++ /dev/null @@ -1,37 +0,0 @@ - - -localStorage different port numbers - - - - - - - - - - - - - From d4b95bebd8c227caf1a752549d2cd1d5d255cc08 Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Wed, 29 Apr 2009 10:07:59 -0700 Subject: [PATCH 37/46] Bug 490655 - Clear recent history dialog too wide [en-US] --- browser/locales/en-US/chrome/browser/sanitize.dtd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/locales/en-US/chrome/browser/sanitize.dtd b/browser/locales/en-US/chrome/browser/sanitize.dtd index 1381d457349c..7e4adc35ff81 100644 --- a/browser/locales/en-US/chrome/browser/sanitize.dtd +++ b/browser/locales/en-US/chrome/browser/sanitize.dtd @@ -58,5 +58,5 @@ that require it. --> mockup at bug 480169 --> - + From 3eed5f9f3e27e3887a505be8f1d6eeb426b1491e Mon Sep 17 00:00:00 2001 From: Drew Willcoxon Date: Fri, 15 May 2009 09:24:33 +0200 Subject: [PATCH 38/46] Bug 489700 - Focus ring is cut off for XUL dropdown in Clear Recent History dialog. r=johnath --- browser/base/content/sanitize.xul | 4 ++- .../browser/preferences/preferences.css | 12 -------- .../gnomestripe/browser/sanitizeDialog.css | 28 ++++++++++++------- .../browser/preferences/preferences.css | 12 -------- .../pinstripe/browser/sanitizeDialog.css | 24 +++++++++------- .../browser/preferences/preferences.css | 12 -------- .../winstripe/browser/sanitizeDialog.css | 28 ++++++++++++------- 7 files changed, 53 insertions(+), 67 deletions(-) diff --git a/browser/base/content/sanitize.xul b/browser/base/content/sanitize.xul index 695b106bdafa..edb5580b8d20 100644 --- a/browser/base/content/sanitize.xul +++ b/browser/base/content/sanitize.xul @@ -128,6 +128,8 @@ value="&clearTimeDuration.suffix;"/> + + #ifdef CRH_DIALOG_TREE_VIEW - + &sanitizeEverythingUndoWarning; diff --git a/browser/themes/gnomestripe/browser/preferences/preferences.css b/browser/themes/gnomestripe/browser/preferences/preferences.css index 07283592685c..1e1c9ae39506 100644 --- a/browser/themes/gnomestripe/browser/preferences/preferences.css +++ b/browser/themes/gnomestripe/browser/preferences/preferences.css @@ -157,18 +157,6 @@ radio[pane=paneAdvanced] { /** * Clear Private Data */ -#SanitizeDurationBox { - padding-bottom: 10px; -} - -#sanitizeDurationChoice { - margin: 0; -} - -#sanitizeDurationLabel { - -moz-margin-start: 3px; -} - #SanitizeDialogPane > groupbox { margin-top: 0; } diff --git a/browser/themes/gnomestripe/browser/sanitizeDialog.css b/browser/themes/gnomestripe/browser/sanitizeDialog.css index 0ef7ef08a726..751d8611f223 100644 --- a/browser/themes/gnomestripe/browser/sanitizeDialog.css +++ b/browser/themes/gnomestripe/browser/sanitizeDialog.css @@ -1,3 +1,13 @@ +#sanitizeDurationChoice { + -moz-margin-end: 0; +} + +/* Align the duration label with the warning box and item list */ +#sanitizeDurationLabel { + -moz-margin-start: 3px; +} + + /* Hide the duration dropdown suffix label if it's empty. Otherwise it takes up a little space, causing the end of the dropdown to not be aligned with the warning box. */ @@ -32,9 +42,8 @@ } #sanitizeEverythingWarningDescBox { - padding: 0; + padding: 0 16px; margin: 0; - padding-left: 16px; } @@ -43,8 +52,8 @@ padding: 0; margin-top: 6px; margin-bottom: 6px; - margin-left: -6px; - margin-right: 0; + -moz-margin-start: -6px; + -moz-margin-end: 0; } .expander-up, @@ -73,16 +82,15 @@ /* Make the item list the same width as the warning box */ #itemList { - margin-left: 0; - margin-right: 0; + -moz-margin-start: 0; + -moz-margin-end: 0; } -/* Make the rightmost dialog button ("accept") align wih right side of the - warning box */ +/* Align the last dialog button with the end of the warning box */ .prefWindow-dlgbuttons { - margin-right: 0; + -moz-margin-end: 0; } .dialog-button[dlgtype="accept"] { - margin-right: 0; + -moz-margin-end: 0; } diff --git a/browser/themes/pinstripe/browser/preferences/preferences.css b/browser/themes/pinstripe/browser/preferences/preferences.css index 2643c9317feb..0d6bc2b3e100 100644 --- a/browser/themes/pinstripe/browser/preferences/preferences.css +++ b/browser/themes/pinstripe/browser/preferences/preferences.css @@ -276,18 +276,6 @@ caption { /** * Clear Private Data */ -#SanitizeDurationBox { - padding-bottom: 10px; -} - -#sanitizeDurationChoice { - margin: 0; -} - -#sanitizeDurationLabel { - -moz-margin-start: 3px; -} - #SanitizeDialogPane > groupbox { margin-top: 0; } diff --git a/browser/themes/pinstripe/browser/sanitizeDialog.css b/browser/themes/pinstripe/browser/sanitizeDialog.css index d097ca7da0fc..d09230801fc8 100644 --- a/browser/themes/pinstripe/browser/sanitizeDialog.css +++ b/browser/themes/pinstripe/browser/sanitizeDialog.css @@ -1,3 +1,9 @@ +/* Align the duration label with the warning box and item list */ +#sanitizeDurationLabel { + -moz-margin-start: 1px; +} + + /* Hide the duration dropdown suffix label if it's empty. Otherwise it takes up a little space, causing the end of the dropdown to not be aligned with the warning box. */ @@ -32,9 +38,8 @@ } #sanitizeEverythingWarningDescBox { - padding: 0; + padding: 0 16px; margin: 0; - padding-left: 16px; } @@ -43,8 +48,8 @@ padding: 0; margin-top: 6px; margin-bottom: 6px; - margin-left: -2px; - margin-right: 0; + -moz-margin-start: -2px; + -moz-margin-end: 0; } .expander-up, @@ -74,16 +79,15 @@ /* Make the item list the same width as the warning box */ #itemList { - margin-left: 0; - margin-right: 0; + -moz-margin-start: 0; + -moz-margin-end: 0; } -/* Make the rightmost dialog button ("accept") align wih right side of the - warning box */ +/* Align the last dialog button with the end of the warning box */ .prefWindow-dlgbuttons { - margin-right: 0; + -moz-margin-end: 0; } .dialog-button[dlgtype="accept"] { - margin-right: 0; + -moz-margin-end: 0; } diff --git a/browser/themes/winstripe/browser/preferences/preferences.css b/browser/themes/winstripe/browser/preferences/preferences.css index 710e0be9df1e..1dd74153f92e 100644 --- a/browser/themes/winstripe/browser/preferences/preferences.css +++ b/browser/themes/winstripe/browser/preferences/preferences.css @@ -177,18 +177,6 @@ radio[pane=paneAdvanced][selected="true"] { /** * Clear Private Data */ -#SanitizeDurationBox { - padding-bottom: 10px; -} - -#sanitizeDurationChoice { - margin: 0; -} - -#sanitizeDurationLabel { - -moz-margin-start: 3px; -} - #SanitizeDialogPane > groupbox { margin-top: 0; } diff --git a/browser/themes/winstripe/browser/sanitizeDialog.css b/browser/themes/winstripe/browser/sanitizeDialog.css index f8396dc75e4c..c1b142b68664 100644 --- a/browser/themes/winstripe/browser/sanitizeDialog.css +++ b/browser/themes/winstripe/browser/sanitizeDialog.css @@ -1,3 +1,13 @@ +#sanitizeDurationChoice { + -moz-margin-end: 0; +} + +/* Align the duration label with the warning box and item list */ +#sanitizeDurationLabel { + -moz-margin-start: 3px; +} + + /* Hide the duration dropdown suffix label if it's empty. Otherwise it takes up a little space, causing the end of the dropdown to not be aligned with the warning box. */ @@ -32,9 +42,8 @@ } #sanitizeEverythingWarningDescBox { - padding: 0; + padding: 0 16px; margin: 0; - padding-left: 16px; } @@ -43,8 +52,8 @@ padding: 0; margin-top: 6px; margin-bottom: 6px; - margin-left: -1px; - margin-right: 0; + -moz-margin-start: -1px; + -moz-margin-end: 0; } .expander-up, @@ -69,16 +78,15 @@ /* Make the item list the same width as the warning box */ #itemList { - margin-left: 0; - margin-right: 0; + -moz-margin-start: 0; + -moz-margin-end: 0; } -/* Make the rightmost dialog button ("cancel") align wih right side of the - warning box */ +/* Align the last dialog button with the end of the warning box */ .prefWindow-dlgbuttons { - margin-right: 0; + -moz-margin-end: 0; } .dialog-button[dlgtype="cancel"] { - margin-right: 0; + -moz-margin-end: 0; } From 0c68c4784f21f36d5d038942e0ee86f729f07d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20O=E2=80=99Shannessy?= Date: Thu, 14 May 2009 09:46:50 -0700 Subject: [PATCH 39/46] Bug 491431 - No way to distinguish tab detach vs tab close, r=gavin --- browser/base/content/tabbrowser.xml | 13 ++-- browser/base/content/test/Makefile.in | 1 + .../base/content/test/browser_bug491431.js | 69 +++++++++++++++++++ 3 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 browser/base/content/test/browser_bug491431.js diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index c871c825df3c..4448d4cc4939 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -31,6 +31,7 @@ - Michael Ventnor - Mark Pilgrim - Dão Gottwald + - Paul O’Shannessy - - Alternatively, the contents of this file may be used under the terms of - either the GNU General Public License Version 2 or later (the "GPL"), or @@ -1487,7 +1488,7 @@ @@ -1501,7 +1502,7 @@ - + @@ -1511,7 +1512,7 @@ var browser = this.getBrowserForTab(aTab); - if (aFireBeforeUnload) { + if (!aTabWillBeMoved) { let ds = browser.docShell; if (ds.contentViewer && !ds.contentViewer.permitUnload()) return null; @@ -1550,8 +1551,8 @@ // Dispatch a notification. // We dispatch it before any teardown so that event listeners can // inspect the tab that's about to close. - var evt = document.createEvent("Events"); - evt.initEvent("TabClose", true, false); + var evt = document.createEvent("UIEvent"); + evt.initUIEvent("TabClose", true, false, window, aTabWillBeMoved ? 1 : 0); aTab.dispatchEvent(evt); // Remove the tab's filter and progress listener. @@ -1716,7 +1717,7 @@ // First, start teardown of the other browser. Make sure to not // fire the beforeunload event in the process. Close the other // window if this was its last tab. - var endRemoveArgs = remoteBrowser._beginRemoveTab(aOtherTab, false, true); + var endRemoveArgs = remoteBrowser._beginRemoveTab(aOtherTab, true, true); // Unhook our progress listener var ourIndex = aOurTab._tPos; diff --git a/browser/base/content/test/Makefile.in b/browser/base/content/test/Makefile.in index e4754a5cb3dd..fc6298e63430 100644 --- a/browser/base/content/test/Makefile.in +++ b/browser/base/content/test/Makefile.in @@ -115,6 +115,7 @@ _BROWSER_FILES = browser_sanitize-timespans.js \ browser_overflowScroll.js \ browser_sanitizeDialog.js \ browser_tabs_owner.js \ + browser_bug491431.js \ $(NULL) ifeq (,$(filter mac cocoa,$(MOZ_WIDGET_TOOLKIT))) diff --git a/browser/base/content/test/browser_bug491431.js b/browser/base/content/test/browser_bug491431.js new file mode 100644 index 000000000000..717bf02973cc --- /dev/null +++ b/browser/base/content/test/browser_bug491431.js @@ -0,0 +1,69 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is bug 491431 test. + * + * The Initial Developer of the Original Code is + * Mozilla Corporation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul O’Shannessy + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +let testPage = "data:text/plain,test bug 491431 Page"; + +function test() { + waitForExplicitFinish(); + + let newWin, tabA, tabB; + + // test normal close + tabA = gBrowser.addTab(testPage); + gBrowser.addEventListener("TabClose", function(aEvent) { + gBrowser.removeEventListener("TabClose", arguments.callee, true); + ok(!aEvent.detail, "This was a normal tab close"); + + // test tab close by moving + tabB = gBrowser.addTab(testPage); + gBrowser.addEventListener("TabClose", function(aEvent) { + gBrowser.removeEventListener("TabClose", arguments.callee, true); + executeSoon(function() { + ok(aEvent.detail, "This was a tab closed by moving"); + + // cleanup + newWin.close(); + executeSoon(finish); + }); + }, true); + newWin = gBrowser.replaceTabWithWindow(tabB); + }, true); + gBrowser.removeTab(tabA); +} + From 37bfad5344f3f472573b950e0efd3e1fa099f250 Mon Sep 17 00:00:00 2001 From: Arpad Borsos Date: Thu, 14 May 2009 11:31:35 +0200 Subject: [PATCH 40/46] Bug 492937 - kill nsLineLayout::mPlacedFloats and related code; r+sr=roc --- layout/generic/nsBlockReflowState.cpp | 16 ++++------------ layout/generic/nsBlockReflowState.h | 1 - layout/generic/nsLineLayout.cpp | 16 ++++------------ layout/generic/nsLineLayout.h | 5 ----- 4 files changed, 8 insertions(+), 30 deletions(-) diff --git a/layout/generic/nsBlockReflowState.cpp b/layout/generic/nsBlockReflowState.cpp index 0d59fe1d9449..bed22e113855 100644 --- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -585,11 +585,10 @@ nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, mBlock->ComputeFloatWidth(*this, floatAvailableSpace, aPlaceholder) <= aAvailableWidth)) { // And then place it - PRBool isLeftFloat; // force it to fit if we're at the top of the block and we can't // break before this PRBool forceFit = IsAdjacentWithTop() && !aLineLayout.LineIsBreakable(); - placed = FlowAndPlaceFloat(fc, &isLeftFloat, aReflowStatus, forceFit); + placed = FlowAndPlaceFloat(fc, aReflowStatus, forceFit); NS_ASSERTION(placed || !forceFit, "If we asked for force-fit, it should have been placed"); if (forceFit || (placed && !NS_FRAME_IS_TRUNCATED(aReflowStatus))) { @@ -599,8 +598,7 @@ nsBlockReflowState::AddFloat(nsLineLayout& aLineLayout, nsRect availSpace(nsPoint(floatAvailSpace.mRect.x + BorderPadding().left, mY), floatAvailSpace.mRect.Size()); - aLineLayout.UpdateBand(availSpace, isLeftFloat, - aPlaceholder->GetOutOfFlowFrame()); + aLineLayout.UpdateBand(availSpace, aPlaceholder->GetOutOfFlowFrame()); // Record this float in the current-line list mCurrentLineFloats.Append(fc); @@ -752,7 +750,6 @@ nsBlockReflowState::CanPlaceFloat(const nsSize& aFloatSize, PRUint8 aFloats, PRBool nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, - PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus, PRBool aForceFit) { @@ -892,14 +889,11 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, // coordinates are computed relative to the translation in the // spacemanager which means that the impacted region will be // inside the border/padding area. - PRBool isLeftFloat; nscoord floatX, floatY; if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { - isLeftFloat = PR_TRUE; floatX = floatAvailableSpace.mRect.x; } else { - isLeftFloat = PR_FALSE; if (!keepFloatOnSameLine) { floatX = floatAvailableSpace.mRect.XMost() - floatSize.width; } @@ -910,7 +904,6 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, floatX = floatAvailableSpace.mRect.x; } } - *aIsLeftFloat = isLeftFloat; const nsMargin& borderPadding = BorderPadding(); floatY = mY - borderPadding.top; if (floatY < 0) { @@ -936,7 +929,7 @@ nsBlockReflowState::FlowAndPlaceFloat(nsFloatCache* aFloatCache, if (region.width < 0) { // Preserve the right margin-edge for left floats and the left // margin-edge for right floats - if (isLeftFloat) { + if (NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats) { region.x = region.XMost(); } region.width = 0; @@ -1039,9 +1032,8 @@ nsBlockReflowState::PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aList, PRB } #endif // Place the float - PRBool isLeftFloat; nsReflowStatus reflowStatus; - PRBool placed = FlowAndPlaceFloat(fc, &isLeftFloat, reflowStatus, aForceFit); + PRBool placed = FlowAndPlaceFloat(fc, reflowStatus, aForceFit); NS_ASSERTION(placed || !aForceFit, "If we're in force-fit mode, we should have placed the float"); diff --git a/layout/generic/nsBlockReflowState.h b/layout/generic/nsBlockReflowState.h index 9d611b5faf4a..fe13eaaace28 100644 --- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -119,7 +119,6 @@ public: const nsFlowAreaRect& aFloatAvailableSpace, PRBool aForceFit); PRBool FlowAndPlaceFloat(nsFloatCache* aFloatCache, - PRBool* aIsLeftFloat, nsReflowStatus& aReflowStatus, PRBool aForceFit); PRBool PlaceBelowCurrentLineFloats(nsFloatCacheFreeList& aFloats, PRBool aForceFit); diff --git a/layout/generic/nsLineLayout.cpp b/layout/generic/nsLineLayout.cpp index 596e429b45ba..07b206afd8e4 100644 --- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -89,9 +89,6 @@ #define FIX_BUG_50257 -#define PLACED_LEFT 0x1 -#define PLACED_RIGHT 0x2 - nsLineLayout::nsLineLayout(nsPresContext* aPresContext, nsFloatManager* aFloatManager, const nsHTMLReflowState* aOuterReflowState, @@ -118,7 +115,6 @@ nsLineLayout::nsLineLayout(nsPresContext* aPresContext, mTextAlign = mStyleText->mTextAlign; mLineNumber = 0; mFlags = 0; // default all flags to false except those that follow here... - mPlacedFloats = 0; mTotalPlacedFrames = 0; mTopEdge = 0; mTrimmableWidth = 0; @@ -204,7 +200,6 @@ nsLineLayout::BeginLineReflow(nscoord aX, nscoord aY, SetFlag(LL_FIRSTLETTERSTYLEOK, PR_FALSE); SetFlag(LL_ISTOPOFPAGE, aIsTopOfPage); - mPlacedFloats = 0; SetFlag(LL_IMPACTEDBYFLOATS, aImpactedByFloats); mTotalPlacedFrames = 0; SetFlag(LL_LINEISEMPTY, PR_TRUE); @@ -292,14 +287,13 @@ nsLineLayout::EndLineReflow() void nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace, - PRBool aPlacedLeftFloat, nsIFrame* aFloatFrame) { #ifdef REALLY_NOISY_REFLOW - printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p placedLeft=%s\n will set mImpacted to PR_TRUE\n", + printf("nsLL::UpdateBand %d, %d, %d, %d, frame=%p\n will set mImpacted to PR_TRUE\n", aNewAvailSpace.x, aNewAvailSpace.y, aNewAvailSpace.width, aNewAvailSpace.height, - aFloatFrame, aPlacedLeftFloat?"true":"false"); + aFloatFrame); #endif #ifdef DEBUG if ((aNewAvailSpace.width != NS_UNCONSTRAINEDSIZE) && CRAZY_WIDTH(aNewAvailSpace.width)) { @@ -325,10 +319,9 @@ nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace, nscoord deltaWidth = aNewAvailSpace.width - (mRootSpan->mRightEdge - mRootSpan->mLeftEdge); #ifdef NOISY_REFLOW nsFrame::ListTag(stdout, mBlockReflowState->frame); - printf(": UpdateBand: %d,%d,%d,%d deltaWidth=%d deltaX=%d %s float\n", + printf(": UpdateBand: %d,%d,%d,%d deltaWidth=%d deltaX=%d\n", aNewAvailSpace.x, aNewAvailSpace.y, - aNewAvailSpace.width, aNewAvailSpace.height, deltaWidth, deltaX, - aPlacedLeftFloat ? "left" : "right"); + aNewAvailSpace.width, aNewAvailSpace.height, deltaWidth, deltaX); #endif // Update the root span position @@ -365,7 +358,6 @@ nsLineLayout::UpdateBand(const nsRect& aNewAvailSpace, } mTopEdge = aNewAvailSpace.y; - mPlacedFloats |= (aPlacedLeftFloat ? PLACED_LEFT : PLACED_RIGHT); SetFlag(LL_IMPACTEDBYFLOATS, PR_TRUE); SetFlag(LL_LASTFLOATWASLETTERFRAME, diff --git a/layout/generic/nsLineLayout.h b/layout/generic/nsLineLayout.h index da6bb788a5ef..c23c43c032b9 100644 --- a/layout/generic/nsLineLayout.h +++ b/layout/generic/nsLineLayout.h @@ -95,12 +95,9 @@ public: * due to available space for the line boxes changing. * @param aX/aY/aWidth/aHeight are the new available * space rectangle, relative to the containing block. - * @param aPlacedLeftFloat whether we placed a left float or a right - * float to trigger the available space change * @param aFloatFrame the float frame that was placed. */ void UpdateBand(const nsRect& aNewAvailableSpace, - PRBool aPlacedLeftFloat, nsIFrame* aFloatFrame); nsresult BeginSpan(nsIFrame* aFrame, @@ -548,8 +545,6 @@ protected: PRUint8 mTextAlign; - PRUint8 mPlacedFloats; - nsresult NewPerFrameData(PerFrameData** aResult); nsresult NewPerSpanData(PerSpanData** aResult); From 998da51c36be46568bcf94bf97db98c53fb88c92 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Fri, 15 May 2009 11:37:59 +0100 Subject: [PATCH 41/46] Bug 485592 Fix nsTextToSubURI::UnEscapeAndConvert allocator mismatches r=smontagu sr=jag --- intl/uconv/src/nsTextToSubURI.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/intl/uconv/src/nsTextToSubURI.cpp b/intl/uconv/src/nsTextToSubURI.cpp index 2b7a0db02b80..b9f8b49f802a 100644 --- a/intl/uconv/src/nsTextToSubURI.cpp +++ b/intl/uconv/src/nsTextToSubURI.cpp @@ -84,7 +84,7 @@ NS_IMETHODIMP nsTextToSubURI::ConvertAndEscape( if(NS_SUCCEEDED(rv = encoder->GetMaxLength(text, ulen, &outlen))) { if(outlen >= 256) { - pBuf = (char*)PR_Malloc(outlen+1); + pBuf = (char*)NS_Alloc(outlen+1); } if(nsnull == pBuf) { outlen = 255; @@ -105,7 +105,7 @@ NS_IMETHODIMP nsTextToSubURI::ConvertAndEscape( } } if(pBuf != buf) - PR_Free(pBuf); + NS_Free(pBuf); } NS_RELEASE(encoder); } @@ -123,7 +123,7 @@ NS_IMETHODIMP nsTextToSubURI::UnEscapeAndConvert( nsresult rv = NS_OK; // unescape the string, unescape changes the input - char *unescaped = nsCRT::strdup((char *) text); + char *unescaped = NS_strdup(text); if (nsnull == unescaped) return NS_ERROR_OUT_OF_MEMORY; unescaped = nsUnescape(unescaped); @@ -140,7 +140,7 @@ NS_IMETHODIMP nsTextToSubURI::UnEscapeAndConvert( PRInt32 len = strlen(unescaped); PRInt32 outlen = 0; if (NS_SUCCEEDED(rv = decoder->GetMaxLength(unescaped, len, &outlen))) { - pBuf = (PRUnichar *) PR_Malloc((outlen+1)*sizeof(PRUnichar*)); + pBuf = (PRUnichar *) NS_Alloc((outlen+1)*sizeof(PRUnichar*)); if (nsnull == pBuf) rv = NS_ERROR_OUT_OF_MEMORY; else { @@ -149,13 +149,13 @@ NS_IMETHODIMP nsTextToSubURI::UnEscapeAndConvert( *_retval = pBuf; } else - PR_Free(pBuf); + NS_Free(pBuf); } } NS_RELEASE(decoder); } } - PR_Free(unescaped); + NS_Free(unescaped); return rv; } @@ -210,7 +210,7 @@ nsresult nsTextToSubURI::convertURItoUnicode(const nsAFlatCString &aCharset, rv = unicodeDecoder->GetMaxLength(aURI.get(), srcLen, &dstLen); NS_ENSURE_SUCCESS(rv, rv); - PRUnichar *ustr = (PRUnichar *) nsMemory::Alloc(dstLen * sizeof(PRUnichar)); + PRUnichar *ustr = (PRUnichar *) NS_Alloc(dstLen * sizeof(PRUnichar)); NS_ENSURE_TRUE(ustr, NS_ERROR_OUT_OF_MEMORY); rv = unicodeDecoder->Convert(aURI.get(), &srcLen, ustr, &dstLen); @@ -218,7 +218,7 @@ nsresult nsTextToSubURI::convertURItoUnicode(const nsAFlatCString &aCharset, if (NS_SUCCEEDED(rv)) _retval.Assign(ustr, dstLen); - nsMemory::Free(ustr); + NS_Free(ustr); return rv; } From 822ee0c47976e3299c04dd99f674c49556e9603c Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Thu, 14 May 2009 07:37:40 -0400 Subject: [PATCH 42/46] bug 483062 - figure out how to get crash stacks from xpcshell tests - followup to fix packaged-tests. r=bsmedberg --HG-- extra : rebase_source : 01ef1b53cabf31479599280bae2f1b9fd99b7f87 --- testing/xpcshell/Makefile.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testing/xpcshell/Makefile.in b/testing/xpcshell/Makefile.in index dc5ad961e150..0184a01d5b92 100644 --- a/testing/xpcshell/Makefile.in +++ b/testing/xpcshell/Makefile.in @@ -60,6 +60,11 @@ TEST_HARNESS_FILES := \ tail.js \ $(NULL) +# Extra files needed from $(topsrcdir)/build +EXTRA_BUILD_FILES := \ + automationutils.py \ + $(NULL) + # Components / typelibs that don't get packaged with # the build, but that we need for the test harness. TEST_HARNESS_COMPONENTS := \ @@ -72,5 +77,6 @@ PKG_STAGE = $(DIST)/test-package-stage stage-package: $(NSINSTALL) -D $(PKG_STAGE)/xpcshell/tests @(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -) + @(cd $(topsrcdir)/build && tar $(TAR_CREATE_FLAGS) - $(EXTRA_BUILD_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -) @(cd $(DEPTH)/_tests/xpcshell/ && tar $(TAR_CREATE_FLAGS) - *) | (cd $(PKG_STAGE)/xpcshell/tests && tar -xf -) @(cd $(DIST)/bin/components && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_COMPONENTS)) | (cd $(PKG_STAGE)/bin/components && tar -xf -) From c1d061934ad2899512317bbdbde781ac7304f069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 15 May 2009 15:33:59 +0200 Subject: [PATCH 43/46] Bug 491221 - default button styling missing in the bookmarks properties dialog. r=mak, enn --- browser/base/content/browser-places.js | 36 +++++++++---------- .../places/content/bookmarkProperties.js | 26 -------------- .../places/content/bookmarkProperties.xul | 1 - toolkit/content/widgets/tree.xml | 13 ++++--- 4 files changed, 27 insertions(+), 49 deletions(-) diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 58761cc6a5f3..00acb857f5e5 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -65,7 +65,7 @@ var StarUI = { // to avoid impacting startup / new window performance element.hidden = false; element.addEventListener("popuphidden", this, false); - element.addEventListener("keypress", this, true); + element.addEventListener("keypress", this, false); return this.panel = element; }, @@ -112,25 +112,25 @@ var StarUI = { } break; case "keypress": - if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) { - // If the panel is visible the ESC key is mapped to the cancel button - // unless we are editing a folder in the folderTree, or an - // autocomplete popup is open. - if (!this._element("editBookmarkPanelContent").hidden) { - var elt = aEvent.target; - if ((elt.localName != "tree" || !elt.hasAttribute("editing")) && - !elt.popupOpen) - this.cancelButtonOnCommand(); - } + if (aEvent.getPreventDefault()) { + // The event has already been consumed inside of the panel. + break; } - else if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN) { - // hide the panel unless the folder tree or an expander are focused - // or an autocomplete popup is open. - if (aEvent.target.localName != "tree" && - aEvent.target.className != "expander-up" && - aEvent.target.className != "expander-down" && - !aEvent.target.popupOpen) + switch (aEvent.keyCode) { + case KeyEvent.DOM_VK_ESCAPE: + if (!this._element("editBookmarkPanelContent").hidden) + this.cancelButtonOnCommand(); + break; + case KeyEvent.DOM_VK_RETURN: + if (aEvent.target.className == "expander-up" || + aEvent.target.className == "expander-down" || + aEvent.target.id == "editBMPanel_newFolderButton") { + //XXX Why is this necessary? The getPreventDefault() check should + // be enough. + break; + } this.panel.hidePopup(); + break; } break; } diff --git a/browser/components/places/content/bookmarkProperties.js b/browser/components/places/content/bookmarkProperties.js index abd6ba5d2dfe..e1905cd2ba2c 100644 --- a/browser/components/places/content/bookmarkProperties.js +++ b/browser/components/places/content/bookmarkProperties.js @@ -374,10 +374,6 @@ var BookmarkPropertiesPanel = { this._element("siteLocationField") .addEventListener("input", this, false); } - - // Set on document to get the event before an autocomplete popup could - // be hidden on Enter. - document.addEventListener("keypress", this, true); } window.sizeToContent(); @@ -388,27 +384,6 @@ var BookmarkPropertiesPanel = { handleEvent: function BPP_handleEvent(aEvent) { var target = aEvent.target; switch (aEvent.type) { - case "keypress": - function canAcceptDialog(aElement) { - // on Enter we accept the dialog unless: - // - the folder tree is focused - // - an expander is focused - // - an autocomplete (eg. tags) popup is open - // - a menulist is open - // - a multiline textbox is focused - return aElement.localName != "tree" && - aElement.className != "expander-up" && - aElement.className != "expander-down" && - !aElement.popupOpen && - !aElement.open && - !(aElement.localName == "textbox" && - aElement.getAttribute("multiline") == "true"); - } - if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN && - canAcceptDialog(target)) - document.documentElement.acceptDialog(); - break; - case "input": if (target.id == "editBMPanel_locationField" || target.id == "editBMPanel_feedLocationField" || @@ -506,7 +481,6 @@ var BookmarkPropertiesPanel = { // currently registered EventListener on the EventTarget has no effect. this._element("tagsSelectorRow") .removeEventListener("DOMAttrModified", this, false); - document.removeEventListener("keypress", this, true); this._element("folderTreeRow") .removeEventListener("DOMAttrModified", this, false); this._element("locationField") diff --git a/browser/components/places/content/bookmarkProperties.xul b/browser/components/places/content/bookmarkProperties.xul index 8af977c759b9..28a0fd3407de 100644 --- a/browser/components/places/content/bookmarkProperties.xul +++ b/browser/components/places/content/bookmarkProperties.xul @@ -54,7 +54,6 @@
@@ -722,11 +724,13 @@ if (this._editingColumn) { this.stopEditing(true); this.focus(); - event.stopPropagation(); } else { - this.changeOpenState(this.currentIndex); + if (!this.changeOpenState(this.currentIndex)) + return; // don't consume the event if the open state wasn't changed } + event.stopPropagation(); + event.preventDefault(); ]]> @@ -735,6 +739,7 @@ this.stopEditing(false); this.focus(); event.stopPropagation(); + event.preventDefault(); } ]]> From 502872806918eec76dfa3e06176329431e1e4613 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Fri, 15 May 2009 15:44:05 +0200 Subject: [PATCH 44/46] Bug 493178 - replacetabwithwindow doesn't always return a value. r=gavin --- browser/base/content/tabbrowser.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 4448d4cc4939..806b21cf7527 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2281,7 +2281,7 @@ Date: Fri, 15 May 2009 22:56:58 +0800 Subject: [PATCH 45/46] Bug 492736 - robust nsXULTreeAccessible::GetLastChild, r=marcoz, davidb --- accessible/src/xul/nsXULTreeAccessible.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/accessible/src/xul/nsXULTreeAccessible.cpp b/accessible/src/xul/nsXULTreeAccessible.cpp index b56b3dc5581a..2184ce34be8a 100644 --- a/accessible/src/xul/nsXULTreeAccessible.cpp +++ b/accessible/src/xul/nsXULTreeAccessible.cpp @@ -269,20 +269,27 @@ NS_IMETHODIMP nsXULTreeAccessible::GetFirstChild(nsIAccessible **aFirstChild) return NS_OK; } -NS_IMETHODIMP nsXULTreeAccessible::GetLastChild(nsIAccessible **aLastChild) +NS_IMETHODIMP +nsXULTreeAccessible::GetLastChild(nsIAccessible **aLastChild) { + NS_ENSURE_ARG_POINTER(aLastChild); + *aLastChild = nsnull; + NS_ENSURE_TRUE(mTree && mTreeView, NS_ERROR_FAILURE); - PRInt32 rowCount; + PRInt32 rowCount = 0; mTreeView->GetRowCount(&rowCount); if (rowCount > 0) { nsCOMPtr column = GetLastVisibleColumn(mTree); - return GetCachedTreeitemAccessible(rowCount - 1, column, aLastChild); + nsresult rv = GetCachedTreeitemAccessible(rowCount - 1, column, aLastChild); + NS_ENSURE_SUCCESS(rv, rv); } - else // if there is not any rows, use treecols as tree's last child - nsAccessible::GetLastChild(aLastChild); - return NS_OK; + if (*aLastChild) + return NS_OK; + + // If there is not any rows, use treecols as tree's last child. + return nsAccessible::GetLastChild(aLastChild); } // tree's children count is row count + treecols count From ed5dd519a53be7bcc8e25e75b584d486c888c3ae Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Fri, 15 May 2009 23:15:24 +0900 Subject: [PATCH 46/46] Bug 488851 - Enable SSE2 for pixman. r=jmuizelaar --- gfx/cairo/libpixman/src/Makefile.in | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/gfx/cairo/libpixman/src/Makefile.in b/gfx/cairo/libpixman/src/Makefile.in index 150ea7f7e9cd..e3579dec40d9 100644 --- a/gfx/cairo/libpixman/src/Makefile.in +++ b/gfx/cairo/libpixman/src/Makefile.in @@ -69,6 +69,7 @@ else USE_MMX=1 USE_SSE2=1 MMX_CFLAGS= +SSE2_CFLAGS= endif endif ifeq (arm,$(findstring arm,$(OS_TEST))) @@ -81,11 +82,11 @@ ifdef GNU_CC ifeq (86,$(findstring 86,$(OS_TEST))) USE_MMX=1 MMX_CFLAGS=-mmmx -Winline -# See bug 410509 why we can't use SSE2 yet on linux -#USE_SSE2=1 -#MMX_CFLAGS+=-msse -msse2 +USE_SSE2=1 +SSE2_CFLAGS=-msse2 -Winline ifneq ($(MOZ_WIDGET_TOOLKIT),os2) MMX_CFLAGS+=--param inline-unit-growth=10000 --param large-function-growth=10000 +SSE2_CFLAGS+=--param inline-unit-growth=10000 --param large-function-growth=10000 endif endif ifeq (arm,$(findstring arm,$(OS_TEST))) @@ -177,7 +178,7 @@ pixman-mmx.$(OBJ_SUFFIX): pixman-mmx.c Makefile Makefile.in pixman-sse2.$(OBJ_SUFFIX): pixman-sse2.c Makefile Makefile.in $(REPORT_BUILD) @$(MAKE_DEPS_AUTO_CC) - $(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $(MMX_CFLAGS) $(_VPATH_SRCS) + $(ELOG) $(CC) $(OUTOPTION)$@ -c $(COMPILE_CFLAGS) $(SSE2_CFLAGS) $(_VPATH_SRCS) pixman-arm-neon.$(OBJ_SUFFIX): pixman-arm-neon.c Makefile Makefile.in $(REPORT_BUILD)