Fix for
bug 471126 (leak content nodes (and sometimes dom windows) after clicking on nytimes.com articles). r=bent, sr=bz.
--- a/editor/composer/src/nsComposerCommandsUpdater.cpp
+++ b/editor/composer/src/nsComposerCommandsUpdater.cpp
@@ -49,25 +49,29 @@
#include "nsString.h"
#include "nsICommandManager.h"
#include "nsIDocShell.h"
#include "nsITransactionManager.h"
nsComposerCommandsUpdater::nsComposerCommandsUpdater()
-: mDOMWindow(nsnull)
-, mDirtyState(eStateUninitialized)
+: mDirtyState(eStateUninitialized)
, mSelectionCollapsed(eStateUninitialized)
, mFirstDoOfFirstUndo(PR_TRUE)
{
}
nsComposerCommandsUpdater::~nsComposerCommandsUpdater()
{
+ // cancel any outstanding update timer
+ if (mUpdateTimer)
+ {
+ mUpdateTimer->Cancel();
+ }
}
NS_IMPL_ISUPPORTS4(nsComposerCommandsUpdater, nsISelectionListener,
nsIDocumentStateListener, nsITransactionListener, nsITimerCallback)
#if 0
#pragma mark -
#endif
@@ -236,17 +240,17 @@ nsComposerCommandsUpdater::DidMerge(nsIT
#if 0
#pragma mark -
#endif
nsresult
nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow)
{
NS_ENSURE_ARG(aDOMWindow);
- mDOMWindow = aDOMWindow;
+ mDOMWindow = do_GetWeakReference(aDOMWindow);
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWindow));
if (window)
{
mDocShell = do_GetWeakReference(window->GetDocShell());
}
return NS_OK;
}
@@ -359,20 +363,22 @@ nsComposerCommandsUpdater::UpdateOneComm
commandUpdater->CommandStatusChanged(aCommand);
return NS_OK;
}
PRBool
nsComposerCommandsUpdater::SelectionIsCollapsed()
{
- if (!mDOMWindow) return PR_TRUE;
+ nsCOMPtr<nsIDOMWindow> domWindow = do_QueryReferent(mDOMWindow);
+ if (!domWindow)
+ return PR_TRUE;
nsCOMPtr<nsISelection> domSelection;
- if (NS_SUCCEEDED(mDOMWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection)
+ if (NS_SUCCEEDED(domWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection)
{
PRBool selectionCollapsed = PR_FALSE;
domSelection->GetIsCollapsed(&selectionCollapsed);
return selectionCollapsed;
}
NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection");
--- a/editor/composer/src/nsComposerCommandsUpdater.h
+++ b/editor/composer/src/nsComposerCommandsUpdater.h
@@ -114,17 +114,17 @@ protected:
nsresult UpdateCommandGroup(const nsAString& aCommandGroup);
already_AddRefed<nsPICommandUpdater> GetCommandUpdater();
nsresult PrimeUpdateTimer();
void TimerCallback();
nsCOMPtr<nsITimer> mUpdateTimer;
- nsIDOMWindow* mDOMWindow; // Weak reference
+ nsWeakPtr mDOMWindow;
nsWeakPtr mDocShell;
PRInt8 mDirtyState;
PRInt8 mSelectionCollapsed;
PRPackedBool mFirstDoOfFirstUndo;
};
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -388,16 +388,18 @@ private:
nsresult SyncParentSubDocMap();
nsresult GetDocumentSelection(nsISelection **aSelection);
nsresult GetClipboardEventTarget(nsIDOMNode **aEventTarget);
nsresult FireClipboardEvent(PRUint32 msg, PRBool* aPreventDefault);
+ void DestroyPresShell();
+
#ifdef NS_PRINTING
// Called when the DocViewer is notified that the state
// of Printing or PP has changed
void SetIsPrintingInDocShellTree(nsIDocShellTreeNode* aParentNode,
PRBool aIsPrintingOrPP,
PRBool aStartAtTop);
#endif // NS_PRINTING
@@ -669,16 +671,19 @@ DocumentViewerImpl::Init(nsIWidget* aPar
NS_ENSURE_SUCCESS(rv, rv);
return InitInternal(aParentWidget, nsnull, aBounds, PR_TRUE, PR_FALSE);
}
nsresult
DocumentViewerImpl::InitPresentationStuff(PRBool aDoInitialReflow, PRBool aReenableRefresh)
{
+ NS_ASSERTION(!mPresShell,
+ "Someone should have destroyed the presshell!");
+
// Create the style set...
nsStyleSet *styleSet;
nsresult rv = CreateStyleSet(mDocument, &styleSet);
NS_ENSURE_SUCCESS(rv, rv);
// Now make the shell for the document
rv = mDocument->CreateShell(mPresContext, mViewManager, styleSet,
getter_AddRefs(mPresShell));
@@ -742,24 +747,26 @@ DocumentViewerImpl::InitPresentationStuf
// Now trigger a refresh
if (aReenableRefresh && mEnableRendering && mViewManager) {
mViewManager->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
}
// now register ourselves as a selection listener, so that we get
// called when the selection changes in the window
- nsDocViewerSelectionListener *selectionListener =
- new nsDocViewerSelectionListener();
- NS_ENSURE_TRUE(selectionListener, NS_ERROR_OUT_OF_MEMORY);
-
- selectionListener->Init(this);
-
- // mSelectionListener is a owning reference
- mSelectionListener = selectionListener;
+ if (!mSelectionListener) {
+ nsDocViewerSelectionListener *selectionListener =
+ new nsDocViewerSelectionListener();
+ NS_ENSURE_TRUE(selectionListener, NS_ERROR_OUT_OF_MEMORY);
+
+ selectionListener->Init(this);
+
+ // mSelectionListener is a owning reference
+ mSelectionListener = selectionListener;
+ }
nsCOMPtr<nsISelection> selection;
rv = GetDocumentSelection(getter_AddRefs(selection));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
rv = selPrivate->AddSelectionListener(mSelectionListener);
if (NS_FAILED(rv))
@@ -1510,29 +1517,17 @@ DocumentViewerImpl::Destroy()
if (mPreviousViewer) {
mPreviousViewer->Destroy();
mPreviousViewer = nsnull;
}
mDeviceContext = nsnull;
if (mPresShell) {
- // Break circular reference (or something)
- mPresShell->EndObservingDocument();
-
- nsCOMPtr<nsISelection> selection;
- GetDocumentSelection(getter_AddRefs(selection));
-
- nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
-
- if (selPrivate && mSelectionListener)
- selPrivate->RemoveSelectionListener(mSelectionListener);
-
- mPresShell->Destroy();
- mPresShell = nsnull;
+ DestroyPresShell();
}
if (mPresContext) {
mPresContext->SetContainer(nsnull);
mPresContext->SetLinkHandler(nsnull);
mPresContext = nsnull;
}
@@ -1635,20 +1630,17 @@ DocumentViewerImpl::SetDOMDocument(nsIDO
}
if (mPresContext) {
// Save the linkhandler (nsPresShell::Destroy removes it from
// mPresContext).
linkHandler = mPresContext->GetLinkHandler();
}
- mPresShell->EndObservingDocument();
- mPresShell->Destroy();
-
- mPresShell = nsnull;
+ DestroyPresShell();
// This destroys the root view because it was associated with the root frame,
// which has been torn down. Recreate the viewmanager and root view.
MakeWindow(currentSize);
}
// And if we're already given a prescontext...
if (mPresContext) {
@@ -1949,40 +1941,28 @@ DocumentViewerImpl::Hide(void)
if (mIsSticky) {
// This window is sticky, that means that it might be shown again
// and we don't want the presshell n' all that to be thrown away
// just because the window is hidden.
return NS_OK;
}
- // Break circular reference (or something)
- mPresShell->EndObservingDocument();
- nsCOMPtr<nsISelection> selection;
-
- GetDocumentSelection(getter_AddRefs(selection));
-
- nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
-
- if (selPrivate && mSelectionListener) {
- selPrivate->RemoveSelectionListener(mSelectionListener);
- }
-
nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
if (docShell) {
nsCOMPtr<nsILayoutHistoryState> layoutState;
mPresShell->CaptureHistoryState(getter_AddRefs(layoutState), PR_TRUE);
}
- mPresShell->Destroy();
+ DestroyPresShell();
+
// Clear weak refs
mPresContext->SetContainer(nsnull);
mPresContext->SetLinkHandler(nsnull);
- mPresShell = nsnull;
mPresContext = nsnull;
mViewManager = nsnull;
mWindow = nsnull;
mDeviceContext = nsnull;
mParentWidget = nsnull;
nsCOMPtr<nsIBaseWindow> base_win(do_QueryReferent(mContainer));
@@ -4161,24 +4141,17 @@ NS_IMETHODIMP DocumentViewerImpl::SetPag
// XXX Page mode is only partially working; it's currently used for
// reftests that require a paginated context
mIsPageMode = aPageMode;
// Get the current size of what is being viewed
nsRect bounds;
mWindow->GetBounds(bounds);
if (mPresShell) {
- // Break circular reference (or something)
- mPresShell->EndObservingDocument();
- nsCOMPtr<nsISelection> selection;
- nsresult rv = GetDocumentSelection(getter_AddRefs(selection));
- nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
- if (NS_SUCCEEDED(rv) && selPrivate && mSelectionListener)
- selPrivate->RemoveSelectionListener(mSelectionListener);
- mPresShell->Destroy();
+ DestroyPresShell();
}
if (mPresContext) {
mPresContext->SetContainer(nsnull);
mPresContext->SetLinkHandler(nsnull);
}
mPresShell = nsnull;
@@ -4205,8 +4178,24 @@ NS_IMETHODIMP DocumentViewerImpl::SetPag
}
NS_IMETHODIMP
DocumentViewerImpl::GetHistoryEntry(nsISHEntry **aHistoryEntry)
{
NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
return NS_OK;
}
+
+void
+DocumentViewerImpl::DestroyPresShell()
+{
+ // Break circular reference (or something)
+ mPresShell->EndObservingDocument();
+
+ nsCOMPtr<nsISelection> selection;
+ GetDocumentSelection(getter_AddRefs(selection));
+ nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(selection);
+ if (selPrivate && mSelectionListener)
+ selPrivate->RemoveSelectionListener(mSelectionListener);
+
+ mPresShell->Destroy();
+ mPresShell = nsnull;
+}
--- a/layout/base/tests/Makefile.in
+++ b/layout/base/tests/Makefile.in
@@ -60,16 +60,17 @@ DEFINES += -D_IMPL_NS_LAYOUT
test_bug416896.html \
test_bug420499.xul \
test_bug423523.html \
test_bug445810.html \
test_bug449781.html \
test_bug450930.xhtml \
test_bug458898.html \
test_bug465448.xul \
+ test_bug471126.html \
$(NULL)
# test_bug396024.html is currently disabled because it interacts badly with
# the "You can't print-preview while the page is loading" dialog.
# (See bug 407080)
# Tests for bug 441782 don't pass reliably on Windows, because of bug 469208
ifneq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
_TEST_FILES += \
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug471126.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=471126
+-->
+<head>
+ <title>Test for Bug 471126</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=471126">Mozilla Bug 471126</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 471126 **/
+
+function test()
+{
+ var selection = window.getSelection();
+ selection.collapse(document.documentElement, 0);
+ document.documentElement.addEventListener("click", function(){ var foo = window; }, false);
+}
+test();
+ok(true, "Shoudn't leak");
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -209,17 +209,18 @@ class nsIScrollableView;
*/
class nsFrameSelection : public nsISupports {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_FRAME_SELECTION_IID)
enum HINT { HINTLEFT = 0, HINTRIGHT = 1}; //end of this line or beginning of next
/*interfaces for addref and release and queryinterface*/
- NS_DECL_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
/** Init will initialize the frame selector with the necessary pres shell to
* be used by most of the methods
* @param aShell is the parameter to be used for most of the other calls for callbacks etc
* @param aLimiter limits the selection to nodes with aLimiter parents
*/
void Init(nsIPresShell *aShell, nsIContent *aLimiter);
@@ -546,17 +547,16 @@ public:
* 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();
- virtual ~nsFrameSelection();
void StartBatchChanges();
void EndBatchChanges();
/*unsafe*/
nsresult DeleteFromDocument();
nsIPresShell *GetShell()const { return mShell; }
@@ -610,17 +610,17 @@ private:
PRUint32 GetBatching() const {return mBatching; }
PRBool GetNotifyFrames() const { return mNotifyFrames; }
void SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
// nsFrameSelection may get deleted when calling this,
// so remember to use nsCOMPtr when needed.
nsresult NotifySelectionListeners(SelectionType aType); // add parameters to say collapsed etc?
- nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
+ nsRefPtr<nsTypedSelection> mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
// Table selection support.
// Interfaces that let us get info based on cellmap locations
nsITableLayout* GetTableLayout(nsIContent *aTableContent) const;
nsITableCellLayout* GetCellLayout(nsIContent *aCellContent) const;
nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -191,17 +191,18 @@ class nsTypedSelection : public nsISelec
public nsISelectionPrivate,
public nsSupportsWeakReference
{
public:
nsTypedSelection();
nsTypedSelection(nsFrameSelection *aList);
virtual ~nsTypedSelection();
- NS_DECL_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTypedSelection, nsISelection)
NS_DECL_NSISELECTION
NS_DECL_NSISELECTION2
NS_DECL_NSISELECTIONPRIVATE
// utility methods for scrolling the selection into view
nsresult GetPresContext(nsPresContext **aPresContext);
nsresult GetPresShell(nsIPresShell **aPresShell);
nsresult GetRootScrollableView(nsIScrollableView **aScrollableView);
@@ -352,17 +353,17 @@ private:
PRBool aUseBeginning);
PRInt32 FindRangeGivenPoint(nsIDOMNode* aBeginNode, PRInt32 aBeginOffset,
nsIDOMNode* aEndNode, PRInt32 aEndOffset,
PRInt32 aStartSearchingHere);
nsTArray<RangeData> mRanges;
nsTArray<PRInt32> mRangeEndings; // references info mRanges
nsCOMPtr<nsIDOMRange> mAnchorFocusRange;
- nsFrameSelection *mFrameSelection;
+ nsRefPtr<nsFrameSelection> mFrameSelection;
nsWeakPtr mPresShellWeak;
nsRefPtr<nsAutoScrollTimer> mAutoScrollTimer;
nsCOMArray<nsISelectionListener> mSelectionListeners;
nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
CachedOffsetForFrame *mCachedOffsetForFrame;
nsDirection mDirection;
SelectionType mType;
};
@@ -805,22 +806,19 @@ nsSelectionIterator::IsDone()
////////////BEGIN nsFrameSelection methods
nsFrameSelection::nsFrameSelection()
: mScrollableViewProvider(nsnull),
mDelayedMouseEvent(PR_FALSE, 0, nsnull, nsMouseEvent::eReal)
{
PRInt32 i;
for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
- mDomSelections[i] = nsnull;
- }
- for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
mDomSelections[i] = new nsTypedSelection(this);
if (!mDomSelections[i])
- return;
+ break;
mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
}
mBatching = 0;
mChangesDuringBatching = PR_FALSE;
mNotifyFrames = PR_TRUE;
mLimiter = nsnull; //no default limiter.
mAncestorLimiter = nsnull;
@@ -850,28 +848,53 @@ nsFrameSelection::nsFrameSelection()
mDisplaySelection = nsISelectionController::SELECTION_OFF;
mDelayedMouseEventValid = PR_FALSE;
mSelectionChangeReason = nsISelectionListener::NO_REASON;
}
-nsFrameSelection::~nsFrameSelection()
-{
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsFrameSelection)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsFrameSelection)
+ PRInt32 i;
+ for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
+ tmp->mDomSelections[i] = nsnull;
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCellParent)
+ tmp->mSelectingTableCellMode = 0;
+ tmp->mDragSelectingCells = PR_FALSE;
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEndSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mAppendStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mUnselectCellOnMouseUp)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mMaintainRange)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsFrameSelection)
PRInt32 i;
- for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
- if (mDomSelections[i]) {
- delete mDomSelections[i];
- }
- }
-}
-
-
-NS_IMPL_ISUPPORTS1(nsFrameSelection, nsFrameSelection)
+ for (i = 0; i < nsISelectionController::NUM_SELECTIONTYPES; ++i) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDomSelections[i],
+ nsISelection)
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCellParent)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEndSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAppendStartSelectedCell)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mUnselectCellOnMouseUp)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mMaintainRange)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameSelection)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameSelection)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameSelection)
+ NS_INTERFACE_MAP_ENTRY(nsFrameSelection)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
nsresult
nsFrameSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
{
if (!mShell)
{
NS_ASSERTION(0,"fetch desired X failed\n");
@@ -1224,16 +1247,19 @@ nsFrameSelection::MoveCaret(PRUint32
nsCOMPtr<nsIDOMNode> weakNodeUsed;
PRInt32 offsetused = 0;
PRBool isCollapsed;
nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
nsresult result = mDomSelections[index]->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(result))
return result;
if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP || aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
{
result = FetchDesiredX(desiredX);
if (NS_FAILED(result))
return result;
@@ -1620,16 +1646,18 @@ nsFrameSelection::GetFrameFromLevel(nsIF
return NS_OK;
}
nsresult
nsFrameSelection::MaintainSelection(nsSelectionAmount aAmount)
{
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
mMaintainedAmount = aAmount;
mMaintainRange = nsnull;
nsCOMPtr<nsIDOMNode> startNode;
nsCOMPtr<nsIDOMNode> endNode;
PRInt32 startOffset;
PRInt32 endOffset;
@@ -1733,16 +1761,18 @@ nsFrameSelection::AdjustForMaintainedSel
if (!mMaintainRange)
return PR_FALSE;
nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aContent);
if (!domNode)
return PR_FALSE;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return PR_FALSE;
nsCOMPtr<nsIDOMNode> rangeStartNode, rangeEndNode;
PRInt32 rangeStartOffset, rangeEndOffset;
mMaintainRange->GetStartContainer(getter_AddRefs(rangeStartNode));
mMaintainRange->GetEndContainer(getter_AddRefs(rangeEndNode));
mMaintainRange->GetStartOffset(&rangeStartOffset);
mMaintainRange->GetEndOffset(&rangeEndOffset);
@@ -1881,24 +1911,30 @@ nsFrameSelection::HandleDrag(nsIFrame *a
nsresult
nsFrameSelection::StartAutoScrollTimer(nsIView *aView,
nsPoint aPoint,
PRUint32 aDelay)
{
NS_ENSURE_STATE(mShell);
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
return mDomSelections[index]->StartAutoScrollTimer(mShell->GetPresContext(),
aView, aPoint, aDelay);
}
void
nsFrameSelection::StopAutoScrollTimer()
{
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return;
+
mDomSelections[index]->StopAutoScrollTimer();
}
/**
hard to go from nodes to frames, easy the other way!
*/
nsresult
nsFrameSelection::TakeFocus(nsIContent *aNewFocus,
@@ -1926,16 +1962,19 @@ nsFrameSelection::TakeFocus(nsIContent *
//HACKHACKHACK
if (!aNewFocus->GetParent())
return NS_ERROR_FAILURE;
//END HACKHACKHACK /checking for root frames/content
mHint = aHint;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aNewFocus);
//traverse through document and unselect crap here
if (!aContinueSelection) {//single click? setting cursor down
PRUint32 batching = mBatching;//hack to use the collapse code.
PRBool changes = mChangesDuringBatching;
mBatching = 1;
if (aMultipleSelection) {
@@ -2403,17 +2442,17 @@ nsFrameSelection::EndBatchChanges()
}
}
nsresult
nsFrameSelection::NotifySelectionListeners(SelectionType aType)
{
PRInt8 index = GetIndexFromSelectionType(aType);
- if (index >=0)
+ if (index >=0 && mDomSelections[index])
{
return mDomSelections[index]->NotifySelectionListeners();
}
return NS_ERROR_FAILURE;
}
// Start of Table Selection methods
@@ -2449,16 +2488,19 @@ nsFrameSelection::GetTableLayout(nsICont
nsITableLayout *tableLayoutObject = do_QueryFrame(tableFrame);
return tableLayoutObject;
}
nsresult
nsFrameSelection::ClearNormalSelection()
{
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
return mDomSelections[index]->RemoveAllRanges();
}
// Table selection support.
// TODO: Separate table methods into a separate nsITableSelection interface
nsresult
nsFrameSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent)
{
@@ -2482,16 +2524,19 @@ nsFrameSelection::HandleTableSelection(n
nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
if (!childNode)
return NS_ERROR_FAILURE;
// When doing table selection, always set the direction to next so
// we can be sure that anchorNode's offset always points to the
// selected cell
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
mDomSelections[index]->SetDirection(eDirNext);
// Stack-class to wrap all table selection changes in
// BeginBatchChanges() / EndBatchChanges()
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
if (mMouseDownState && mDragSelectingCells)
@@ -2811,16 +2856,18 @@ nsFrameSelection::SelectBlockOfCells(nsI
PRInt32 curRowIndex, curColIndex;
if (mDragSelectingCells)
{
// Drag selecting: remove selected cells outside of new block limits
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIDOMNode> cellNode;
nsCOMPtr<nsIDOMRange> range;
result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
if (NS_FAILED(result)) return result;
PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
@@ -3061,16 +3108,19 @@ nsFrameSelection::GetFirstSelectedCellAn
*aCell = nsnull;
// aRange is optional
if (aRange)
*aRange = nsnull;
nsCOMPtr<nsIDOMRange> firstRange;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
nsresult result = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(firstRange));
if (NS_FAILED(result)) return result;
if (!firstRange) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> cellNode;
result = GetFirstCellNodeInRange(firstRange, getter_AddRefs(cellNode));
if (NS_FAILED(result)) return result;
if (!cellNode) return NS_OK;
@@ -3097,16 +3147,19 @@ nsFrameSelection::GetNextSelectedCellAnd
*aCell = nsnull;
// aRange is optional
if (aRange)
*aRange = nsnull;
PRInt32 rangeCount;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
nsresult result = mDomSelections[index]->GetRangeCount(&rangeCount);
if (NS_FAILED(result)) return result;
// Don't even try if index exceeds range count
if (mSelectedCellIndex >= rangeCount)
{
// Should we reset index?
// Maybe better to force recalling GetFirstSelectedCell()
@@ -3403,28 +3456,34 @@ nsFrameSelection::CreateAndAddRange(nsID
// Set range around child at given offset
nsresult result = range->SetStart(aParentNode, aOffset);
if (NS_FAILED(result)) return result;
result = range->SetEnd(aParentNode, aOffset+1);
if (NS_FAILED(result)) return result;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
return mDomSelections[index]->AddRange(range);
}
// End of Table Selection
void
nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
{
if (mAncestorLimiter != aLimiter) {
mAncestorLimiter = aLimiter;
PRInt8 index =
GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return;
+
if (!IsValidSelectionPoint(this, mDomSelections[index]->FetchFocusNode())) {
ClearNormalSelection();
if (mAncestorLimiter) {
PostReason(nsISelectionListener::NO_REASON);
TakeFocus(mAncestorLimiter, 0, 0, HINTLEFT, PR_FALSE, PR_FALSE);
}
}
}
@@ -3442,16 +3501,19 @@ nsFrameSelection::DeleteFromDocument()
{
nsresult res;
// If we're already collapsed, then set ourselves to include the
// last item BEFORE the current range, rather than the range itself,
// before we do the delete.
PRBool isCollapsed;
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+ if (!mDomSelections[index])
+ return NS_ERROR_NULL_POINTER;
+
mDomSelections[index]->GetIsCollapsed( &isCollapsed);
if (isCollapsed)
{
// If the offset is positive, then it's easy:
if (mDomSelections[index]->FetchFocusOffset() > 0)
{
mDomSelections[index]->Extend(mDomSelections[index]->FetchFocusNode(), mDomSelections[index]->FetchFocusOffset() - 1);
}
@@ -3526,18 +3588,17 @@ nsFrameSelection::GetDelayedCaretData()
#pragma mark -
#endif
// nsTypedSelection implementation
// note: this can return a nil anchor node
nsTypedSelection::nsTypedSelection()
- : mFrameSelection(nsnull)
- , mCachedOffsetForFrame(nsnull)
+ : mCachedOffsetForFrame(nsnull)
, mDirection(eDirNext)
, mType(nsISelectionController::SELECTION_NORMAL)
{
}
nsTypedSelection::nsTypedSelection(nsFrameSelection *aList)
: mFrameSelection(aList)
, mCachedOffsetForFrame(nsnull)
@@ -3556,62 +3617,47 @@ nsTypedSelection::~nsTypedSelection()
}
mScrollEvent.Revoke();
if (mCachedOffsetForFrame) {
delete mCachedOffsetForFrame;
mCachedOffsetForFrame = nsnull;
}
-
- mFrameSelection = nsnull;
-}
-
-
-// QueryInterface implementation for nsRange
-NS_INTERFACE_MAP_BEGIN(nsTypedSelection)
+}
+
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsTypedSelection)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsTypedSelection)
+ tmp->RemoveAllRanges();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFrameSelection)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsTypedSelection)
+ {
+ PRUint32 i, count = tmp->mRanges.Length();
+ for (i = 0; i < count; ++i) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRanges[i].mRange)
+ }
+ }
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mAnchorFocusRange)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFrameSelection)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+// QueryInterface implementation for nsTypedSelection
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsTypedSelection)
NS_INTERFACE_MAP_ENTRY(nsISelection)
NS_INTERFACE_MAP_ENTRY(nsISelection2)
NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Selection)
NS_INTERFACE_MAP_END
-
-NS_IMETHODIMP_(nsrefcnt)
-nsTypedSelection::AddRef()
-{
- if (mFrameSelection) {
- return mFrameSelection->AddRef();
- }
- NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt");
- NS_ASSERT_OWNINGTHREAD(nsTypedSelection);
- ++mRefCnt;
- NS_LOG_ADDREF(this, mRefCnt, "nsTypedSelection", sizeof(*this));
- return mRefCnt;
-}
-
-NS_IMETHODIMP_(nsrefcnt)
-nsTypedSelection::Release()
-{
- if (mFrameSelection) {
- return mFrameSelection->Release();
- }
- NS_PRECONDITION(0 != mRefCnt, "dup release");
- NS_ASSERT_OWNINGTHREAD(nsTypedSelection);
- --mRefCnt;
- NS_LOG_RELEASE(this, mRefCnt, "nsTypedSelection");
- if (mRefCnt == 0) {
- mRefCnt = 1; /* stabilize */
- NS_DELETEXPCOM(this);
- return 0;
- }
- return mRefCnt;
-}
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTypedSelection)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTypedSelection)
NS_IMETHODIMP
nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
{
mPresShellWeak = do_GetWeakReference(aPresShell);
return NS_OK;
}
@@ -4095,17 +4141,18 @@ nsTypedSelection::Clear(nsPresContext* a
selectFrames(aPresContext, mRanges[i].mRange, 0);
mRanges.Clear();
mRangeEndings.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->GetDisplaySelection() ==
+ if (mFrameSelection &&
+ mFrameSelection->GetDisplaySelection() ==
nsISelectionController::SELECTION_ATTENTION) {
mFrameSelection->SetDisplaySelection(nsISelectionController::SELECTION_ON);
}
return NS_OK;
}
// nsTypedSelection::MoveIndexToFirstMatch
@@ -4455,17 +4502,17 @@ nsTypedSelection::FindRangeGivenPoint(
}
//utility method to get the primary frame of node or use the offset to get frame of child node
#if 0
NS_IMETHODIMP
nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aReturnFrame)
{
- if (!aNode || !aReturnFrame)
+ if (!aNode || !aReturnFrame || !mFrameSelection)
return NS_ERROR_NULL_POINTER;
if (aOffset < 0)
return NS_ERROR_FAILURE;
*aReturnFrame = 0;
nsresult result = NS_OK;
@@ -4925,17 +4972,18 @@ nsTypedSelection::GetFrameSelection(nsFr
*aFrameSelection = mFrameSelection;
NS_IF_ADDREF(*aFrameSelection);
return NS_OK;
}
NS_IMETHODIMP
nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
{
- mFrameSelection->SetAncestorLimiter(aContent);
+ if (mFrameSelection)
+ mFrameSelection->SetAncestorLimiter(aContent);
return NS_OK;
}
nsresult
nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
nsIView *aView,
nsPoint& aPoint,
PRUint32 aDelay)
@@ -5471,18 +5519,17 @@ nsTypedSelection::Collapse(nsIDOMNode* a
return NS_ERROR_FAILURE;
nsresult result;
// Delete all of the current ranges
nsCOMPtr<nsPresContext> presContext;
GetPresContext(getter_AddRefs(presContext));
Clear(presContext);
// Turn off signal for table selection
- if (mFrameSelection)
- mFrameSelection->ClearTableCellSelection();
+ mFrameSelection->ClearTableCellSelection();
nsCOMPtr<nsIDOMRange> range;
NS_NewRange(getter_AddRefs(range));
if (! range){
NS_ASSERTION(PR_FALSE,"Couldn't make a range - nsFrameSelection::Collapse");
return NS_ERROR_UNEXPECTED;
}
result = range->SetEnd(aParentNode, aOffset);
@@ -5510,18 +5557,16 @@ nsTypedSelection::Collapse(nsIDOMNode* a
#endif
result = AddItem(range);
setAnchorFocusRange(0);
selectFrames(presContext, range,PR_TRUE);
if (NS_FAILED(result))
return result;
- if (!mFrameSelection)
- return NS_OK;//nothing to do
return mFrameSelection->NotifySelectionListeners(GetType());
}
/*
* Sets the whole selection to be one point
* at the start of the current selection
*/
NS_IMETHODIMP
@@ -5934,18 +5979,16 @@ nsTypedSelection::Extend(nsIDOMNode* aPa
const char *tagString;
content->Tag()->GetUTF8String(&tagString);
printf ("Sel. Extend to %p %s %d\n", content.get(), tagString, aOffset);
}
else {
printf ("Sel. Extend set to null parent.\n");
}
#endif
- if (!mFrameSelection)
- return NS_OK;//nothing to do
return mFrameSelection->NotifySelectionListeners(GetType());
}
static nsresult
GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
{
NS_ASSERTION((aChild && aParent), "bad args");
nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);