Beispiel #1
0
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
void
js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
{
    const char *usageStr = "usage";
    PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
    RootedId id(cx, NameToId(usageAtom));
    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->nativeLookup(cx, id));
    JS_ASSERT(!shape->configurable());
    JS_ASSERT(!shape->writable());
    JS_ASSERT(shape->hasDefaultGetter());

    RootedValue usage(cx);
    if (!JS_LookupProperty(cx, callee, "usage", &usage))
        return;

    if (usage.isUndefined()) {
        JS_ReportError(cx, "%s", msg);
    } else {
        JSString *str = usage.toString();
        JS::Anchor<JSString *> a_str(str);
        const jschar *chars = JS_GetStringCharsZ(cx, str);
        if (!chars)
            return;
        JS_ReportError(cx, "%s. Usage: %hs", msg, chars);
    }
}
Beispiel #2
0
void
nsFileInputStream::Serialize(InputStreamParams& aParams,
                             FileDescriptorArray& aFileDescriptors)
{
    FileInputStreamParams params;

    if (mFD) {
        FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFD));
        NS_ASSERTION(fd, "This should never be null!");

        DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
        NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");

        params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;

        Close();
    } else {
        NS_WARNING("This file has not been opened (or could not be opened). "
                   "Sending an invalid file descriptor to the other process!");

        params.fileDescriptorIndex() = UINT32_MAX;
    }

    int32_t behaviorFlags = mBehaviorFlags;

    // The receiving process (or thread) is going to have an open file
    // descriptor automatically so transferring this flag is meaningless.
    behaviorFlags &= ~nsIFileInputStream::DEFER_OPEN;

    params.behaviorFlags() = behaviorFlags;
    params.ioFlags() = mIOFlags;

    aParams = params;
}
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
void
ReportUsageError(JSContext *cx, JSObject *callee, const char *msg)
{
    const char *usageStr = "usage";
    PropertyName *usageAtom = js_Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
    DebugOnly<Shape *> shape = callee->nativeLookup(cx, NameToId(usageAtom));
    JS_ASSERT(!shape->configurable());
    JS_ASSERT(!shape->writable());
    JS_ASSERT(shape->hasDefaultGetter());

    jsval usage;
    if (!JS_LookupProperty(cx, callee, "usage", &usage))
        return;

    if (JSVAL_IS_VOID(usage)) {
        JS_ReportError(cx, "%s", msg);
    } else {
        JSString *str = JSVAL_TO_STRING(usage);
        JS::Anchor<JSString *> a_str(str);
        const jschar *chars = JS_GetStringCharsZ(cx, str);
        if (!chars)
            return;
        JS_ReportError(cx, "%s. Usage: %hs", msg, chars);
    }
}
Beispiel #4
0
void
GCMarker::checkZone(void* p)
{
    MOZ_ASSERT(started);
    DebugOnly<Cell*> cell = static_cast<Cell*>(p);
    MOZ_ASSERT_IF(cell->isTenured(), cell->asTenured().zone()->isCollecting());
}
Beispiel #5
0
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
void
js::ReportUsageError(JSContext *cx, HandleObject callee, const char *msg)
{
    const char *usageStr = "usage";
    PropertyName *usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
    RootedId id(cx, NameToId(usageAtom));
    DebugOnly<Shape *> shape = static_cast<Shape *>(callee->as<NativeObject>().lookup(cx, id));
    MOZ_ASSERT(!shape->configurable());
    MOZ_ASSERT(!shape->writable());
    MOZ_ASSERT(shape->hasDefaultGetter());

    RootedValue usage(cx);
    if (!JS_LookupProperty(cx, callee, "usage", &usage))
        return;

    if (usage.isUndefined()) {
        JS_ReportError(cx, "%s", msg);
    } else {
        JSString *str = usage.toString();
        if (!str->ensureFlat(cx))
            return;
        AutoStableStringChars chars(cx);
        if (!chars.initTwoByte(cx, str))
            return;

        JS_ReportError(cx, "%s. Usage: %hs", msg, chars.twoByteRange().start().get());
    }
}
Beispiel #6
0
/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
void
js::ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg)
{
    const char* usageStr = "usage";
    PropertyName* usageAtom = Atomize(cx, usageStr, strlen(usageStr))->asPropertyName();
    RootedId id(cx, NameToId(usageAtom));
    DebugOnly<Shape*> shape = static_cast<Shape*>(callee->as<JSFunction>().lookup(cx, id));
    MOZ_ASSERT(!shape->configurable());
    MOZ_ASSERT(!shape->writable());
    MOZ_ASSERT(shape->hasDefaultGetter());

    RootedValue usage(cx);
    if (!JS_GetProperty(cx, callee, "usage", &usage))
        return;

    if (!usage.isString()) {
        JS_ReportErrorASCII(cx, "%s", msg);
    } else {
        RootedString usageStr(cx, usage.toString());
        JSAutoByteString str;
        if (!str.encodeUtf8(cx, usageStr))
            return;
        JS_ReportErrorUTF8(cx, "%s. Usage: %s", msg, str.ptr());
    }
}
Beispiel #7
0
JSObject *
RegExpCompartment::createMatchResultTemplateObject(JSContext *cx)
{
    JS_ASSERT(!matchResultTemplateObject_);

    /* Create template array object */
    RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
    if (!templateObject)
        return matchResultTemplateObject_; // = nullptr

    /* Set dummy index property */
    RootedValue index(cx, Int32Value(0));
    if (!baseops::DefineProperty(cx, templateObject, cx->names().index, index,
                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
        return matchResultTemplateObject_; // = nullptr

    /* Set dummy input property */
    RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
    if (!baseops::DefineProperty(cx, templateObject, cx->names().input, inputVal,
                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
        return matchResultTemplateObject_; // = nullptr

    // Make sure that the properties are in the right slots.
    DebugOnly<Shape *> shape = templateObject->lastProperty();
    JS_ASSERT(shape->previous()->slot() == 0 &&
              shape->previous()->propidRef() == NameToId(cx->names().index));
    JS_ASSERT(shape->slot() == 1 &&
              shape->propidRef() == NameToId(cx->names().input));

    matchResultTemplateObject_ = templateObject;

    return matchResultTemplateObject_;
}
Beispiel #8
0
static inline void
CheckMarkedThing(JSTracer *trc, T *thing)
{
    JS_ASSERT(trc);
    JS_ASSERT(thing);
    JS_ASSERT(thing->compartment());
    JS_ASSERT(thing->compartment()->rt == trc->runtime);
    JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc), !thing->compartment()->scheduledForDestruction);
    JS_ASSERT(trc->debugPrinter || trc->debugPrintArg);

    DebugOnly<JSRuntime *> rt = trc->runtime;

#ifdef DEBUG
    rt->assertValidThread();
#endif

    JS_ASSERT_IF(thing->compartment()->requireGCTracer(), IS_GC_MARKING_TRACER(trc));

    JS_ASSERT(thing->isAligned());

    JS_ASSERT_IF(rt->gcStrictCompartmentChecking,
                 thing->compartment()->isCollecting() ||
                 thing->compartment() == rt->atomsCompartment);

    JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc) && ((GCMarker *)trc)->getMarkColor() == GRAY,
                 thing->compartment()->isGCMarkingGray() ||
                 thing->compartment() == rt->atomsCompartment);
}
void
nsTemporaryFileInputStream::Serialize(InputStreamParams& aParams,
                                      FileDescriptorArray& aFileDescriptors)
{
  TemporaryFileInputStreamParams params;

  MutexAutoLock lock(mFileDescOwner->FileMutex());
  MOZ_ASSERT(mFileDescOwner->mFD);
  if (!mClosed) {
    FileHandleType fd = FileHandleType(PR_FileDesc2NativeHandle(mFileDescOwner->mFD));
    NS_ASSERTION(fd, "This should never be null!");

    DebugOnly<FileDescriptor*> dbgFD = aFileDescriptors.AppendElement(fd);
    NS_ASSERTION(dbgFD->IsValid(), "Sending an invalid file descriptor!");

    params.fileDescriptorIndex() = aFileDescriptors.Length() - 1;

    Close();
  } else {
    NS_WARNING("The stream is already closed. "
               "Sending an invalid file descriptor to the other process!");

    params.fileDescriptorIndex() = UINT32_MAX;
  }
  params.startPos() = mCurPos;
  params.endPos() = mEndPos;
  aParams = params;
}
Beispiel #10
0
NativeObject*
JSCompartment::getOrCreateIterResultTemplateObject(JSContext* cx)
{
    if (iterResultTemplate_)
        return iterResultTemplate_;

    // Create template plain object
    RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
    if (!templateObject)
        return iterResultTemplate_; // = nullptr

    // Create a new group for the template.
    Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
    RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(),
                                                                  proto));
    if (!group)
        return iterResultTemplate_; // = nullptr
    templateObject->setGroup(group);

    // Set dummy `value` property
    if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
                                  JSPROP_ENUMERATE))
    {
        return iterResultTemplate_; // = nullptr
    }

    // Set dummy `done` property
    if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
                                  JSPROP_ENUMERATE))
    {
        return iterResultTemplate_; // = nullptr
    }

    if (!group->unknownProperties()) {
        // Update `value` property typeset, since it can be any value.
        HeapTypeSet* types = group->maybeGetProperty(NameToId(cx->names().value));
        MOZ_ASSERT(types);
        {
            AutoEnterAnalysis enter(cx);
            types->makeUnknown(cx);
        }
    }

    // Make sure that the properties are in the right slots.
    DebugOnly<Shape*> shape = templateObject->lastProperty();
    MOZ_ASSERT(shape->previous()->slot() == JSCompartment::IterResultObjectValueSlot &&
               shape->previous()->propidRef() == NameToId(cx->names().value));
    MOZ_ASSERT(shape->slot() == JSCompartment::IterResultObjectDoneSlot &&
               shape->propidRef() == NameToId(cx->names().done));

    iterResultTemplate_.set(templateObject);

    return iterResultTemplate_;
}
void
CodeGeneratorX86::visitBox(LBox* box)
{
    const LDefinition* type = box->getDef(TYPE_INDEX);

    DebugOnly<const LAllocation*> a = box->getOperand(0);
    MOZ_ASSERT(!a->isConstant());

    // On x86, the input operand and the output payload have the same
    // virtual register. All that needs to be written is the type tag for
    // the type definition.
    masm.mov(ImmWord(MIRTypeToTag(box->type())), ToRegister(type));
}
Beispiel #12
0
ArrayObject*
RegExpCompartment::createMatchResultTemplateObject(JSContext* cx)
{
    MOZ_ASSERT(!matchResultTemplateObject_);

    /* Create template array object */
    RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, RegExpObject::MaxPairCount,
                                     nullptr, TenuredObject));
    if (!templateObject)
        return matchResultTemplateObject_; // = nullptr

    // Create a new group for the template.
    Rooted<TaggedProto> proto(cx, templateObject->getTaggedProto());
    ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, templateObject->getClass(), proto);
    if (!group)
        return matchResultTemplateObject_; // = nullptr
    templateObject->setGroup(group);

    /* Set dummy index property */
    RootedValue index(cx, Int32Value(0));
    if (!NativeDefineProperty(cx, templateObject, cx->names().index, index, nullptr, nullptr,
                              JSPROP_ENUMERATE))
    {
        return matchResultTemplateObject_; // = nullptr
    }

    /* Set dummy input property */
    RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
    if (!NativeDefineProperty(cx, templateObject, cx->names().input, inputVal, nullptr, nullptr,
                              JSPROP_ENUMERATE))
    {
        return matchResultTemplateObject_; // = nullptr
    }

    // Make sure that the properties are in the right slots.
    DebugOnly<Shape*> shape = templateObject->lastProperty();
    MOZ_ASSERT(shape->previous()->slot() == 0 &&
               shape->previous()->propidRef() == NameToId(cx->names().index));
    MOZ_ASSERT(shape->slot() == 1 &&
               shape->propidRef() == NameToId(cx->names().input));

    // Make sure type information reflects the indexed properties which might
    // be added.
    AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::StringType());
    AddTypePropertyId(cx, templateObject, JSID_VOID, TypeSet::UndefinedType());

    matchResultTemplateObject_.set(templateObject);

    return matchResultTemplateObject_;
}
Beispiel #13
0
bool
js::CurrentThreadCanAccessZone(Zone *zone)
{
    if (CurrentThreadCanAccessRuntime(zone->runtime_))
        return true;
    if (InParallelSection()) {
        DebugOnly<PerThreadData *> pt = js::TlsPerThreadData.get();
        JS_ASSERT(pt && pt->associatedWith(zone->runtime_));
        return true;
    }

    // Only zones in use by an exclusive thread can be used off the main thread
    // or outside of PJS. We don't keep track of which thread owns such zones
    // though, so this check is imperfect.
    return zone->usedByExclusiveThread;
}
Beispiel #14
0
ArrayObject *
RegExpCompartment::createMatchResultTemplateObject(JSContext *cx)
{
    MOZ_ASSERT(!matchResultTemplateObject_);

    /* Create template array object */
    RootedArrayObject templateObject(cx, NewDenseUnallocatedArray(cx, 0, nullptr, TenuredObject));
    if (!templateObject)
        return matchResultTemplateObject_; // = nullptr

    // Create a new type for the template.
    Rooted<TaggedProto> proto(cx, templateObject->getTaggedProto());
    types::TypeObject *type =
        cx->compartment()->types.newTypeObject(cx, templateObject->getClass(), proto);
    templateObject->setType(type);

    /* Set dummy index property */
    RootedValue index(cx, Int32Value(0));
    if (!baseops::DefineProperty(cx, templateObject, cx->names().index, index,
                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
        return matchResultTemplateObject_; // = nullptr

    /* Set dummy input property */
    RootedValue inputVal(cx, StringValue(cx->runtime()->emptyString));
    if (!baseops::DefineProperty(cx, templateObject, cx->names().input, inputVal,
                                 JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE))
        return matchResultTemplateObject_; // = nullptr

    // Make sure that the properties are in the right slots.
    DebugOnly<Shape *> shape = templateObject->lastProperty();
    MOZ_ASSERT(shape->previous()->slot() == 0 &&
               shape->previous()->propidRef() == NameToId(cx->names().index));
    MOZ_ASSERT(shape->slot() == 1 &&
               shape->propidRef() == NameToId(cx->names().input));

    // Make sure type information reflects the indexed properties which might
    // be added.
    types::AddTypePropertyId(cx, templateObject, JSID_VOID, types::Type::StringType());
    types::AddTypePropertyId(cx, templateObject, JSID_VOID, types::Type::UndefinedType());

    matchResultTemplateObject_.set(templateObject);

    return matchResultTemplateObject_;
}
Beispiel #15
0
bool
JSCompartment::wrap(JSContext *cx, MutableHandleValue vp, HandleObject existingArg)
{
    JS_ASSERT(cx->compartment() == this);
    JS_ASSERT(this != rt->atomsCompartment);
    JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment());
    JS_ASSERT_IF(existingArg, vp.isObject());
    JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg));

    unsigned flags = 0;

    JS_CHECK_CHROME_RECURSION(cx, return false);

    AutoDisableProxyCheck adpc(rt);

    /* Only GC things have to be wrapped or copied. */
    if (!vp.isMarkable())
        return true;

    if (vp.isString()) {
        JSString *str = vp.toString();

        /* If the string is already in this compartment, we are done. */
        if (str->zone() == zone())
            return true;

        /* If the string is an atom, we don't have to copy. */
        if (str->isAtom()) {
            JS_ASSERT(str->zone() == cx->runtime()->atomsCompartment->zone());
            return true;
        }
    }

    /*
     * Wrappers should really be parented to the wrapped parent of the wrapped
     * object, but in that case a wrapped global object would have a NULL
     * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead,
     * we parent all wrappers to the global object in their home compartment.
     * This loses us some transparency, and is generally very cheesy.
     */
    HandleObject global = cx->global();

    /* Unwrap incoming objects. */
    if (vp.isObject()) {
        RootedObject obj(cx, &vp.toObject());

        if (obj->compartment() == this)
            return WrapForSameCompartment(cx, obj, vp);

        /* Translate StopIteration singleton. */
        if (obj->is<StopIterationObject>())
            return js_FindClassObject(cx, JSProto_StopIteration, vp);

        /* Unwrap the object, but don't unwrap outer windows. */
        obj = UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags);

        if (obj->compartment() == this)
            return WrapForSameCompartment(cx, obj, vp);

        if (cx->runtime()->preWrapObjectCallback) {
            obj = cx->runtime()->preWrapObjectCallback(cx, global, obj, flags);
            if (!obj)
                return false;
        }

        if (obj->compartment() == this)
            return WrapForSameCompartment(cx, obj, vp);
        vp.setObject(*obj);

#ifdef DEBUG
        {
            JSObject *outer = GetOuterObject(cx, obj);
            JS_ASSERT(outer && outer == obj);
        }
#endif
    }

    RootedValue key(cx, vp);

    /* If we already have a wrapper for this value, use it. */
    if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) {
        vp.set(p->value);
        if (vp.isObject()) {
            DebugOnly<JSObject *> obj = &vp.toObject();
            JS_ASSERT(obj->isCrossCompartmentWrapper());
            JS_ASSERT(obj->getParent() == global);
        }
        return true;
    }

    if (vp.isString()) {
        Rooted<JSLinearString *> str(cx, vp.toString()->ensureLinear(cx));
        if (!str)
            return false;

        JSString *wrapped = js_NewStringCopyN<CanGC>(cx, str->chars(), str->length());
        if (!wrapped)
            return false;

        vp.setString(wrapped);
        if (!putWrapper(key, vp))
            return false;

        if (str->zone()->isGCMarking()) {
            /*
             * All string wrappers are dropped when collection starts, but we
             * just created a new one.  Mark the wrapped string to stop it being
             * finalized, because if it was then the pointer in this
             * compartment's wrapper map would be left dangling.
             */
            JSString *tmp = str;
            MarkStringUnbarriered(&rt->gcMarker, &tmp, "wrapped string");
            JS_ASSERT(tmp == str);
        }

        return true;
    }

    RootedObject proto(cx, Proxy::LazyProto);
    RootedObject obj(cx, &vp.toObject());
    RootedObject existing(cx, existingArg);
    if (existing) {
        /* Is it possible to reuse |existing|? */
        if (!existing->getTaggedProto().isLazy() ||
            existing->getClass() != &ObjectProxyClass ||
            existing->getParent() != global ||
            obj->isCallable())
        {
            existing = NULL;
        }
    }

    /*
     * We hand in the original wrapped object into the wrap hook to allow
     * the wrap hook to reason over what wrappers are currently applied
     * to the object.
     */
    RootedObject wrapper(cx);
    wrapper = cx->runtime()->wrapObjectCallback(cx, existing, obj, proto, global, flags);
    if (!wrapper)
        return false;

    // We maintain the invariant that the key in the cross-compartment wrapper
    // map is always directly wrapped by the value.
    JS_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject());

    vp.setObject(*wrapper);
    return putWrapper(key, vp);
}
Beispiel #16
0
void
EventQueue::CoalesceEvents()
{
  NS_ASSERTION(mEvents.Length(), "There should be at least one pending event!");
  uint32_t tail = mEvents.Length() - 1;
  AccEvent* tailEvent = mEvents[tail];

  switch(tailEvent->mEventRule) {
    case AccEvent::eCoalesceReorder:
    {
      DebugOnly<Accessible*> target = tailEvent->mAccessible.get();
      MOZ_ASSERT(target->IsApplication() ||
                 target->IsOuterDoc() ||
                 target->IsXULTree(),
                 "Only app or outerdoc accessible reorder events are in the queue");
      MOZ_ASSERT(tailEvent->GetEventType() == nsIAccessibleEvent::EVENT_REORDER, "only reorder events should be queued");
      break; // case eCoalesceReorder
    }

    case AccEvent::eCoalesceOfSameType:
    {
      // Coalesce old events by newer event.
      for (uint32_t index = tail - 1; index < tail; index--) {
        AccEvent* accEvent = mEvents[index];
        if (accEvent->mEventType == tailEvent->mEventType &&
          accEvent->mEventRule == tailEvent->mEventRule) {
          accEvent->mEventRule = AccEvent::eDoNotEmit;
          return;
        }
      }
      break; // case eCoalesceOfSameType
    }

    case AccEvent::eCoalesceSelectionChange:
    {
      AccSelChangeEvent* tailSelChangeEvent = downcast_accEvent(tailEvent);
      for (uint32_t index = tail - 1; index < tail; index--) {
        AccEvent* thisEvent = mEvents[index];
        if (thisEvent->mEventRule == tailEvent->mEventRule) {
          AccSelChangeEvent* thisSelChangeEvent =
            downcast_accEvent(thisEvent);

          // Coalesce selection change events within same control.
          if (tailSelChangeEvent->mWidget == thisSelChangeEvent->mWidget) {
            CoalesceSelChangeEvents(tailSelChangeEvent, thisSelChangeEvent, index);
            return;
          }
        }
      }
      break; // eCoalesceSelectionChange
    }

    case AccEvent::eCoalesceStateChange:
    {
      // If state change event is duped then ignore previous event. If state
      // change event is opposite to previous event then no event is emitted
      // (accessible state wasn't changed).
      for (uint32_t index = tail - 1; index < tail; index--) {
        AccEvent* thisEvent = mEvents[index];
        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
            thisEvent->mEventType == tailEvent->mEventType &&
            thisEvent->mAccessible == tailEvent->mAccessible) {
          AccStateChangeEvent* thisSCEvent = downcast_accEvent(thisEvent);
          AccStateChangeEvent* tailSCEvent = downcast_accEvent(tailEvent);
          if (thisSCEvent->mState == tailSCEvent->mState) {
            thisEvent->mEventRule = AccEvent::eDoNotEmit;
            if (thisSCEvent->mIsEnabled != tailSCEvent->mIsEnabled)
              tailEvent->mEventRule = AccEvent::eDoNotEmit;
          }
        }
      }
      break; // eCoalesceStateChange
    }

    case AccEvent::eCoalesceTextSelChange:
    {
      // Coalesce older event by newer event for the same selection or target.
      // Events for same selection may have different targets and vice versa one
      // target may be pointed by different selections (for latter see
      // bug 927159).
      for (uint32_t index = tail - 1; index < tail; index--) {
        AccEvent* thisEvent = mEvents[index];
        if (thisEvent->mEventRule != AccEvent::eDoNotEmit &&
            thisEvent->mEventType == tailEvent->mEventType) {
          AccTextSelChangeEvent* thisTSCEvent = downcast_accEvent(thisEvent);
          AccTextSelChangeEvent* tailTSCEvent = downcast_accEvent(tailEvent);
          if (thisTSCEvent->mSel == tailTSCEvent->mSel ||
              thisEvent->mAccessible == tailEvent->mAccessible)
            thisEvent->mEventRule = AccEvent::eDoNotEmit;
        }

      }
      break; // eCoalesceTextSelChange
    }

    case AccEvent::eRemoveDupes:
    {
      // Check for repeat events, coalesce newly appended event by more older
      // event.
      for (uint32_t index = tail - 1; index < tail; index--) {
        AccEvent* accEvent = mEvents[index];
        if (accEvent->mEventType == tailEvent->mEventType &&
          accEvent->mEventRule == tailEvent->mEventRule &&
          accEvent->mAccessible == tailEvent->mAccessible) {
          tailEvent->mEventRule = AccEvent::eDoNotEmit;
          return;
        }
      }
      break; // case eRemoveDupes
    }

    default:
      break; // case eAllowDupes, eDoNotEmit
  } // switch
}