author | Mats Palmgren <matspal@gmail.com> |
Tue, 20 Dec 2011 10:15:41 +0100 | |
changeset 83036 | e3766ee732ccbec82ad84e76eb60f87a3e8182d3 |
parent 83035 | feaccb6a4c352008d68264ab1ceb1343bbc6cf3d |
child 83037 | a08580f18cb72e6bba342a5caaf1eb30119ef812 |
push id | 4243 |
push user | mpalmgren@mozilla.com |
push date | Tue, 20 Dec 2011 09:16:07 +0000 |
treeherder | mozilla-inbound@0aa9c3a5b7e1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | smaug |
bugs | 619273 |
milestone | 11.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
|
--- 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,