Bug 336381, Crash [@ nsRange::InsertNode] involving ranges in detached nodes, r=sicking,glazman,sr=bz

This commit is contained in:
Olli.Pettay%helsinki.fi 2006-05-15 19:35:12 +00:00
Родитель 90bc7b47f2
Коммит 1c7c15de85
7 изменённых файлов: 305 добавлений и 218 удалений

Просмотреть файл

@ -139,7 +139,9 @@ public:
/** XUL elements */
eXUL = 1 << 9,
/** svg elements */
eSVG = 1 << 10
eSVG = 1 << 10,
/** document fragments */
eDOCUMENT_FRAGMENT = 1 << 11
};
/**
@ -341,11 +343,6 @@ public:
return mNodeInfo->NodeInfoManager()->DocumentPrincipal();
}
/**
* IsNodeOfType()? Do we need a non-QI way to tell apart documents and
* content?
*/
/**
* Called before the capture phase of the event flow.
* This is used to create the event target chain and implementations

Просмотреть файл

@ -153,6 +153,8 @@ public:
return nsnull;
}
virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
protected:
nsresult Clone(nsINodeInfo *aNodeInfo, PRBool aDeep,
nsIContent **aResult) const;
@ -190,6 +192,11 @@ nsDocumentFragment::~nsDocumentFragment()
{
}
PRBool
nsDocumentFragment::IsNodeOfType(PRUint32 aFlags) const
{
return !(aFlags & ~(eCONTENT | eDOCUMENT_FRAGMENT));
}
// QueryInterface implementation for nsDocumentFragment
NS_INTERFACE_MAP_BEGIN(nsDocumentFragment)

Просмотреть файл

@ -944,10 +944,74 @@ nsresult nsRange::GetCommonAncestorContainer(nsIDOMNode** aCommonParent)
return nsContentUtils::GetCommonAncestor(mStartParent, mEndParent, aCommonParent);
}
nsresult nsRange::IsValidBoundary(nsIDOMNode* aNode)
{
if (!aNode) {
// DOM 2 Range specification doesn't define the error code for this case,
// but this is the best one we have.
return NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR;
}
PRUint16 nodeType = 0;
aNode->GetNodeType(&nodeType);
switch (nodeType) {
case nsIDOMNode::DOCUMENT_TYPE_NODE:
case nsIDOMNode::ENTITY_NODE:
case nsIDOMNode::NOTATION_NODE:
return NS_ERROR_DOM_RANGE_INVALID_NODE_TYPE_ERR;
case nsIDOMNode::DOCUMENT_NODE:
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
case nsIDOMNode::ATTRIBUTE_NODE:
return NS_OK;
default:
break;
}
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
if (!content) {
// DOM 2 Range specification doesn't define the error code for this case,
// but this is the best one we have.
return NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR;
}
// Elements etc. must be in document or in document fragment,
// text nodes in document, in document fragment or in attribute.
if (content->IsInDoc()) {
return NS_OK;
}
nsINode* parent = content->GetNodeParent();
if (parent) {
if (parent->IsNodeOfType(nsINode::eATTRIBUTE)) {
return NS_OK;
}
do {
if (parent->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT)) {
return NS_OK;
}
parent = parent->GetNodeParent();
} while(parent);
}
#ifdef DEBUG_smaug
nsAutoString name;
content->Tag()->ToString(name);
printf("nsRange::IsValidBoundary: node is not a valid boundary point [%s]\n",
NS_ConvertUTF16toUTF8(name).get());
#endif
// DOM 2 Range specification doesn't define the error code for this case,
// but this is the best one we have.
return NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR;
}
nsresult nsRange::SetStart(nsIDOMNode* aParent, PRInt32 aOffset)
{
VALIDATE_ACCESS(aParent);
nsresult rv = IsValidBoundary(aParent);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 len = GetNodeLength(aParent);
if ( (aOffset < 0) || (len < 0) || (aOffset > len) )
return NS_ERROR_DOM_INDEX_SIZE_ERR;
@ -996,7 +1060,9 @@ nsresult nsRange::SetStartAfter(nsIDOMNode* aSibling)
nsresult nsRange::SetEnd(nsIDOMNode* aParent, PRInt32 aOffset)
{
VALIDATE_ACCESS(aParent);
nsresult rv = IsValidBoundary(aParent);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 len = GetNodeLength(aParent);
if ( (aOffset < 0) || (len < 0) || (aOffset > len) )
return NS_ERROR_DOM_INDEX_SIZE_ERR;

Просмотреть файл

@ -215,6 +215,8 @@ protected:
nsresult ContentOwnsUs(nsIDOMNode* domNode);
nsresult GetIsPositioned(PRBool* aIsPositioned);
nsresult IsValidBoundary(nsIDOMNode* aNode);
};
// Make a new nsIDOMRange object

Просмотреть файл

@ -1574,20 +1574,22 @@ nsEditor::ReplaceContainer(nsIDOMNode *inNode,
// (Note: A nsAutoSelectionReset object must be created
// before calling this to initialize mRangeUpdater)
nsAutoReplaceContainerSelNotify selStateNotify(mRangeUpdater, inNode, *outNode);
nsCOMPtr<nsIDOMNode> child;
PRBool bHasMoreChildren;
inNode->HasChildNodes(&bHasMoreChildren);
while (bHasMoreChildren)
{
inNode->GetFirstChild(getter_AddRefs(child));
res = DeleteNode(child);
if (NS_FAILED(res)) return res;
res = InsertNode(child, *outNode, -1);
if (NS_FAILED(res)) return res;
nsAutoTxnsConserveSelection conserveSelection(this);
nsCOMPtr<nsIDOMNode> child;
PRBool bHasMoreChildren;
inNode->HasChildNodes(&bHasMoreChildren);
}
while (bHasMoreChildren)
{
inNode->GetFirstChild(getter_AddRefs(child));
res = DeleteNode(child);
if (NS_FAILED(res)) return res;
res = InsertNode(child, *outNode, -1);
if (NS_FAILED(res)) return res;
inNode->HasChildNodes(&bHasMoreChildren);
}
}
// insert new container into tree
res = InsertNode( *outNode, parent, offset);
if (NS_FAILED(res)) return res;
@ -1681,9 +1683,13 @@ nsEditor::InsertContainerAbove( nsIDOMNode *inNode,
// put inNode in new parent, outNode
res = DeleteNode(inNode);
if (NS_FAILED(res)) return res;
res = InsertNode(inNode, *outNode, 0);
if (NS_FAILED(res)) return res;
{
nsAutoTxnsConserveSelection conserveSelection(this);
res = InsertNode(inNode, *outNode, 0);
if (NS_FAILED(res)) return res;
}
// put new parent in doc
return InsertNode(*outNode, parent, offset);
}

Просмотреть файл

@ -988,6 +988,10 @@ nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aCo
mNotifyOnInput = PR_TRUE;
mScrollableView = nsnull;
mDidPreDestroy = PR_FALSE;
#ifdef DEBUG
mCreateFrameForCalled = PR_FALSE;
#endif
}
nsTextControlFrame::~nsTextControlFrame()
@ -1364,11 +1368,201 @@ void nsTextControlFrame::PostCreateFrames() {
NS_IMETHODIMP
nsTextControlFrame::CreateFrameFor(nsPresContext* aPresContext,
nsIContent * aContent,
nsIFrame** aFrame)
nsIContent* aContent,
nsIFrame** aFrame)
{
#ifdef DEBUG
NS_ASSERTION(!mCreateFrameForCalled, "CreateFrameFor called more than once!");
mCreateFrameForCalled = PR_TRUE;
#endif
// Note, we must set aFrame to nsnull.
*aFrame = nsnull;
return NS_ERROR_FAILURE;
nsIPresShell *shell = aPresContext->GetPresShell();
if (!shell)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
if (!domdoc)
return NS_ERROR_FAILURE;
// Don't create any frames here, but just setup the editor.
// This way DOM Ranges (which editor uses) work properly since the anonymous
// content is bound to tree after CreateAnonymousContent but before this
// method.
nsresult rv = NS_OK;
mEditor = do_CreateInstance(kTextEditorCID, &rv);
if (NS_FAILED(rv))
return rv;
if (!mEditor)
return NS_ERROR_OUT_OF_MEMORY;
// Create selection
mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
if (NS_FAILED(rv))
return rv;
// Create a SelectionController
mSelCon = NS_STATIC_CAST(nsISelectionController*,
new nsTextInputSelectionImpl(mFrameSel, shell, aContent));
if (!mSelCon)
return NS_ERROR_OUT_OF_MEMORY;
mTextListener = new nsTextInputListener();
if (!mTextListener)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mTextListener);
mTextListener->SetFrame(this);
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
// Setup the editor flags
PRUint32 editorFlags = 0;
if (IsPlainTextControl())
editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
if (IsSingleLineTextControl())
editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
if (IsPasswordTextControl())
editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
// All gfxtextcontrolframe2's are widgets
editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
// Use async reflow and painting for text widgets to improve
// performance.
// XXX: Using editor async updates exposes bugs 158782, 151882,
// and 165130, so we're disabling it for now, until they
// can be addressed.
// editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
// Now initialize the editor.
//
// NOTE: Conversion of '\n' to <BR> happens inside the
// editor's Init() call.
rv = mEditor->Init(domdoc, shell, aContent, mSelCon, editorFlags);
if (NS_FAILED(rv))
return rv;
// Initialize the controller for the editor
if (!SuppressEventHandlers(aPresContext)) {
nsCOMPtr<nsIControllers> controllers;
nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
do_QueryInterface(mContent);
if (inputElement) {
rv = inputElement->GetControllers(getter_AddRefs(controllers));
} else {
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
do_QueryInterface(mContent);
if (!textAreaElement)
return NS_ERROR_FAILURE;
rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
}
if (NS_FAILED(rv))
return rv;
if (controllers) {
PRUint32 numControllers;
PRBool found = PR_FALSE;
rv = controllers->GetControllerCount(&numControllers);
for (PRUint32 i = 0; i < numControllers; i ++) {
nsCOMPtr<nsIController> controller;
rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
if (NS_SUCCEEDED(rv) && controller) {
nsCOMPtr<nsIControllerContext> editController =
do_QueryInterface(controller);
if (editController) {
editController->SetCommandContext(mEditor);
found = PR_TRUE;
}
}
}
if (!found)
rv = NS_ERROR_FAILURE;
}
}
// Initialize the plaintext editor
nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(mEditor));
if (textEditor) {
// Set up wrapping
if (IsTextArea()) {
// wrap=off means -1 for wrap width no matter what cols is
nsFormControlHelper::nsHTMLTextWrap wrapProp;
nsFormControlHelper::GetWrapPropertyEnum(mContent, wrapProp);
if (wrapProp == nsFormControlHelper::eHTMLTextWrap_Off) {
// do not wrap when wrap=off
textEditor->SetWrapWidth(-1);
} else {
// Set wrapping normally otherwise
textEditor->SetWrapWidth(GetCols());
}
} else {
// Never wrap non-textareas
textEditor->SetWrapWidth(-1);
}
// Set max text field length
PRInt32 maxLength;
if (GetMaxLength(&maxLength)) {
textEditor->SetMaxTextLength(maxLength);
}
}
// Get the caret and make it a selection listener.
nsCOMPtr<nsISelection> domSelection;
if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
getter_AddRefs(domSelection))) &&
domSelection) {
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
nsCOMPtr<nsICaret> caret;
nsCOMPtr<nsISelectionListener> listener;
if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret) {
listener = do_QueryInterface(caret);
if (listener) {
selPriv->AddSelectionListener(listener);
}
}
selPriv->AddSelectionListener(NS_STATIC_CAST(nsISelectionListener*,
mTextListener));
}
if (mContent) {
rv = mEditor->GetFlags(&editorFlags);
if (NS_FAILED(rv))
return rv;
// Check if the readonly attribute is set.
if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::readonly))
editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
// Check if the disabled attribute is set.
if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled))
editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
// Disable the selection if necessary.
if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
mEditor->SetFlags(editorFlags);
}
return NS_OK;
}
// nsTextControlFrame::SetEnableRealTimeSpell
@ -1565,18 +1759,11 @@ nsTextControlFrame::CreateAnonymousContent(nsPresContext* aPresContext,
if (!doc)
return NS_ERROR_FAILURE;
nsresult rv;
nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(doc, &rv);
if (NS_FAILED(rv))
return rv;
if (!domdoc)
return NS_ERROR_FAILURE;
// Now create a DIV and add it to the anonymous content child list.
nsCOMPtr<nsINodeInfo> nodeInfo;
rv = doc->NodeInfoManager()->GetNodeInfo(nsHTMLAtoms::div, nsnull,
kNameSpaceID_XHTML,
getter_AddRefs(nodeInfo));
nsresult rv = doc->NodeInfoManager()->GetNodeInfo(nsHTMLAtoms::div, nsnull,
kNameSpaceID_XHTML,
getter_AddRefs(nodeInfo));
if (NS_FAILED(rv))
return rv;
@ -1620,189 +1807,7 @@ nsTextControlFrame::CreateAnonymousContent(nsPresContext* aPresContext,
return rv;
// rv = divContent->SetAttr(kNameSpaceID_None,nsXULAtoms::debug, NS_LITERAL_STRING("true"), PR_FALSE);
rv = aChildList.AppendElement(divContent);
if (NS_FAILED(rv))
return rv;
// Create an editor
mEditor = do_CreateInstance(kTextEditorCID, &rv);
if (NS_FAILED(rv))
return rv;
if (!mEditor)
return NS_ERROR_OUT_OF_MEMORY;
// Create selection
mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
if (NS_FAILED(rv))
return rv;
// Create a SelectionController
mSelCon = NS_STATIC_CAST(nsISelectionController*,
new nsTextInputSelectionImpl(mFrameSel, shell, divContent));
if (!mSelCon)
return NS_ERROR_OUT_OF_MEMORY;
mTextListener = new nsTextInputListener();
if (!mTextListener)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(mTextListener);
mTextListener->SetFrame(this);
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
// Setup the editor flags
PRUint32 editorFlags = 0;
if (IsPlainTextControl())
editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
if (IsSingleLineTextControl())
editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
if (IsPasswordTextControl())
editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
// All gfxtextcontrolframe2's are widgets
editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
// Use async reflow and painting for text widgets to improve
// performance.
// XXX: Using editor async updates exposes bugs 158782, 151882,
// and 165130, so we're disabling it for now, until they
// can be addressed.
// editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
// Now initialize the editor.
//
// NOTE: Conversion of '\n' to <BR> happens inside the
// editor's Init() call.
rv = mEditor->Init(domdoc, shell, divContent, mSelCon, editorFlags);
if (NS_FAILED(rv))
return rv;
// Initialize the controller for the editor
if (!SuppressEventHandlers(aPresContext))
{
nsCOMPtr<nsIControllers> controllers;
nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement = do_QueryInterface(mContent);
if (inputElement)
rv = inputElement->GetControllers(getter_AddRefs(controllers));
else
{
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement = do_QueryInterface(mContent);
if (!textAreaElement)
return NS_ERROR_FAILURE;
rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
}
if (NS_FAILED(rv))
return rv;
if (controllers)
{
PRUint32 numControllers;
PRBool found = PR_FALSE;
rv = controllers->GetControllerCount(&numControllers);
for (PRUint32 i = 0; i < numControllers; i ++)
{
nsCOMPtr<nsIController> controller;
rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
if (NS_SUCCEEDED(rv) && controller)
{
nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
if (editController)
{
editController->SetCommandContext(mEditor);
found = PR_TRUE;
}
}
}
if (!found)
rv = NS_ERROR_FAILURE;
}
}
// Initialize the plaintext editor
nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(mEditor));
if (textEditor) {
// Set up wrapping
if (IsTextArea()) {
// wrap=off means -1 for wrap width no matter what cols is
nsFormControlHelper::nsHTMLTextWrap wrapProp;
nsFormControlHelper::GetWrapPropertyEnum(mContent, wrapProp);
if (wrapProp == nsFormControlHelper::eHTMLTextWrap_Off) {
// do not wrap when wrap=off
textEditor->SetWrapWidth(-1);
} else {
// Set wrapping normally otherwise
textEditor->SetWrapWidth(GetCols());
}
} else {
// Never wrap non-textareas
textEditor->SetWrapWidth(-1);
}
// Set max text field length
PRInt32 maxLength;
if (GetMaxLength(&maxLength)) {
textEditor->SetMaxTextLength(maxLength);
}
}
// Get the caret and make it a selection listener.
nsCOMPtr<nsISelection> domSelection;
if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSelection))) && domSelection)
{
nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
nsCOMPtr<nsICaret> caret;
nsCOMPtr<nsISelectionListener> listener;
if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret)
{
listener = do_QueryInterface(caret);
if (listener)
{
selPriv->AddSelectionListener(listener);
}
}
selPriv->AddSelectionListener(NS_STATIC_CAST(nsISelectionListener *, mTextListener));
}
if (mContent)
{
rv = mEditor->GetFlags(&editorFlags);
if (NS_FAILED(rv))
return rv;
// Check if the readonly attribute is set.
if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::readonly))
editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
// Check if the disabled attribute is set.
if (mContent->HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled))
editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
// Disable the selection if necessary.
if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
mEditor->SetFlags(editorFlags);
}
return NS_OK;
return aChildList.AppendElement(divContent);
}
NS_IMETHODIMP

Просмотреть файл

@ -288,6 +288,10 @@ private:
// XXX This seems unsafe; what's keeping it around?
nsIScrollableView *mScrollableView;
nsString mFocusedValue;
#ifdef DEBUG
PRBool mCreateFrameForCalled;
#endif
};
#endif