Bug 615450 - Prevent the reinitialization of the HTML editor while an editor operation is in progress on a document; r,a=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 30 Nov 2010 23:21:32 -0500
changeset 58546 92d0320fb62e7a02e1e3b49289016fb4d338199d
parent 58545 8800f54d55d0966f5a52e30729ea41cb63d3b978
child 58547 af358fe76628a70384809c486e9a0f47b6f7c4ab
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersroc
bugs615450
milestone2.0b8pre
Bug 615450 - Prevent the reinitialization of the HTML editor while an editor operation is in progress on a document; r,a=roc This should prevent crashes, update view count and edit action nesting count mismatches, and more!
content/html/document/src/nsHTMLDocument.cpp
editor/libeditor/html/crashtests/615450-1.html
editor/libeditor/html/crashtests/crashtests.list
editor/libeditor/html/nsHTMLEditRules.cpp
editor/libeditor/html/nsHTMLEditRules.h
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2971,17 +2971,17 @@ public:
   DeferredContentEditableCountChangeEvent(nsHTMLDocument *aDoc,
                                           nsIContent *aElement)
     : mDoc(aDoc)
     , mElement(aElement)
   {
   }
 
   NS_IMETHOD Run() {
-    if (mElement->GetOwnerDoc() == mDoc) {
+    if (mElement && mElement->GetOwnerDoc() == mDoc) {
       mDoc->DeferredContentEditableCountChange(mElement);
     }
     return NS_OK;
   }
 
 private:
   nsRefPtr<nsHTMLDocument> mDoc;
   nsCOMPtr<nsIContent> mElement;
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/html/crashtests/615450-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html contenteditable="true">
+<head>
+<script>
+
+function boom()
+{
+  document.documentElement.appendChild(document.body);
+  document.documentElement.contentEditable = "false";
+  try { document.execCommand("outdent", false, null); } catch(e) { }
+  document.body.contentEditable = "true";
+  try { document.execCommand("inserthtml", false, "x"); } catch(e) { }
+}
+
+</script>
+</head>
+<body onload="boom();"><div style="margin-left: 40px;"><span contenteditable="true">p q r s</span> T</div></body></html>
--- a/editor/libeditor/html/crashtests/crashtests.list
+++ b/editor/libeditor/html/crashtests/crashtests.list
@@ -14,8 +14,9 @@ load 456727-2.html
 asserts(1) load 467647-1.html # bug 382210
 load 499844-1.html
 load 503709-1.xhtml
 load 513375-1.xhtml
 load 535632-1.xhtml
 load 574558-1.xhtml
 load 582138-1.xhtml
 load 612565-1.html
+load 615450-1.html
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -81,16 +81,17 @@
 #include "DeleteTextTxn.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 
 #include "nsFrameSelection.h"
 #include "nsIDOM3Node.h"
 #include "nsContentUtils.h"
 #include "nsTArray.h"
+#include "nsIHTMLDocument.h"
 
 //const static char* kMOZEditorBogusNodeAttr="MOZ_EDITOR_BOGUS_NODE";
 //const static char* kMOZEditorBogusNodeValue="TRUE";
 
 enum
 {
   kLonely = 0,
   kPrevSib = 1,
@@ -192,16 +193,17 @@ NS_NewHTMLEditRules(nsIEditRules** aInst
  ********************************************************/
 
 nsHTMLEditRules::nsHTMLEditRules() : 
 mDocChangeRange(nsnull)
 ,mListenerEnabled(PR_TRUE)
 ,mReturnInEmptyLIKillsList(PR_TRUE)
 ,mDidDeleteSelection(PR_FALSE)
 ,mDidRangedDelete(PR_FALSE)
+,mRestoreContentEditableCount(PR_FALSE)
 ,mUtilRange(nsnull)
 ,mJoinOffset(0)
 {
   nsString emptyString;
   // populate mCachedStyles
   mCachedStyles[0] = StyleCache(nsEditProperty::b, emptyString, emptyString);
   mCachedStyles[1] = StyleCache(nsEditProperty::i, emptyString, emptyString);
   mCachedStyles[2] = StyleCache(nsEditProperty::u, emptyString, emptyString);
@@ -372,17 +374,28 @@ nsHTMLEditRules::BeforeEdit(PRInt32 acti
         (action == nsEditor::kOpInsertBreak))
     {
       nsCOMPtr<nsIDOMNode> selNode = selStartNode;
       if (aDirection == nsIEditor::eNext)
         selNode = selEndNode;
       res = CacheInlineStyles(selNode);
       NS_ENSURE_SUCCESS(res, res);
     }
-    
+
+    // Stabilize the document against contenteditable count changes
+    nsCOMPtr<nsIDOMDocument> doc;
+    res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
+    NS_ENSURE_SUCCESS(res, res);
+    nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
+    NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
+    if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
+      htmlDoc->ChangeContentEditableCount(nsnull, +1);
+      mRestoreContentEditableCount = PR_TRUE;
+    }
+
     // check that selection is in subtree defined by body node
     ConfirmSelectionInBody();
     // let rules remember the top level action
     mTheAction = action;
   }
   return NS_OK;
 }
 
@@ -418,16 +431,29 @@ nsHTMLEditRules::AfterEdit(PRInt32 actio
       NS_ENSURE_SUCCESS(res, res);
       nsCOMPtr<nsISelectionPrivate> privateSelection(do_QueryInterface(selection));
       nsCOMPtr<nsFrameSelection> frameSelection;
       privateSelection->GetFrameSelection(getter_AddRefs(frameSelection));
       if (frameSelection) {
         frameSelection->UndefineCaretBidiLevel();
       }
     }
+
+    // Reset the contenteditable count to its previous value
+    if (mRestoreContentEditableCount) {
+      nsCOMPtr<nsIDOMDocument> doc;
+      res = mHTMLEditor->GetDocument(getter_AddRefs(doc));
+      NS_ENSURE_SUCCESS(res, res);
+      nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(doc);
+      NS_ENSURE_TRUE(htmlDoc, NS_ERROR_FAILURE);
+      if (htmlDoc->GetEditingState() == nsIHTMLDocument::eContentEditable) {
+        htmlDoc->ChangeContentEditableCount(nsnull, -1);
+      }
+      mRestoreContentEditableCount = PR_FALSE;
+    }
   }
 
   return res;
 }
 
 
 nsresult
 nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection)
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -306,16 +306,17 @@ protected:
 // data members
 protected:
   nsHTMLEditor           *mHTMLEditor;
   nsCOMPtr<nsIDOMRange>   mDocChangeRange;
   PRPackedBool            mListenerEnabled;
   PRPackedBool            mReturnInEmptyLIKillsList;
   PRPackedBool            mDidDeleteSelection;
   PRPackedBool            mDidRangedDelete;
+  PRPackedBool            mRestoreContentEditableCount;
   nsCOMPtr<nsIDOMRange>   mUtilRange;
   PRUint32                mJoinOffset;  // need to remember an int across willJoin/didJoin...
   nsCOMPtr<nsIDOMNode>    mNewBlock;
   nsRangeStore            mRangeItem;
   StyleCache              mCachedStyles[SIZE_STYLE_TABLE];
 };
 
 nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult);