Index: src/orca/liveregions.py
===================================================================
--- src/orca/liveregions.py	(revision 3529)
+++ src/orca/liveregions.py	(working copy)
@@ -389,22 +389,30 @@
         attrs = self._getAttrDictionary(event.source)
         content = [] 
         labels = []
+        
+        # A message is divided into two parts: labels and content.  We
+        # will first try to get the content.  If there is None, 
+        # assume it is an invalid message and return None
         if event.type.startswith('object:children-changed:add'):
-            # Get a handle to the Text interface for the target.
-            try:
-                targetitext = event.any_data.queryText()
-            except NotImplementedError:
-                return None
-
             # Get the text based on the atomic property
             try:
                 if attrs['container-atomic'] == 'true':
-                    content.append(self._script.expandEOCs(event.source))
+                    # expand the source if atomic is true
+                    newcontent = self._script.expandEOCs(event.source)
                 else:
-                    content.append(targetitext.getText(0, -1))
+                    # expand the target if atomic is false
+                    newcontent = self._script.expandEOCs(event.any_data)
             except (KeyError, TypeError):
-                content.append(targetitext.getText(0, -1))
+                    # expand the target if there is no ARIA markup
+                    newcontent = self._script.expandEOCs(event.any_data)
 
+            # add our content to the returned message or return None if no
+            # content
+            if newcontent:
+                content.append(newcontent)
+            else:
+                return None
+
         else: #object:text-changed:insert
             # Get a handle to the Text interface for the source.
             # Serious problems if this fails.
@@ -417,21 +425,28 @@
             # We found an embed character.  We can expect a children-changed
             # event, which we will act on, so just return.
             txt = sourceitext.getText(0, -1)
-            if txt.find(self._script.EMBEDDED_OBJECT_CHARACTER) == 0:
+            if txt.count(self._script.EMBEDDED_OBJECT_CHARACTER) > 0:
                 return None
 
-            # Get labeling information
-            labels = self._getLabelsAsUtterances(event.source)
-
             # Get the text based on the atomic property
             try:
                 if attrs['container-atomic'] == 'true':
-                    content.append(txt)
+                    newcontent = txt
                 else:
-                    content.append(txt[event.detail1:]) 
+                    newcontent = txt[event.detail1:event.detail1+event.detail2]
             except KeyError:
-                content.append(txt)
+                newcontent = txt[event.detail1:event.detail1+event.detail2]
 
+            # add our content to the returned message or return None if no
+            # content
+            if len(newcontent) > 0:
+                content.append(newcontent)
+            else:
+                return None
+
+        # Get the labeling information now that we have good content.
+        labels = self._getLabelsAsUtterances(event.source)
+
         return {'content':content, 'labels':labels}
 
     def flushMessages(self):
Index: src/orca/Gecko.py
===================================================================
--- src/orca/Gecko.py	(revision 3529)
+++ src/orca/Gecko.py	(working copy)
@@ -62,6 +62,7 @@
 import speech
 import speechgenerator
 import speechserver
+import time
 import where_am_I
 import bookmarks
 
@@ -1911,6 +1912,7 @@
         # loading a page.
         #
         self._loadingDocumentContent = False
+        self._loadingDocumentTime = 0.0
 
         # In tabbed content (i.e., Firefox's support for one tab per
         # URL), we also keep track of the caret context in each tab.
@@ -3880,6 +3882,7 @@
             # Reset the live region manager.
             self.liveMngr.reset()
             self._loadingDocumentContent = False
+            self._loadingDocumentTime = time.time()
 
     def onDocumentLoadStopped(self, event):
         """Called when a web page load is interrupted."""
@@ -3888,6 +3891,7 @@
         #
         if event.source.getRole() == pyatspi.ROLE_DOCUMENT_FRAME:
             self._loadingDocumentContent = False
+            self._loadingDocumentTime = time.time()
 
     def onNameChanged(self, event):
         """Called whenever a property on an object changes.
@@ -5215,8 +5219,7 @@
         # regions.  We will handle everything else as a live region.  We
         # will do the cheap tests first
         if self._loadingDocumentContent \
-              or not  settings.inferLiveRegions \
-              or not event.type.endswith(':system'):
+              or not  settings.inferLiveRegions:
             return False
 
         # Ideally, we would like to do a inDocumentContent() call to filter out
@@ -5226,35 +5229,42 @@
 
         # event.type specific checks
         if event.type.startswith('object:children-changed'):
-            # This will filter out lists that are not of interest and
-            # events from other tabs.
-            stateset = event.source.getState()
-            if stateset.contains(pyatspi.STATE_FOCUSABLE) \
-                   or stateset.contains(pyatspi.STATE_FOCUSED) \
-                   or not stateset.contains(pyatspi.STATE_VISIBLE):
-                return False
+            if event.type.endswith(':system'):
+                # This will filter out list items that are not of interest and
+                # events from other tabs.
+                stateset = event.any_data.getState()
+                if stateset.contains(pyatspi.STATE_SELECTABLE) \
+                    or not stateset.contains(pyatspi.STATE_VISIBLE):
+                    return False
 
-            # Now we need to look at the object attributes
-            attrs = self._getAttrDictionary(event.source)
-            # Good live region markup
-            if attrs.has_key('container-live'):
-                return True
+                # Now we need to look at the object attributes
+                attrs = self._getAttrDictionary(event.source)
+                # Good live region markup
+                if attrs.has_key('container-live'):
+                    return True
 
-            # We see this one with the URL bar opening (sometimes)
-            if attrs.has_key('tag') and attrs['tag'] == 'xul:richlistbox':
-                return False
+                # We see this one with the URL bar opening (sometimes)
+                if attrs.has_key('tag') and attrs['tag'] == 'xul:richlistbox':
+                    return False
 
-            # This eliminates all ARIA widgets that are not considered live
-            if attrs.has_key('xml-roles') \
-                                    and not attrs.has_key('container-live'):
-                return False
+                # This eliminates all ARIA widgets that are not considered live
+                if attrs.has_key('xml-roles') \
+                                      and attrs['xml-roles'] != 'alert':
+                    return False
+            else:
+                # Some alerts have been seen without the :system postfix.
+                # We will take care of them separately.
+                attrs = self._getAttrDictionary(event.any_data)
+                if attrs.has_key('xml-roles') \
+                                      and attrs['xml-roles'] != 'alert':
+                    return False
 
-        else: # object:text-inserted event
+        elif event.type.startswith('object:text-changed:insert:system'):
             # Live regions will not be focusable.
             # Filter out events from hidden tabs (not VISIBLE)
             stateset = event.source.getState()
             if stateset.contains(pyatspi.STATE_FOCUSABLE) \
-                   or stateset.contains(pyatspi.STATE_FOCUSED) \
+                   or stateset.contains(pyatspi.STATE_SELECTABLE) \
                    or not stateset.contains(pyatspi.STATE_VISIBLE):
                 return False
 
@@ -5271,12 +5281,19 @@
                 return False
 
             # This eliminates all ARIA widgets that are not considered live
-            if attrs.has_key('xml-roles') \
-                                and not attrs.has_key('container-live'):
+            if attrs.has_key('xml-roles'):
                 return False
-        # It sure looks like a live region
-        return True
+        else: # object:text-inserted events
+            return False
 
+        # This last filter gets rid of some events that come in after
+        # window:activate event.  They are usually areas of a page that
+        # are built dynamically.
+        if time.time() - self._loadingDocumentTime > 2.0:
+            return True
+        else:
+            return False
+
     def getCharacterOffsetInParent(self, obj):
         """Returns the character offset of the embedded object
         character for this object in its parent's accessible text.
