diff options
Diffstat (limited to 'editor/libeditor')
31 files changed, 403 insertions, 70 deletions
diff --git a/editor/libeditor/CompositionTransaction.cpp b/editor/libeditor/CompositionTransaction.cpp index 25938fa60..bde53d2e5 100644 --- a/editor/libeditor/CompositionTransaction.cpp +++ b/editor/libeditor/CompositionTransaction.cpp @@ -33,7 +33,7 @@ CompositionTransaction::CompositionTransaction( , mReplaceLength(aReplaceLength) , mRanges(aTextRangeArray) , mStringToInsert(aStringToInsert) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) , mRangeUpdater(aRangeUpdater) , mFixed(false) { @@ -45,6 +45,7 @@ CompositionTransaction::~CompositionTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(CompositionTransaction, EditTransactionBase, + mEditorBase, mTextNode) // mRangeList can't lead to cycles @@ -60,9 +61,13 @@ NS_IMPL_RELEASE_INHERITED(CompositionTransaction, EditTransactionBase) NS_IMETHODIMP CompositionTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Fail before making any changes if there's no selection controller nsCOMPtr<nsISelectionController> selCon; - mEditorBase.GetSelectionController(getter_AddRefs(selCon)); + mEditorBase->GetSelectionController(getter_AddRefs(selCon)); NS_ENSURE_TRUE(selCon, NS_ERROR_NOT_INITIALIZED); // Advance caret: This requires the presentation shell to get the selection. @@ -108,9 +113,13 @@ CompositionTransaction::DoTransaction() NS_IMETHODIMP CompositionTransaction::UndoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Get the selection first so we'll fail before making any changes if we // can't get it - RefPtr<Selection> selection = mEditorBase.GetSelection(); + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NOT_INITIALIZED); nsresult rv = mTextNode->DeleteData(mOffset, mStringToInsert.Length()); @@ -171,7 +180,10 @@ CompositionTransaction::GetTxnDescription(nsAString& aString) nsresult CompositionTransaction::SetSelectionForRanges() { - return SetIMESelection(mEditorBase, mTextNode, mOffset, + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + return SetIMESelection(*mEditorBase, mTextNode, mOffset, mStringToInsert.Length(), mRanges); } diff --git a/editor/libeditor/CompositionTransaction.h b/editor/libeditor/CompositionTransaction.h index acb3d8beb..c2134bedd 100644 --- a/editor/libeditor/CompositionTransaction.h +++ b/editor/libeditor/CompositionTransaction.h @@ -90,7 +90,7 @@ private: nsString mStringToInsert; // The editor, which is used to get the selection controller. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/CreateElementTransaction.cpp b/editor/libeditor/CreateElementTransaction.cpp index 5e4bd961c..5e2b9e1ad 100644 --- a/editor/libeditor/CreateElementTransaction.cpp +++ b/editor/libeditor/CreateElementTransaction.cpp @@ -50,6 +50,7 @@ CreateElementTransaction::~CreateElementTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction, EditTransactionBase, + mEditorBase, mParent, mNewNode, mRefNode) @@ -63,7 +64,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP CreateElementTransaction::DoTransaction() { - MOZ_ASSERT(mEditorBase && mTag && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } mNewNode = mEditorBase->CreateHTMLContent(mTag); NS_ENSURE_STATE(mNewNode); @@ -106,7 +109,9 @@ CreateElementTransaction::DoTransaction() NS_IMETHODIMP CreateElementTransaction::UndoTransaction() { - MOZ_ASSERT(mEditorBase && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; mParent->RemoveChild(*mNewNode, rv); @@ -117,7 +122,9 @@ CreateElementTransaction::UndoTransaction() NS_IMETHODIMP CreateElementTransaction::RedoTransaction() { - MOZ_ASSERT(mEditorBase && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } // First, reset mNewNode so it has no attributes or content // XXX We never actually did this, we only cleared mNewNode's contents if it diff --git a/editor/libeditor/CreateElementTransaction.h b/editor/libeditor/CreateElementTransaction.h index 70fecceae..fd5abb8a8 100644 --- a/editor/libeditor/CreateElementTransaction.h +++ b/editor/libeditor/CreateElementTransaction.h @@ -57,7 +57,7 @@ protected: virtual ~CreateElementTransaction(); // The document into which the new node will be inserted. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // The tag (mapping to object type) for the new element. nsCOMPtr<nsIAtom> mTag; diff --git a/editor/libeditor/DeleteNodeTransaction.cpp b/editor/libeditor/DeleteNodeTransaction.cpp index 7f485b066..ae9951e28 100644 --- a/editor/libeditor/DeleteNodeTransaction.cpp +++ b/editor/libeditor/DeleteNodeTransaction.cpp @@ -23,6 +23,7 @@ DeleteNodeTransaction::~DeleteNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteNodeTransaction, EditTransactionBase, + mEditorBase, mNode, mParent, mRefNode) diff --git a/editor/libeditor/DeleteNodeTransaction.h b/editor/libeditor/DeleteNodeTransaction.h index d0bc0dd46..369bbdf56 100644 --- a/editor/libeditor/DeleteNodeTransaction.h +++ b/editor/libeditor/DeleteNodeTransaction.h @@ -55,7 +55,7 @@ protected: nsCOMPtr<nsIContent> mRefNode; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // Range updater object. RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/DeleteRangeTransaction.cpp b/editor/libeditor/DeleteRangeTransaction.cpp index 977de4873..16d2344ba 100644 --- a/editor/libeditor/DeleteRangeTransaction.cpp +++ b/editor/libeditor/DeleteRangeTransaction.cpp @@ -33,6 +33,7 @@ DeleteRangeTransaction::DeleteRangeTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteRangeTransaction, EditAggregateTransaction, + mEditorBase, mRange) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteRangeTransaction) @@ -62,7 +63,9 @@ DeleteRangeTransaction::Init(EditorBase* aEditorBase, NS_IMETHODIMP DeleteRangeTransaction::DoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } // build the child transactions nsCOMPtr<nsINode> startParent = mRange->GetStartParent(); @@ -111,16 +114,18 @@ DeleteRangeTransaction::DoTransaction() NS_IMETHODIMP DeleteRangeTransaction::UndoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); - + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } return EditAggregateTransaction::UndoTransaction(); } NS_IMETHODIMP DeleteRangeTransaction::RedoTransaction() { - MOZ_ASSERT(mRange && mEditorBase); - + if (NS_WARN_IF(!mRange) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } return EditAggregateTransaction::RedoTransaction(); } @@ -136,6 +141,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteBetween(nsINode* aNode, int32_t aStartOffset, int32_t aEndOffset) { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // see what kind of node we have if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { // if the node is a chardata node, then delete chardata content @@ -185,6 +194,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode, int32_t aOffset, nsIEditor::EDirection aAction) { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // see what kind of node we have if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) { // if the node is a chardata node, then delete chardata content @@ -217,6 +230,10 @@ DeleteRangeTransaction::CreateTxnsToDeleteContent(nsINode* aNode, nsresult DeleteRangeTransaction::CreateTxnsToDeleteNodesBetween() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + nsCOMPtr<nsIContentIterator> iter = NS_NewContentSubtreeIterator(); nsresult rv = iter->Init(mRange); diff --git a/editor/libeditor/DeleteRangeTransaction.h b/editor/libeditor/DeleteRangeTransaction.h index 9b60a5ba2..9bb4f520a 100644 --- a/editor/libeditor/DeleteRangeTransaction.h +++ b/editor/libeditor/DeleteRangeTransaction.h @@ -67,7 +67,7 @@ protected: RefPtr<nsRange> mRange; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; // Range updater object. RangeUpdater* mRangeUpdater; diff --git a/editor/libeditor/DeleteTextTransaction.cpp b/editor/libeditor/DeleteTextTransaction.cpp index 6de3181da..624aeaa3c 100644 --- a/editor/libeditor/DeleteTextTransaction.cpp +++ b/editor/libeditor/DeleteTextTransaction.cpp @@ -25,7 +25,7 @@ DeleteTextTransaction::DeleteTextTransaction( uint32_t aOffset, uint32_t aNumCharsToDelete, RangeUpdater* aRangeUpdater) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mCharData(&aCharData) , mOffset(aOffset) , mNumCharsToDelete(aNumCharsToDelete) @@ -36,6 +36,7 @@ DeleteTextTransaction::DeleteTextTransaction( } NS_IMPL_CYCLE_COLLECTION_INHERITED(DeleteTextTransaction, EditTransactionBase, + mEditorBase, mCharData) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DeleteTextTransaction) @@ -45,7 +46,7 @@ nsresult DeleteTextTransaction::Init() { // Do nothing if the node is read-only - if (!mEditorBase.IsModifiableNode(mCharData)) { + if (NS_WARN_IF(!mEditorBase) || !mEditorBase->IsModifiableNode(mCharData)) { return NS_ERROR_FAILURE; } @@ -55,7 +56,9 @@ DeleteTextTransaction::Init() NS_IMETHODIMP DeleteTextTransaction::DoTransaction() { - MOZ_ASSERT(mCharData); + if (NS_WARN_IF(!mCharData) || NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } // Get the text that we're about to delete nsresult rv = mCharData->SubstringData(mOffset, mNumCharsToDelete, @@ -69,8 +72,8 @@ DeleteTextTransaction::DoTransaction() } // Only set selection to deletion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); rv = selection->Collapse(mCharData, mOffset); NS_ASSERTION(NS_SUCCEEDED(rv), @@ -86,8 +89,9 @@ DeleteTextTransaction::DoTransaction() NS_IMETHODIMP DeleteTextTransaction::UndoTransaction() { - MOZ_ASSERT(mCharData); - + if (NS_WARN_IF(!mCharData)) { + return NS_ERROR_NOT_INITIALIZED; + } return mCharData->InsertData(mOffset, mDeletedText); } diff --git a/editor/libeditor/DeleteTextTransaction.h b/editor/libeditor/DeleteTextTransaction.h index 855d14349..0c8dafbc3 100644 --- a/editor/libeditor/DeleteTextTransaction.h +++ b/editor/libeditor/DeleteTextTransaction.h @@ -53,7 +53,7 @@ public: protected: // The provider of basic editing operations. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The CharacterData node to operate upon. RefPtr<nsGenericDOMDataNode> mCharData; diff --git a/editor/libeditor/EditorBase.cpp b/editor/libeditor/EditorBase.cpp index 13505b2d3..0c4a2a41d 100644 --- a/editor/libeditor/EditorBase.cpp +++ b/editor/libeditor/EditorBase.cpp @@ -175,7 +175,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EditorBase) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorObservers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocStateListeners) NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventTarget) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mEventListener) + + if (tmp->mEventListener) { + EditorEventListener* listener = + reinterpret_cast<EditorEventListener*>(tmp->mEventListener.get()); + listener->Disconnect(); + tmp->mEventListener = nullptr; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSavedSel); NS_IMPL_CYCLE_COLLECTION_UNLINK(mRangeUpdater); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -466,6 +473,13 @@ EditorBase::PreDestroy(bool aDestroyingFrames) mSpellcheckCheckboxState = eTriUnset; mRootElement = nullptr; + // Transaction may grab this instance. Therefore, they should be released + // here for stopping the circular reference with this instance. + if (mTxnMgr) { + mTxnMgr->Clear(); + mTxnMgr = nullptr; + } + mDidPreDestroy = true; return NS_OK; } diff --git a/editor/libeditor/HTMLEditorDataTransfer.cpp b/editor/libeditor/HTMLEditorDataTransfer.cpp index b9cd8adb9..c56fbead7 100644 --- a/editor/libeditor/HTMLEditorDataTransfer.cpp +++ b/editor/libeditor/HTMLEditorDataTransfer.cpp @@ -1538,6 +1538,13 @@ HTMLEditor::CanPaste(int32_t aSelectionType, NS_ENSURE_ARG_POINTER(aCanPaste); *aCanPaste = false; + // Always enable the paste command when inside of a HTML or XHTML document. + nsCOMPtr<nsIDocument> doc = GetDocument(); + if (doc && doc->IsHTMLOrXHTML()) { + *aCanPaste = true; + return NS_OK; + } + // can't paste if readonly if (!IsModifiable()) { return NS_OK; @@ -2382,27 +2389,26 @@ HTMLEditor::ReplaceOrphanedStructure( } // If we found substructure, paste it instead of its descendants. - // Only replace with the substructure if all the nodes in the list are - // descendants. - bool shouldReplaceNodes = true; - for (uint32_t i = 0; i < aNodeArray.Length(); i++) { + // Postprocess list to remove any descendants of this node so that we don't + // insert them twice. + uint32_t removedCount = 0; + uint32_t originalLength = aNodeArray.Length(); + for (uint32_t i = 0; i < originalLength; i++) { uint32_t idx = aStartOrEnd == StartOrEnd::start ? - i : (aNodeArray.Length() - i - 1); + (i - removedCount) : (originalLength - i - 1); OwningNonNull<nsINode> endpoint = aNodeArray[idx]; - if (!EditorUtils::IsDescendantOf(endpoint, replaceNode)) { - shouldReplaceNodes = false; - break; + if (endpoint == replaceNode || + EditorUtils::IsDescendantOf(endpoint, replaceNode)) { + aNodeArray.RemoveElementAt(idx); + removedCount++; } } - if (shouldReplaceNodes) { - // Now replace the removed nodes with the structural parent - aNodeArray.Clear(); - if (aStartOrEnd == StartOrEnd::end) { - aNodeArray.AppendElement(*replaceNode); - } else { - aNodeArray.InsertElementAt(0, *replaceNode); - } + // Now replace the removed nodes with the structural parent + if (aStartOrEnd == StartOrEnd::end) { + aNodeArray.AppendElement(*replaceNode); + } else { + aNodeArray.InsertElementAt(0, *replaceNode); } } diff --git a/editor/libeditor/InsertNodeTransaction.cpp b/editor/libeditor/InsertNodeTransaction.cpp index 6af33b74f..6317b8c15 100644 --- a/editor/libeditor/InsertNodeTransaction.cpp +++ b/editor/libeditor/InsertNodeTransaction.cpp @@ -28,7 +28,7 @@ InsertNodeTransaction::InsertNodeTransaction(nsIContent& aNode, : mNode(&aNode) , mParent(&aParent) , mOffset(aOffset) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) { } @@ -37,6 +37,7 @@ InsertNodeTransaction::~InsertNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertNodeTransaction, EditTransactionBase, + mEditorBase, mNode, mParent) @@ -48,7 +49,9 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP InsertNodeTransaction::DoTransaction() { - MOZ_ASSERT(mNode && mParent); + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } uint32_t count = mParent->GetChildCount(); if (mOffset > static_cast<int32_t>(count) || mOffset == -1) { @@ -59,15 +62,15 @@ InsertNodeTransaction::DoTransaction() // Note, it's ok for ref to be null. That means append. nsCOMPtr<nsIContent> ref = mParent->GetChildAt(mOffset); - mEditorBase.MarkNodeDirty(GetAsDOMNode(mNode)); + mEditorBase->MarkNodeDirty(GetAsDOMNode(mNode)); ErrorResult rv; mParent->InsertBefore(*mNode, ref, rv); NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult()); // Only set selection to insertion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); // Place the selection just after the inserted element selection->Collapse(mParent, mOffset + 1); @@ -80,8 +83,9 @@ InsertNodeTransaction::DoTransaction() NS_IMETHODIMP InsertNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mNode && mParent); - + if (NS_WARN_IF(!mNode) || NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; mParent->RemoveChild(*mNode, rv); return rv.StealNSResult(); diff --git a/editor/libeditor/InsertNodeTransaction.h b/editor/libeditor/InsertNodeTransaction.h index 5af7b8aff..1b9739d6c 100644 --- a/editor/libeditor/InsertNodeTransaction.h +++ b/editor/libeditor/InsertNodeTransaction.h @@ -50,7 +50,7 @@ protected: int32_t mOffset; // The editor for this transaction. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; }; } // namespace mozilla diff --git a/editor/libeditor/InsertTextTransaction.cpp b/editor/libeditor/InsertTextTransaction.cpp index 009b2d023..0434b2dd5 100644 --- a/editor/libeditor/InsertTextTransaction.cpp +++ b/editor/libeditor/InsertTextTransaction.cpp @@ -26,7 +26,7 @@ InsertTextTransaction::InsertTextTransaction(Text& aTextNode, : mTextNode(&aTextNode) , mOffset(aOffset) , mStringToInsert(aStringToInsert) - , mEditorBase(aEditorBase) + , mEditorBase(&aEditorBase) , mRangeUpdater(aRangeUpdater) { } @@ -36,6 +36,7 @@ InsertTextTransaction::~InsertTextTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(InsertTextTransaction, EditTransactionBase, + mEditorBase, mTextNode) NS_IMPL_ADDREF_INHERITED(InsertTextTransaction, EditTransactionBase) @@ -50,12 +51,16 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP InsertTextTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTextNode)) { + return NS_ERROR_NOT_AVAILABLE; + } + nsresult rv = mTextNode->InsertData(mOffset, mStringToInsert); NS_ENSURE_SUCCESS(rv, rv); // Only set selection to insertion point if editor gives permission - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); DebugOnly<nsresult> rv = selection->Collapse(mTextNode, mOffset + mStringToInsert.Length()); diff --git a/editor/libeditor/InsertTextTransaction.h b/editor/libeditor/InsertTextTransaction.h index f97f20e37..b6867ad76 100644 --- a/editor/libeditor/InsertTextTransaction.h +++ b/editor/libeditor/InsertTextTransaction.h @@ -76,7 +76,7 @@ private: nsString mStringToInsert; // The editor, which we'll need to get the selection. - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; RangeUpdater* mRangeUpdater; }; diff --git a/editor/libeditor/JoinNodeTransaction.cpp b/editor/libeditor/JoinNodeTransaction.cpp index 228d1f4d6..50727cce1 100644 --- a/editor/libeditor/JoinNodeTransaction.cpp +++ b/editor/libeditor/JoinNodeTransaction.cpp @@ -21,7 +21,7 @@ using namespace dom; JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase, nsINode& aLeftNode, nsINode& aRightNode) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mLeftNode(&aLeftNode) , mRightNode(&aRightNode) , mOffset(0) @@ -29,6 +29,7 @@ JoinNodeTransaction::JoinNodeTransaction(EditorBase& aEditorBase, } NS_IMPL_CYCLE_COLLECTION_INHERITED(JoinNodeTransaction, EditTransactionBase, + mEditorBase, mLeftNode, mRightNode, mParent) @@ -39,7 +40,8 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) nsresult JoinNodeTransaction::CheckValidity() { - if (!mEditorBase.IsModifiableNode(mLeftNode->GetParentNode())) { + if (NS_WARN_IF(!mEditorBase) || + !mEditorBase->IsModifiableNode(mLeftNode->GetParentNode())) { return NS_ERROR_FAILURE; } return NS_OK; @@ -50,6 +52,12 @@ JoinNodeTransaction::CheckValidity() NS_IMETHODIMP JoinNodeTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase) || + NS_WARN_IF(!mLeftNode) || + NS_WARN_IF(!mRightNode)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Get the parent node nsCOMPtr<nsINode> leftParent = mLeftNode->GetParentNode(); NS_ENSURE_TRUE(leftParent, NS_ERROR_NULL_POINTER); @@ -65,7 +73,7 @@ JoinNodeTransaction::DoTransaction() mParent = leftParent; mOffset = mLeftNode->Length(); - return mEditorBase.JoinNodesImpl(mRightNode, mLeftNode, mParent); + return mEditorBase->JoinNodesImpl(mRightNode, mLeftNode, mParent); } //XXX: What if instead of split, we just deleted the unneeded children of @@ -73,7 +81,11 @@ JoinNodeTransaction::DoTransaction() NS_IMETHODIMP JoinNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mParent); + if (NS_WARN_IF(!mParent) || + NS_WARN_IF(!mRightNode) || + NS_WARN_IF(!mLeftNode)) { + return NS_ERROR_NOT_INITIALIZED; + } // First, massage the existing node so it is in its post-split state ErrorResult rv; diff --git a/editor/libeditor/JoinNodeTransaction.h b/editor/libeditor/JoinNodeTransaction.h index 84208cb45..827d9dfaf 100644 --- a/editor/libeditor/JoinNodeTransaction.h +++ b/editor/libeditor/JoinNodeTransaction.h @@ -47,7 +47,7 @@ public: NS_DECL_EDITTRANSACTIONBASE protected: - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The nodes to operate upon. After the merge, mRightNode remains and // mLeftNode is removed from the content tree. diff --git a/editor/libeditor/PlaceholderTransaction.cpp b/editor/libeditor/PlaceholderTransaction.cpp index 1031b45ab..fa808afad 100644 --- a/editor/libeditor/PlaceholderTransaction.cpp +++ b/editor/libeditor/PlaceholderTransaction.cpp @@ -35,6 +35,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PlaceholderTransaction, if (tmp->mStartSel) { ImplCycleCollectionUnlink(*tmp->mStartSel); } + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEditorBase); NS_IMPL_CYCLE_COLLECTION_UNLINK(mEndSel); NS_IMPL_CYCLE_COLLECTION_UNLINK_END @@ -43,6 +44,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PlaceholderTransaction, if (tmp->mStartSel) { ImplCycleCollectionTraverse(cb, *tmp->mStartSel, "mStartSel", 0); } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEditorBase); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndSel); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -76,6 +78,10 @@ PlaceholderTransaction::DoTransaction() NS_IMETHODIMP PlaceholderTransaction::UndoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Undo transactions. nsresult rv = EditAggregateTransaction::UndoTransaction(); NS_ENSURE_SUCCESS(rv, rv); @@ -91,6 +97,10 @@ PlaceholderTransaction::UndoTransaction() NS_IMETHODIMP PlaceholderTransaction::RedoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Redo transactions. nsresult rv = EditAggregateTransaction::RedoTransaction(); NS_ENSURE_SUCCESS(rv, rv); @@ -261,6 +271,10 @@ PlaceholderTransaction::Commit() nsresult PlaceholderTransaction::RememberEndingSelection() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); mEndSel.SaveSelection(selection); diff --git a/editor/libeditor/PlaceholderTransaction.h b/editor/libeditor/PlaceholderTransaction.h index 867a82ce3..8193239be 100644 --- a/editor/libeditor/PlaceholderTransaction.h +++ b/editor/libeditor/PlaceholderTransaction.h @@ -84,7 +84,7 @@ protected: SelectionState mEndSel; // The editor for this transaction. - EditorBase* mEditorBase; + RefPtr<EditorBase> mEditorBase; }; } // namespace mozilla diff --git a/editor/libeditor/SplitNodeTransaction.cpp b/editor/libeditor/SplitNodeTransaction.cpp index 113ff7a61..8965b5399 100644 --- a/editor/libeditor/SplitNodeTransaction.cpp +++ b/editor/libeditor/SplitNodeTransaction.cpp @@ -19,7 +19,7 @@ using namespace dom; SplitNodeTransaction::SplitNodeTransaction(EditorBase& aEditorBase, nsIContent& aNode, int32_t aOffset) - : mEditorBase(aEditorBase) + : mEditorBase(&aEditorBase) , mExistingRightNode(&aNode) , mOffset(aOffset) { @@ -30,6 +30,7 @@ SplitNodeTransaction::~SplitNodeTransaction() } NS_IMPL_CYCLE_COLLECTION_INHERITED(SplitNodeTransaction, EditTransactionBase, + mEditorBase, mParent, mNewLeftNode) @@ -41,6 +42,10 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase) NS_IMETHODIMP SplitNodeTransaction::DoTransaction() { + if (NS_WARN_IF(!mEditorBase)) { + return NS_ERROR_NOT_INITIALIZED; + } + // Create a new node ErrorResult rv; // Don't use .downcast directly because AsContent has an assertion we want @@ -48,16 +53,16 @@ SplitNodeTransaction::DoTransaction() NS_ASSERTION(!rv.Failed() && clone, "Could not create clone"); NS_ENSURE_TRUE(!rv.Failed() && clone, rv.StealNSResult()); mNewLeftNode = dont_AddRef(clone.forget().take()->AsContent()); - mEditorBase.MarkNodeDirty(mExistingRightNode->AsDOMNode()); + mEditorBase->MarkNodeDirty(mExistingRightNode->AsDOMNode()); // Get the parent node mParent = mExistingRightNode->GetParentNode(); NS_ENSURE_TRUE(mParent, NS_ERROR_NULL_POINTER); // Insert the new node - rv = mEditorBase.SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode); - if (mEditorBase.GetShouldTxnSetSelection()) { - RefPtr<Selection> selection = mEditorBase.GetSelection(); + rv = mEditorBase->SplitNodeImpl(*mExistingRightNode, mOffset, *mNewLeftNode); + if (mEditorBase->GetShouldTxnSetSelection()) { + RefPtr<Selection> selection = mEditorBase->GetSelection(); NS_ENSURE_TRUE(selection, NS_ERROR_NULL_POINTER); rv = selection->Collapse(mNewLeftNode, mOffset); } @@ -67,10 +72,14 @@ SplitNodeTransaction::DoTransaction() NS_IMETHODIMP SplitNodeTransaction::UndoTransaction() { - MOZ_ASSERT(mNewLeftNode && mParent); + if (NS_WARN_IF(!mEditorBase) || + NS_WARN_IF(!mNewLeftNode) || + NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } // This assumes Do inserted the new node in front of the prior existing node - return mEditorBase.JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent); + return mEditorBase->JoinNodesImpl(mExistingRightNode, mNewLeftNode, mParent); } /* Redo cannot simply resplit the right node, because subsequent transactions @@ -80,7 +89,10 @@ SplitNodeTransaction::UndoTransaction() NS_IMETHODIMP SplitNodeTransaction::RedoTransaction() { - MOZ_ASSERT(mNewLeftNode && mParent); + if (NS_WARN_IF(!mNewLeftNode) || + NS_WARN_IF(!mParent)) { + return NS_ERROR_NOT_INITIALIZED; + } ErrorResult rv; // First, massage the existing node so it is in its post-split state diff --git a/editor/libeditor/SplitNodeTransaction.h b/editor/libeditor/SplitNodeTransaction.h index 36119518b..4c50143ec 100644 --- a/editor/libeditor/SplitNodeTransaction.h +++ b/editor/libeditor/SplitNodeTransaction.h @@ -49,7 +49,7 @@ public: protected: virtual ~SplitNodeTransaction(); - EditorBase& mEditorBase; + RefPtr<EditorBase> mEditorBase; // The node to operate upon. nsCOMPtr<nsIContent> mExistingRightNode; diff --git a/editor/libeditor/StyleSheetTransactions.cpp b/editor/libeditor/StyleSheetTransactions.cpp index 6a31a16e2..cf32898a9 100644 --- a/editor/libeditor/StyleSheetTransactions.cpp +++ b/editor/libeditor/StyleSheetTransactions.cpp @@ -57,6 +57,7 @@ AddStyleSheetTransaction::AddStyleSheetTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(AddStyleSheetTransaction, EditTransactionBase, + mEditor, mSheet) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AddStyleSheetTransaction) @@ -111,6 +112,7 @@ RemoveStyleSheetTransaction::RemoveStyleSheetTransaction() NS_IMPL_CYCLE_COLLECTION_INHERITED(RemoveStyleSheetTransaction, EditTransactionBase, + mEditor, mSheet) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RemoveStyleSheetTransaction) diff --git a/editor/libeditor/StyleSheetTransactions.h b/editor/libeditor/StyleSheetTransactions.h index bf615b263..d6855981b 100644 --- a/editor/libeditor/StyleSheetTransactions.h +++ b/editor/libeditor/StyleSheetTransactions.h @@ -36,7 +36,7 @@ public: protected: // The editor that created this transaction. - nsIEditor* mEditor; + nsCOMPtr<nsIEditor> mEditor; // The style sheet to add. RefPtr<mozilla::StyleSheet> mSheet; }; @@ -62,7 +62,7 @@ public: protected: // The editor that created this transaction. - nsIEditor* mEditor; + nsCOMPtr<nsIEditor> mEditor; // The style sheet to remove. RefPtr<StyleSheet> mSheet; diff --git a/editor/libeditor/TextEditorDataTransfer.cpp b/editor/libeditor/TextEditorDataTransfer.cpp index 0388aa4a8..2cc2906fa 100644 --- a/editor/libeditor/TextEditorDataTransfer.cpp +++ b/editor/libeditor/TextEditorDataTransfer.cpp @@ -383,6 +383,13 @@ TextEditor::CanPaste(int32_t aSelectionType, NS_ENSURE_ARG_POINTER(aCanPaste); *aCanPaste = false; + // Always enable the paste command when inside of a HTML or XHTML document. + nsCOMPtr<nsIDocument> doc = GetDocument(); + if (doc && doc->IsHTMLOrXHTML()) { + *aCanPaste = true; + return NS_OK; + } + // can't paste if readonly if (!IsModifiable()) { return NS_OK; diff --git a/editor/libeditor/tests/chrome.ini b/editor/libeditor/tests/chrome.ini index 98db30001..dd13370a5 100644 --- a/editor/libeditor/tests/chrome.ini +++ b/editor/libeditor/tests/chrome.ini @@ -12,3 +12,4 @@ support-files = green.png [test_set_document_title_transaction.html] [test_texteditor_keyevent_handling.html] skip-if = (debug && os=='win') || (os == 'linux') # Bug 1116205, leaks on windows debug, fails delete key on linux +[test_pasteImgTextarea.xul] diff --git a/editor/libeditor/tests/mochitest.ini b/editor/libeditor/tests/mochitest.ini index 447fb8b65..33b164819 100644 --- a/editor/libeditor/tests/mochitest.ini +++ b/editor/libeditor/tests/mochitest.ini @@ -217,6 +217,9 @@ skip-if = toolkit == 'android' [test_bug1258085.html] [test_bug1268736.html] [test_bug1270235.html] +[test_bug1306532.html] +subsuite = clipboard +skip-if = toolkit == 'android' [test_bug1310912.html] skip-if = toolkit == 'android' # bug 1315898 [test_bug1314790.html] @@ -224,6 +227,7 @@ skip-if = toolkit == 'android' # bug 1315898 [test_bug1328023.html] [test_bug1330796.html] [test_bug1332876.html] +[test_bug1352799.html] [test_CF_HTML_clipboard.html] subsuite = clipboard @@ -243,3 +247,5 @@ skip-if = toolkit == 'android' [test_css_chrome_load_access.html] skip-if = toolkit == 'android' # chrome urls not available due to packaging [test_selection_move_commands.html] +[test_pasteImgTextarea.html] +skip-if = toolkit == 'android' # bug 1299578 diff --git a/editor/libeditor/tests/test_bug1306532.html b/editor/libeditor/tests/test_bug1306532.html new file mode 100644 index 000000000..1d7b3e7af --- /dev/null +++ b/editor/libeditor/tests/test_bug1306532.html @@ -0,0 +1,64 @@ +<!DOCTYPE HTML> +<html><head> +<title>Test for bug 1306532</title> +<style src="/tests/SimpleTest/test.css" type="text/css"></style> +<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> +<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + +<script class="testbody" type="application/javascript"> + +function runTest() { + // Copy content from table. + var selection = getSelection(); + var startRange = document.createRange(); + startRange.setStart(headingone, 0); + startRange.setEnd(celltwo, 0); + selection.removeAllRanges(); + selection.addRange(startRange); + SpecialPowers.wrap(document).execCommand("copy", false, null); + + // Paste content into "pasteframe" + var pasteContainer = pasteframe.contentDocument.body; + var pasteRange = pasteframe.contentDocument.createRange(); + pasteRange.selectNodeContents(pasteContainer); + pasteRange.collapse(false); + selection.removeAllRanges(); + selection.addRange(pasteRange); + SpecialPowers.wrap(pasteframe.contentDocument).execCommand("paste", false, null); + + is(pasteContainer.querySelector("#headingone").textContent, "Month", "First heading should be 'Month'."); + is(pasteContainer.querySelector("#headingtwo").textContent, "Savings", "Second heading should be 'Savings'."); + is(pasteContainer.querySelector("#cellone").textContent, "January", "First cell should be 'January'."); + is(pasteContainer.querySelector("#celltwo").textContent, "$100", "Second cell should be '$100'."); + + SimpleTest.finish(); +} + +SimpleTest.waitForExplicitFinish(); + +</script> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1306532">Mozilla Bug 1306532</a> +<p id="display"></p> + +<pre id="test"> +</pre> + +<div id="container"> +<table border="1"> + <tr> + <th id="headingone">Month</th> + <th id="headingtwo">Savings</th> + </tr> + <tr> + <td id="cellone">January</td> + <td id="celltwo">$100</td> + </tr> +</table> +</div> + +<iframe onload="runTest();" id="pasteframe" src="data:text/html,<html><body contenteditable='true'>"></iframe> + +</body> +</html> diff --git a/editor/libeditor/tests/test_bug1352799.html b/editor/libeditor/tests/test_bug1352799.html new file mode 100644 index 000000000..daedc40fc --- /dev/null +++ b/editor/libeditor/tests/test_bug1352799.html @@ -0,0 +1,98 @@ +<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1352799
+-->
+<head>
+ <title>Test for Bug 1352799</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=1352799">Mozilla Bug 1352799</a>
+<p id="display"></p>
+<div id="content">
+<div id="input-container" style="display: none;">
+<input id="input" maxlength="1">
+</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1352799 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ var input = document.getElementById("input");
+
+ var inputcontainer = document.getElementById('input-container');
+ input.setAttribute('maxlength', 2);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+ synthesizeKey('3', {});
+
+ is(input.value, '12', 'value should be 12 with maxlength = 2');
+
+ input.value = '';
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.setAttribute('maxlength', 4);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('4', {});
+ synthesizeKey('5', {});
+ synthesizeKey('6', {});
+ synthesizeKey('7', {});
+ synthesizeKey('8', {});
+
+ is(input.value, '4567', 'value should be 4567 with maxlength = 4');
+
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.setAttribute('maxlength', 2);
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+
+ todo_is(input.value, '45', 'value should be 45 with maxlength = 2');
+
+ input.value = '';
+ inputcontainer.style.display = 'none';
+
+ window.setTimeout(() => {
+ input.removeAttribute('maxlength');
+ inputcontainer.style.display = 'block';
+
+ input.focus();
+
+ synthesizeKey('1', {});
+ synthesizeKey('2', {});
+ synthesizeKey('3', {});
+ synthesizeKey('4', {});
+ synthesizeKey('5', {});
+ synthesizeKey('6', {});
+ synthesizeKey('7', {});
+ synthesizeKey('8', {});
+
+ is(input.value, '12345678', 'value should be 12345678 without maxlength');
+
+ SimpleTest.finish();
+ }, 0);
+ }, 0);
+ }, 0);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/editor/libeditor/tests/test_pasteImgTextarea.html b/editor/libeditor/tests/test_pasteImgTextarea.html new file mode 100644 index 000000000..3168ae729 --- /dev/null +++ b/editor/libeditor/tests/test_pasteImgTextarea.html @@ -0,0 +1,20 @@ +<!doctype html> +<script src="/tests/SimpleTest/SimpleTest.js"></script> +<script src="/tests/SimpleTest/SpawnTask.js"></script> +<img id="i" src="green.png"> +<textarea id="t"></textarea> + +<script> +let loaded = new Promise(resolve => addLoadEvent(resolve)); + add_task(function*() { + yield loaded; + SpecialPowers.setCommandNode(window, document.getElementById("i")); + SpecialPowers.doCommand(window, "cmd_copyImageContents"); + let input = document.getElementById("t"); + input.focus(); + var controller = + SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste"); + is(controller.isCommandEnabled("cmd_paste"), true, + "paste should be enabled in html textareas when an image is on the clipboard"); + }); +</script> diff --git a/editor/libeditor/tests/test_pasteImgTextarea.xul b/editor/libeditor/tests/test_pasteImgTextarea.xul new file mode 100644 index 000000000..545027aa3 --- /dev/null +++ b/editor/libeditor/tests/test_pasteImgTextarea.xul @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<?xml-stylesheet href="chrome://global/skin" type="text/css"?> +<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?> +<window xmlns:html="http://www.w3.org/1999/xhtml" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> + + <body xmlns="http://www.w3.org/1999/xhtml"> + <html:img id="i" src="green.png" /> + <html:textarea id="t"></html:textarea> + </body> + <script type="text/javascript"><![CDATA[ + let loaded = new Promise(resolve => addLoadEvent(resolve)); + add_task(function*() { + yield loaded; + SpecialPowers.setCommandNode(window, document.getElementById("i")); + SpecialPowers.doCommand(window, "cmd_copyImageContents"); + let input = document.getElementById("t"); + input.focus(); + var controller = + SpecialPowers.wrap(input).controllers.getControllerForCommand("cmd_paste"); + is(controller.isCommandEnabled("cmd_paste"), false, + "paste should not be enabled in xul textareas when an image is on the clipboard"); + }); + ]]></script> +</window> |