summaryrefslogtreecommitdiffstats
path: root/editor/libeditor
diff options
context:
space:
mode:
Diffstat (limited to 'editor/libeditor')
-rw-r--r--editor/libeditor/CompositionTransaction.cpp20
-rw-r--r--editor/libeditor/CompositionTransaction.h2
-rw-r--r--editor/libeditor/CreateElementTransaction.cpp13
-rw-r--r--editor/libeditor/CreateElementTransaction.h2
-rw-r--r--editor/libeditor/DeleteNodeTransaction.cpp1
-rw-r--r--editor/libeditor/DeleteNodeTransaction.h2
-rw-r--r--editor/libeditor/DeleteRangeTransaction.cpp27
-rw-r--r--editor/libeditor/DeleteRangeTransaction.h2
-rw-r--r--editor/libeditor/DeleteTextTransaction.cpp18
-rw-r--r--editor/libeditor/DeleteTextTransaction.h2
-rw-r--r--editor/libeditor/EditorBase.cpp16
-rw-r--r--editor/libeditor/HTMLEditorDataTransfer.cpp38
-rw-r--r--editor/libeditor/InsertNodeTransaction.cpp18
-rw-r--r--editor/libeditor/InsertNodeTransaction.h2
-rw-r--r--editor/libeditor/InsertTextTransaction.cpp11
-rw-r--r--editor/libeditor/InsertTextTransaction.h2
-rw-r--r--editor/libeditor/JoinNodeTransaction.cpp20
-rw-r--r--editor/libeditor/JoinNodeTransaction.h2
-rw-r--r--editor/libeditor/PlaceholderTransaction.cpp14
-rw-r--r--editor/libeditor/PlaceholderTransaction.h2
-rw-r--r--editor/libeditor/SplitNodeTransaction.cpp28
-rw-r--r--editor/libeditor/SplitNodeTransaction.h2
-rw-r--r--editor/libeditor/StyleSheetTransactions.cpp2
-rw-r--r--editor/libeditor/StyleSheetTransactions.h4
-rw-r--r--editor/libeditor/TextEditorDataTransfer.cpp7
-rw-r--r--editor/libeditor/tests/chrome.ini1
-rw-r--r--editor/libeditor/tests/mochitest.ini6
-rw-r--r--editor/libeditor/tests/test_bug1306532.html64
-rw-r--r--editor/libeditor/tests/test_bug1352799.html98
-rw-r--r--editor/libeditor/tests/test_pasteImgTextarea.html20
-rw-r--r--editor/libeditor/tests/test_pasteImgTextarea.xul27
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>