Bug 619273 - Move the selection state bit from frames to content nodes. r=smaug
authorMats Palmgren <matspal@gmail.com>
Tue, 20 Dec 2011 10:15:41 +0100
changeset 83114 e3766ee732ccbec82ad84e76eb60f87a3e8182d3
parent 83104 feaccb6a4c352008d68264ab1ceb1343bbc6cf3d
child 83115 a08580f18cb72e6bba342a5caaf1eb30119ef812
push id21732
push userbmo@edmorley.co.uk
push dateTue, 20 Dec 2011 16:54:34 +0000
treeherdermozilla-central@a8506ab2c654 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs619273
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 619273 - Move the selection state bit from frames to content nodes. r=smaug
accessible/src/html/nsHTMLTableAccessible.cpp
content/base/public/nsINode.h
content/base/public/nsIRange.h
content/base/src/nsGenericDOMDataNode.h
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsRange.cpp
content/base/src/nsRange.h
content/base/src/nsTextNode.cpp
layout/base/nsDisplayList.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsFrameSelection.h
layout/generic/nsIFrame.h
layout/generic/nsSelection.cpp
layout/generic/nsTextFrame.h
layout/generic/nsTextFrameThebes.cpp
layout/mathml/nsMathMLmoFrame.cpp
layout/printing/nsPrintEngine.cpp
layout/svg/base/src/nsSVGGlyphFrame.cpp
layout/svg/base/src/nsSVGGlyphFrame.h
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -101,19 +101,17 @@ nsHTMLTableCellAccessible::NativeState()
 {
   PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
 
   nsIFrame *frame = mContent->GetPrimaryFrame();
   NS_ASSERTION(frame, "No frame for valid cell accessible!");
 
   if (frame) {
     state |= states::SELECTABLE;
-    bool isSelected = false;
-    frame->GetSelected(&isSelected);
-    if (isSelected)
+    if (frame->IsSelected())
       state |= states::SELECTED;
   }
 
   return state;
 }
 
 nsresult
 nsHTMLTableCellAccessible::GetAttributesInternal(nsIPersistentProperties *aAttributes)
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -912,16 +912,28 @@ public:
     CheckNotNativeAnonymous();
     return false;
 #else
     return HasFlag(NODE_IS_IN_ANONYMOUS_SUBTREE);
 #endif
   }
 
   /**
+   * Returns true if |this| node is the common ancestor of the start/end
+   * nodes of a Range in a Selection or a descendant of such a common ancestor.
+   * This node is definitely not selected when |false| is returned, but it may
+   * or may not be selected when |true| is returned.
+   */
+  bool IsSelectionDescendant() const
+  {
+    return IsDescendantOfCommonAncestorForRangeInSelection() ||
+           IsCommonAncestorForRangeInSelection();
+  }
+
+  /**
    * Get the root content of an editor. So, this node must be a descendant of
    * an editor. Note that this should be only used for getting input or textarea
    * editor's root content. This method doesn't support HTML editors.
    */
   nsIContent* GetTextEditorRootContent(nsIEditor** aEditor = nsnull);
 
   /**
    * Get the nearest selection root, ie. the node that will be selected if the
@@ -1196,16 +1208,21 @@ private:
     // documents with different id mappings.
     ElementHasID,
     // Set if the element might have inline style.
     ElementMayHaveStyle,
     // Set if the element has a name attribute set.
     ElementHasName,
     // Set if the element might have a contenteditable attribute set.
     ElementMayHaveContentEditableAttr,
+    // Set if the node is the common ancestor of the start/end nodes of a Range
+    // that is in a Selection.
+    NodeIsCommonAncestorForRangeInSelection,
+    // Set if the node is a descendant of a node with the above bit set.
+    NodeIsDescendantOfCommonAncestorForRangeInSelection,
     // Guard value
     BooleanFlagCount
   };
 
   void SetBoolFlag(BooleanFlag name, bool value) {
     PR_STATIC_ASSERT(BooleanFlagCount <= 8*sizeof(mBoolFlags));
     mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name);
   }
@@ -1230,16 +1247,28 @@ public:
     { return GetBoolFlag(NodeHasRenderingObservers); }
   void SetHasRenderingObservers(bool aValue)
     { SetBoolFlag(NodeHasRenderingObservers, aValue); }
   bool HasID() const { return GetBoolFlag(ElementHasID); }
   bool MayHaveStyle() const { return GetBoolFlag(ElementMayHaveStyle); }
   bool HasName() const { return GetBoolFlag(ElementHasName); }
   bool MayHaveContentEditableAttr() const
     { return GetBoolFlag(ElementMayHaveContentEditableAttr); }
+  bool IsCommonAncestorForRangeInSelection() const
+    { return GetBoolFlag(NodeIsCommonAncestorForRangeInSelection); }
+  void SetCommonAncestorForRangeInSelection()
+    { SetBoolFlag(NodeIsCommonAncestorForRangeInSelection); }
+  void ClearCommonAncestorForRangeInSelection()
+    { ClearBoolFlag(NodeIsCommonAncestorForRangeInSelection); }
+  bool IsDescendantOfCommonAncestorForRangeInSelection() const
+    { return GetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
+  void SetDescendantOfCommonAncestorForRangeInSelection()
+    { SetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
+  void ClearDescendantOfCommonAncestorForRangeInSelection()
+    { ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); }
 
 protected:
   void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); }
   void SetInDocument() { SetBoolFlag(IsInDocument); }
   void ClearInDocument() { ClearBoolFlag(IsInDocument); }
   void SetIsElement() { SetBoolFlag(NodeIsElement); }
   void ClearIsElement() { ClearBoolFlag(NodeIsElement); }
   void SetHasID() { SetBoolFlag(ElementHasID); }
--- a/content/base/public/nsIRange.h
+++ b/content/base/public/nsIRange.h
@@ -35,18 +35,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsIRange_h___
 #define nsIRange_h___
 
 #include "nsISupports.h"
 #include "nsCOMPtr.h"
+#include "nsHashKeys.h"
 #include "nsINode.h"
 #include "nsIDOMRange.h"
+#include "nsTHashtable.h"
 
 // IID for the nsIRange interface
 #define NS_IRANGE_IID \
 { 0x09dec26b, 0x1ab7, 0x4ff0, \
  { 0xa1, 0x67, 0x7f, 0x22, 0x9c, 0xaa, 0xc3, 0x04 } }
 
 class nsIDOMFontFaceList;
 
@@ -55,17 +57,18 @@ public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IRANGE_IID)
 
   nsIRange()
     : mRoot(nsnull),
       mStartOffset(0),
       mEndOffset(0),
       mIsPositioned(false),
       mIsDetached(false),
-      mMaySpanAnonymousSubtrees(false)
+      mMaySpanAnonymousSubtrees(false),
+      mInSelection(false)
   {
   }
 
   nsINode* GetRoot() const
   {
     return mRoot;
   }
 
@@ -105,16 +108,43 @@ public:
            mStartOffset == mEndOffset;
   }
 
   void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees)
   {
     mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees;
   }
 
+  /**
+   * Return true iff this range is part of at least one Selection object
+   * and isn't detached.
+   */
+  bool IsInSelection() const
+  {
+    return mInSelection;
+  }
+
+  /**
+   * Called when the range is added/removed from a Selection.
+   */
+  void SetInSelection(bool aInSelection)
+  {
+    if (mInSelection == aInSelection || mIsDetached) {
+      return;
+    }
+    mInSelection = aInSelection;
+    nsINode* commonAncestor = GetCommonAncestor();
+    NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
+    if (mInSelection) {
+      RegisterCommonAncestor(commonAncestor);
+    } else {
+      UnregisterCommonAncestor(commonAncestor);
+    }
+  }
+
   virtual nsINode* GetCommonAncestor() const = 0;
 
   virtual void Reset() = 0;
 
   // XXXbz we could make these non-virtual if a bunch of nsRange stuff
   // became nsIRange stuff... and if no one outside layout needs them.
   virtual nsresult SetStart(nsINode* aParent, PRInt32 aOffset) = 0;
   virtual nsresult SetEnd(nsINode* aParent, PRInt32 aOffset) = 0;
@@ -123,23 +153,29 @@ public:
   // Work around hiding warnings
   NS_IMETHOD SetStart(nsIDOMNode* aParent, PRInt32 aOffset) = 0;
   NS_IMETHOD SetEnd(nsIDOMNode* aParent, PRInt32 aOffset) = 0;
   NS_IMETHOD CloneRange(nsIDOMRange** aNewRange) = 0;
 
   // To support the font inspector API
   NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult) = 0;
 
+  typedef nsTHashtable<nsPtrHashKey<nsIRange> > RangeHashTable;
 protected:
+  void RegisterCommonAncestor(nsINode* aNode);
+  void UnregisterCommonAncestor(nsINode* aNode);
+  nsINode* IsValidBoundary(nsINode* aNode);
+
   nsCOMPtr<nsINode> mRoot;
   nsCOMPtr<nsINode> mStartParent;
   nsCOMPtr<nsINode> mEndParent;
   PRInt32 mStartOffset;
   PRInt32 mEndOffset;
 
   bool mIsPositioned;
   bool mIsDetached;
   bool mMaySpanAnonymousSubtrees;
+  bool mInSelection;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIRange, NS_IRANGE_IID)
 
 #endif /* nsIRange_h___ */
--- a/content/base/src/nsGenericDOMDataNode.h
+++ b/content/base/src/nsGenericDOMDataNode.h
@@ -58,21 +58,18 @@
 // non-whitespace, we may need to create a frame for it. This bit must
 // not be set on nodes that already have a frame.
 #define NS_CREATE_FRAME_IF_NON_WHITESPACE (1 << NODE_TYPE_SPECIFIC_BITS_OFFSET)
 
 // This bit is set to indicate that if the text node changes to
 // whitespace, we may need to reframe it (or its ancestors).
 #define NS_REFRAME_IF_WHITESPACE (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 1))
 
-// This bit is set to indicate that the text may be part of a selection.
-#define NS_TEXT_IN_SELECTION (1 << (NODE_TYPE_SPECIFIC_BITS_OFFSET + 2))
-
 // Make sure we have enough space for those bits
-PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 2 < 32);
+PR_STATIC_ASSERT(NODE_TYPE_SPECIFIC_BITS_OFFSET + 1 < 32);
 
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIDOMNodeList;
 class nsIFrame;
 class nsIDOMText;
 class nsINodeInfo;
 class nsURI;
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -86,16 +86,19 @@
 #include "nsMutationEvent.h"
 #include "nsNodeUtils.h"
 #include "nsDocument.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif /* MOZ_XUL */
 #include "nsFrameManager.h"
 #include "nsFrameSelection.h"
+#ifdef DEBUG
+#include "nsIRange.h"
+#endif
 
 #include "nsBindingManager.h"
 #include "nsXBLBinding.h"
 #include "nsIXBLService.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIBoxObject.h"
 #include "nsClientRect.h"
 #include "nsSVGUtils.h"
@@ -4927,16 +4930,21 @@ nsGenericElement::List(FILE* out, PRInt3
   fputs(NS_LossyConvertUTF16toASCII(mNodeInfo->QualifiedName()).get(), out);
 
   fprintf(out, "@%p", (void *)this);
 
   ListAttributes(out);
 
   fprintf(out, " state=[%llx]", State().GetInternalValue());
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
+  if (IsCommonAncestorForRangeInSelection()) {
+    nsIRange::RangeHashTable* ranges =
+      static_cast<nsIRange::RangeHashTable*>(GetProperty(nsGkAtoms::range));
+    fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
+  }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%d<", mRefCnt.get());
 
   nsIContent* child = GetFirstChild();
   if (child) {
     fputs("\n", out);
     
     for (; child; child = child->GetNextSibling()) {
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -831,16 +831,17 @@ GK_ATOM(properties, "properties")
 GK_ATOM(property, "property")
 GK_ATOM(pubdate, "pubdate")
 GK_ATOM(q, "q")
 GK_ATOM(query, "query")
 GK_ATOM(queryset, "queryset")
 GK_ATOM(querytype, "querytype")
 GK_ATOM(radio, "radio")
 GK_ATOM(radiogroup, "radiogroup")
+GK_ATOM(range, "range")
 GK_ATOM(readonly, "readonly")
 GK_ATOM(rect, "rect")
 GK_ATOM(rectangle, "rectangle")
 GK_ATOM(ref, "ref")
 GK_ATOM(refresh, "refresh")
 GK_ATOM(rel, "rel")
 GK_ATOM(rem, "rem")
 GK_ATOM(removeelement, "removeelement")
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -15,16 +15,17 @@
  * The Original Code is mozilla.org code.
  *
  * The Initial Developer of the Original Code is
  * Netscape Communications Corporation.
  * Portions created by the Initial Developer are Copyright (C) 1998
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
+ *   Mats Palmgren <matspal@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -151,16 +152,79 @@ nsRange::CompareNodeToRange(nsINode* aNo
   *outNodeAfter = nsContentUtils::ComparePoints(rangeEndParent,
                                                 rangeEndOffset,
                                                 parent, nodeEnd,
                                                 &disconnected) < 0;
   NS_ENSURE_TRUE(!disconnected, NS_ERROR_DOM_WRONG_DOCUMENT_ERR);
   return NS_OK;
 }
 
+struct FindSelectedRangeData
+{
+  nsINode*  mNode;
+  nsIRange* mResult;
+  PRUint32  mStartOffset;
+  PRUint32  mEndOffset;
+};
+
+static PLDHashOperator
+FindSelectedRange(nsPtrHashKey<nsIRange>* aEntry, void* userArg)
+{
+  nsIRange* range = aEntry->GetKey();
+  if (range->IsInSelection() && !range->Collapsed()) {
+    FindSelectedRangeData* data = static_cast<FindSelectedRangeData*>(userArg);
+    PRInt32 cmp = nsContentUtils::ComparePoints(data->mNode, data->mEndOffset,
+                                                range->GetStartParent(),
+                                                range->StartOffset());
+    if (cmp == 1) {
+      cmp = nsContentUtils::ComparePoints(data->mNode, data->mStartOffset,
+                                          range->GetEndParent(),
+                                          range->EndOffset());
+      if (cmp == -1) {
+        data->mResult = range;
+        return PL_DHASH_STOP;
+      }
+    }
+  }
+  return PL_DHASH_NEXT;
+}
+
+static nsINode*
+GetNextRangeCommonAncestor(nsINode* aNode)
+{
+  while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
+    if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
+      return nsnull;
+    }
+    aNode = aNode->GetNodeParent();
+  }
+  return aNode;
+}
+
+/* static */ bool
+nsRange::IsNodeSelected(nsINode* aNode, PRUint32 aStartOffset,
+                        PRUint32 aEndOffset)
+{
+  NS_PRECONDITION(aNode, "bad arg");
+
+  FindSelectedRangeData data = { aNode, nsnull, aStartOffset, aEndOffset };
+  nsINode* n = GetNextRangeCommonAncestor(aNode);
+  NS_ASSERTION(n || !aNode->IsSelectionDescendant(),
+               "orphan selection descendant");
+  for (; n; n = GetNextRangeCommonAncestor(n->GetNodeParent())) {
+    RangeHashTable* ranges =
+      static_cast<RangeHashTable*>(n->GetProperty(nsGkAtoms::range));
+    ranges->EnumerateEntries(FindSelectedRange, &data);
+    if (data.mResult) {
+      return true;
+    }
+  }
+  return false;
+}
+
 /******************************************************
  * non members
  ******************************************************/
 
 nsresult
 NS_NewRangeUtils(nsIRangeUtils** aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
@@ -220,18 +284,20 @@ NS_NewRange(nsIDOMRange** aResult)
 }
 
 /******************************************************
  * constructor/destructor
  ******************************************************/
 
 nsRange::~nsRange() 
 {
+  NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use");
+
+  // we want the side effects (releases and list removals)
   DoSetRange(nsnull, 0, nsnull, 0, nsnull);
-  // we want the side effects (releases and list removals)
 } 
 
 /******************************************************
  * nsISupports
  ******************************************************/
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
 
@@ -255,16 +321,104 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+static void
+RangeHashTableDtor(void* aObject, nsIAtom* aPropertyName, void* aPropertyValue,
+                   void* aData)
+{
+  nsIRange::RangeHashTable* ranges =
+    static_cast<nsIRange::RangeHashTable*>(aPropertyValue);
+  delete ranges;
+}
+
+static void MarkDescendants(nsINode* aNode)
+{
+  // Set NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
+  // descendants unless aNode is already marked as a range common ancestor
+  // or a descendant of one, in which case all of our descendants have the
+  // bit set already.
+  if (!aNode->IsSelectionDescendant()) {
+    // don't set the Descendant bit on |aNode| itself
+    nsINode* node = aNode->GetNextNode(aNode);
+    while (node) {
+      node->SetDescendantOfCommonAncestorForRangeInSelection();
+      if (!node->IsCommonAncestorForRangeInSelection()) {
+        node = node->GetNextNode(aNode);
+      } else {
+        // optimize: skip this sub-tree since it's marked already.
+        node = node->GetNextNonChildNode(aNode);
+      }
+    }
+  }
+}
+
+static void UnmarkDescendants(nsINode* aNode)
+{
+  // Unset NodeIsDescendantOfCommonAncestorForRangeInSelection on aNode's
+  // descendants unless aNode is a descendant of another range common ancestor.
+  // Also, exclude descendants of range common ancestors (but not the common
+  // ancestor itself).
+  if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
+    // we know |aNode| doesn't have any bit set
+    nsINode* node = aNode->GetNextNode(aNode);
+    while (node) {
+      node->ClearDescendantOfCommonAncestorForRangeInSelection();
+      if (!node->IsCommonAncestorForRangeInSelection()) {
+        node = node->GetNextNode(aNode);
+      } else {
+        // We found an ancestor of an overlapping range, skip its descendants.
+        node = node->GetNextNonChildNode(aNode);
+      }
+    }
+  }
+}
+
+void
+nsIRange::RegisterCommonAncestor(nsINode* aNode)
+{
+  NS_PRECONDITION(aNode, "bad arg");
+  NS_ASSERTION(IsInSelection(), "registering range not in selection");
+
+  MarkDescendants(aNode);
+
+  RangeHashTable* ranges =
+    static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
+  if (!ranges) {
+    ranges = new RangeHashTable;
+    ranges->Init();
+    aNode->SetProperty(nsGkAtoms::range, ranges, RangeHashTableDtor);
+  }
+  ranges->PutEntry(this);
+  aNode->SetCommonAncestorForRangeInSelection();
+}
+
+void
+nsIRange::UnregisterCommonAncestor(nsINode* aNode)
+{
+  NS_PRECONDITION(aNode, "bad arg");
+  NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
+  RangeHashTable* ranges =
+    static_cast<RangeHashTable*>(aNode->GetProperty(nsGkAtoms::range));
+  NS_ASSERTION(ranges->GetEntry(this), "unknown range");
+
+  if (ranges->Count() == 1) {
+    aNode->ClearCommonAncestorForRangeInSelection();
+    aNode->DeleteProperty(nsGkAtoms::range);
+    UnmarkDescendants(aNode);
+  } else {
+    ranges->RemoveEntry(this);
+  }
+}
+
 /******************************************************
  * nsIMutationObserver implementation
  ******************************************************/
 
 void
 nsRange::CharacterDataChanged(nsIDocument* aDocument,
                               nsIContent* aContent,
                               CharacterDataChangeInfo* aInfo)
@@ -288,16 +442,25 @@ nsRange::CharacterDataChanged(nsIDocumen
                    "only a split can start before the end");
       NS_ASSERTION(static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd + 1,
                    "mStartOffset is beyond the end of this node");
       newStartOffset = static_cast<PRUint32>(mStartOffset) - aInfo->mChangeStart;
       newStartNode = aInfo->mDetails->mNextSibling;
       if (NS_UNLIKELY(aContent == mRoot)) {
         newRoot = IsValidBoundary(newStartNode);
       }
+
+      bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent;
+      if (isCommonAncestor) {
+        UnregisterCommonAncestor(mStartParent);
+        RegisterCommonAncestor(newStartNode);
+      }
+      if (mStartParent->IsDescendantOfCommonAncestorForRangeInSelection()) {
+        newStartNode->SetDescendantOfCommonAncestorForRangeInSelection();
+      }
     } else {
       // If boundary is inside changed text, position it before change
       // else adjust start offset for the change in length.
       mStartOffset = static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd ?
         aInfo->mChangeStart :
         mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
           aInfo->mReplaceLength;
     }
@@ -312,16 +475,26 @@ nsRange::CharacterDataChanged(nsIDocumen
       // splitText(), aInfo->mDetails->mNextSibling is the new text node
       NS_ASSERTION(aInfo->mDetails->mType ==
                    CharacterDataChangeInfo::Details::eSplit,
                    "only a split can start before the end");
       NS_ASSERTION(static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd + 1,
                    "mEndOffset is beyond the end of this node");
       newEndOffset = static_cast<PRUint32>(mEndOffset) - aInfo->mChangeStart;
       newEndNode = aInfo->mDetails->mNextSibling;
+
+      bool isCommonAncestor = IsInSelection() && mStartParent == mEndParent;
+      if (isCommonAncestor && !newStartNode) {
+        // The split occurs inside the range.
+        UnregisterCommonAncestor(mStartParent);
+        RegisterCommonAncestor(mStartParent->GetParent());
+        newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
+      } else if (mEndParent->IsDescendantOfCommonAncestorForRangeInSelection()) {
+        newEndNode->SetDescendantOfCommonAncestorForRangeInSelection();
+      }
     } else {
       mEndOffset = static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd ?
         aInfo->mChangeStart :
         mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd +
           aInfo->mReplaceLength;
     }
   }
 
@@ -350,21 +523,39 @@ nsRange::CharacterDataChanged(nsIDocumen
       newStartNode = mStartParent;
       newStartOffset = mStartOffset;
     }
     if (!newEndNode) {
       newEndNode = mEndParent;
       newEndOffset = mEndOffset;
     }
     DoSetRange(newStartNode, newStartOffset, newEndNode, newEndOffset,
-               newRoot ? newRoot : mRoot.get()
-#ifdef DEBUG
-               , !newEndNode->GetParent() || !newStartNode->GetParent()
-#endif
-               );
+               newRoot ? newRoot : mRoot.get(),
+               !newEndNode->GetParent() || !newStartNode->GetParent());
+  }
+}
+
+void
+nsRange::ContentAppended(nsIDocument* aDocument,
+                         nsIContent*  aContainer,
+                         nsIContent*  aFirstNewContent,
+                         PRInt32      aNewIndexInContainer)
+{
+  NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
+
+  nsINode* container = NODE_FROM(aContainer, aDocument);
+  if (container->IsSelectionDescendant() && IsInSelection()) {
+    nsINode* child = aFirstNewContent;
+    while (child) {
+      if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
+        MarkDescendants(child);
+        child->SetDescendantOfCommonAncestorForRangeInSelection();
+      }
+      child = child->GetNextSibling();
+    }
   }
 }
 
 void
 nsRange::ContentInserted(nsIDocument* aDocument,
                          nsIContent* aContainer,
                          nsIContent* aChild,
                          PRInt32 aIndexInContainer)
@@ -375,50 +566,68 @@ nsRange::ContentInserted(nsIDocument* aD
 
   // Adjust position if a sibling was inserted.
   if (container == mStartParent && aIndexInContainer < mStartOffset) {
     ++mStartOffset;
   }
   if (container == mEndParent && aIndexInContainer < mEndOffset) {
     ++mEndOffset;
   }
+  if (container->IsSelectionDescendant() &&
+      !aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
+    MarkDescendants(aChild);
+    aChild->SetDescendantOfCommonAncestorForRangeInSelection();
+  }
 }
 
 void
 nsRange::ContentRemoved(nsIDocument* aDocument,
                         nsIContent* aContainer,
                         nsIContent* aChild,
                         PRInt32 aIndexInContainer,
                         nsIContent* aPreviousSibling)
 {
   NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned");
 
   nsINode* container = NODE_FROM(aContainer, aDocument);
+  bool gravitateStart = false;
+  bool gravitateEnd = false;
 
   // Adjust position if a sibling was removed...
   if (container == mStartParent) {
     if (aIndexInContainer < mStartOffset) {
       --mStartOffset;
     }
   }
   // ...or gravitate if an ancestor was removed.
   else if (nsContentUtils::ContentIsDescendantOf(mStartParent, aChild)) {
-    mStartParent = container;
-    mStartOffset = aIndexInContainer;
+    gravitateStart = true;
   }
 
   // Do same thing for end boundry.
   if (container == mEndParent) {
     if (aIndexInContainer < mEndOffset) {
       --mEndOffset;
     }
   }
   else if (nsContentUtils::ContentIsDescendantOf(mEndParent, aChild)) {
-    mEndParent = container;
-    mEndOffset = aIndexInContainer;
+    gravitateEnd = true;
+  }
+
+  if (gravitateStart || gravitateEnd) {
+    DoSetRange(gravitateStart ? container : mStartParent.get(),
+               gravitateStart ? aIndexInContainer : mStartOffset,
+               gravitateEnd ? container : mEndParent.get(),
+               gravitateEnd ? aIndexInContainer : mEndOffset,
+               mRoot);
+  }
+  if (container->IsSelectionDescendant() &&
+      aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
+    aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
+    UnmarkDescendants(aChild);
   }
 }
 
 void
 nsRange::ParentChainChanged(nsIContent *aContent)
 {
   NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
   nsINode* newRoot = IsValidBoundary(mStartParent);
@@ -502,21 +711,17 @@ static PRUint32 GetNodeLength(nsINode *a
 // It's important that all setting of the range start/end points 
 // go through this function, which will do all the right voodoo
 // for content notification of range ownership.  
 // Calling DoSetRange with either parent argument null will collapse
 // the range to have both endpoints point to the other node
 void
 nsRange::DoSetRange(nsINode* aStartN, PRInt32 aStartOffset,
                     nsINode* aEndN, PRInt32 aEndOffset,
-                    nsINode* aRoot
-#ifdef DEBUG
-                    , bool aNotInsertedYet
-#endif
-                    )
+                    nsINode* aRoot, bool aNotInsertedYet)
 {
   NS_PRECONDITION((aStartN && aEndN && aRoot) ||
                   (!aStartN && !aEndN && !aRoot),
                   "Set all or none");
   NS_PRECONDITION(!aRoot || aNotInsertedYet ||
                   (nsContentUtils::ContentIsDescendantOf(aStartN, aRoot) &&
                    nsContentUtils::ContentIsDescendantOf(aEndN, aRoot) &&
                    aRoot == IsValidBoundary(aStartN) &&
@@ -540,22 +745,39 @@ nsRange::DoSetRange(nsINode* aStartN, PR
   if (mRoot != aRoot) {
     if (mRoot) {
       mRoot->RemoveMutationObserver(this);
     }
     if (aRoot) {
       aRoot->AddMutationObserver(this);
     }
   }
- 
+  bool checkCommonAncestor = (mStartParent != aStartN || mEndParent != aEndN) &&
+                             IsInSelection() && !aNotInsertedYet;
+  nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nsnull;
   mStartParent = aStartN;
   mStartOffset = aStartOffset;
   mEndParent = aEndN;
   mEndOffset = aEndOffset;
   mIsPositioned = !!mStartParent;
+  if (checkCommonAncestor) {
+    nsINode* newCommonAncestor = GetCommonAncestor();
+    if (newCommonAncestor != oldCommonAncestor) {
+      if (oldCommonAncestor) {
+        UnregisterCommonAncestor(oldCommonAncestor);
+      }
+      if (newCommonAncestor) {
+        RegisterCommonAncestor(newCommonAncestor);
+      } else {
+        NS_ASSERTION(mIsDetached, "unexpected disconnected nodes");
+        mInSelection = false;
+      }
+    }
+  }
+
   // This needs to be the last thing this function does.  See comment
   // in ParentChainChanged.
   mRoot = aRoot;
 }
 
 static PRInt32
 IndexOf(nsIDOMNode* aChildNode)
 {
@@ -659,17 +881,17 @@ nsRange::GetCommonAncestorContainer(nsID
   nsINode* container = nsContentUtils::GetCommonAncestor(mStartParent, mEndParent);
   if (container) {
     return CallQueryInterface(container, aCommonParent);
   }
 
   return NS_ERROR_NOT_INITIALIZED;
 }
 
-nsINode* nsRange::IsValidBoundary(nsINode* aNode)
+nsINode* nsIRange::IsValidBoundary(nsINode* aNode)
 {
   if (!aNode) {
     return nsnull;
   }
 
   if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
     nsIContent* content = static_cast<nsIContent*>(aNode);
     if (content->Tag() == nsGkAtoms::documentTypeNodeName) {
--- a/content/base/src/nsRange.h
+++ b/content/base/src/nsRange.h
@@ -72,19 +72,17 @@ public:
 
 // -------------------------------------------------------------------------------
 
 class nsRange : public nsIRange,
                 public nsIDOMNSRange,
                 public nsStubMutationObserver
 {
 public:
-  nsRange()
-  {
-  }
+  nsRange(){}
   virtual ~nsRange();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsRange, nsIRange)
 
   // nsIDOMRange interface
   NS_DECL_NSIDOMRANGE
 
@@ -111,24 +109,23 @@ public:
 
   NS_IMETHOD GetUsedFontFaces(nsIDOMFontFaceList** aResult);
 
   // nsIMutationObserver methods
   NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
+  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
 
 private:
   // no copy's or assigns
   nsRange(const nsRange&);
   nsRange& operator=(const nsRange&);
 
-  nsINode* IsValidBoundary(nsINode* aNode);
- 
   /**
    * Cut or delete the range's contents.
    *
    * @param aFragment nsIDOMDocumentFragment containing the nodes.
    *                  May be null to indicate the caller doesn't want a fragment.
    */
   nsresult CutContents(nsIDOMDocumentFragment** frag);
 
@@ -152,26 +149,27 @@ public:
  *****************************************************************************/
   static nsresult CompareNodeToRange(nsINode* aNode, nsIDOMRange* aRange,
                                      bool *outNodeBefore,
                                      bool *outNodeAfter);
   static nsresult CompareNodeToRange(nsINode* aNode, nsIRange* aRange,
                                      bool *outNodeBefore,
                                      bool *outNodeAfter);
 
+  static bool IsNodeSelected(nsINode* aNode, PRUint32 aStartOffset,
+                             PRUint32 aEndOffset);
+
 protected:
+  // CharacterDataChanged set aNotInsertedYet to true to disable an assertion
+  // and suppress re-registering a range common ancestor node since
+  // the new text node of a splitText hasn't been inserted yet.
+  // CharacterDataChanged does the re-registering when needed.
   void DoSetRange(nsINode* aStartN, PRInt32 aStartOffset,
                   nsINode* aEndN, PRInt32 aEndOffset,
-                  nsINode* aRoot
-#ifdef DEBUG
-                  // CharacterDataChanged use this to disable an assertion since
-                  // the new text node of a splitText hasn't been inserted yet.
-                  , bool aNotInsertedYet = false
-#endif
-                  );
+                  nsINode* aRoot, bool aNotInsertedYet = false);
 };
 
 // Make a new nsIDOMRange object
 nsresult NS_NewRange(nsIDOMRange** aInstancePtrResult);
 
 // Make a new nsIRangeUtils object
 nsresult NS_NewRangeUtils(nsIRangeUtils** aInstancePtrResult);
 
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -41,16 +41,19 @@
 
 #include "nsTextNode.h"
 #include "nsContentUtils.h"
 #include "nsIDOMEventListener.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIAttribute.h"
 #include "nsIDocument.h"
 #include "nsThreadUtils.h"
+#ifdef DEBUG
+#include "nsIRange.h"
+#endif
 
 using namespace mozilla::dom;
 
 /**
  * class used to implement attr() generated content
  */
 class nsAttributeTextNode : public nsTextNode,
                             public nsStubMutationObserver
@@ -219,16 +222,22 @@ nsTextNode::AppendTextForNormalize(const
 void
 nsTextNode::List(FILE* out, PRInt32 aIndent) const
 {
   PRInt32 index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
   fprintf(out, "Text@%p", static_cast<const void*>(this));
   fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
+  if (IsCommonAncestorForRangeInSelection()) {
+    typedef nsTHashtable<nsPtrHashKey<nsIRange> > RangeHashTable;
+    RangeHashTable* ranges =
+      static_cast<RangeHashTable*>(GetProperty(nsGkAtoms::range));
+    fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0);
+  }
   fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));
   fprintf(out, " refcount=%d<", mRefCnt.get());
 
   nsAutoString tmp;
   ToCString(tmp, 0, mText.GetLength());
   fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
 
   fputs(">\n", out);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -212,18 +212,18 @@ public:
   /**
    * Calling this setter makes us include all out-of-flow descendant
    * frames in the display list, wherever they may be positioned (even
    * outside the dirty rects).
    */
   void SetIncludeAllOutOfFlows() { mIncludeAllOutOfFlows = true; }
   bool GetIncludeAllOutOfFlows() const { return mIncludeAllOutOfFlows; }
   /**
-   * Calling this setter makes us exclude all leaf frames that does
-   * not have the NS_FRAME_SELECTED_CONTENT bit.
+   * Calling this setter makes us exclude all leaf frames that aren't
+   * selected.
    */
   void SetSelectedFramesOnly() { mSelectedFramesOnly = true; }
   bool GetSelectedFramesOnly() { return mSelectedFramesOnly; }
   /**
    * Calling this setter makes us compute accurate visible regions at the cost
    * of performance if regions get very complex.
    */
   void SetAccurateVisibleRegions() { mAccurateVisibleRegions = true; }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -86,16 +86,17 @@
 #include "nsCSSAnonBoxes.h"
 #include "nsCSSPseudoElements.h"
 #include "nsIHTMLContentSink.h" 
 #include "nsCSSFrameConstructor.h"
 
 #include "nsFrameTraversal.h"
 #include "nsStyleChangeList.h"
 #include "nsIDOMRange.h"
+#include "nsRange.h"
 #include "nsITableLayout.h"    //selection necessity
 #include "nsITableCellLayout.h"//  "
 #include "nsITextControlFrame.h"
 #include "nsINameSpaceManager.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsStyleStructInlines.h"
 
 #ifdef IBMBIDI
@@ -439,18 +440,17 @@ nsFrame::Init(nsIContent*      aContent,
     NS_ADDREF(aContent);
   }
 
   if (aPrevInFlow) {
     // Make sure the general flags bits are the same
     nsFrameState state = aPrevInFlow->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
-    mState |= state & (NS_FRAME_SELECTED_CONTENT |
-                       NS_FRAME_INDEPENDENT_SELECTION |
+    mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
                        NS_FRAME_IS_SPECIAL |
                        NS_FRAME_MAY_BE_TRANSFORMED);
   }
   if (mParent) {
     nsFrameState state = mParent->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
@@ -563,18 +563,17 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
          nextSib->Properties().Get(nsIFrame::IBSplitSpecialPrevSibling()),
          "IB sibling chain is inconsistent");
       nextSib->Properties().Delete(nsIFrame::IBSplitSpecialPrevSibling());
     }
   }
 
   shell->NotifyDestroyingFrame(this);
 
-  if ((mState & NS_FRAME_EXTERNAL_REFERENCE) ||
-      (mState & NS_FRAME_SELECTED_CONTENT)) {
+  if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
     shell->ClearFrameRefs(this);
   }
 
   if (view) {
     // Break association between view and frame
     view->SetFrame(nsnull);
 
     // Destroy the view
@@ -1199,20 +1198,17 @@ void nsDisplaySelectionOverlay::Paint(ns
 * Refreshes each content's frame
 *********************************************************/
 
 nsresult
 nsFrame::DisplaySelectionOverlay(nsDisplayListBuilder*   aBuilder,
                                  nsDisplayList*          aList,
                                  PRUint16                aContentType)
 {
-//check frame selection state
-  if ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) != NS_FRAME_SELECTED_CONTENT)
-    return NS_OK;
-  if (!IsVisibleForPainting(aBuilder))
+  if (!IsSelected() || !IsVisibleForPainting(aBuilder))
     return NS_OK;
     
   nsPresContext* presContext = PresContext();
   nsIPresShell *shell = presContext->PresShell();
   if (!shell)
     return NS_OK;
 
   PRInt16 displaySelection = shell->GetSelectionFlags();
@@ -1917,17 +1913,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
   }
 
   // Mark the display list items for absolutely positioned children
   child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty);
 
   if (childType != nsGkAtoms::placeholderFrame &&
       aBuilder->GetSelectedFramesOnly() &&
       child->IsLeaf() &&
-      !(child->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) {
+      !aChild->IsSelected()) {
     return NS_OK;
   }
 
   if (aBuilder->GetIncludeAllOutOfFlows() &&
       (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
     dirty = child->GetVisualOverflowRect();
   } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
     // No need to descend into child to catch placeholders for visible
@@ -2488,19 +2484,17 @@ nsFrame::HandlePress(nsPresContext* aPre
   fc->SetDelayedCaretData(0);
 
   // Check if any part of this frame is selected, and if the
   // user clicked inside the selected region. If so, we delay
   // starting a new selection since the user may be trying to
   // drag the selected region to some other app.
 
   SelectionDetails *details = 0;
-  bool isSelected = ((GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT);
-
-  if (isSelected)
+  if (GetContent()->IsSelectionDescendant())
   {
     bool inSelection = false;
     details = frameselection->LookUpSelection(offsets.content, 0,
         offsets.EndOffset(), false);
 
     //
     // If there are any details, check to see if the user clicked
     // within any selected region of the frame.
@@ -5194,18 +5188,19 @@ nsIFrame::IsVisibleOrCollapsedForPaintin
     return false;
   nsISelection* sel = aBuilder->GetBoundingSelection();
   return !sel || IsVisibleInSelection(sel);
 }
 
 bool
 nsIFrame::IsVisibleInSelection(nsISelection* aSelection)
 {
-  if ((mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT)
-    return true;
+  if (!GetContent() || !GetContent()->IsSelectionDescendant()) {
+    return false;
+  }
   
   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
   bool vis;
   nsresult rv = aSelection->ContainsNode(node, true, &vis);
   return NS_FAILED(rv) || vis;
 }
 
 /* virtual */ bool
@@ -5251,21 +5246,21 @@ nsIFrame::GetFrameSelection()
 {
   nsFrameSelection* fs =
     const_cast<nsFrameSelection*>(GetConstFrameSelection());
   NS_IF_ADDREF(fs);
   return fs;
 }
 
 const nsFrameSelection*
-nsIFrame::GetConstFrameSelection()
-{
-  nsIFrame *frame = this;
+nsIFrame::GetConstFrameSelection() const
+{
+  nsIFrame* frame = const_cast<nsIFrame*>(this);
   while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
-    nsITextControlFrame *tcf = do_QueryFrame(frame);
+    nsITextControlFrame* tcf = do_QueryFrame(frame);
     if (tcf) {
       return tcf->GetOwnedFrameSelection();
     }
     frame = frame->GetParent();
   }
 
   return PresContext()->PresShell()->ConstFrameSelection();
 }
@@ -5333,49 +5328,23 @@ nsFrame::DumpBaseRegressionData(nsPresCo
     }
     aIndent--;
     IndentBy(out, aIndent);
     fprintf(out, "</child-list>\n");
   }
 }
 #endif
 
-void
-nsIFrame::SetSelected(bool aSelected, SelectionType aType)
-{
-  NS_ASSERTION(!GetPrevContinuation(),
-               "Should only be called on first in flow");
-  if (aType != nsISelectionController::SELECTION_NORMAL)
-    return;
-
-  // check whether style allows selection
-  bool selectable;
-  IsSelectable(&selectable, nsnull);
-  if (!selectable)
-    return;
-
-  for (nsIFrame* f = this; f; f = f->GetNextContinuation()) {
-    if (aSelected) {
-      AddStateBits(NS_FRAME_SELECTED_CONTENT);
-    } else {
-      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-    }
-
-    // Repaint this frame subtree's entire area
-    InvalidateFrameSubtree();
-  }
-}
-
-NS_IMETHODIMP
-nsFrame::GetSelected(bool *aSelected) const
-{
-  if (!aSelected )
-    return NS_ERROR_NULL_POINTER;
-  *aSelected = !!(mState & NS_FRAME_SELECTED_CONTENT);
-  return NS_OK;
+bool
+nsIFrame::IsFrameSelected() const
+{
+  NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
+               "use the public IsSelected() instead");
+  return nsRange::IsNodeSelected(GetContent(), 0,
+                                 GetContent()->GetChildCount());
 }
 
 NS_IMETHODIMP
 nsFrame::GetPointFromOffset(PRInt32 inOffset, nsPoint* outPoint)
 {
   NS_PRECONDITION(outPoint != nsnull, "Null parameter");
   nsRect contentRect = GetContentRect() - GetPosition();
   nsPoint pt = contentRect.TopLeft();
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -227,17 +227,16 @@ public:
   NS_IMETHOD  SetNextContinuation(nsIFrame*);
   virtual nsIFrame* GetPrevInFlowVirtual() const;
   NS_IMETHOD  SetPrevInFlow(nsIFrame*);
   virtual nsIFrame* GetNextInFlowVirtual() const;
   NS_IMETHOD  SetNextInFlow(nsIFrame*);
   NS_IMETHOD  GetOffsetFromView(nsPoint& aOffset, nsIView** aView) const;
   virtual nsIAtom* GetType() const;
 
-  NS_IMETHOD  GetSelected(bool *aSelected) const;
   NS_IMETHOD  IsSelectable(bool* aIsSelectable, PRUint8* aSelectStyle) const;
 
   NS_IMETHOD  GetSelectionController(nsPresContext *aPresContext, nsISelectionController **aSelCon);
 
   virtual bool PeekOffsetNoAmount(bool aForward, PRInt32* aOffset);
   virtual bool PeekOffsetCharacter(bool aForward, PRInt32* aOffset,
                                      bool aRespectClusters = true);
   virtual bool PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -591,27 +591,26 @@ public:
    * Dragging or extending selection will never allow for a subset
    * (or the whole) of the maintained selection to become unselected.
    * Primary use: double click selecting then dragging on second click
    * @param aAmount the initial amount of text selected (word, line or paragraph).
    *                For "line", use eSelectBeginLine.
    */
   nsresult MaintainSelection(nsSelectionAmount aAmount = eSelectNoAmount);
 
-
   nsFrameSelection();
 
   void StartBatchChanges();
   void EndBatchChanges();
   /*unsafe*/
   nsresult DeleteFromDocument();
 
   nsIPresShell *GetShell()const  { return mShell; }
 
-  void DisconnectFromPresShell() { StopAutoScrollTimer(); mShell = nsnull; }
+  void DisconnectFromPresShell();
 private:
   nsresult TakeFocus(nsIContent *aNewFocus,
                      PRUint32 aContentOffset,
                      PRUint32 aContentEndOffset,
                      HINT aHint,
                      bool aContinueSelection,
                      bool aMultipleSelection);
 
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -194,18 +194,18 @@ typedef PRUint64 nsFrameState;
 // page and b) invisible: no borders, zero height, ignored in margin
 // collapsing, etc. See nsContainerFrame.h
 #define NS_FRAME_IS_OVERFLOW_CONTAINER              NS_FRAME_STATE_BIT(7)
 
 // If this bit is set, then the frame has been moved out of the flow,
 // e.g., it is absolutely positioned or floated
 #define NS_FRAME_OUT_OF_FLOW                        NS_FRAME_STATE_BIT(8)
 
-// If this bit is set, then the frame reflects content that may be selected
-#define NS_FRAME_SELECTED_CONTENT                   NS_FRAME_STATE_BIT(9)
+// This bit is available for re-use.
+//#define NS_FRAME_SELECTED_CONTENT                   NS_FRAME_STATE_BIT(9)
 
 // If this bit is set, then the frame is dirty and needs to be reflowed.
 // This bit is set when the frame is first created.
 // This bit is cleared by DidReflow after the required call to Reflow has
 // finished.
 // Do not set this bit yourself if you plan to pass the frame to
 // nsIPresShell::FrameNeedsReflow.  Pass the right arguments instead.
 #define NS_FRAME_IS_DIRTY                           NS_FRAME_STATE_BIT(10)
@@ -256,16 +256,17 @@ typedef PRUint64 nsFrameState;
 #define NS_FRAME_HAS_CHILD_WITH_VIEW                NS_FRAME_STATE_BIT(18)
 
 // If this bit is set, then reflow may be dispatched from the current
 // frame instead of the root frame.
 #define NS_FRAME_REFLOW_ROOT                        NS_FRAME_STATE_BIT(19)
 
 // Bits 20-31 and 60-63 of the frame state are reserved for implementations.
 #define NS_FRAME_IMPL_RESERVED                      nsFrameState(0xF0000000FFF00000)
+#define NS_FRAME_RESERVED                           ~NS_FRAME_IMPL_RESERVED
 
 // This bit is set on floats whose parent does not contain their
 // placeholder.  This can happen for two reasons:  (1) the float was
 // split, and this piece is the continuation, or (2) the entire float
 // didn't fit on the page.
 #define NS_FRAME_IS_PUSHED_FLOAT                    NS_FRAME_STATE_BIT(32)
 
 // This bit acts as a loop flag for recursive paint server drawing.
@@ -292,19 +293,16 @@ typedef PRUint64 nsFrameState;
 
 // Frame is or is a descendant of something with a fixed height, and
 // has no closer ancestor that is overflow:auto or overflow:scroll.
 #define NS_FRAME_IN_CONSTRAINED_HEIGHT              NS_FRAME_STATE_BIT(39)
 
 // This is only set during painting
 #define NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO    NS_FRAME_STATE_BIT(40)
 
-// Bits 0-19 and bits 32-59 of the frame state are reserved by this API.
-#define NS_FRAME_RESERVED                           ~NS_FRAME_IMPL_RESERVED
-
 // Box layout bits
 #define NS_STATE_IS_HORIZONTAL                      NS_FRAME_STATE_BIT(22)
 #define NS_STATE_IS_DIRECTION_NORMAL                NS_FRAME_STATE_BIT(31)
 
 // Helper macros
 #define NS_SUBTREE_DIRTY(_frame)  \
   (((_frame)->GetStateBits() &      \
     (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) != 0)
@@ -575,16 +573,22 @@ public:
    * content for its content node.
    * If the frame is a placeholder, it also ensures the out-of-flow frame's
    * removal and destruction.
    */
   void Destroy() { DestroyFrom(this); }
 
 protected:
   /**
+   * Return true if the frame is part of a Selection.
+   * Helper method to implement the public IsSelected() API.
+   */
+  virtual bool IsFrameSelected() const;
+
+  /**
    * Implements Destroy(). Do not call this directly except from within a
    * DestroyFrom() implementation.
    *
    * @note This will always be called, so it is not necessary to override
    *       Destroy() in subclasses of nsFrame, just DestroyFrom().
    *
    * @param  aDestructRoot is the root of the subtree being destroyed
    */
@@ -2300,35 +2304,23 @@ public:
   bool ClearOverflowRects();
 
   /**
    * Determine whether borders should not be painted on certain sides of the
    * frame.
    */
   virtual PRIntn GetSkipSides() const { return 0; }
 
-  /** Selection related calls
+  /**
+   * @returns true if this frame is selected.
    */
-  /** 
-   *  Called to set the selection status of the frame.
-   *  
-   *  This must be called on the primary frame, but all continuations
-   *  will be affected the same way.
-   *
-   *  This sets or clears NS_FRAME_SELECTED_CONTENT for each frame in the
-   *  continuation chain, if the frames are currently selectable.
-   *  The frames are unconditionally invalidated, if this selection type
-   *  is supported at all.
-   *  @param aSelected is it selected?
-   *  @param aType the selection type of the selection that you are setting on the frame
-   */
-  virtual void SetSelected(bool          aSelected,
-                           SelectionType aType);
-
-  NS_IMETHOD  GetSelected(bool *aSelected) const = 0;
+  bool IsSelected() const {
+    return (GetContent() && GetContent()->IsSelectionDescendant()) ?
+      IsFrameSelected() : false;
+  }
 
   /**
    *  called to discover where this frame, or a parent frame has user-select style
    *  applied, which affects that way that it is selected.
    *    
    *  @param aIsSelectable out param. Set to true if the frame can be selected
    *                       (i.e. is not affected by user-select: none)
    *  @param aSelectStyle  out param. Returns the type of selection style found
@@ -2347,20 +2339,17 @@ public:
    *  Call to get nsFrameSelection for this frame.
    */
   already_AddRefed<nsFrameSelection> GetFrameSelection();
 
   /**
    * GetConstFrameSelection returns an object which methods are safe to use for
    * example in nsIFrame code.
    */
-  const nsFrameSelection* GetConstFrameSelection();
-
-  /** EndSelection related calls
-   */
+  const nsFrameSelection* GetConstFrameSelection() const;
 
   /**
    *  called to find the previous/next character, word, or line  returns the actual 
    *  nsIFrame and the frame offset.  THIS DOES NOT CHANGE SELECTION STATE
    *  uses frame's begin selection state to start. if no selection on this frame will 
    *  return NS_ERROR_FAILURE
    *  @param aPOS is defined in nsFrameSelection
    */
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -262,17 +262,16 @@ public:
 
   // Note: StartAutoScrollTimer might destroy arbitrary frames etc.
   nsresult     StartAutoScrollTimer(nsIFrame *aFrame,
                                     nsPoint& aPoint,
                                     PRUint32 aDelay);
 
   nsresult     StopAutoScrollTimer();
 
-
 private:
   friend class nsAutoScrollTimer;
 
   // Note: DoAutoScroll might destroy arbitrary frames etc.
   nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
 
 public:
   SelectionType GetType(){return mType;}
@@ -1697,18 +1696,18 @@ nsFrameSelection::HandleDrag(nsIFrame *a
   if (!newFrame)
     return;
 
   nsIFrame::ContentOffsets offsets =
       newFrame->GetContentOffsetsFromPoint(newPoint);
   if (!offsets.content)
     return;
 
-  if ((newFrame->GetStateBits() & NS_FRAME_SELECTED_CONTENT) &&
-       AdjustForMaintainedSelection(offsets.content, offsets.offset))
+  if (newFrame->IsSelected() &&
+      AdjustForMaintainedSelection(offsets.content, offsets.offset))
     return;
 
   // Adjust offsets according to maintained amount
   if (mMaintainRange && 
       mMaintainedAmount != eSelectNoAmount) {    
     
     nsINode* rangenode = mMaintainRange->GetStartParent();
     PRInt32 rangeOffset = mMaintainRange->StartOffset();
@@ -1906,17 +1905,16 @@ printf(" * TakeFocus - moving into new c
 
   // Don't notify selection listeners if batching is on:
   if (GetBatching())
     return NS_OK;
   return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
 }
 
 
-
 SelectionDetails*
 nsFrameSelection::LookUpSelection(nsIContent *aContent,
                                   PRInt32 aContentOffset,
                                   PRInt32 aContentLength,
                                   bool aSlowCheck) const
 {
   if (!aContent || !mShell)
     return nsnull;
@@ -2473,18 +2471,17 @@ printf("HandleTableSelection: Mouse down
           GetFirstSelectedContent(GetFirstCellRange());
         if (previousCellNode)
         {
           // We have at least 1 other selected cell
 
           // Check if new cell is already selected
           nsIFrame  *cellFrame = childContent->GetPrimaryFrame();
           if (!cellFrame) return NS_ERROR_NULL_POINTER;
-          result = cellFrame->GetSelected(&isSelected);
-          if (NS_FAILED(result)) return result;
+          isSelected = cellFrame->IsSelected();
         }
         else
         {
           // No cells selected -- remove non-cell selection
           mDomSelections[index]->RemoveAllRanges();
         }
         mDragSelectingCells = true;    // Signal to start drag-cell-selection
         mSelectingTableCellMode = aTarget;
@@ -3372,16 +3369,26 @@ nsMouseEvent*
 nsFrameSelection::GetDelayedCaretData()
 {
   if (mDelayedMouseEventValid)
     return &mDelayedMouseEvent;
   
   return nsnull;
 }
 
+void
+nsFrameSelection::DisconnectFromPresShell()
+{
+  StopAutoScrollTimer();
+  for (PRInt32 i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; i++) {
+    mDomSelections[i]->Clear(nsnull);
+  }
+  mShell = nsnull;
+}
+
 //END nsISelection interface implementations
 
 #if 0
 #pragma mark -
 #endif
 
 // nsTypedSelection implementation
 
@@ -3401,16 +3408,21 @@ nsTypedSelection::nsTypedSelection(nsFra
   , mType(nsISelectionController::SELECTION_NORMAL)
 {
 }
 
 nsTypedSelection::~nsTypedSelection()
 {
   setAnchorFocusRange(-1);
 
+  PRUint32 count = mRanges.Length();
+  for (PRUint32 i = 0; i < count; ++i) {
+    mRanges[i].mRange->SetInSelection(false);
+  }
+
   if (mAutoScrollTimer) {
     mAutoScrollTimer->Stop();
     mAutoScrollTimer = nsnull;
   }
 
   mScrollEvent.Revoke();
 
   if (mCachedOffsetForFrame) {
@@ -3725,16 +3737,18 @@ nsTypedSelection::AddItem(nsIRange *aIte
     return NS_ERROR_UNEXPECTED;
   if (aOutIndex)
     *aOutIndex = -1;
 
   // a common case is that we have no ranges yet
   if (mRanges.Length() == 0) {
     if (!mRanges.AppendElement(RangeData(aItem)))
       return NS_ERROR_OUT_OF_MEMORY;
+    aItem->SetInSelection(true);
+
     if (aOutIndex)
       *aOutIndex = 0;
     return NS_OK;
   }
 
   PRInt32 startIndex, endIndex;
   GetIndicesForInterval(aItem->GetStartParent(), aItem->StartOffset(),
                         aItem->GetEndParent(), aItem->EndOffset(),
@@ -3762,16 +3776,17 @@ nsTypedSelection::AddItem(nsIRange *aIte
       *aOutIndex = startIndex;
     return NS_OK;
   }
 
   if (startIndex == endIndex) {
     // The new range doesn't overlap any existing ranges
     if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
       return NS_ERROR_OUT_OF_MEMORY;
+    aItem->SetInSelection(true);
     if (aOutIndex)
       *aOutIndex = startIndex;
     return NS_OK;
   }
 
   // We now know that at least 1 existing range overlaps with the range that
   // we are trying to add. In fact, the only ranges of interest are those at
   // the two end points, startIndex and endIndex - 1 (which may point to the
@@ -3783,16 +3798,19 @@ nsTypedSelection::AddItem(nsIRange *aIte
     return NS_ERROR_OUT_OF_MEMORY;
 
   if (endIndex - 1 != startIndex) {
     if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Remove all the overlapping ranges
+  for (PRInt32 i = startIndex; i < endIndex; ++i) {
+    mRanges[i].mRange->SetInSelection(false);
+  }
   mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
 
   nsTArray<RangeData> temp;
   for (PRInt32 i = overlaps.Length() - 1; i >= 0; i--) {
     nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -3806,16 +3824,20 @@ nsTypedSelection::AddItem(nsIRange *aIte
 
   if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Merge the leftovers back in to mRanges
   if (!mRanges.InsertElementsAt(startIndex, temp))
     return NS_ERROR_OUT_OF_MEMORY;
 
+  for (PRUint32 i = 0; i < temp.Length(); ++i) {
+    temp[i].mRange->SetInSelection(true);
+  }
+
   *aOutIndex = startIndex + insertionPoint;
   return NS_OK;
 }
 
 nsresult
 nsTypedSelection::RemoveItem(nsIRange *aItem)
 {
   if (!aItem)
@@ -3832,16 +3854,17 @@ nsTypedSelection::RemoveItem(nsIRange *a
       idx = (PRInt32)i;
       break;
     }
   }
   if (idx < 0)
     return NS_ERROR_INVALID_ARG;
 
   mRanges.RemoveElementAt(idx);
+  aItem->SetInSelection(false);
   return NS_OK;
 }
 
 nsresult
 nsTypedSelection::RemoveCollapsedRanges()
 {
   PRUint32 i = 0;
   while (i < mRanges.Length()) {
@@ -3855,18 +3878,19 @@ nsTypedSelection::RemoveCollapsedRanges(
   return NS_OK;
 }
 
 nsresult
 nsTypedSelection::Clear(nsPresContext* aPresContext)
 {
   setAnchorFocusRange(-1);
 
-  for (PRInt32 i = 0; i < (PRInt32)mRanges.Length(); i ++) {
-    selectFrames(aPresContext, mRanges[i].mRange, 0);
+  for (PRUint32 i = 0; i < mRanges.Length(); ++i) {
+    mRanges[i].mRange->SetInSelection(false);
+    selectFrames(aPresContext, mRanges[i].mRange, false);
   }
   mRanges.Clear();
 
   // Reset direction so for more dependable table selection range handling
   SetDirection(eDirNext);
 
   // If this was an ATTENTION selection, change it back to normal now
   if (mFrameSelection &&
@@ -4269,104 +4293,91 @@ nsTypedSelection::GetPrimaryFrameForFocu
 }
 
 //select all content children of aContent
 nsresult
 nsTypedSelection::SelectAllFramesForContent(nsIContentIterator *aInnerIter,
                                   nsIContent *aContent,
                                   bool aSelected)
 {
-  if (!mFrameSelection)
-    return NS_OK; // nothing to do
-  nsIPresShell* shell = mFrameSelection->GetShell();
-  if (!shell)
-    return NS_OK;
-  nsresult result;
-  if (!aInnerIter)
-    return NS_ERROR_NULL_POINTER;
-  result = aInnerIter->Init(aContent);
+  nsresult result = aInnerIter->Init(aContent);
   nsIFrame *frame;
   if (NS_SUCCEEDED(result))
   {
     // First select frame of content passed in
     frame = aContent->GetPrimaryFrame();
-    if (frame)
-    {
-      frame->SetSelected(aSelected, mType);
-      if (mFrameSelection->GetTableCellSelection())
-      {
-        nsITableCellLayout *tcl = do_QueryFrame(frame);
-        if (tcl)
-        {
-          return NS_OK;
-        }
-      }
+    if (frame && frame->GetType() == nsGkAtoms::textFrame) {
+      nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+      textFrame->SetSelectedRange(0, aContent->GetText()->GetLength(), aSelected, mType);
     }
     // Now iterated through the child frames and set them
-    while (!aInnerIter->IsDone())
-    {
+    while (!aInnerIter->IsDone()) {
       nsCOMPtr<nsIContent> innercontent =
         do_QueryInterface(aInnerIter->GetCurrentNode());
 
       frame = innercontent->GetPrimaryFrame();
-      if (frame)
-      {
-        frame->SetSelected(aSelected, mType);
+      if (frame) {
+        if (frame->GetType() == nsGkAtoms::textFrame) {
+          nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
+          textFrame->SetSelectedRange(0, innercontent->GetText()->GetLength(), aSelected, mType);
+        } else {
+          frame->InvalidateFrameSubtree();  // frame continuations?
+        }
       }
 
       aInnerIter->Next();
     }
 
     return NS_OK;
   }
 
   return NS_ERROR_FAILURE;
 }
 
-
-
 //the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
 nsresult
 nsTypedSelection::selectFrames(nsPresContext* aPresContext, nsIRange *aRange, bool aFlags)
 {
-  if (!mFrameSelection || !aPresContext)
+  if (!mFrameSelection || !aPresContext || !aPresContext->GetPresShell())
     return NS_OK; // nothing to do
-  nsIPresShell *presShell = aPresContext->GetPresShell();
-  if (!presShell)
+
+  if (mFrameSelection->GetTableCellSelection()) {
+    nsINode* node = aRange->GetCommonAncestor();
+    nsCOMPtr<nsIContent> content = do_QueryInterface(node);
+    nsIFrame* frame = content ? content->GetPrimaryFrame()
+                              : aPresContext->FrameManager()->GetRootFrame();
+    if (frame) {
+      frame->InvalidateFrameSubtree();
+    }
     return NS_OK;
-
-  nsCOMPtr<nsIDOMRange> domRange = do_QueryInterface(aRange);
-  if (!domRange || !aPresContext) 
-    return NS_ERROR_NULL_POINTER;
+  }
 
   nsresult result;
   nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
                                               kCSubtreeIteratorCID,
                                               &result);
   if (NS_FAILED(result))
     return result;
 
   nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
                                               kCContentIteratorCID,
                                               &result);
 
-  if ((NS_SUCCEEDED(result)) && iter && inneriter)
-  {
+  if ((NS_SUCCEEDED(result)) && iter) {
     result = iter->Init(aRange);
 
     // loop through the content iterator for each content node
     // for each text node, call SetSelected on it:
     nsCOMPtr<nsIContent> content = do_QueryInterface(aRange->GetStartParent());
 
     // we must call first one explicitly
     if (!content)
       return NS_ERROR_UNEXPECTED;
 
-    if (content->IsNodeOfType(nsINode::eTEXT))
-    {
+    if (content->IsNodeOfType(nsINode::eTEXT)) {
       nsIFrame* frame = content->GetPrimaryFrame();
       // The frame could be an SVG text frame, in which case we'll ignore
       // it.
       if (frame && frame->GetType() == nsGkAtoms::textFrame)
       {
         nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
         PRUint32 startOffset = aRange->StartOffset();
         PRUint32 endOffset;
@@ -4375,23 +4386,19 @@ nsTypedSelection::selectFrames(nsPresCon
         } else {
           endOffset = content->GetText()->GetLength();
         }
         textFrame->SetSelectedRange(startOffset, endOffset, aFlags, mType);
       }
     }
 
     iter->First();
-
-    while (!iter->IsDone())
-    {
+    while (!iter->IsDone()) {
       content = do_QueryInterface(iter->GetCurrentNode());
-
       SelectAllFramesForContent(inneriter, content, aFlags);
-
       iter->Next();
     }
 
     //we must now do the last one  if it is not the same as the first
     if (aRange->GetEndParent() != aRange->GetStartParent())
     {
       content = do_QueryInterface(aRange->GetEndParent(), &result);
       if (NS_FAILED(result) || !content)
@@ -4408,16 +4415,17 @@ nsTypedSelection::selectFrames(nsPresCon
           textFrame->SetSelectedRange(0, aRange->EndOffset(), aFlags, mType);
         }
       }
     }
   }
   return result;
 }
 
+
 // nsTypedSelection::LookUpSelection
 //
 //    This function is called when a node wants to know where the selection is
 //    over itself.
 //
 //    Usually, this is called when we already know there is a selection over
 //    the node in question, and we only need to find the boundaries of it on
 //    that node. This is when slowCheck is false--a strict test is not needed.
@@ -4780,23 +4788,17 @@ nsTypedSelection::AddRange(nsIRange* aRa
   setAnchorFocusRange(rangeIndex);
   
   // Make sure the caret appears on the next line, if at a newline
   if (mType == nsISelectionController::SELECTION_NORMAL)
     SetInterlinePosition(true);
 
   nsRefPtr<nsPresContext>  presContext;
   GetPresContext(getter_AddRefs(presContext));
-
-  // Ensure all frames are properly constructed for selectFrames, bug 602331.
-  nsIPresShell* presShell = presContext ? presContext->GetPresShell() : nsnull;
-  if (presShell) {
-    presShell->FlushPendingNotifications(Flush_Frames);
-  }
-  selectFrames(presContext, aRange, true);        
+  selectFrames(presContext, aRange, true);
 
   if (!mFrameSelection)
     return NS_OK;//nothing to do
 
   return mFrameSelection->NotifySelectionListeners(GetType());
 }
 
 // nsTypedSelection::RemoveRange
@@ -4921,37 +4923,31 @@ nsTypedSelection::Collapse(nsINode* aPar
   result = range->SetEnd(aParentNode, aOffset);
   if (NS_FAILED(result))
     return result;
   result = range->SetStart(aParentNode, aOffset);
   if (NS_FAILED(result))
     return result;
 
 #ifdef DEBUG_SELECTION
-  if (aParentNode)
-  {
-    nsCOMPtr<nsIContent>content;
-    content = do_QueryInterface(aParentNode);
-    if (!content)
-      return NS_ERROR_FAILURE;
-
-    printf ("Sel. Collapse to %p %s %d\n", content.get(),
-            nsAtomCString(content->Tag()).get(), aOffset);
-  }
-  else {
-    printf ("Sel. Collapse set to null parent.\n");
+  if (aParentNode) {
+    nsCOMPtr<nsIContent> content = do_QueryInterface(aParentNode);
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aParentNode);
+    printf ("Sel. Collapse to %p %s %d\n", aParentNode,
+            content ? nsAtomCString(content->Tag()).get()
+                    : (doc ? "DOCUMENT" : "???"),
+            aOffset);
   }
 #endif
 
-
   result = AddItem(range);
+  if (NS_FAILED(result))
+    return result;
   setAnchorFocusRange(0);
   selectFrames(presContext, range, true);
-  if (NS_FAILED(result))
-    return result;
   return mFrameSelection->NotifySelectionListeners(GetType());
 }
 
 /*
  * Sets the whole selection to be one point
  * at the start of the current selection
  */
 NS_IMETHODIMP
--- a/layout/generic/nsTextFrame.h
+++ b/layout/generic/nsTextFrame.h
@@ -160,21 +160,17 @@ public:
    * This is called only on the primary text frame. It indicates that
    * the selection state of the given character range has changed.
    * Text in the range is unconditionally invalidated
    * (nsTypedSelection::Repaint depends on this).
    * @param aSelected true if the selection has been added to the range,
    * false otherwise
    * @param aType the type of selection added or removed
    */
-  virtual void SetSelected(bool          aSelected,
-                           SelectionType aType);
-  void SetSelectedRange(PRUint32 aStart,
-                        PRUint32 aEnd,
-                        bool aSelected,
+  void SetSelectedRange(PRUint32 aStart, PRUint32 aEnd, bool aSelected,
                         SelectionType aType);
 
   virtual bool PeekOffsetNoAmount(bool aForward, PRInt32* aOffset);
   virtual bool PeekOffsetCharacter(bool aForward, PRInt32* aOffset,
                                      bool aRespectClusters = true);
   virtual bool PeekOffsetWord(bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
                                 PRInt32* aOffset, PeekWordState* aState);
 
@@ -471,16 +467,22 @@ protected:
   // This does *not* indicate the length of text currently mapped by the frame;
   // instead it's a hint saying that this frame *wants* to map this much text
   // so if we create a new continuation, this is where that continuation should
   // start.
   PRInt32     mContentLengthHint;
   nscoord     mAscent;
   gfxTextRun* mTextRun;
 
+  /**
+   * Return true if the frame is part of a Selection.
+   * Helper method to implement the public IsSelected() API.
+   */
+  virtual bool IsFrameSelected() const;
+
   // The caller of this method must call DestroySelectionDetails() on the
   // return value, if that return value is not null.  Calling
   // DestroySelectionDetails() on a null value is still OK, just not necessary.
   SelectionDetails* GetSelectionDetails();
 
   void UnionAdditionalOverflow(nsPresContext* aPresContext,
                                const nsHTMLReflowState& aBlockReflowState,
                                PropertyProvider& aProvider,
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -87,16 +87,17 @@
 #include "nsIUGenCategory.h"
 #include "nsUnicharUtilCIID.h"
 
 #include "nsTextFragment.h"
 #include "nsGkAtoms.h"
 #include "nsFrameSelection.h"
 #include "nsISelection.h"
 #include "nsIDOMRange.h"
+#include "nsRange.h"
 #include "nsCSSRendering.h"
 #include "nsContentUtils.h"
 #include "nsLineBreaker.h"
 #include "nsIWordBreaker.h"
 #include "nsGenericDOMDataNode.h"
 
 #include "nsILineIterator.h"
 
@@ -3684,16 +3685,17 @@ nsTextFrame::Init(nsIContent*      aCont
   // might be invalid if the content was modified while there was no frame
   aContent->DeleteProperty(nsGkAtoms::newline);
   if (PresContext()->BidiEnabled()) {
     aContent->DeleteProperty(nsGkAtoms::flowlength);
   }
 
   // Since our content has a frame now, this flag is no longer needed.
   aContent->UnsetFlags(NS_CREATE_FRAME_IF_NON_WHITESPACE);
+
   // We're not a continuing frame.
   // mContentOffset = 0; not necessary since we get zeroed out at init
   return nsFrame::Init(aContent, aParent, aPrevInFlow);
 }
 
 void
 nsTextFrame::ClearFrameOffsetCache()
 {
@@ -4309,16 +4311,19 @@ GetGeneratedContentOwner(nsIFrame* aFram
   }
   return aFrame;
 }
 
 SelectionDetails*
 nsTextFrame::GetSelectionDetails()
 {
   const nsFrameSelection* frameSelection = GetConstFrameSelection();
+  if (frameSelection->GetTableCellSelection()) {
+    return nsnull;
+  }
   if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) {
     SelectionDetails* details =
       frameSelection->LookUpSelection(mContent, GetContentOffset(),
                                       GetContentLength(), false);
     SelectionDetails* sd;
     for (sd = details; sd; sd = sd->mNext) {
       sd->mStart += mContentOffset;
       sd->mEnd += mContentOffset;
@@ -4570,17 +4575,17 @@ nsTextFrame::UnionAdditionalOverflow(nsP
       }
 
       aVisualOverflowRect->UnionRect(*aVisualOverflowRect,
                                      nsRect(0, top, width, bottom - top));
     }
   }
   // When this frame is not selected, the text-decoration area must be in
   // frame bounds.
-  if (!(GetStateBits() & NS_FRAME_SELECTED_CONTENT) ||
+  if (!IsSelected() ||
       !CombineSelectionUnderlineRect(aPresContext, *aVisualOverflowRect))
     return;
   AddStateBits(TEXT_SELECTION_UNDERLINE_OVERFLOWED);
 }
 
 static gfxFloat
 ComputeDescentLimitForSelectionUnderline(nsPresContext* aPresContext,
                                          nsTextFrame* aFrame,
@@ -5020,23 +5025,17 @@ nsTextFrame::PaintTextWithSelectionColor
         }
       }
     }
     sdptr = sdptr->mNext;
   }
   *aAllTypes = allTypes;
 
   if (!allTypes) {
-    // Nothing is selected in the given text range.
-    if (aContentLength == aProvider.GetOriginalLength()) {
-      // It's the full text range so we can remove the FRAME_SELECTED_CONTENT
-      // bit to avoid going through this slow path until something is selected
-      // in this frame again.
-      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-    }
+    // Nothing is selected in the given text range. XXX can this still occur?
     return false;
   }
 
   const gfxFloat startXOffset = aTextBaselinePt.x - aFramePt.x;
   gfxFloat xOffset, hyphenWidth;
   PRUint32 offset, length; // in transformed string
   SelectionType type;
   nsTextRangeStyle rangeStyle;
@@ -5174,33 +5173,28 @@ bool
 nsTextFrame::PaintTextWithSelection(gfxContext* aCtx,
     const gfxPoint& aFramePt,
     const gfxPoint& aTextBaselinePt, const gfxRect& aDirtyRect,
     PropertyProvider& aProvider,
     PRUint32 aContentOffset, PRUint32 aContentLength,
     nsTextPaintStyle& aTextPaintStyle,
     const nsCharClipDisplayItem::ClipEdges& aClipEdges)
 {
+  NS_ASSERTION(GetContent()->IsSelectionDescendant(), "wrong paint path");
+
   SelectionDetails* details = GetSelectionDetails();
   if (!details) {
-    if (aContentLength == aProvider.GetOriginalLength()) {
-      // It's the full text range so we can remove the FRAME_SELECTED_CONTENT
-      // bit to avoid going through this slow path until something is selected
-      // in this frame again.
-      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-    }
     return false;
   }
 
   SelectionType allTypes;
   if (!PaintTextWithSelectionColors(aCtx, aFramePt, aTextBaselinePt, aDirtyRect,
                                     aProvider, aContentOffset, aContentLength,
                                     aTextPaintStyle, details, &allTypes,
-                                    aClipEdges))
-  {
+                                    aClipEdges)) {
     DestroySelectionDetails(details);
     return false;
   }
   PRInt32 i;
   // Iterate through just the selection types that paint decorations and
   // paint decorations for any that actually occur in this frame. Paint
   // higher-numbered selection types below lower-numered ones on the
   // general principal that lower-numbered selections are higher priority.
@@ -5407,25 +5401,26 @@ nsTextFrame::PaintText(nsRenderingContex
   textBaselinePt.x += rtl ? -snappedRightEdge : snappedLeftEdge;
   nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedLeftEdge,
                                              snappedRightEdge);
   nsTextPaintStyle textPaintStyle(this);
 
   gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
                     aDirtyRect.width, aDirtyRect.height);
   // Fork off to the (slower) paint-with-selection path if necessary.
-  if (nsLayoutUtils::GetNonGeneratedAncestor(this)->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
+  if (IsSelected()) {
     gfxSkipCharsIterator tmp(provider.GetStart());
     PRInt32 contentOffset = tmp.ConvertSkippedToOriginal(startOffset);
     PRInt32 contentLength =
       tmp.ConvertSkippedToOriginal(startOffset + maxLength) - contentOffset;
     if (PaintTextWithSelection(ctx, framePt, textBaselinePt, dirtyRect,
                                provider, contentOffset, contentLength,
-                               textPaintStyle, clipEdges))
+                               textPaintStyle, clipEdges)) {
       return;
+    }
   }
 
   nscolor foregroundColor = textPaintStyle.GetTextColor();
   const nsStyleText* textStyle = GetStyleText();
   if (textStyle->mTextShadow) {
     // Text shadow happens with the last value being painted at the back,
     // ie. it is painted first.
     for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
@@ -5605,18 +5600,17 @@ nsTextFrame::GetSelectionStatus(PRInt16*
 
   return selectionValue;
 }
 
 bool
 nsTextFrame::IsVisibleInSelection(nsISelection* aSelection)
 {
   // Check the quick way first
-  bool isSelected = (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
-  if (!isSelected)
+  if (!GetContent()->IsSelectionDescendant())
     return false;
     
   SelectionDetails* details = GetSelectionDetails();
   bool found = false;
     
   // where are the selection points "really"
   SelectionDetails *sdptr = details;
   while (sdptr) {
@@ -5796,69 +5790,52 @@ nsTextFrame::CombineSelectionUnderlineRe
         style, descentLimit);
     aRect.UnionRect(aRect, decorationArea);
   }
   DestroySelectionDetails(details);
 
   return !aRect.IsEmpty() && !givenRect.Contains(aRect);
 }
 
-void
-nsTextFrame::SetSelected(bool          aSelected,
-                         SelectionType aType)
-{
-  SetSelectedRange(0, mContent->GetText()->GetLength(), aSelected, aType);
+bool
+nsTextFrame::IsFrameSelected() const
+{
+  NS_ASSERTION(!GetContent() || GetContent()->IsSelectionDescendant(),
+               "use the public IsSelected() instead");
+  return nsRange::IsNodeSelected(GetContent(), GetContentOffset(),
+                                 GetContentEnd());
 }
 
 void
-nsTextFrame::SetSelectedRange(PRUint32 aStart,
-                              PRUint32 aEnd,
-                              bool aSelected,
+nsTextFrame::SetSelectedRange(PRUint32 aStart, PRUint32 aEnd, bool aSelected,
                               SelectionType aType)
 {
   NS_ASSERTION(!GetPrevContinuation(), "Should only be called for primary frame");
   DEBUG_VERIFY_NOT_DIRTY(mState);
 
   // Selection is collapsed, which can't affect text frame rendering
   if (aStart == aEnd)
     return;
 
   if (aType == nsISelectionController::SELECTION_NORMAL) {
     // check whether style allows selection
     bool selectable;
     IsSelectable(&selectable, nsnull);
-    if (!selectable)
+    if (!selectable) {
       return;
-  }
-
-  bool anySelected = false;
+    }
+  }
 
   nsTextFrame* f = this;
   while (f && f->GetContentEnd() <= PRInt32(aStart)) {
-    if (f->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
-      anySelected = true;
-    }
     f = static_cast<nsTextFrame*>(f->GetNextContinuation());
   }
 
   nsPresContext* presContext = PresContext();
   while (f && f->GetContentOffset() < PRInt32(aEnd)) {
-    if (aSelected) {
-      f->AddStateBits(NS_FRAME_SELECTED_CONTENT);
-      anySelected = true;
-    } else { // we need to see if any other selection is available.
-      SelectionDetails *details = f->GetSelectionDetails();
-      if (details) {
-        anySelected = true;
-        DestroySelectionDetails(details);
-      } else {
-        f->RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-      }
-    }
-
     // We may need to reflow to recompute the overflow area for
     // spellchecking or IME underline if their underline is thicker than
     // the normal decoration line.
     if (aType & SelectionTypesWithDecorations) {
       bool didHaveOverflowingSelection =
         (f->GetStateBits() & TEXT_SELECTION_UNDERLINE_OVERFLOWED) != 0;
       nsRect r(nsPoint(0, 0), GetSize());
       bool willHaveOverflowingSelection =
@@ -5869,32 +5846,16 @@ nsTextFrame::SetSelectedRange(PRUint32 a
                                                    NS_FRAME_IS_DIRTY);
       }
     }
     // Selection might change anything. Invalidate the overflow area.
     f->InvalidateOverflowRect();
 
     f = static_cast<nsTextFrame*>(f->GetNextContinuation());
   }
-
-  // Scan remaining continuations to see if any are selected
-  while (f && !anySelected) {
-    if (f->GetStateBits() & NS_FRAME_SELECTED_CONTENT) {
-      anySelected = true;
-    }
-    f = static_cast<nsTextFrame*>(f->GetNextContinuation());
-  }
-
-  if (anySelected) {
-    mContent->SetFlags(NS_TEXT_IN_SELECTION);
-  } else {
-    // This is only legal because there is only one presentation for the
-    // content with a selection
-    mContent->UnsetFlags(NS_TEXT_IN_SELECTION);
-  }
 }
 
 NS_IMETHODIMP
 nsTextFrame::GetPointFromOffset(PRInt32 inOffset,
                                 nsPoint* outPoint)
 {
   if (!outPoint)
     return NS_ERROR_NULL_POINTER;
@@ -7618,26 +7579,16 @@ nsTextFrame::ReflowText(nsLineLayout& aL
     NS_ASSERTION(numJustifiableCharacters <= charsFit,
                  "Bad justifiable character count");
     aLineLayout.SetTextJustificationWeights(numJustifiableCharacters,
         charsFit - numJustifiableCharacters);
   }
 
   SetLength(contentLength, &aLineLayout, ALLOW_FRAME_CREATION_AND_DESTRUCTION);
 
-  if (mContent->HasFlag(NS_TEXT_IN_SELECTION)) {
-    SelectionDetails* details = GetSelectionDetails();
-    if (details) {
-      AddStateBits(NS_FRAME_SELECTED_CONTENT);
-      DestroySelectionDetails(details);
-    } else {
-      RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-    }
-  }
-
   Invalidate(aMetrics.VisualOverflow());
 
 #ifdef NOISY_REFLOW
   ListTag(stdout);
   printf(": desiredSize=%d,%d(b=%d) status=%x\n",
          aMetrics.width, aMetrics.height, aMetrics.ascent,
          aStatus);
 #endif
@@ -7991,22 +7942,19 @@ nsTextFrame::List(FILE* out, PRInt32 aIn
     fprintf(out, " prev-continuation=%p", static_cast<void*>(prevContinuation));
   }
   if (nsnull != mNextContinuation) {
     fprintf(out, " next-continuation=%p", static_cast<void*>(mNextContinuation));
   }
 
   // Output the rect and state
   fprintf(out, " {%d,%d,%d,%d}", mRect.x, mRect.y, mRect.width, mRect.height);
-  if (0 != mState) {
-    if (mState & NS_FRAME_SELECTED_CONTENT) {
-      fprintf(out, " [state=%016llx] SELECTED", mState);
-    } else {
-      fprintf(out, " [state=%016llx]", mState);
-    }
+  fprintf(out, " [state=%016llx]", mState);
+  if (IsSelected()) {
+    fprintf(out, " SELECTED");
   }
   fprintf(out, " [content=%p]", static_cast<void*>(mContent));
   if (HasOverflowAreas()) {
     nsRect overflowArea = GetVisualOverflowRect();
     fprintf(out, " [vis-overflow=%d,%d,%d,%d]",
             overflowArea.x, overflowArea.y,
             overflowArea.width, overflowArea.height);
     overflowArea = GetScrollableOverflowRect();
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -84,22 +84,17 @@ nsMathMLmoFrame::GetMathMLFrameType()
 
 // since a mouse click implies selection, we cannot just rely on the
 // frame's state bit in our child text frame. So we will first check
 // its selected state bit, and use this little helper to double check.
 bool
 nsMathMLmoFrame::IsFrameInSelection(nsIFrame* aFrame)
 {
   NS_ASSERTION(aFrame, "null arg");
-  if (!aFrame)
-    return false;
-
-  bool isSelected = false;
-  aFrame->GetSelected(&isSelected);
-  if (!isSelected)
+  if (!aFrame || !aFrame->IsSelected())
     return false;
 
   const nsFrameSelection* frameSelection = aFrame->GetConstFrameSelection();
   SelectionDetails* details =
     frameSelection->LookUpSelection(aFrame->GetContent(), 0, 1, true);
 
   if (!details)
     return false;
--- a/layout/printing/nsPrintEngine.cpp
+++ b/layout/printing/nsPrintEngine.cpp
@@ -2541,25 +2541,17 @@ nsPrintEngine::FindSelectionBoundsWithLi
                                            nsRect&         aEndRect)
 {
   NS_ASSERTION(aPresContext, "Pointer is null!");
   NS_ASSERTION(aParentFrame, "Pointer is null!");
 
   aRect += aParentFrame->GetPosition();
   for (; !aChildFrames.AtEnd(); aChildFrames.Next()) {
     nsIFrame* child = aChildFrames.get();
-    // only leaf frames have this bit flipped
-    // then check the hard way
-    bool isSelected = (child->GetStateBits() & NS_FRAME_SELECTED_CONTENT)
-      == NS_FRAME_SELECTED_CONTENT;
-    if (isSelected) {
-      isSelected = child->IsVisibleForPainting();
-    }
-
-    if (isSelected) {
+    if (child->IsSelected() && child->IsVisibleForPainting()) {
       nsRect r = child->GetRect();
       if (aStartFrame == nsnull) {
         aStartFrame = child;
         aStartRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
       } else {
         aEndFrame = child;
         aEndRect.SetRect(aRect.x + r.x, aRect.y + r.y, r.width, r.height);
       }
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -266,52 +266,16 @@ nsSVGGlyphFrame::DidSetStyleContext(nsSt
   nsSVGGlyphFrameBase::DidSetStyleContext(aOldStyleContext);
 
   if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
     ClearTextRun();
     NotifyGlyphMetricsChange();
   }
 }
 
-void
-nsSVGGlyphFrame::SetSelected(bool          aSelected,
-                             SelectionType aType)
-{
-#if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
-  printf("nsSVGGlyphFrame(%p)::SetSelected()\n", this);
-#endif
-
-  if (aType != nsISelectionController::SELECTION_NORMAL)
-    return;
-
-  // check whether style allows selection
-  bool selectable;
-  IsSelectable(&selectable, nsnull);
-  if (!selectable)
-    return;
-
-  if (aSelected) {
-    AddStateBits(NS_FRAME_SELECTED_CONTENT);
-  } else {
-    RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
-  }
-
-  nsSVGUtils::UpdateGraphic(this);
-}
-
-NS_IMETHODIMP
-nsSVGGlyphFrame::GetSelected(bool *aSelected) const
-{
-  nsresult rv = nsSVGGlyphFrameBase::GetSelected(aSelected);
-#if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
-  printf("nsSVGGlyphFrame(%p)::GetSelected()=%d\n", this, *aSelected);
-#endif
-  return rv;
-}
-
 NS_IMETHODIMP
 nsSVGGlyphFrame::IsSelectable(bool* aIsSelectable,
                               PRUint8* aSelectStyle) const
 {
   nsresult rv = nsSVGGlyphFrameBase::IsSelectable(aIsSelectable, aSelectStyle);
 #if defined(DEBUG) && defined(SVG_DEBUG_SELECTION)
   printf("nsSVGGlyphFrame(%p)::IsSelectable()=(%d,%d)\n", this, *aIsSelectable, aSelectStyle);
 #endif
@@ -979,19 +943,17 @@ nsresult
 nsSVGGlyphFrame::GetHighlight(PRUint32 *charnum, PRUint32 *nchars,
                               nscolor *foreground, nscolor *background)
 {
   *foreground = NS_RGB(255,255,255);
   *background = NS_RGB(0,0,0); 
   *charnum=0;
   *nchars=0;
 
-  bool hasHighlight =
-    (mState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
-
+  bool hasHighlight = IsSelected();
   if (!hasHighlight) {
     NS_ERROR("nsSVGGlyphFrame::GetHighlight() called by renderer when there is no highlight");
     return NS_ERROR_FAILURE;
   }
 
   nsPresContext *presContext = PresContext();
 
   // The selection ranges are relative to the uncompressed text in
--- a/layout/svg/base/src/nsSVGGlyphFrame.h
+++ b/layout/svg/base/src/nsSVGGlyphFrame.h
@@ -130,19 +130,16 @@ public:
   bool EndsWithWhitespace() const;
   bool IsAllWhitespace() const;
 
   // nsIFrame interface:
   NS_IMETHOD  CharacterDataChanged(CharacterDataChangeInfo* aInfo);
 
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext);
 
-  virtual void SetSelected(bool          aSelected,
-                           SelectionType aType);
-  NS_IMETHOD  GetSelected(bool *aSelected) const;
   NS_IMETHOD  IsSelectable(bool* aIsSelectable, PRUint8* aSelectStyle) const;
 
   NS_IMETHOD Init(nsIContent*      aContent,
                   nsIFrame*        aParent,
                   nsIFrame*        aPrevInFlow);
 
   /**
    * Get the "type" of the frame
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -313,18 +313,17 @@ inline nscolor EnsureDifferentColors(nsc
     }
     return colorA;
 }
 
 void
 nsTableCellFrame::DecorateForSelection(nsRenderingContext& aRenderingContext,
                                        nsPoint aPt)
 {
-  NS_ASSERTION(GetStateBits() & NS_FRAME_SELECTED_CONTENT,
-               "Should only be called for selected cells");
+  NS_ASSERTION(IsSelected(), "Should only be called for selected cells");
   PRInt16 displaySelection;
   nsPresContext* presContext = PresContext();
   displaySelection = DisplaySelection(presContext);
   if (displaySelection) {
     nsRefPtr<nsFrameSelection> frameSelection =
       presContext->PresShell()->FrameSelection();
 
     if (frameSelection->GetTableCellSelection()) {
@@ -494,19 +493,17 @@ nsTableCellFrame::BuildDisplayList(nsDis
     if (!tableFrame->IsBorderCollapse() && HasBorder() &&
         emptyCellStyle == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
       nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
           nsDisplayBorder(aBuilder, this));
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // and display the selection border if we need to
-    bool isSelected =
-      (GetStateBits() & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
-    if (isSelected) {
+    if (IsSelected()) {
       nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
           nsDisplayGeneric(aBuilder, this, ::PaintTableCellSelection,
                            "TableCellSelection",
                            nsDisplayItem::TYPE_TABLE_CELL_SELECTION));
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -3662,17 +3662,17 @@ nsTableFrame::GetCellDataAt(PRInt32     
                             PRInt32        aColIndex,
                             nsIDOMElement* &aCell,   //out params
                             PRInt32&       aStartRowIndex,
                             PRInt32&       aStartColIndex,
                             PRInt32&       aRowSpan,
                             PRInt32&       aColSpan,
                             PRInt32&       aActualRowSpan,
                             PRInt32&       aActualColSpan,
-                            bool&        aIsSelected)
+                            bool&          aIsSelected)
 {
   // Initialize out params
   aCell = nsnull;
   aStartRowIndex = 0;
   aStartColIndex = 0;
   aRowSpan = 0;
   aColSpan = 0;
   aIsSelected = false;
@@ -3695,18 +3695,17 @@ nsTableFrame::GetCellDataAt(PRInt32     
   aColSpan = cellFrame->GetColSpan();
   aActualRowSpan = GetEffectiveRowSpan(*cellFrame);
   aActualColSpan = GetEffectiveColSpan(*cellFrame);
 
   // If these aren't at least 1, we have a cellmap error
   if (aActualRowSpan == 0 || aActualColSpan == 0)
     return NS_ERROR_FAILURE;
 
-  result = cellFrame->GetSelected(&aIsSelected);
-  if (NS_FAILED(result)) return result;
+  aIsSelected = cellFrame->IsSelected();
 
   // do this last, because it addrefs,
   // and we don't want the caller leaking it on error
   nsIContent* content = cellFrame->GetContent();
   if (!content) return NS_ERROR_FAILURE;
 
   return CallQueryInterface(content, &aCell);
 }
--- a/layout/tables/nsTableOuterFrame.cpp
+++ b/layout/tables/nsTableOuterFrame.cpp
@@ -366,24 +366,16 @@ nsTableOuterFrame::BuildDisplayListForIn
   while (kid) {
     nsresult rv = BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
     NS_ENSURE_SUCCESS(rv, rv);
     kid = kid->GetNextSibling();
   }
   return NS_OK;
 }
 
-void
-nsTableOuterFrame::SetSelected(bool          aSelected,
-                               SelectionType aType)
-{
-  nsFrame::SetSelected(aSelected, aType);
-  InnerTableFrame()->SetSelected(aSelected, aType);
-}
-
 nsIFrame*
 nsTableOuterFrame::GetParentStyleContextFrame()
 {
   // The table outer frame and the (inner) table frame split the style
   // data by giving the table frame the style context associated with
   // the table content node and creating a style context for the outer
   // frame that is a *child* of the table frame's style context,
   // matching the ::-moz-table-outer pseudo-element.  html.css has a
--- a/layout/tables/nsTableOuterFrame.h
+++ b/layout/tables/nsTableOuterFrame.h
@@ -154,21 +154,16 @@ public:
    * @see nsGkAtoms::tableOuterFrame
    */
   virtual nsIAtom* GetType() const;
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const;
 #endif
 
-  /** SetSelected needs to be overridden to talk to inner tableframe
-   */
-  void SetSelected(bool aSelected,
-                   SelectionType aType);
-
   virtual nsIFrame* GetParentStyleContextFrame();
 
   /*---------------- nsITableLayout methods ------------------------*/
 
   /** @see nsITableFrame::GetCellDataAt */
   NS_IMETHOD GetCellDataAt(PRInt32 aRowIndex, PRInt32 aColIndex, 
                            nsIDOMElement* &aCell,   //out params
                            PRInt32& aStartRowIndex, PRInt32& aStartColIndex,