Bug 611182 - Part 3: Handle dynamic changes to the editable documents and create/remove the bogus node if needed; r=bzbarsky a=blocking-beta8+
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 11 Nov 2010 16:40:52 -0500
changeset 57481 54d00e3411ab46ca27d20bfe9528aebcc10bcb32
parent 57480 a9f682f84cab0340bf96ca0f04003fa8531586c2
child 57482 553642bc2cfacead753059b8da9951c24acc3da3
push id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersbzbarsky, blocking-beta8
bugs611182
milestone2.0b8pre
Bug 611182 - Part 3: Handle dynamic changes to the editable documents and create/remove the bogus node if needed; r=bzbarsky a=blocking-beta8+
editor/libeditor/base/nsEditRules.h
editor/libeditor/html/nsHTMLEditRules.cpp
editor/libeditor/html/nsHTMLEditRules.h
editor/libeditor/html/nsHTMLEditor.cpp
editor/libeditor/html/tests/test_bug611182.html
editor/libeditor/text/nsTextEditRules.cpp
editor/libeditor/text/nsTextEditRules.h
--- a/editor/libeditor/base/nsEditRules.h
+++ b/editor/libeditor/base/nsEditRules.h
@@ -35,18 +35,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsEditRules_h__
 #define nsEditRules_h__
 
 // FB45AC36-E8F1-44ae-8FB7-466E1BE119B0
 #define NS_IEDITRULES_IID \
-{ 0xfb45ac36, 0xe8f1, 0x44ae, \
-  { 0x8f, 0xb7, 0x46, 0x6e, 0x1b, 0xe1, 0x19, 0xb0 } }
+{ 0x2cc50d11, 0x9909, 0x433f, \
+  { 0xb6, 0xfb, 0x4c, 0xf2, 0x56, 0xe5, 0xe5, 0x71 } }
 
 class nsPlaintextEditor;
 class nsISelection;
 
 /***************************************************************************
  * base for an object to encapsulate any additional info needed to be passed
  * to rules system by the editor
  */
@@ -74,14 +74,15 @@ public:
 
   NS_IMETHOD Init(nsPlaintextEditor *aEditor)=0;
   NS_IMETHOD DetachEditor()=0;
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)=0;
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled)=0;
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult)=0;
   NS_IMETHOD DocumentIsEmpty(PRBool *aDocumentIsEmpty)=0;
+  NS_IMETHOD DocumentModified()=0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIEditRules, NS_IEDITRULES_IID)
 
 #endif //nsEditRules_h__
 
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -9155,8 +9155,33 @@ nsHTMLEditRules::WillRelativeChangeZInde
   NS_ENSURE_SUCCESS(res, res);
 
   nsAutoSelectionReset selectionResetter(aSelection, mHTMLEditor);
 
   nsCOMPtr<nsIHTMLAbsPosEditor> absPosHTMLEditor = mHTMLEditor;
   PRInt32 zIndex;
   return absPosHTMLEditor->RelativeChangeElementZIndex(elt, aChange, &zIndex);
 }
+
+NS_IMETHODIMP
+nsHTMLEditRules::DocumentModified()
+{
+  nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, &nsHTMLEditRules::DocumentModifiedWorker));
+  return NS_OK;
+}
+
+void
+nsHTMLEditRules::DocumentModifiedWorker()
+{
+  nsCOMPtr<nsISelection> selection;
+  nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS(res, );
+
+  // Delete our bogus node, if we have one, since the document might not be
+  // empty any more.
+  if (mBogusNode) {
+    mEditor->DeleteNode(mBogusNode);
+    mBogusNode = nsnull;
+  }
+
+  // Try to recreate the bogus node if needed.
+  CreateBogusNodeIfNeeded(selection);
+}
--- a/editor/libeditor/html/nsHTMLEditRules.h
+++ b/editor/libeditor/html/nsHTMLEditRules.h
@@ -87,16 +87,17 @@ public:
 
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor);
   NS_IMETHOD DetachEditor();
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
+  NS_IMETHOD DocumentModified();
 
   // nsIHTMLEditRules methods
   
   NS_IMETHOD GetListState(PRBool *aMixed, PRBool *aOL, PRBool *aUL, PRBool *aDL);
   NS_IMETHOD GetListItemState(PRBool *aMixed, PRBool *aLI, PRBool *aDT, PRBool *aDD);
   NS_IMETHOD GetIndentState(PRBool *aCanIndent, PRBool *aCanOutdent);
   NS_IMETHOD GetAlignment(PRBool *aMixed, nsIHTMLEditor::EAlignment *aAlign);
   NS_IMETHOD GetParagraphState(PRBool *aMixed, nsAString &outFormat);
@@ -295,16 +296,17 @@ protected:
   nsresult ConfirmSelectionInBody();
   nsresult InsertMozBRIfNeeded(nsIDOMNode *aNode);
   PRBool   IsEmptyInline(nsIDOMNode *aNode);
   PRBool   ListIsEmptyLine(nsCOMArray<nsIDOMNode> &arrayOfNodes);
   nsresult RemoveAlignment(nsIDOMNode * aNode, const nsAString & aAlignType, PRBool aChildrenOnly);
   nsresult MakeSureElemStartsOrEndsOnCR(nsIDOMNode *aNode, PRBool aStarts);
   nsresult AlignBlock(nsIDOMElement * aElement, const nsAString * aAlignType, PRBool aContentsOnly);
   nsresult RelativeChangeIndentationOfElementNode(nsIDOMNode *aNode, PRInt8 aRelativeChange);
+  void DocumentModifiedWorker();
 
 // data members
 protected:
   nsHTMLEditor           *mHTMLEditor;
   nsCOMPtr<nsIDOMRange>   mDocChangeRange;
   PRPackedBool            mListenerEnabled;
   PRPackedBool            mReturnInEmptyLIKillsList;
   PRPackedBool            mDidDeleteSelection;
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -3851,33 +3851,41 @@ nsHTMLEditor::ContentAppended(nsIDocumen
 {
   ContentInserted(aDocument, aContainer, aFirstNewContent, 0);
 }
 
 void
 nsHTMLEditor::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
                               nsIContent* aChild, PRInt32 /* unused */)
 {
-  if (!aChild || !aChild->IsElement()) {
+  if (!aChild) {
     return;
   }
 
   if (ShouldReplaceRootElement()) {
     ResetRootElementAndEventTarget();
   }
+  // We don't need to handle our own modifications
+  else if (!mAction) {
+    mRules->DocumentModified();
+  }
 }
 
 void
 nsHTMLEditor::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
                              nsIContent* aChild, PRInt32 aIndexInContainer,
                              nsIContent* aPreviousSibling)
 {
   if (SameCOMIdentity(aChild, mRootElement)) {
     ResetRootElementAndEventTarget();
   }
+  // We don't need to handle our own modifications
+  else if (!mAction) {
+    mRules->DocumentModified();
+  }
 }
 
 #ifdef XP_MAC
 #pragma mark -
 #pragma mark  support utils
 #pragma mark -
 #endif
 
--- a/editor/libeditor/html/tests/test_bug611182.html
+++ b/editor/libeditor/html/tests/test_bug611182.html
@@ -47,16 +47,17 @@ SimpleTest.waitForFocus(function() {
         }
       }
     }
     ok(false, "Failed to find the text node");
     return null;
   }
 
   function testBackspace(src, callback) {
+    ok(true, "Testing " + src);
     iframe.addEventListener("load", function() {
       iframe.removeEventListener("load", arguments.callee, false);
 
       var doc = iframe.contentDocument;
       var win = iframe.contentWindow;
       iframe.focus();
       var textNode = findTextNode(doc);
       var sel = win.getSelection();
@@ -67,26 +68,112 @@ SimpleTest.waitForFocus(function() {
       // TODO: compare screenshots to make sure that no bogus node exists in the document
 
       callback();
     }, false);
     iframe.src = src;
   }
 
   const TEST_URIS = [
-    "data:text/html,<html contenteditable>fooz bar</html>",
-    "data:text/html,<html contenteditable><body>fooz bar</body></html>",
     "data:text/html,<body contenteditable>fooz bar</body>",
     "data:text/html,<body contenteditable><p>fooz bar</p></body>",
     "data:text/html,<body contenteditable><div>fooz bar</div></body>",
     "data:text/html,<body contenteditable><span>fooz bar</span></body>",
     "data:text/html,<p contenteditable>fooz bar</p>",
     "data:text/html,<!DOCTYPE html><html><body contenteditable>fooz bar</body></html>",
     'data:application/xhtml+xml,<html xmlns="http://www.w3.org/1999/xhtml"><body contenteditable="true">fooz bar</body></html>',
     "data:text/html,<body onload=\"document.designMode='on'\">fooz bar</body>",
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'r.appendChild(b);' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                       'b.contentEditable = "true";' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                       'b.contentEditable = "true";' +
+                       'r.appendChild(b);' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'r.appendChild(b);' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                       'b.setAttribute("contenteditable", "true");' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                       'b.setAttribute("contenteditable", "true");' +
+                       'r.appendChild(b);' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'r.appendChild(b);' +
+                       'b.contentEditable = "true";' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'b.contentEditable = "true";' +
+                       'r.appendChild(b);' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'r.appendChild(b);' +
+                       'b.setAttribute("contenteditable", "true");' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                     '};' +
+                   '<\/script><body></body></html>',
+    'data:text/html,<html><script>' +
+                     'onload = function() {' +
+                       'var old = document.body;' +
+                       'old.parentNode.removeChild(old);' +
+                       'var r = document.documentElement;' +
+                       'var b = document.createElement("body");' +
+                       'b.setAttribute("contenteditable", "true");' +
+                       'r.appendChild(b);' +
+                       'b.appendChild(document.createTextNode("fooz bar"));' +
+                     '};' +
+                   '<\/script><body></body></html>',
   ];
   var currentTest = 0;
   function runAllTests() {
     if (currentTest == TEST_URIS.length) {
       SimpleTest.finish();
       return;
     }
     testBackspace(TEST_URIS[currentTest++], runAllTests);
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -1330,8 +1330,14 @@ nsTextEditRules::CreateMozBR(nsIDOMNode 
   nsCOMPtr<nsIDOMElement> brElem = do_QueryInterface(*outBRNode);
   if (brElem)
   {
     res = mEditor->SetAttribute(brElem, NS_LITERAL_STRING("type"), NS_LITERAL_STRING("_moz"));
     NS_ENSURE_SUCCESS(res, res);
   }
   return res;
 }
+
+NS_IMETHODIMP
+nsTextEditRules::DocumentModified()
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -70,16 +70,17 @@ public:
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor);
   NS_IMETHOD DetachEditor();
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection);
   NS_IMETHOD WillDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, PRBool *aCancel, PRBool *aHandled);
   NS_IMETHOD DidDoAction(nsISelection *aSelection, nsRulesInfo *aInfo, nsresult aResult);
   NS_IMETHOD DocumentIsEmpty(PRBool *aDocumentIsEmpty);
+  NS_IMETHOD DocumentModified();
 
   // nsTextEditRules action id's
   enum 
   {
     kDefault             = 0,
     // any editor that has a txn mgr
     kUndo                = 1000,
     kRedo                = 1001,