Fix for bug 237964 (Allow editable areas in browser (contentEditable)). r/sr=sicking.
authorpeterv@propagandism.org
Wed, 27 Jun 2007 15:29:45 -0700
changeset 2881 689d384fef4fa4ba19bd508fae4991c11fb63c83
parent 2880 008740160094b53148060da8803a893ae3156d90
child 2882 a6c15d5b1eeee3232d35cee4b922d7cb68396f1b
push idunknown
push userunknown
push dateunknown
bugs237964
milestone1.9a6pre
Fix for bug 237964 (Allow editable areas in browser (contentEditable)). r/sr=sicking.
browser/installer/unix/packages-static
browser/installer/windows/packages-static
content/base/public/nsIContent.h
content/base/public/nsINode.h
content/base/public/nsISelectionPrivate.idl
content/base/src/nsGenericDOMDataNode.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsTextNode.cpp
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/document/src/Makefile.in
content/html/document/src/nsHTMLDocument.cpp
content/html/document/src/nsHTMLDocument.h
content/html/document/src/nsIHTMLDocument.h
content/xul/content/src/nsXULElement.cpp
content/xul/content/src/nsXULElement.h
docshell/base/nsDocShellEditorData.cpp
docshell/base/nsWebShell.cpp
dom/public/idl/html/nsIDOMNSHTMLElement.idl
editor/composer/public/nsIEditingSession.idl
editor/composer/src/nsEditingSession.cpp
editor/composer/src/nsEditingSession.h
editor/composer/src/res/EditorOverride.css
editor/docs/Editor_Embedding_Guide.html
editor/idl/nsIEditor.idl
editor/idl/nsIPlaintextEditor.idl
editor/libeditor/base/DeleteElementTxn.cpp
editor/libeditor/base/DeleteElementTxn.h
editor/libeditor/base/DeleteRangeTxn.cpp
editor/libeditor/base/DeleteTextTxn.cpp
editor/libeditor/base/JoinElementTxn.cpp
editor/libeditor/base/nsEditPropertyAtomList.h
editor/libeditor/base/nsEditor.cpp
editor/libeditor/html/nsHTMLEditRules.cpp
editor/libeditor/html/nsHTMLEditor.cpp
editor/libeditor/html/nsHTMLEditor.h
editor/libeditor/text/nsEditorEventListeners.cpp
editor/libeditor/text/nsTextEditRules.cpp
editor/ui/composer/content/editor.js
embedding/browser/activex/src/control/MozillaBrowser.cpp
embedding/config/basebrowser-installer-win.pkg
embedding/config/basebrowser-mac-macho
embedding/config/basebrowser-qnx
embedding/config/basebrowser-unix
embedding/config/basebrowser-win
embedding/config/minimo-qnx
embedding/config/minimo-unix
embedding/qa/testembed/nsIEditSession.cpp
embedding/tests/wxEmbed/EditorFrame.cpp
extensions/spellcheck/src/mozInlineSpellChecker.cpp
layout/base/nsCaret.cpp
layout/build/layout.pkg
layout/generic/nsFrame.cpp
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
layout/style/Makefile.in
layout/style/contenteditable.css
layout/style/designmode.css
layout/style/forms.css
toolkit/content/widgets/editor.xml
xpfe/global/resources/content/bindings/editor.xml
xpinstall/packager/packages-os2
xpinstall/packager/packages-static-unix
xpinstall/packager/packages-static-win
xpinstall/packager/packages-unix
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -281,16 +281,18 @@ bin/defaults/autoconfig/prefcalls.js
 ; Style Sheets, Graphics and other Resources used by the layout engine. 
 bin/res/hiddenWindow.html
 bin/res/ua.css
 bin/res/html.css
 bin/res/quirk.css
 bin/res/forms.css
 bin/res/platform-forms.css
 bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
 bin/res/table-add-column-after-active.gif
 bin/res/table-add-column-after-hover.gif
 bin/res/table-add-column-after.gif
 bin/res/table-add-column-before-active.gif
 bin/res/table-add-column-before-hover.gif
 bin/res/table-add-column-before.gif
 bin/res/table-add-row-after-active.gif
 bin/res/table-add-row-after-hover.gif
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -268,16 +268,18 @@ bin\defaults\autoconfig\prefcalls.js
 ; [Layout Engine Resources]
 ; Style Sheets, Graphics and other Resources used by the layout engine. 
 bin\res\hiddenWindow.html
 bin\res\ua.css
 bin\res\html.css
 bin\res\quirk.css
 bin\res\forms.css
 bin\res\EditorOverride.css
+bin\res\contenteditable.css
+bin\res\designmode.css
 bin\res\table-add-column-after-active.gif
 bin\res\table-add-column-after-hover.gif
 bin\res\table-add-column-after.gif
 bin\res\table-add-column-before-active.gif
 bin\res\table-add-column-before-hover.gif
 bin\res\table-add-column-before.gif
 bin\res\table-add-row-after-active.gif
 bin\res\table-add-row-after-hover.gif
--- a/content/base/public/nsIContent.h
+++ b/content/base/public/nsIContent.h
@@ -58,19 +58,18 @@ class nsICSSStyleRule;
 class nsRuleWalker;
 class nsAttrValue;
 class nsAttrName;
 class nsTextFragment;
 class nsIDocShell;
 
 // IID for the nsIContent interface
 #define NS_ICONTENT_IID       \
-{ 0xb6408b0, 0x20c6, 0x4d60, \
-  { 0xb7, 0x2f, 0x90, 0xb7, 0x7a, 0x9d, 0xb9, 0xb6 } }
-
+{ 0x36b375cb, 0xf01e, 0x4c18, \
+  { 0xbf, 0x9e, 0xba, 0xad, 0x77, 0x1d, 0xce, 0x22 } }
 
 // hack to make egcs / gcc 2.95.2 happy
 class nsIContent_base : public nsINode {
 public:
 #ifdef MOZILLA_INTERNAL_API
   // If you're using the external API, the only thing you can know about
   // nsIContent is that it exists with an IID
 
@@ -710,21 +709,18 @@ public:
   /**
    * Method to get the _intrinsic_ content state of this content node.  This is
    * the state that is independent of the node's presentation.  To get the full
    * content state, use nsIEventStateManager.  Also see nsIEventStateManager
    * for the possible bits that could be set here.
    */
   // XXXbz this is PRInt32 because all the ESM content state APIs use
   // PRInt32.  We should really use PRUint32 instead.
-  virtual PRInt32 IntrinsicState() const
-  {
-    return 0;
-  }
-    
+  virtual PRInt32 IntrinsicState() const;
+
   /* The default script type (language) ID for this content.
      All content must support fetching the default script language.
    */
   virtual PRUint32 GetScriptTypeID() const
   { return nsIProgrammingLanguage::JAVASCRIPT; }
 
   /* Not all content supports setting a new default language */
   virtual nsresult SetScriptTypeID(PRUint32 aLang)
@@ -785,16 +781,22 @@ public:
 
   /**
    * Returns an atom holding the name of the "class" attribute on this
    * content node (if applicable).  Returns null if there is no
    * "class" attribute for this type of content node.
    */
   virtual nsIAtom *GetClassAttributeName() const = 0;
 
+  /**
+   * Should be called when the node can become editable or when it can stop
+   * being editable (for example when its contentEditable attribute changes,
+   * when it is moved into an editable parent, ...).
+   */
+  virtual void UpdateEditableState();
 
 #ifdef DEBUG
   /**
    * List the content (and anything it contains) out to the given
    * file stream. Use aIndent as the base indent during formatting.
    */
   virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const = 0;
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -84,34 +84,36 @@ class nsNodeSupportsWeakRefTearoff;
 
 // Forces the XBL code to treat this node as if it were
 // in the document and therefore should get bindings attached.
 #define NODE_FORCE_XBL_BINDINGS      0x00000040U
 
 // Whether a binding manager may have a pointer to this
 #define NODE_MAY_BE_IN_BINDING_MNGR  0x00000080U
 
+#define NODE_IS_EDITABLE             0x00000100U
+
 // Four bits for the script-type ID
-#define NODE_SCRIPT_TYPE_OFFSET                8
+#define NODE_SCRIPT_TYPE_OFFSET                9
 
 // Remaining bits are node type specific.
-#define NODE_TYPE_SPECIFIC_BITS_OFFSET       0x0c
+#define NODE_TYPE_SPECIFIC_BITS_OFFSET       0x0d
 
 // Useful macro for getting a node given an nsIContent and an nsIDocument
 // Returns the first argument cast to nsINode if it is non-null, otherwise
 // returns the second (which may be null)
 #define NODE_FROM(content_, document_)                  \
   ((content_) ? NS_STATIC_CAST(nsINode*, (content_)) :  \
                 NS_STATIC_CAST(nsINode*, (document_)))
 
 
 // IID for the nsINode interface
 #define NS_INODE_IID \
-{ 0x22ab1440, 0xa6ee, 0x4da7, \
-  { 0xbc, 0x3b, 0x94, 0x2e, 0x56, 0x0d, 0xdc, 0xe0 } }
+{ 0xd3e63f80, 0x9e98, 0x47d7, \
+  { 0xac, 0x8d, 0xad, 0x6f, 0x20, 0x6c, 0xe7, 0xc6 } }
 
 // hack to make egcs / gcc 2.95.2 happy
 class nsINode_base : public nsPIDOMEventTarget {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID)
 };
 
 /**
@@ -591,16 +593,26 @@ public:
 
   void UnsetFlags(PtrBits aFlagsToUnset)
   {
     PtrBits* flags = HasSlots() ? &FlagsAsSlots()->mFlags :
                                   &mFlagsOrSlots;
     *flags &= ~aFlagsToUnset;
   }
 
+  void SetEditableFlag(PRBool aEditable)
+  {
+    if (aEditable) {
+      SetFlags(NODE_IS_EDITABLE);
+    }
+    else {
+      UnsetFlags(NODE_IS_EDITABLE);
+    }
+  }
+
 protected:
 
   // Override this function to create a custom slots class.
   virtual nsINode::nsSlots* CreateSlots();
 
   PRBool HasSlots() const
   {
     return !(mFlagsOrSlots & NODE_DOESNT_HAVE_SLOTS);
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -36,30 +36,31 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsISelectionListener.idl"
 #include "nsIEnumerator.idl"
 
 interface nsIDOMRange;
 interface nsISelectionListener;
+interface nsIContent;
 
 %{C++
 class nsFrameSelection;
 class nsIFrame;
 class nsIPresShell;
 struct nsPoint;
 %}
 
 [ptr] native nsFrameSelection(nsFrameSelection);
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native nsIPresShell(nsIPresShell);
 [ref] native nsPointRef(nsPoint);
 
-[scriptable, uuid(3225CA54-D7E1-4FF5-8EE9-091B0BFCDA1F)]
+[scriptable, uuid(b416c692-eeb8-4186-addd-c444e81b68e5)]
 interface nsISelectionPrivate : nsISupports
  {
     const short ENDOFPRECEDINGLINE=0;
     const short STARTOFNEXTLINE=1;
     
     attribute boolean interlinePosition;
 
     /* startBatchChanges
@@ -110,10 +111,12 @@ interface nsISelectionPrivate : nsISuppo
      * Returns cached value for nsTextFrame::GetPointFromOffset.
      */
     [noscript] void getCachedFrameOffset(in nsIFrame aFrame, in PRInt32 inOffset, in nsPointRef aPoint);
 
     /* getFrameSelection
      * Returnes a reference to the frame selection associated with this selection 
      */
     [noscript] nsFrameSelection getFrameSelection();
+
+    [noscript] void setAncestorLimiter(in nsIContent aContent);
 };
 
--- a/content/base/src/nsGenericDOMDataNode.cpp
+++ b/content/base/src/nsGenericDOMDataNode.cpp
@@ -593,16 +593,18 @@ nsGenericDOMDataNode::BindToTree(nsIDocu
     mParentPtrBits |= PARENT_BIT_INDOCUMENT;
     if (mText.IsBidi()) {
       aDocument->SetBidiEnabled(PR_TRUE);
     }
   }
 
   nsNodeUtils::ParentChainChanged(this);
 
+  UpdateEditableState();
+
   NS_POSTCONDITION(aDocument == GetCurrentDoc(), "Bound to wrong document");
   NS_POSTCONDITION(aParent == GetParent(), "Bound to wrong parent");
   NS_POSTCONDITION(aBindingParent == GetBindingParent(),
                    "Bound to wrong binding parent");
 
   return NS_OK;
 }
 
--- a/content/base/src/nsGenericElement.cpp
+++ b/content/base/src/nsGenericElement.cpp
@@ -295,16 +295,38 @@ nsIContent::SetNativeAnonymous(PRBool aA
     SetFlags(NODE_IS_ANONYMOUS);
     SetFlags(NODE_IS_ANONYMOUS_FOR_EVENTS);
   } else {
     UnsetFlags(NODE_IS_ANONYMOUS);
     UnsetFlags(NODE_IS_ANONYMOUS_FOR_EVENTS);
   }
 }
 
+PRInt32
+nsIContent::IntrinsicState() const
+{
+  PRBool editable = HasFlag(NODE_IS_EDITABLE);
+  if (!editable) {
+    nsIDocument *doc = GetCurrentDoc();
+    if (doc) {
+      editable = doc->HasFlag(NODE_IS_EDITABLE);
+    }
+  }
+
+  return editable ? NS_EVENT_STATE_MOZ_READWRITE : NS_EVENT_STATE_MOZ_READONLY;
+}
+
+void
+nsIContent::UpdateEditableState()
+{
+  nsIContent *parent = GetParent();
+
+  SetEditableFlag(parent && parent->HasFlag(NODE_IS_EDITABLE));
+}
+
 //----------------------------------------------------------------------
 
 nsChildContentList::~nsChildContentList()
 {
   MOZ_COUNT_DTOR(nsChildContentList);
 }
 
 NS_IMETHODIMP
@@ -1997,16 +2019,18 @@ nsGenericElement::BindToTree(nsIDocument
         if (binding) {
           rv = BindNodesInInsertPoints(binding, this, aDocument);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       }
     }
   }
 
+  UpdateEditableState();
+
   // Now recurse into our kids
   PRUint32 i;
   // Don't call GetChildCount() here since that'll make XUL generate
   // template children, which we're not in a consistent enough state for.
   // Additionally, there's not really a need to generate the children here.
   for (i = 0; i < mAttrsAndChildren.ChildCount(); ++i) {
     // The child can remove itself from the parent in BindToTree.
     nsCOMPtr<nsIContent> child = mAttrsAndChildren.ChildAt(i);
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -203,16 +203,17 @@ GK_ATOM(combobox, "combobox")
 GK_ATOM(command, "command")
 GK_ATOM(commands, "commands")
 GK_ATOM(commandset, "commandset")
 GK_ATOM(commandupdate, "commandupdate")
 GK_ATOM(commandupdater, "commandupdater")
 GK_ATOM(comment, "comment")
 GK_ATOM(compact, "compact")
 GK_ATOM(concat, "concat")
+GK_ATOM(contenteditable, "contenteditable")
 GK_ATOM(conditions, "conditions")
 GK_ATOM(constructor, "constructor")
 GK_ATOM(container, "container")
 GK_ATOM(containment, "containment")
 GK_ATOM(contains, "contains")
 GK_ATOM(content, "content")
 GK_ATOM(headerContentDisposition, "content-disposition")
 GK_ATOM(headerContentLanguage, "content-language")
@@ -405,16 +406,17 @@ GK_ATOM(implements, "implements")
 GK_ATOM(import, "import")
 GK_ATOM(include, "include")
 GK_ATOM(includes, "includes")
 GK_ATOM(increment, "increment")
 GK_ATOM(indent, "indent")
 GK_ATOM(index, "index")
 GK_ATOM(infer, "infer")
 GK_ATOM(infinity, "infinity")
+GK_ATOM(inherit, "inherit")
 GK_ATOM(inherits, "inherits")
 GK_ATOM(inheritstyle, "inheritstyle")
 GK_ATOM(input, "input")
 GK_ATOM(ins, "ins")
 GK_ATOM(insertafter, "insertafter")
 GK_ATOM(insertbefore, "insertbefore")
 GK_ATOM(instanceOf, "instanceOf")
 GK_ATOM(intersection, "intersection")
--- a/content/base/src/nsTextNode.cpp
+++ b/content/base/src/nsTextNode.cpp
@@ -234,17 +234,19 @@ nsTextNode::CloneDataNode(nsINodeInfo *a
 
 #ifdef DEBUG
 void
 nsTextNode::List(FILE* out, PRInt32 aIndent) const
 {
   PRInt32 index;
   for (index = aIndent; --index >= 0; ) fputs("  ", out);
 
-  fprintf(out, "Text@%p refcount=%d<", this, mRefCnt.get());
+  fprintf(out, "Text@%p", this);
+  fprintf(out, " intrinsicstate=[%08x]", IntrinsicState());
+  fprintf(out, " refcount=%d<", mRefCnt.get());
 
   nsAutoString tmp;
   ToCString(tmp, 0, mText.GetLength());
   fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
 
   fputs(">\n", out);
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -1097,28 +1097,22 @@ nsGenericHTMLElement::GetSpellcheck(PRBo
     }
   }
 
   // Is this a chrome element?
   if (nsContentUtils::IsChromeDoc(GetOwnerDoc())) {
     return NS_OK;                       // Not spellchecked by default
   }
 
-  // Is this the actual body of the current document?
   if (IsCurrentBodyElement()) {
-    // Is designMode on?
-    nsCOMPtr<nsIDOMNSHTMLDocument> nsHTMLDocument =
-      do_QueryInterface(GetCurrentDoc());
-    if (!nsHTMLDocument) {
-      return PR_FALSE;
+    nsCOMPtr<nsIHTMLDocument> doc = do_QueryInterface(GetCurrentDoc());
+    if (doc) {
+      *aSpellcheck = doc->IsEditingOn();
     }
 
-    nsAutoString designMode;
-    nsHTMLDocument->GetDesignMode(designMode);
-    *aSpellcheck = designMode.EqualsLiteral("on");
     return NS_OK;
   }
 
   // Is this element editable?
   nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(this);
   if (!formControl) {
     return NS_OK;                       // Not spellchecked by default
   }
@@ -1159,42 +1153,77 @@ nsGenericHTMLElement::SetSpellcheck(PRBo
 }
 
 PRBool
 nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
 {
   return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
 }
 
+void
+nsGenericHTMLElement::UpdateEditableState()
+{
+  // XXX Should we do this only when in a document?
+  ContentEditableTristate value = GetContentEditableValue();
+  if (value != eInherit) {
+    SetEditableFlag(value);
+
+    return;
+  }
+
+  nsGenericElement::UpdateEditableState();
+}
+
 nsresult
 nsGenericHTMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                  nsIContent* aBindingParent,
                                  PRBool aCompileEventHandlers)
 {
   nsresult rv = nsGenericElement::BindToTree(aDocument, aParent,
                                              aBindingParent,
                                              aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (aDocument && HasFlag(NODE_IS_EDITABLE) &&
+      GetContentEditableValue() == eTrue) {
+    nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(aDocument);
+    if (htmlDocument) {
+      htmlDocument->ChangeContentEditableCount(this, +1);
+    }
+  }
+
   // XXXbz if we already have a style attr parsed, this won't do
   // anything... need to fix that.
   ReparseStyleAttribute();
 
   if (aDocument) {
     // If we're in a document now, let our mapped attrs know what their new
     // sheet is.
     nsHTMLStyleSheet* sheet = aDocument->GetAttributeStyleSheet();
     if (sheet) {
       mAttrsAndChildren.SetMappedAttrStyleSheet(sheet);
     }
   }
 
   return rv;
 }
 
+void
+nsGenericHTMLElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
+{
+  if (GetContentEditableValue() == eTrue) {
+    nsCOMPtr<nsIHTMLDocument> htmlDocument = do_QueryInterface(GetCurrentDoc());
+    if (htmlDocument) {
+      htmlDocument->ChangeContentEditableCount(this, -1);
+    }
+  }
+
+  nsGenericElement::UnbindFromTree(aDeep, aNullParent);
+}
+
 already_AddRefed<nsIDOMHTMLFormElement>
 nsGenericHTMLElement::FindForm(nsIForm* aCurrentForm)
 {
   nsIContent* content = this;
   while (content) {
     // If the current ancestor is a form, return it as our form
     if (content->Tag() == nsGkAtoms::form &&
         content->IsNodeOfType(nsINode::eHTML)) {
@@ -1394,27 +1423,59 @@ nsGenericHTMLElement::GetEventListenerMa
     return rv;
   }
 
   return nsGenericElement::GetEventListenerManagerForAttr(aManager, aTarget,
                                                           aDefer);
 }
 
 nsresult
+nsGenericHTMLElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                              nsIAtom* aPrefix, const nsAString& aValue,
+                              PRBool aNotify)
+{
+  PRBool contentEditable = aNameSpaceID == kNameSpaceID_None &&
+                           aName == nsGkAtoms::contenteditable;
+  PRInt32 change;
+  if (contentEditable) {
+    change = GetContentEditableValue() == eTrue ? -1 : 0;
+  }
+
+  nsresult rv = nsGenericElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
+                                          aNotify);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (contentEditable) {
+    if (aValue.IsEmpty() || aValue.LowerCaseEqualsLiteral("true")) {
+      change += 1;
+    }
+
+    ChangeEditableState(change);
+  }
+
+  return NS_OK;
+}
+
+nsresult
 nsGenericHTMLElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
                                 PRBool aNotify)
 {
   // Check for event handlers
-  if (aNameSpaceID == kNameSpaceID_None &&
-      nsContentUtils::IsEventAttributeName(aAttribute, EventNameType_HTML)) {
-    nsCOMPtr<nsIEventListenerManager> manager;
-    GetListenerManager(PR_FALSE, getter_AddRefs(manager));
-
-    if (manager) {
-      manager->RemoveScriptEventListener(aAttribute);
+  if (aNameSpaceID == kNameSpaceID_None) {
+    if (aAttribute == nsGkAtoms::contenteditable) {
+      ChangeEditableState(GetContentEditableValue() == eTrue ? -1 : 0);
+    }
+    else if (nsContentUtils::IsEventAttributeName(aAttribute,
+                                                  EventNameType_HTML)) {
+      nsCOMPtr<nsIEventListenerManager> manager;
+      GetListenerManager(PR_FALSE, getter_AddRefs(manager));
+
+      if (manager) {
+        manager->RemoveScriptEventListener(aAttribute);
+      }
     }
   }
 
   return nsGenericElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
 }
 
 const nsAttrValue*
 nsGenericHTMLElement::GetClasses() const
@@ -1575,16 +1636,21 @@ nsGenericHTMLElement::ParseAttribute(PRI
     }
 
     if (aAttribute == nsGkAtoms::name && !aValue.IsEmpty()) {
       // Store name as an atom.  name="" means that the element has no name,
       // not that it has an emptystring as the name.
       aResult.ParseAtom(aValue);
       return PR_TRUE;
     }
+
+    if (aAttribute == nsGkAtoms::contenteditable) {
+      aResult.ParseAtom(aValue);
+      return PR_TRUE;
+    }
   }
 
   return nsGenericElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
                                           aResult);
 }
 
 PRBool
 nsGenericHTMLElement::IsAttributeMapped(const nsIAtom* aAttribute) const
@@ -2016,29 +2082,71 @@ nsGenericHTMLElement::ParseStyleAttribut
 
 /**
  * Handle attributes common to all html elements
  */
 void
 nsGenericHTMLElement::MapCommonAttributesInto(const nsMappedAttributes* aAttributes,
                                               nsRuleData* aData)
 {
+  if (aData->mSID == eStyleStruct_UserInterface) {
+    nsRuleDataUserInterface *ui = aData->mUserInterfaceData;
+    if (ui->mUserModify.GetUnit() == eCSSUnit_Null) {
+      const nsAttrValue* value =
+        aAttributes->GetAttr(nsGkAtoms::contenteditable);
+      if (value) {
+        if (value->Equals(nsGkAtoms::_empty, eCaseMatters) ||
+            value->Equals(nsGkAtoms::_true, eIgnoreCase)) {
+          ui->mUserModify.SetIntValue(NS_STYLE_USER_MODIFY_READ_WRITE,
+                                      eCSSUnit_Enumerated);
+        }
+        else {
+          ui->mUserModify.SetIntValue(NS_STYLE_USER_MODIFY_READ_ONLY,
+                                      eCSSUnit_Enumerated);
+        }
+      }
+    }
+  }
   if (aData->mSID == eStyleStruct_Visibility) {
     const nsAttrValue* value = aAttributes->GetAttr(nsGkAtoms::lang);
     if (value && value->Type() == nsAttrValue::eString) {
       aData->mDisplayData->mLang.SetStringValue(value->GetStringValue(),
                                                 eCSSUnit_String);
     }
   }
 }
 
+void
+nsGenericHTMLFormElement::UpdateEditableFormControlState()
+{
+  ContentEditableTristate value = GetContentEditableValue();
+  if (value != eInherit) {
+    SetEditableFlag(value);
+
+    return;
+  }
+
+  nsIContent *parent = GetParent();
+  PRBool editable = parent && parent->HasFlag(NODE_IS_EDITABLE);
+
+  if (!editable) {
+    // If not contentEditable we still need to check the readonly attribute.
+    PRBool roState;
+    GetBoolAttr(nsGkAtoms::readonly, &roState);
+
+    editable = !roState;
+  }
+
+  SetEditableFlag(editable);
+}
 
 
 /* static */ const nsGenericHTMLElement::MappedAttributeEntry
 nsGenericHTMLElement::sCommonAttributeMap[] = {
+  { &nsGkAtoms::contenteditable },
   { &nsGkAtoms::lang },
   { nsnull }
 };
 
 /* static */ const nsGenericElement::MappedAttributeEntry
 nsGenericHTMLElement::sImageMarginSizeAttributeMap[] = {
   { &nsGkAtoms::width },
   { &nsGkAtoms::height },
@@ -2523,16 +2631,57 @@ nsGenericHTMLElement::GetURIListAttr(nsI
       if (iter >= end)
         break;
     }
   }
 
   return NS_OK;
 }
 
+nsresult
+nsGenericHTMLElement::GetContentEditable(nsAString& aContentEditable)
+{
+  ContentEditableTristate value = GetContentEditableValue();
+
+  if (value == eTrue) {
+    aContentEditable.AssignLiteral("true");
+  }
+  else if (value == eFalse) {
+    aContentEditable.AssignLiteral("false");
+  }
+  else {
+    aContentEditable.AssignLiteral("inherit");
+  }
+
+  return NS_OK;
+}
+
+nsresult
+nsGenericHTMLElement::SetContentEditable(const nsAString& aContentEditable)
+{
+  nsString contentEditable;
+  ToLowerCase(aContentEditable, contentEditable);
+
+  if (contentEditable.EqualsLiteral("inherit")) {
+    UnsetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, PR_TRUE);
+
+    return NS_OK;
+  }
+
+  if (!contentEditable.EqualsLiteral("true") &&
+      !contentEditable.EqualsLiteral("false")) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  SetAttr(kNameSpaceID_None, nsGkAtoms::contenteditable, contentEditable,
+          PR_TRUE);
+
+  return NS_OK;
+}
+
 //----------------------------------------------------------------------
 
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsGenericHTMLFrameElement, TabIndex, tabindex, 0)
 
 nsGenericHTMLFormElement::nsGenericHTMLFormElement(nsINodeInfo *aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
 {
   mForm = nsnull;
@@ -3148,20 +3297,29 @@ nsGenericHTMLElement::RemoveFocus(nsPres
 }
 
 PRBool
 nsGenericHTMLElement::IsFocusable(PRInt32 *aTabIndex)
 {
   PRInt32 tabIndex = 0;   // Default value for non HTML elements with -moz-user-focus
   GetTabIndex(&tabIndex);
 
-  // Just check for disabled attribute on all HTML elements
-  PRBool disabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
-  if (disabled) {
-    tabIndex = -1;
+  PRBool disabled;
+  if (IsEditableRoot()) {
+    disabled = PR_FALSE;
+    if (!HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)) {
+      tabIndex = 0;
+    }
+  }
+  else {
+    // Just check for disabled attribute on all HTML elements
+    disabled = HasAttr(kNameSpaceID_None, nsGkAtoms::disabled);
+    if (disabled) {
+      tabIndex = -1;
+    }
   }
 
   if (aTabIndex) {
     *aTabIndex = tabIndex;
   }
 
   // If a tabindex is specified at all, or the default tabindex is 0, we're focusable
   return tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex));
@@ -3763,8 +3921,98 @@ nsGenericHTMLElement::RecompileScriptEve
             continue;
         }
 
         nsAutoString value;
         GetAttr(kNameSpaceID_None, attr, value);
         AddScriptEventListener(attr, value, PR_TRUE);
     }
 }
+
+PRBool
+nsGenericHTMLElement::IsEditableRoot() const
+{
+  nsIDocument *document = GetCurrentDoc();
+  if (!document) {
+    return PR_FALSE;
+  }
+
+  if (document->HasFlag(NODE_IS_EDITABLE)) {
+    return this == document->GetRootContent();
+  }
+
+  if (!HasFlag(NODE_IS_EDITABLE)) {
+    return PR_FALSE;
+  }
+
+  nsIContent *parent = GetParent();
+
+  return !parent || !parent->HasFlag(NODE_IS_EDITABLE);
+}
+
+nsIContent*
+nsGenericHTMLElement::FindEditableRoot()
+{
+  nsIDocument *document = GetCurrentDoc();
+  if (!document) {
+    return nsnull;
+  }
+
+  if (document->HasFlag(NODE_IS_EDITABLE)) {
+    return document->GetRootContent();
+  }
+
+  if (!HasFlag(NODE_IS_EDITABLE)) {
+    return nsnull;
+  }
+
+  nsIContent *parent, *content = this;
+  while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+    content = parent;
+  }
+
+  return content;
+}
+
+static void
+MakeContentDescendantsEditable(nsIContent *aContent, nsIDocument *aDocument)
+{
+  PRInt32 stateBefore = aContent->IntrinsicState();
+
+  aContent->UpdateEditableState();
+
+  if (aDocument && stateBefore != aContent->IntrinsicState()) {
+    aDocument->ContentStatesChanged(aContent, nsnull,
+                                    NS_EVENT_STATE_MOZ_READONLY |
+                                    NS_EVENT_STATE_MOZ_READWRITE);
+  }
+
+  PRUint32 i, n = aContent->GetChildCount();
+  for (i = 0; i < n; ++i) {
+    nsIContent *child = aContent->GetChildAt(i);
+    if (!child->HasAttr(kNameSpaceID_None, nsGkAtoms::contenteditable)) {
+      MakeContentDescendantsEditable(child, aDocument);
+    }
+  }
+}
+
+void
+nsGenericHTMLElement::ChangeEditableState(PRInt32 aChange)
+{
+  nsIDocument* document = GetCurrentDoc();
+  if (!document) {
+    return;
+  }
+
+  if (aChange != 0) {
+    nsCOMPtr<nsIHTMLDocument> htmlDocument =
+      do_QueryInterface(document);
+    if (htmlDocument) {
+      htmlDocument->ChangeContentEditableCount(this, aChange);
+    }
+  }
+
+  if (document->HasFlag(NODE_IS_EDITABLE)) {
+    document = nsnull;
+  }
+
+  MakeContentDescendantsEditable(this, document);
+}
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -39,16 +39,17 @@
 #define nsGenericHTMLElement_h___
 
 #include "nsGenericElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsINameSpaceManager.h"  // for kNameSpaceID_None
 #include "nsIFormControl.h"
 #include "nsIDOMNSHTMLFrameElement.h"
 #include "nsFrameLoader.h"
+#include "nsGkAtoms.h"
 
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIDOMNodeList;
 class nsIFrame;
 class nsMappedAttributes;
 class nsIStyleRule;
 class nsISupportsArray;
@@ -160,16 +161,18 @@ public:
   // SetSpellcheck() such that classes that inherit interfaces with those 
   // methods properly override them
   NS_IMETHOD Focus();
   NS_IMETHOD Blur();
   NS_IMETHOD GetTabIndex(PRInt32 *aTabIndex);
   NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
   NS_IMETHOD GetSpellcheck(PRBool* aSpellcheck);
   NS_IMETHOD SetSpellcheck(PRBool aSpellcheck);
+  nsresult GetContentEditable(nsAString &aContentEditable);
+  nsresult SetContentEditable(const nsAString &aContentEditable);
 
   /**
    * Get the frame's offset information for offsetTop/Left/Width/Height.
    * @note This method flushes pending notifications (Flush_Layout).
    * @param aRect the offset information [OUT]
    * @param aOffsetParent the parent the offset is relative to (offsetParent)
    *        [OUT]
    */
@@ -191,16 +194,26 @@ public:
    * @return the frame's client dimensions
    */
   nsRect GetClientAreaRect();
 
   // Implementation for nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
+  virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
+                              PRBool aNullParent = PR_TRUE);
+  nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                   const nsAString& aValue, PRBool aNotify)
+  {
+    return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
+  }
+  virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                           nsIAtom* aPrefix, const nsAString& aValue,
+                           PRBool aNotify);
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                              PRBool aNotify);
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
   virtual void RemoveFocus(nsPresContext *aPresContext);
   virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
   virtual void PerformAccesskey(PRBool aKeyCausesActivation,
                                 PRBool aIsTrustedEvent);
 
@@ -220,16 +233,18 @@ public:
   // HTML element methods
   void Compact() { mAttrsAndChildren.Compact(); }
   const nsAttrValue* GetParsedAttr(nsIAtom* aAttr) const
   {
     return mAttrsAndChildren.GetAttr(aAttr);
   }
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
+  virtual void UpdateEditableState();
+
   virtual const nsAttrValue* GetClasses() const;
   virtual nsIAtom *GetIDAttributeName() const;
   virtual nsIAtom *GetClassAttributeName() const;
   NS_IMETHOD WalkContentStyleRules(nsRuleWalker* aRuleWalker);
   virtual nsICSSStyleRule* GetInlineStyleRule();
   NS_IMETHOD SetInlineStyleRule(nsICSSStyleRule* aStyleRule, PRBool aNotify);
   already_AddRefed<nsIURI> GetBaseURI() const;
 
@@ -763,16 +778,60 @@ protected:
    */
   PRBool IsCurrentBodyElement();
 
   /**
    * Ensures all editors associated with a subtree are synced, for purposes of
    * spellchecking.
    */
   static void SyncEditorsOnSubtree(nsIContent* content);
+
+  enum ContentEditableTristate {
+    eInherit = -1,
+    eFalse = 0,
+    eTrue = 1
+  };
+
+  /**
+   * Returns eTrue if the element has a contentEditable attribute and its value
+   * is "true" or an empty string. Returns eFalse if the element has a
+   * contentEditable attribute and its value is "false". Otherwise returns
+   * eInherit.
+   */
+  NS_HIDDEN_(ContentEditableTristate) GetContentEditableValue() const
+  {
+    static const nsIContent::AttrValuesArray values[] =
+      { &nsGkAtoms::_false, &nsGkAtoms::_true, &nsGkAtoms::_empty, nsnull };
+
+    PRInt32 value = FindAttrValueIn(kNameSpaceID_None,
+                                    nsGkAtoms::contenteditable, values,
+                                    eIgnoreCase);
+
+    return value > 0 ? eTrue : (value == 0 ? eFalse : eInherit);
+  }
+
+private:
+  /**
+   * Returns whether this element is an editable root. An editable root is
+   * defined as an element that is editable and whose parent is either a
+   * non-editable element or an editable document (so if the whole document is
+   * editable, then there is only one editable root, namely the
+   * documentElement).
+   */
+  PRBool IsEditableRoot() const;
+
+  /**
+   * Returns the first node amongst this node and its ancestors that is an
+   * editable root.
+   *
+   * @see IsEditableRoot for a definition of an editable root.
+   */
+  nsIContent* FindEditableRoot();
+
+  void ChangeEditableState(PRInt32 aChange);
 };
 
 
 //----------------------------------------------------------------------
 
 /**
  * A helper class for form elements that can contain children
  */
@@ -812,16 +871,17 @@ public:
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                              PRBool aNotify);
   virtual PRUint32 GetDesiredIMEState();
+  virtual PRInt32 IntrinsicState() const;
 
 protected:
   /**
    * Find the form for this element and set aFormControl's form to it
    * (aFormControl is passed in to avoid QI)
    *
    * @param aFormControl the form control to set the form for
    */
@@ -833,17 +893,17 @@ protected:
   virtual nsresult AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
                                 const nsAString* aValue, PRBool aNotify);
 
   /**
    * Returns true if the control can be disabled
    */
   PRBool CanBeDisabled() const;
 
-  virtual PRInt32 IntrinsicState() const;
+  void UpdateEditableFormControlState();
 
   void SetFocusAndScrollIntoView(nsPresContext* aPresContext);
 
   /** The form that contains this control */
   nsIForm* mForm;
 };
 
 //----------------------------------------------------------------------
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -233,16 +233,21 @@ public:
   /**
    * Get the radio group container for this button (form or document)
    * @return the radio group container (or null if no form or document)
    */
   virtual already_AddRefed<nsIRadioGroupContainer> GetRadioGroupContainer();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
 
+  virtual void UpdateEditableState()
+  {
+    return UpdateEditableFormControlState();
+  }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLInputElement,
                                                      nsGenericHTMLFormElement)
 
 protected:
   // Helper method
   nsresult SetValueInternal(const nsAString& aValue,
                             nsITextControlFrame* aFrame);
 
@@ -596,16 +601,31 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
                                        NS_EVENT_STATE_CHECKED |
                                        NS_EVENT_STATE_DEFAULT |
                                        NS_EVENT_STATE_BROKEN |
                                        NS_EVENT_STATE_USERDISABLED |
                                        NS_EVENT_STATE_SUPPRESSED |
                                        NS_EVENT_STATE_LOADING);
       }
     }
+
+    // If readonly is changed for text and password we need to handle
+    // :read-only / :read-write
+    if (aNotify && aName == nsGkAtoms::readonly &&
+        (mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_PASSWORD)) {
+      UpdateEditableState();
+
+      nsIDocument* document = GetCurrentDoc();
+      if (document) {
+        mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+        document->ContentStatesChanged(this, nsnull,
+                                       NS_EVENT_STATE_MOZ_READONLY |
+                                       NS_EVENT_STATE_MOZ_READWRITE);
+      }
+    }
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
 
 // nsIDOMHTMLInputElement
 
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -160,16 +160,21 @@ public:
                                nsIContent* aContainer,
                                nsIContent* aChild,
                                PRInt32 aIndexInContainer);
   virtual void ContentRemoved(nsIDocument* aDocument,
                               nsIContent* aContainer,
                               nsIContent* aChild,
                               PRInt32 aIndexInContainer);
 
+  virtual void UpdateEditableState()
+  {
+    return UpdateEditableFormControlState();
+  }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLTextAreaElement,
                                            nsGenericHTMLFormElement)
 
 protected:
   nsCOMPtr<nsIControllers> mControllers;
   /** The current value.  This is null if the frame owns the value. */
   char*                    mValue;
   /** Whether or not the value has changed since its default value was given. */
@@ -197,16 +202,19 @@ protected:
   nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
 
   /**
    * Common method to call from the various mutation observer methods.
    * aContent is a content node that's either the one that changed or its
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
+
+  virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom *aName,
+                                const nsAString* aValue, PRBool aNotify);
 };
 
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(TextArea)
 
 
 nsHTMLTextAreaElement::nsHTMLTextAreaElement(nsINodeInfo *aNodeInfo,
                                              PRBool aFromParser)
@@ -985,8 +993,28 @@ nsHTMLTextAreaElement::ContentRemoved(ns
 void
 nsHTMLTextAreaElement::ContentChanged(nsIContent* aContent)
 {
   if (!mValueChanged && mDoneAddingChildren &&
       nsContentUtils::IsInSameAnonymousTree(this, aContent)) {
     Reset();
   }
 }
+
+nsresult
+nsHTMLTextAreaElement::AfterSetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
+                                    const nsAString* aValue, PRBool aNotify)
+{
+  if (aNotify && aNameSpaceID == kNameSpaceID_None &&
+      aName == nsGkAtoms::readonly) {
+    UpdateEditableState();
+
+    nsIDocument* document = GetCurrentDoc();
+    if (document) {
+      mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+      document->ContentStatesChanged(this, nsnull,
+                                     NS_EVENT_STATE_MOZ_READONLY |
+                                     NS_EVENT_STATE_MOZ_READWRITE);
+    }
+  }
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
+                                            aNotify);
+}
--- a/content/html/document/src/Makefile.in
+++ b/content/html/document/src/Makefile.in
@@ -71,16 +71,17 @@ REQUIRES	= xpcom \
 		  lwbrk \
 		  imglib2 \
 		  xpconnect \
 		  unicharutil \
 		  commandhandler \
 		  composer \
 		  editor \
 		  plugin \
+		  txtsvc \
 		  $(NULL)
 
 CPPSRCS		= \
 		nsHTMLContentSink.cpp \
 		nsHTMLFragmentContentSink.cpp \
 		nsHTMLDocument.cpp \
 		nsMediaDocument.cpp \
 		nsPluginDocument.cpp \
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -120,24 +120,30 @@
 #include "nsIJSContextStack.h"
 #include "nsIDocumentViewer.h"
 #include "nsIWyciwygChannel.h"
 #include "nsIScriptElement.h"
 #include "nsIScriptError.h"
 #include "nsIMutableArray.h"
 #include "nsArrayUtils.h"
 #include "nsIEffectiveTLDService.h"
+#include "nsIEventStateManager.h"
 
 #include "nsIPrompt.h"
 //AHMED 12-2
 #include "nsBidiUtils.h"
 
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsNodeInfoManager.h"
+#include "nsIEditor.h"
+#include "nsIEditorDocShell.h"
+#include "nsIEditorStyleSheets.h"
+#include "nsIInlineSpellChecker.h"
+#include "nsRange.h"
 
 #define NS_MAX_DOCUMENT_WRITE_DEPTH 20
 
 #define DETECTOR_CONTRACTID_MAX 127
 static char g_detector_contractid[DETECTOR_CONTRACTID_MAX + 1];
 static PRBool gInitDetector = PR_FALSE;
 static PRBool gPlugDetector = PR_FALSE;
 
@@ -1199,18 +1205,23 @@ nsHTMLDocument::EndLoad()
   }
 
   // Reset this now, since we're really done "loading" this document.written
   // document.
   NS_ASSERTION(mWriteState == eNotWriting || mWriteState == ePendingClose ||
                mWriteState == eDocumentClosed, "EndLoad called early");
   mWriteState = eNotWriting;
 
+  PRBool turnOnEditing =
+    mParser && (HasFlag(NODE_IS_EDITABLE) || mContentEditableCount > 0);
   // Note: nsDocument::EndLoad nulls out mParser.
   nsDocument::EndLoad();
+  if (turnOnEditing) {
+    EditingStateChanged();
+  }
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::SetTitle(const nsAString& aTitle)
 {
   return nsDocument::SetTitle(aTitle);
 }
 
@@ -2202,24 +2213,24 @@ nsHTMLDocument::OpenCommon(const nsACStr
     // for us and that'll create frames for the root element and the
     // scrollbars work as expected (since the document in the root
     // element was never set to null)
 
     mChildren.AppendChild(root);
     mRootContent = root;
   }
 
-  if (mEditingIsOn) {
+  if (IsEditingOn()) {
     // Reset() blows away all event listeners in the document, and our
     // editor relies heavily on those. Midas is turned on, to make it
     // work, re-initialize it to give it a chance to add its event
     // listeners again.
 
-    SetDesignMode(NS_LITERAL_STRING("off"));
-    SetDesignMode(NS_LITERAL_STRING("on"));
+    TurnEditingOff();
+    EditingStateChanged();
   }
 
   // Zap the old title -- otherwise it would hang around until document.close()
   // (which might never come) if the new document doesn't explicitly set one.
   // Void the title to make sure that we actually respect any titles set by the
   // new document.
   SetTitle(EmptyString());
   mDocumentTitle.SetIsVoid(PR_TRUE);
@@ -3712,97 +3723,311 @@ nsHTMLDocument::GenerateParserKey(void)
   // which is guaranteed to be unique per script.
   return mScriptLoader->GetCurrentScript();
 }
 
 /* attribute DOMString designMode; */
 NS_IMETHODIMP
 nsHTMLDocument::GetDesignMode(nsAString & aDesignMode)
 {
-  if (mEditingIsOn) {
+  if (HasFlag(NODE_IS_EDITABLE)) {
     aDesignMode.AssignLiteral("on");
   }
   else {
     aDesignMode.AssignLiteral("off");
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
+nsresult
+nsHTMLDocument::ChangeContentEditableCount(nsIContent *aElement,
+                                           PRInt32 aChange)
+{
+  NS_ASSERTION(mContentEditableCount + aChange >= 0,
+               "Trying to decrement too much.");
+
+  mContentEditableCount += aChange;
+
+  if (mParser) {
+    return NS_OK;
+  }
+
+  EditingState oldState = mEditingState;
+
+  nsresult rv = EditingStateChanged();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (oldState == mEditingState && mEditingState == eContentEditable) {
+    // We just changed the contentEditable state of a node, we need to reset
+    // the spellchecking state of that node.
+    nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
+    if (node) {
+      nsPIDOMWindow *window = GetWindow();
+      if (!window)
+        return NS_ERROR_FAILURE;
+
+      nsIDocShell *docshell = window->GetDocShell();
+      if (!docshell)
+        return NS_ERROR_FAILURE;
+
+      nsCOMPtr<nsIEditorDocShell> editorDocShell =
+        do_QueryInterface(docshell, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIEditor> editor;
+      rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIDOMRange> range;
+      rv = NS_NewRange(getter_AddRefs(range));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = range->SelectNode(node);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIInlineSpellChecker> spellChecker;
+      rv = editor->GetInlineSpellChecker(PR_FALSE,
+                                         getter_AddRefs(spellChecker));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      if (spellChecker) {
+        rv = spellChecker->SpellCheckRange(range);
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+static void
+NotifyEditableStateChange(nsINode *aNode, nsIDocument *aDocument,
+                          PRBool aEditable)
 {
+  PRUint32 i, n = aNode->GetChildCount();
+  for (i = 0; i < n; ++i) {
+    nsIContent *child = aNode->GetChildAt(i);
+    if (child->HasFlag(NODE_IS_EDITABLE) != aEditable) {
+      aDocument->ContentStatesChanged(child, nsnull,
+                                      NS_EVENT_STATE_MOZ_READONLY |
+                                      NS_EVENT_STATE_MOZ_READWRITE);
+    }
+    NotifyEditableStateChange(child, aDocument, aEditable);
+  }
+}
+
+nsresult
+nsHTMLDocument::TurnEditingOff()
+{
+  NS_ASSERTION(mEditingState != eOff, "Editing is already off.");
+
+  nsPIDOMWindow *window = GetWindow();
+  if (!window)
+    return NS_ERROR_FAILURE;
+
+  nsIDocShell *docshell = window->GetDocShell();
+  if (!docshell)
+    return NS_ERROR_FAILURE;
+
+  nsresult rv;
+  nsCOMPtr<nsIEditorDocShell> editorDocShell =
+    do_QueryInterface(docshell, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIEditor> editor;
+  rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // turn editing off
+  rv = editSession->TearDownEditorOnWindow(window, PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(editor, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!HasFlag(NODE_IS_EDITABLE)) {
+    editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/contenteditable.css"));
+    editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+    rv = docshell->SetAllowJavascript(mScriptsEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docshell->SetAllowPlugins(mPluginsEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  mEditingState = eOff;
+
+  return NS_OK;
+}
+
+nsresult
+nsHTMLDocument::EditingStateChanged()
+{
+  PRBool designMode = HasFlag(NODE_IS_EDITABLE);
+  EditingState newState = designMode ? eDesignMode :
+                          (mContentEditableCount > 0 ? eContentEditable : eOff);
+  if (mEditingState == newState) {
+    // No changes in editing mode.
+    return NS_OK;
+  }
+
+  if (newState == eOff) {
+    // Editing is being turned off.
+    return TurnEditingOff();
+  }
+
   // get editing session
   nsPIDOMWindow *window = GetWindow();
   if (!window)
     return NS_ERROR_FAILURE;
 
   nsIDocShell *docshell = window->GetDocShell();
   if (!docshell)
     return NS_ERROR_FAILURE;
 
+  nsresult rv;
+  nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRBool makeWindowEditable = (mEditingState == eOff);
+  if (makeWindowEditable) {
+    // Editing is being turned on (through designMode or contentEditable)
+    // Turn on editor.
+    rv = editSession->MakeWindowEditable(window, "html", PR_FALSE, PR_FALSE,
+                                         PR_TRUE);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // XXX Need to call TearDownEditorOnWindow for all failures.
+  nsCOMPtr<nsIEditorDocShell> editorDocShell =
+    do_QueryInterface(docshell, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIEditor> editor;
+  rv = editorDocShell->GetEditor(getter_AddRefs(editor));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIEditorStyleSheets> editorss = do_QueryInterface(editor, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/contenteditable.css"));
+
+  // Should we update the editable state of all the nodes in the document? We
+  // need to do this when the designMode value changes, as that overrides
+  // specific states on the elements.
+  PRBool updateState;
+
+  PRBool spellRecheckAll = PR_FALSE;
+  if (designMode) {
+    // designMode is being turned on (overrides contentEditable).
+    editorss->AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+    // Store scripting and plugins state.
+    PRBool tmp;
+    rv = docshell->GetAllowJavascript(&tmp);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mScriptsEnabled = tmp;
+
+    rv = docshell->GetAllowPlugins(&tmp);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mPluginsEnabled = tmp;
+
+    updateState = PR_TRUE;
+    spellRecheckAll = mEditingState == eContentEditable;
+  }
+  else if (mEditingState == eDesignMode) {
+    // designMode is being turned off (contentEditable is still on).
+    editorss->RemoveOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/designmode.css"));
+
+    rv = docshell->SetAllowJavascript(mScriptsEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docshell->SetAllowPlugins(mPluginsEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    updateState = PR_TRUE;
+  }
+  else {
+    // contentEditable is being turned on (and designMode is off).
+    updateState = PR_FALSE;
+  }
+
+  mEditingState = newState;
+
+  if (makeWindowEditable) {
+    // Set the editor to not insert br's on return when in p
+    // elements by default.
+    // XXX Do we only want to do this for designMode?
+    PRBool unused;
+    rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
+                     NS_LITERAL_STRING("false"), &unused);
+
+    if (NS_FAILED(rv)) {
+      // Editor setup failed. Editing is not on after all.
+      // XXX Should we reset the editable flag on nodes?
+      editSession->TearDownEditorOnWindow(window, PR_TRUE);
+      mEditingState = eOff;
+
+      return rv;
+    }
+  }
+
+  if (updateState) {
+    mozAutoDocUpdate(this, UPDATE_CONTENT_STATE, PR_TRUE);
+    NotifyEditableStateChange(this, this, !designMode);
+  }
+
+  // Resync the editor's spellcheck state.
+  if (spellRecheckAll) {
+    nsCOMPtr<nsISelectionController> selcon;
+    nsresult rv = editor->GetSelectionController(getter_AddRefs(selcon));
+    NS_ENSURE_SUCCESS(rv, rv); 
+
+    nsCOMPtr<nsISelection> spellCheckSelection;
+    rv = selcon->GetSelection(nsISelectionController::SELECTION_SPELLCHECK,
+                              getter_AddRefs(spellCheckSelection));
+    if (NS_SUCCEEDED(rv)) {
+      spellCheckSelection->RemoveAllRanges();
+    }
+  }
+  editor->SyncRealTimeSpell();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHTMLDocument::SetDesignMode(const nsAString & aDesignMode)
+{
   nsresult rv = NS_OK;
 
   if (!nsContentUtils::IsCallerTrustedForWrite()) {
     nsCOMPtr<nsIPrincipal> subject;
     nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
     rv = secMan->GetSubjectPrincipal(getter_AddRefs(subject));
     NS_ENSURE_SUCCESS(rv, rv);
     if (subject) {
       rv = secMan->CheckSameOriginPrincipal(subject, NodePrincipal());
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
-  nsCOMPtr<nsIEditingSession> editSession = do_GetInterface(docshell);
-  if (!editSession)
-    return NS_ERROR_FAILURE;
-
-  if (aDesignMode.LowerCaseEqualsLiteral("on") && !mEditingIsOn) {
-    rv = editSession->MakeWindowEditable(window, "html", PR_FALSE);
-
-    if (NS_SUCCEEDED(rv)) {
-      // now that we've successfully created the editor, we can
-      // reset our flag
-      mEditingIsOn = PR_TRUE;
-
-      // Set the editor to not insert br's on return when in p
-      // elements by default.
-      PRBool unused;
-      rv = ExecCommand(NS_LITERAL_STRING("insertBrOnReturn"), PR_FALSE,
-                       NS_LITERAL_STRING("false"), &unused);
-
-      if (NS_FAILED(rv)) {
-        // Editor setup failed. Editing is is not on after all.
-
-        editSession->TearDownEditorOnWindow(window);
-
-        mEditingIsOn = PR_FALSE;
-      } else {
-        // Resync the editor's spellcheck state, since when the editor was
-        // created it asked us whether designMode was on, and we told it no.
-        // Note that reporting "yes" (by setting mEditingIsOn true before
-        // calling MakeWindowEditable()) exposed several crash bugs (see bugs
-        // 348497, 348981).
-        nsCOMPtr<nsIEditor> editor;
-        rv = editSession->GetEditorForWindow(window, getter_AddRefs(editor));
-        if (NS_SUCCEEDED(rv)) {
-          editor->SyncRealTimeSpell();
-        }
-      }
-    }
-  } else if (aDesignMode.LowerCaseEqualsLiteral("off") && mEditingIsOn) {
-    // turn editing off
-    rv = editSession->TearDownEditorOnWindow(window);
-
-    if (NS_SUCCEEDED(rv)) {
-      mEditingIsOn = PR_FALSE;
-    }
+  PRBool editableMode = HasFlag(NODE_IS_EDITABLE);
+  if (aDesignMode.LowerCaseEqualsASCII(editableMode ? "off" : "on")) {
+    SetEditableFlag(!editableMode);
+
+    return EditingStateChanged();
   }
 
-  return rv;
+  return NS_OK;
 }
 
 nsresult
 nsHTMLDocument::GetMidasCommandManager(nsICommandManager** aCmdMgr)
 {
   // initialize return value
   NS_ENSURE_ARG_POINTER(aCmdMgr);
 
@@ -3977,29 +4202,31 @@ nsHTMLDocument::ConvertToMidasInternalCo
           outBooleanValue = !inParam.LowerCaseEqualsLiteral("false");
         }
         outParam.Truncate();
       }
       else {
         NS_ConvertUTF16toUTF8 convertedParam(inParam);
 
         // check to see if we need to convert the parameter
-        PRUint32 j;
-        for (j = 0; j < MidasParamCount; ++j) {
-          if (convertedParam.Equals(gMidasParamTable[j].incomingParamString,
-                                    nsCaseInsensitiveCStringComparator())) {
-            outParam.Assign(gMidasParamTable[j].internalParamString);
-            break;
+        if (outCommandID.EqualsLiteral("cmd_paragraphState")) {
+          PRUint32 j;
+          for (j = 0; j < MidasParamCount; ++j) {
+            if (convertedParam.Equals(gMidasParamTable[j].incomingParamString,
+                                      nsCaseInsensitiveCStringComparator())) {
+              outParam.Assign(gMidasParamTable[j].internalParamString);
+              break;
+            }
           }
+
+          return j != MidasParamCount;
         }
-
-        // if we didn't convert the parameter, just
-        // pass through the parameter that was passed to us
-        if (j == MidasParamCount)
+        else {
           outParam.Assign(convertedParam);
+        }
       }
     }
   } // end else for useNewParam (do convert existing param)
   else {
     // reset results if the command is not found in our table
     outCommandID.SetLength(0);
     outParam.SetLength(0);
     outIsBoolean = PR_FALSE;
@@ -4064,22 +4291,22 @@ nsHTMLDocument::ExecCommand(const nsAStr
   NS_ENSURE_ARG_POINTER(_retval);
 
   //  for optional parameters see dom/src/base/nsHistory.cpp: HistoryImpl::Go()
   //  this might add some ugly JS dependencies?
 
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   // if they are requesting UI from us, let's fail since we have no UI
   if (doShowUI)
-    return NS_ERROR_NOT_IMPLEMENTED;
+    return NS_OK;
 
   nsresult rv = NS_OK;
 
   if (commandID.LowerCaseEqualsLiteral("gethtml"))
     return NS_ERROR_FAILURE;
 
   if (commandID.LowerCaseEqualsLiteral("cut") ||
       (commandID.LowerCaseEqualsLiteral("copy"))) {
@@ -4100,17 +4327,17 @@ nsHTMLDocument::ExecCommand(const nsAStr
   nsIDOMWindow *window = GetWindow();
   if (!window)
     return NS_ERROR_FAILURE;
 
   nsCAutoString cmdToDispatch, paramStr;
   PRBool isBool, boolVal;
   if (!ConvertToMidasInternalCommand(commandID, value,
                                      cmdToDispatch, paramStr, isBool, boolVal))
-    return NS_ERROR_NOT_IMPLEMENTED;
+    return NS_OK;
 
   if (!isBool && paramStr.IsEmpty()) {
     rv = cmdMgr->DoCommand(cmdToDispatch.get(), nsnull, window);
   } else {
     // we have a command that requires a parameter, create params
     nsCOMPtr<nsICommandParams> cmdParams = do_CreateInstance(
                                             NS_COMMAND_PARAMS_CONTRACTID, &rv);
     if (!cmdParams)
@@ -4139,32 +4366,32 @@ nsHTMLDocument::ExecCommand(const nsAStr
 NS_IMETHODIMP
 nsHTMLDocument::ExecCommandShowHelp(const nsAString & commandID,
                                     PRBool *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* boolean queryCommandEnabled(in DOMString commandID); */
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandEnabled(const nsAString & commandID,
                                     PRBool *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   // get command manager and dispatch command to our window if it's acceptable
   nsCOMPtr<nsICommandManager> cmdMgr;
   GetMidasCommandManager(getter_AddRefs(cmdMgr));
   if (!cmdMgr)
     return NS_ERROR_FAILURE;
 
@@ -4185,17 +4412,17 @@ nsHTMLDocument::QueryCommandEnabled(cons
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandIndeterm(const nsAString & commandID,
                                      PRBool *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   // get command manager and dispatch command to our window if it's acceptable
   nsCOMPtr<nsICommandManager> cmdMgr;
   GetMidasCommandManager(getter_AddRefs(cmdMgr));
   if (!cmdMgr)
     return NS_ERROR_FAILURE;
 
@@ -4227,17 +4454,17 @@ nsHTMLDocument::QueryCommandIndeterm(con
 /* boolean queryCommandState(in DOMString commandID); */
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandState(const nsAString & commandID, PRBool *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   // get command manager and dispatch command to our window if it's acceptable
   nsCOMPtr<nsICommandManager> cmdMgr;
   GetMidasCommandManager(getter_AddRefs(cmdMgr));
   if (!cmdMgr)
     return NS_ERROR_FAILURE;
 
@@ -4289,45 +4516,45 @@ nsHTMLDocument::QueryCommandState(const 
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandSupported(const nsAString & commandID,
                                       PRBool *_retval)
 {
   NS_ENSURE_ARG_POINTER(_retval);
   *_retval = PR_FALSE;
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* DOMString queryCommandText(in DOMString commandID); */
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandText(const nsAString & commandID,
                                  nsAString & _retval)
 {
   _retval.SetLength(0);
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* DOMString queryCommandValue(in DOMString commandID); */
 NS_IMETHODIMP
 nsHTMLDocument::QueryCommandValue(const nsAString & commandID,
                                   nsAString &_retval)
 {
   _retval.SetLength(0);
 
   // if editing is not on, bail
-  if (!mEditingIsOn)
+  if (!IsEditingOn())
     return NS_ERROR_FAILURE;
 
   // get command manager and dispatch command to our window if it's acceptable
   nsCOMPtr<nsICommandManager> cmdMgr;
   GetMidasCommandManager(getter_AddRefs(cmdMgr));
   if (!cmdMgr)
     return NS_ERROR_FAILURE;
 
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -54,16 +54,18 @@
 
 // Document.Write() related
 #include "nsIWyciwygChannel.h"
 #include "nsILoadGroup.h"
 #include "nsNetUtil.h"
 
 #include "nsICommandManager.h"
 
+class nsIEditor;
+class nsIEditorDocShell;
 class nsIParser;
 class nsIURI;
 class nsIMarkupDocumentViewer;
 class nsIDocumentCharsetInfo;
 class nsICacheEntryDescriptor;
 
 class nsHTMLDocument : public nsDocument,
                        public nsIHTMLDocument,
@@ -202,16 +204,23 @@ public:
 
 #ifdef DEBUG
   virtual nsresult CreateElem(nsIAtom *aName, nsIAtom *aPrefix,
                               PRInt32 aNamespaceID,
                               PRBool aDocumentDefaultType,
                               nsIContent** aResult);
 #endif
 
+  nsresult ChangeContentEditableCount(nsIContent *aElement, PRInt32 aChange);
+
+  virtual PRBool IsEditingOn()
+  {
+    return mEditingState != eOff;
+  }
+
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLDocument, nsDocument)
 
 protected:
   nsresult GetBodySize(PRInt32* aWidth,
                        PRInt32* aHeight);
 
   nsresult RegisterNamedItems(nsIContent *aContent);
   nsresult UnregisterNamedItems(nsIContent *aContent);
@@ -360,17 +369,29 @@ protected:
   nsresult   GetMidasCommandManager(nsICommandManager** aCommandManager);
   PRBool     ConvertToMidasInternalCommand(const nsAString & inCommandID,
                                            const nsAString & inParam,
                                            nsACString& outCommandID,
                                            nsACString& outParam,
                                            PRBool& isBoolean,
                                            PRBool& boolValue);
   nsCOMPtr<nsICommandManager> mMidasCommandManager;
-  PRBool                      mEditingIsOn;
+
+  nsresult TurnEditingOff();
+  nsresult EditingStateChanged();
+
+  PRUint32 mContentEditableCount;
+  enum EditingState {
+    eOff = 0,
+    eDesignMode,
+    eContentEditable
+  };
+  EditingState mEditingState;
+  PRPackedBool mScriptsEnabled;
+  PRPackedBool mPluginsEnabled;
 
   nsresult   DoClipboardSecurityCheck(PRBool aPaste);
   static jsval       sCutCopyInternal_id;
   static jsval       sPasteInternal_id;
 
   // kNameSpaceID_None for good ol' HTML documents, and
   // kNameSpaceID_XHTML for spiffy new XHTML documents.
   // XXXbz should this be reset if someone manually calls
--- a/content/html/document/src/nsIHTMLDocument.h
+++ b/content/html/document/src/nsIHTMLDocument.h
@@ -50,18 +50,18 @@ class nsIDOMHTMLMapElement;
 class nsHTMLStyleSheet;
 class nsIStyleSheet;
 class nsICSSLoader;
 class nsIContent;
 class nsIDOMHTMLBodyElement;
 class nsIScriptElement;
 
 #define NS_IHTMLDOCUMENT_IID \
-{ 0xcfe72003, 0xcc90, 0x4624, \
- { 0xb4, 0x1b, 0xc3, 0x14, 0x1d, 0x31, 0x7a, 0x71 } }
+{ 0xf6aa3582, 0x67c3, 0x4f42, \
+  { 0xb6, 0xee, 0x89, 0x19, 0x24, 0x5c, 0x15, 0x89 } }
 
 
 /**
  * HTML document extensions to nsIDocument.
  */
 class nsIHTMLDocument : public nsISupports
 {
 public:
@@ -122,13 +122,30 @@ public:
    */
   virtual nsContentList* GetForms() = 0;
 
   /**
    * Get the list of form controls in the document (all elements in
    * the document that are of type nsIContent::eHTML_FORM_CONTROL).
    */
   virtual nsContentList* GetFormControls() = 0;
+
+  /**
+   * Should be called when an element's editable changes as a result of
+   * changing its contentEditable attribute/property.
+   *
+   * @param aElement the element for which the contentEditable
+   *                 attribute/property was changed
+   * @param aChange +1 if the contentEditable attribute/property was changed to
+   *                true, -1 if it was changed to false
+   */
+  virtual nsresult ChangeContentEditableCount(nsIContent *aElement,
+                                              PRInt32 aChange) = 0;
+
+  /**
+   * Returns whether the document is editable.
+   */
+  virtual PRBool IsEditingOn() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIHTMLDocument, NS_IHTMLDOCUMENT_IID)
 
 #endif /* nsIHTMLDocument_h___ */
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1035,16 +1035,25 @@ nsXULElement::AfterSetAttr(PRInt32 aName
         }
 
         // Hide chrome if needed
         if (aName == nsGkAtoms::hidechrome &&
             mNodeInfo->Equals(nsGkAtoms::window)) {
             HideWindowChrome(aValue && NS_LITERAL_STRING("true").Equals(*aValue));
         }
 
+        // handle :read-only/:read-write
+        nsIDocument *document = GetCurrentDoc();
+        if (aName == nsGkAtoms::readonly && document) {
+            mozAutoDocUpdate(document, UPDATE_CONTENT_STATE, PR_TRUE);
+            document->ContentStatesChanged(this, nsnull,
+                                           NS_EVENT_STATE_MOZ_READONLY |
+                                           NS_EVENT_STATE_MOZ_READWRITE);
+        }
+
         // XXX need to check if they're changing an event handler: if
         // so, then we need to unhook the old one.  Or something.
     }
 
     return nsGenericElement::AfterSetAttr(aNamespaceID, aName,
                                           aValue, aNotify);
 }
 
@@ -2093,16 +2102,32 @@ nsXULElement::AddPopupListener(nsIAtom* 
     NS_ENSURE_SUCCESS(rv, rv);
     nsIXULPopupListener* listener = popupListener;
     NS_ADDREF(listener);
     target->AddEventListener(NS_LITERAL_STRING("mousedown"), eventListener, PR_FALSE);
     target->AddEventListener(NS_LITERAL_STRING("contextmenu"), eventListener, PR_FALSE);
     return NS_OK;
 }
 
+PRInt32
+nsXULElement::IntrinsicState() const
+{
+    PRInt32 state = nsGenericElement::IntrinsicState();
+
+    const nsIAtom* tag = Tag();
+    if (GetNameSpaceID() == kNameSpaceID_XUL &&
+        (tag == nsGkAtoms::textbox || tag == nsGkAtoms::textarea) &&
+        !HasAttr(kNameSpaceID_None, nsGkAtoms::readonly)) {
+        state |= NS_EVENT_STATE_MOZ_READWRITE;
+        state &= ~NS_EVENT_STATE_MOZ_READONLY;
+    }
+
+    return state;
+}
+
 //----------------------------------------------------------------------
 
 nsGenericElement::nsAttrInfo
 nsXULElement::GetAttrInfo(PRInt32 aNamespaceID, nsIAtom *aName) const
 {
 
     nsAttrInfo info(nsGenericElement::GetAttrInfo(aNamespaceID, aName));
     if (!info.mValue) {
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -553,16 +553,17 @@ public:
 
     // nsIDOMElement
     NS_FORWARD_NSIDOMELEMENT(nsGenericElement::)
 
     // nsIDOMXULElement
     NS_DECL_NSIDOMXULELEMENT
 
     virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
+    virtual PRInt32 IntrinsicState() const;
 
     nsresult GetStyle(nsIDOMCSSStyleDeclaration** aStyle);
 
     virtual void RecompileScriptEventListeners();
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as nsGenericElement does.
--- a/docshell/base/nsDocShellEditorData.cpp
+++ b/docshell/base/nsDocShellEditorData.cpp
@@ -68,17 +68,17 @@ nsDocShellEditorData::nsDocShellEditorDa
 ----------------------------------------------------------------------------*/
 nsDocShellEditorData::~nsDocShellEditorData()
 {
   if (mEditingSession)
   {
     nsCOMPtr<nsIDOMWindow> domWindow = do_GetInterface(mDocShell);
     // This will eventually call nsDocShellEditorData::SetEditor(nsnull)
     //   which will call mEditorPreDestroy() and delete the editor
-    mEditingSession->TearDownEditorOnWindow(domWindow);
+    mEditingSession->TearDownEditorOnWindow(domWindow, PR_TRUE);
   }
   else if (mEditor) // Should never have this w/o nsEditingSession!
   {
     mEditor->PreDestroy();
     mEditor = nsnull;     // explicit clear to make destruction order predictable
   }
 }
 
@@ -99,17 +99,18 @@ nsDocShellEditorData::MakeEditable(PRBoo
   if (mEditor)
   {
     NS_WARNING("Destroying existing editor on frame");
     
     mEditor->PreDestroy();
     mEditor = nsnull;
   }
   
-  mMakeEditable = PR_TRUE;
+  if (inWaitForUriLoad)
+    mMakeEditable = PR_TRUE;
   return NS_OK;
 }
 
 
 /*---------------------------------------------------------------------------
 
   GetEditable
 
@@ -186,16 +187,18 @@ nsDocShellEditorData::SetEditor(nsIEdito
   {
     if (mEditor)
     {
       mEditor->PreDestroy();
       mEditor = nsnull;
     }
       
     mEditor = inEditor;    // owning addref
+    if (!mEditor)
+      mMakeEditable = PR_FALSE;
   }   
   
   return NS_OK;
 }
 
 
 /*---------------------------------------------------------------------------
 
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -767,17 +767,21 @@ nsWebShell::OnLinkClick(nsIContent* aCon
                         nsIInputStream* aPostDataStream,
                         nsIInputStream* aHeadersDataStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
   if (mFiredUnloadEvent) {
     return NS_OK;
   }
-  
+
+  if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIRunnable> ev =
       new OnLinkClickEvent(this, aContent, aURI, aTargetSpec,
                            aPostDataStream, aHeadersDataStream);
   return NS_DispatchToCurrentThread(ev);
 }
 
 NS_IMETHODIMP
 nsWebShell::OnLinkClickSync(nsIContent *aContent,
@@ -795,16 +799,20 @@ nsWebShell::OnLinkClickSync(nsIContent *
   if (aRequest) {
     *aRequest = nsnull;
   }
 
   if (mFiredUnloadEvent) {
     return NS_OK;
   }
 
+  if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+    return NS_OK;
+  }
+
   {
     // defer to an external protocol handler if necessary...
     nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
     if (extProtService) {
       nsCAutoString scheme;
       aURI->GetScheme(scheme);
       if (!scheme.IsEmpty()) {
         // if the URL scheme does not correspond to an exposed protocol, then we
@@ -890,16 +898,20 @@ nsWebShell::OnLinkClickSync(nsIContent *
   return rv;
 }
 
 NS_IMETHODIMP
 nsWebShell::OnOverLink(nsIContent* aContent,
                        nsIURI* aURI,
                        const PRUnichar* aTargetSpec)
 {
+  if (aContent->HasFlag(NODE_IS_EDITABLE)) {
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIWebBrowserChrome2> browserChrome2 = do_GetInterface(mTreeOwner);
   nsresult rv = NS_ERROR_FAILURE;
 
   nsCOMPtr<nsIWebBrowserChrome> browserChrome;
   if (!browserChrome2) {
     browserChrome = do_GetInterface(mTreeOwner);
     if (!browserChrome)
       return rv;
--- a/dom/public/idl/html/nsIDOMNSHTMLElement.idl
+++ b/dom/public/idl/html/nsIDOMNSHTMLElement.idl
@@ -33,17 +33,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(b0a29b0a-ce2b-4cdf-b98a-4c7ed994d6e2)]
+[scriptable, uuid(eac0a4ee-2e4f-403c-9b77-5cf32cfb42f7)]
 interface nsIDOMNSHTMLElement : nsISupports
 {
   readonly attribute long             offsetTop;
   readonly attribute long             offsetLeft;
   readonly attribute long             offsetWidth;
   readonly attribute long             offsetHeight;
   readonly attribute nsIDOMElement    offsetParent;
            attribute DOMString        innerHTML;
@@ -55,16 +55,18 @@ interface nsIDOMNSHTMLElement : nsISuppo
 
   readonly attribute long             clientTop;
   readonly attribute long             clientLeft;
   readonly attribute long             clientHeight;
   readonly attribute long             clientWidth;
 
            attribute long             tabIndex;
 
+           attribute DOMString        contentEditable;
+
   void blur();
   void focus();
 
   // |top| is optional in JS, scriptability of this method is done in
   // nsHTMLElementSH
   void scrollIntoView(in boolean top);
 
            attribute boolean         spellcheck;
--- a/editor/composer/public/nsIEditingSession.idl
+++ b/editor/composer/public/nsIEditingSession.idl
@@ -38,17 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "domstubs.idl"
 
 interface nsIEditor;
 
-[scriptable, uuid(d39fd2b4-3978-45d2-a4be-ba448171b61b)]
+[scriptable, uuid(aee80d50-2065-4411-834d-0cadfb649a19)]
 
 interface nsIEditingSession : nsISupports
 {
   /**
    *  Error codes when we fail to create an editor
    *  is placed in attribute editorStatus
    */
   const long eEditorOK = 0;
@@ -63,18 +63,26 @@ interface nsIEditingSession : nsISupport
    *  Value is one of the above error codes
    */
   readonly attribute unsigned long editorStatus;
 
   /**
    *  Make this window editable
    *  @param aWindow nsIDOMWindow, the window the embedder needs to make editable
    *  @param aEditorType string, "html" "htmlsimple" "text" "textsimple"
+   *  @param aMakeWholeDocumentEditable if PR_TRUE make the whole document in
+   *                                    aWindow editable, otherwise it's the
+   *                                    embedder who should make the document
+   *                                    (or part of it) editable.
+   *  @param aInteractive if PR_FALSE turn off scripting and plugins
    */
-  void makeWindowEditable(in nsIDOMWindow window, in string aEditorType, in boolean doAfterUriLoad);
+  void makeWindowEditable(in nsIDOMWindow window, in string aEditorType,
+                          in boolean doAfterUriLoad,
+                          in boolean aMakeWholeDocumentEditable,
+                          in boolean aInteractive);
   
   /**
    *  Test whether a specific window has had its editable flag set; it may have an editor
    *  now, or will get one after the uri load.
    *  
    *  Use this, passing the content root window, to test if we've set up editing
    *  for this content.
    */
@@ -88,15 +96,15 @@ interface nsIEditingSession : nsISupport
   /** 
    *  Setup editor and related support objects
    */
   void setupEditorOnWindow(in nsIDOMWindow window);
 
   /** 
    *   Destroy editor and related support objects
    */
-  void tearDownEditorOnWindow(in nsIDOMWindow window);
+  void tearDownEditorOnWindow(in nsIDOMWindow window, in boolean aStopEditing);
 
   void setEditorOnControllers(in nsIDOMWindow aWindow,
                               in nsIEditor aEditor);
 
 };
 
--- a/editor/composer/src/nsEditingSession.cpp
+++ b/editor/composer/src/nsEditingSession.cpp
@@ -90,16 +90,17 @@
 /*---------------------------------------------------------------------------
 
   nsEditingSession
 
 ----------------------------------------------------------------------------*/
 nsEditingSession::nsEditingSession()
 : mDoneSetup(PR_FALSE)
 , mCanCreateEditor(PR_FALSE)
+, mInteractive(PR_FALSE)
 , mScriptsEnabled(PR_TRUE)
 , mPluginsEnabled(PR_TRUE)
 , mProgressListenerRegistered(PR_FALSE)
 , mImageAnimationMode(0)
 , mEditorFlags(0)
 , mEditorStatus(eEditorOK)
 , mBaseCommandControllerId(0)
 , mDocStateControllerId(0)
@@ -123,55 +124,74 @@ NS_IMPL_ISUPPORTS3(nsEditingSession, nsI
                    nsISupportsWeakReference)
 
 /*---------------------------------------------------------------------------
 
   MakeWindowEditable
 
   aEditorType string, "html" "htmlsimple" "text" "textsimple"
   void makeWindowEditable(in nsIDOMWindow aWindow, in string aEditorType, 
-                          in boolean aDoAfterUriLoad);
+                          in boolean aDoAfterUriLoad,
+                          in boolean aMakeWholeDocumentEditable,
+                          in boolean aInteractive);
 ----------------------------------------------------------------------------*/
 #define DEFAULT_EDITOR_TYPE "html"
 
 NS_IMETHODIMP
 nsEditingSession::MakeWindowEditable(nsIDOMWindow *aWindow,
                                      const char *aEditorType, 
-                                     PRBool aDoAfterUriLoad)
+                                     PRBool aDoAfterUriLoad,
+                                     PRBool aMakeWholeDocumentEditable,
+                                     PRBool aInteractive)
 {
   mEditorType.Truncate();
   mEditorFlags = 0;
   mWindowToBeEdited = do_GetWeakReference(aWindow);
 
   // disable plugins
   nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
   if (!docShell) return NS_ERROR_FAILURE;
 
   nsresult rv;
-  // Disable JavaScript in this document:
-  PRBool tmp;
-  rv = docShell->GetAllowJavascript(&tmp);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (aMakeWholeDocumentEditable) {
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    rv = aWindow->GetDocument(getter_AddRefs(domDoc));
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  mScriptsEnabled = tmp;
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    doc->SetEditableFlag(PR_TRUE);
+  }
+
+  mInteractive = aInteractive;
 
-  rv = docShell->SetAllowJavascript(PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (!mInteractive) {
+    // Disable JavaScript in this document:
+    PRBool tmp;
+    rv = docShell->GetAllowJavascript(&tmp);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    mScriptsEnabled = tmp;
 
-  // Disable plugins in this document:
-  rv = docShell->GetAllowPlugins(&tmp);
-  NS_ENSURE_SUCCESS(rv, rv);
+    rv = docShell->SetAllowJavascript(PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  mPluginsEnabled = tmp;
+    // Disable plugins in this document:
+    rv = docShell->GetAllowPlugins(&tmp);
+    NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = docShell->SetAllowPlugins(PR_FALSE);
-  NS_ENSURE_SUCCESS(rv, rv);
+    mPluginsEnabled = tmp;
+
+    rv = docShell->SetAllowPlugins(PR_FALSE);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 
   // Always remove existing editor
-  TearDownEditorOnWindow(aWindow);
+  TearDownEditorOnWindow(aWindow, PR_FALSE);
   
   // Tells embedder that startup is in progress
   mEditorStatus = eEditorCreationInProgress;
 
   //temporary to set editor type here. we will need different classes soon.
   if (!aEditorType)
     aEditorType = DEFAULT_EDITOR_TYPE;
   mEditorType = aEditorType;
@@ -210,17 +230,17 @@ nsEditingSession::MakeWindowEditable(nsI
   if (!aDoAfterUriLoad)
   {
     rv = SetupEditorOnWindow(aWindow);
 
     // mEditorStatus is set to the error reason
     // Since this is used only when editing an existing page,
     //  it IS ok to destroy current editor
     if (NS_FAILED(rv))
-      TearDownEditorOnWindow(aWindow);
+      TearDownEditorOnWindow(aWindow, PR_FALSE);
   }
   return rv;
 }
 
 /*---------------------------------------------------------------------------
 
   WindowIsEditable
 
@@ -356,16 +376,20 @@ nsEditingSession::SetupEditorOnWindow(ns
       mEditorFlags = nsIPlaintextEditor::eEditorPlaintextMask | 
                      nsIPlaintextEditor::eEditorEnableWrapHackMask;
   }
   else // Defaulted to html
   {
     needHTMLController = PR_TRUE;
   }
 
+  if (mInteractive) {
+    mEditorFlags |= nsIPlaintextEditor::eEditorAllowInteraction;
+  }
+
   // make the UI state maintainer
   nsComposerCommandsUpdater *stateMaintainer;
   NS_NEWXPCOM(stateMaintainer, nsComposerCommandsUpdater);
   mStateMaintainer = NS_STATIC_CAST(nsISelectionListener*, stateMaintainer);
 
   if (!mStateMaintainer) return NS_ERROR_OUT_OF_MEMORY;
 
   // now init the state maintainer
@@ -385,23 +409,25 @@ nsEditingSession::SetupEditorOnWindow(ns
     return NS_ERROR_FAILURE;
   }
 
   // Create editor and do other things 
   //  only if we haven't found some error above,
   nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
   if (!docShell) return NS_ERROR_FAILURE;  
 
-  // Disable animation of images in this document:
-  nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
-  if (!utils) return NS_ERROR_FAILURE;
+  if (!mInteractive) {
+    // Disable animation of images in this document:
+    nsCOMPtr<nsIDOMWindowUtils> utils(do_GetInterface(aWindow));
+    if (!utils) return NS_ERROR_FAILURE;
 
-  rv = utils->GetImageAnimationMode(&mImageAnimationMode);
-  if (NS_FAILED(rv)) return rv;
-  utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+    rv = utils->GetImageAnimationMode(&mImageAnimationMode);
+    if (NS_FAILED(rv)) return rv;
+    utils->SetImageAnimationMode(imgIContainer::kDontAnimMode);
+  }
 
   // create and set editor
   nsCOMPtr<nsIEditorDocShell> editorDocShell = do_QueryInterface(docShell, &rv);
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIEditor> editor = do_CreateInstance(classString, &rv);
   if (NS_FAILED(rv)) return rv;
   // set the editor on the docShell. The docShell now owns it.
@@ -475,20 +501,22 @@ nsEditingSession::SetupEditorOnWindow(ns
   // This will trigger documentCreation notification
   return editor->PostCreate();
 }
 
 /*---------------------------------------------------------------------------
 
   TearDownEditorOnWindow
 
-  void tearDownEditorOnWindow (in nsIDOMWindow aWindow);
+  void tearDownEditorOnWindow (in nsIDOMWindow aWindow,
+                               in boolean aStopEditing);
 ----------------------------------------------------------------------------*/
 NS_IMETHODIMP
-nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow)
+nsEditingSession::TearDownEditorOnWindow(nsIDOMWindow *aWindow,
+                                         PRBool aStopEditing)
 {
   if (!mDoneSetup)
     return NS_OK;
 
   nsresult rv;
   
   // Kill any existing reload timer
   if (mLoadBlankDocTimer)
@@ -496,33 +524,17 @@ nsEditingSession::TearDownEditorOnWindow
     mLoadBlankDocTimer->Cancel();
     mLoadBlankDocTimer = nsnull;
   }
 
   nsIDocShell *docShell = GetDocShellFromWindow(aWindow);
 
   mDoneSetup = PR_FALSE;
 
-  nsCOMPtr<nsIDOMDocument> dom_doc;
-  aWindow->GetDocument(getter_AddRefs(dom_doc));
-
-  nsCOMPtr<nsIDOMNSHTMLDocument> html_doc(do_QueryInterface(dom_doc));
-  PRBool isMidas = PR_FALSE;
-
-  if (html_doc) {
-    nsAutoString designMode;
-    html_doc->GetDesignMode(designMode);
-
-    isMidas = designMode.EqualsLiteral("on");
-  }
-
-  if (isMidas) {
-    // We're tearing down a midas editor, unregister callbacks since
-    // we're all done editing here.
-
+  if (aStopEditing) {
     nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
     if (webProgress) {
       webProgress->RemoveProgressListener(this);
 
       mProgressListenerRegistered = PR_FALSE;
     }
   }
 
@@ -617,17 +629,17 @@ nsEditingSession::TearDownEditorOnWindow
     }
 
     //   Clear IDs to trigger creation of new controllers
     mBaseCommandControllerId = 0;
     mDocStateControllerId = 0;
     mHTMLCommandControllerId = 0;
   }
 
-  if (isMidas) {
+  if (aStopEditing && !mInteractive) {
     // Make things the way they were before we started editing.
     if (mScriptsEnabled) {
       docShell->SetAllowJavascript(PR_TRUE);
     }
 
     if (mPluginsEnabled) {
       docShell->SetAllowPlugins(PR_TRUE);
     }
@@ -967,17 +979,17 @@ nsEditingSession::StartDocumentLoad(nsIW
   NS_ENSURE_ARG_POINTER(aWebProgress);
   
   // If we have an editor here, then we got a reload after making the editor.
   // We need to blow it away and make a new one at the end of the load.
   nsCOMPtr<nsIDOMWindow> domWindow;
   aWebProgress->GetDOMWindow(getter_AddRefs(domWindow));
   if (domWindow)
   {
-    TearDownEditorOnWindow(domWindow);
+    TearDownEditorOnWindow(domWindow, PR_FALSE);
   }
     
   if (aIsToBeMadeEditable)
     mEditorStatus = eEditorCreationInProgress;
 
   return NS_OK;
 }
 
@@ -1037,37 +1049,55 @@ nsEditingSession::EndDocumentLoad(nsIWeb
   // did someone set the flag to make this shell editable?
   if (aIsToBeMadeEditable && mCanCreateEditor && editorDocShell)
   {
     PRBool  makeEditable;
     editorDocShell->GetEditable(&makeEditable);
   
     if (makeEditable)
     {
-      mCanCreateEditor = PR_FALSE;
-      rv = SetupEditorOnWindow(domWindow);
+      // do we already have an editor here?
+      nsCOMPtr<nsIEditor> editor;
+      rv = editorDocShell->GetEditor(getter_AddRefs(editor));
       if (NS_FAILED(rv))
+        return rv;
+      if (!editor)
       {
-        // If we had an error, setup timer to load a blank page later
-        if (mLoadBlankDocTimer)
+        mCanCreateEditor = PR_FALSE;
+        rv = SetupEditorOnWindow(domWindow);
+        if (NS_FAILED(rv))
         {
-          // Must cancel previous timer?
-          mLoadBlankDocTimer->Cancel();
-          mLoadBlankDocTimer = NULL;
+          // If we had an error, setup timer to load a blank page later
+          if (mLoadBlankDocTimer)
+          {
+            // Must cancel previous timer?
+            mLoadBlankDocTimer->Cancel();
+            mLoadBlankDocTimer = NULL;
+          }
+  
+          mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+          if (NS_FAILED(rv)) return rv;
+
+          mEditorStatus = eEditorCreationInProgress;
+          mLoadBlankDocTimer->InitWithFuncCallback(
+                                          nsEditingSession::TimerCallback,
+                                          (void*)docShell,
+                                          10, nsITimer::TYPE_ONE_SHOT);
         }
-  
-        mLoadBlankDocTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-        if (NS_FAILED(rv)) return rv;
+      }
 
-        mEditorStatus = eEditorCreationInProgress;
-        mLoadBlankDocTimer->InitWithFuncCallback(
-                                        nsEditingSession::TimerCallback,
-                                        (void*)docShell,
-                                        10, nsITimer::TYPE_ONE_SHOT);
-      }
+      // XXX This should move somewhere else!
+      nsCOMPtr<nsIDOMDocument> domDoc;
+      rv = domWindow->GetDocument(getter_AddRefs(domDoc));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc, &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      doc->SetEditableFlag(PR_TRUE);
     }
   }
   return rv;
 }
 
 
 void
 nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure)
@@ -1139,17 +1169,17 @@ nsEditingSession::EndPageLoad(nsIWebProg
   // we need to make sure that all pages in editor (whether editable or not)
   // can't refresh contents being edited
   nsCOMPtr<nsIRefreshURI> refreshURI = do_QueryInterface(docShell);
   if (refreshURI)
     refreshURI->CancelRefreshURITimers();
 
 #if 0
   // Shouldn't we do this when we want to edit sub-frames?
-  return MakeWindowEditable(domWindow, "html", PR_FALSE);
+  return MakeWindowEditable(domWindow, "html", PR_FALSE, mInteractive);
 #else
   return NS_OK;
 #endif
 }
 
 
 #ifdef XP_MAC
 #pragma mark -
--- a/editor/composer/src/nsEditingSession.h
+++ b/editor/composer/src/nsEditingSession.h
@@ -124,16 +124,18 @@ protected:
   PRPackedBool    mDoneSetup;    // have we prepared for editing yet?
 
   // Used to prevent double creation of editor because nsIWebProgressListener
   //  receives a STATE_STOP notification before the STATE_START 
   //  for our document, so we wait for the STATE_START, then STATE_STOP 
   //  before creating an editor
   PRPackedBool    mCanCreateEditor; 
 
+  PRPackedBool    mInteractive;
+
   // True if scripts were enabled before the editor turned scripts
   // off, otherwise false.
   PRPackedBool    mScriptsEnabled;
 
   // True if plugins were enabled before the editor turned plugins
   // off, otherwise false.
   PRPackedBool    mPluginsEnabled;
 
--- a/editor/composer/src/res/EditorOverride.css
+++ b/editor/composer/src/res/EditorOverride.css
@@ -30,16 +30,20 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+*|* {
+  -moz-user-modify: read-write;
+}
+
 /* Styles to alter look of things in the Editor content window 
  *  that should NOT be removed when we display in completely WYSIWYG 
  *  "Browser Preview" mode.
  *  Anything that should change, like appearance of table borders
  *  and Named Anchors, should be placed in EditorContent.css instead of here.
 */
 
 /* Primary cursor is text I-beam */
--- a/editor/docs/Editor_Embedding_Guide.html
+++ b/editor/docs/Editor_Embedding_Guide.html
@@ -13,35 +13,38 @@ 11/5/02 original by Michael Judge &lt;<a
 3/27/03 updates by Kathleen Brade &lt;<a
  href="mailto:brade@netscape.com">brade@netscape.com</a>&gt;<br>
 <h2>In the Beginning there is MakeEditable</h2>
 <p>Given an nsIWebBrowser instance, get a nsIDOMWindow from the
 GetContentDOMWindow call.&nbsp; Then simply call
 nsIWebBrowser-&gt;do_GetInterface on the nsIWebBrowser to retrieve the
 nsIEditingSession from it.&nbsp; From there you call
 editingSession-&gt;MakeWindowEditable(domWindow, editortype,
-PR_TRUE);&nbsp;&nbsp;&nbsp; The first parameter is the nsIDOMWindow you
-just retrieved, the second is the editor type you want to create and the
+PR_TRUE, PR_FALSE);&nbsp;&nbsp;&nbsp; The first parameter is the nsIDOMWindow
+you just retrieved, the second is the editor type you want to create and the
 third is whether you want the window editable immediately or when the
-document is done loading.&nbsp; In calling this method the editor is
+document is done loading, the fourth is whether you want the editor to make
+the whole document editable, the fifth is whether you want to turn of
+scripts, plugins, ...&nbsp; In calling this method the editor is
 created underneath and the event listeners etc. are all prepared.<br>
 </p>
 <p><i>&nbsp;&nbsp;&nbsp; nsCOMPtr&lt;nsIDOMWindow&gt; domWindow;<br>
 &nbsp;&nbsp;&nbsp; nsresult rv =
 nsIWebBrowser-&gt;GetContentDOMWindow(getter_AddRefs(domWindow));<br>
 &nbsp;&nbsp;&nbsp; if (NS_FAILED(rv)) return NS_ERROR_FAILURE; // we
 are not setup??!!<br>
 </i></p>
 <p><i>&nbsp;&nbsp;&nbsp; nsCOMPtr&lt;nsIEditingSession&gt;
 editingSession;<br>
 &nbsp;&nbsp;&nbsp;
 nsIWebBrowser-&gt;do_GetInterface(getter_AddRefs(editingSession));<br>
 &nbsp;&nbsp;&nbsp; if (editingSession)<br>
 &nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
-editingSession-&gt;MakeWindowEditable(domWindow, "html", PR_TRUE);</i></p>
+editingSession-&gt;MakeWindowEditable(domWindow, "html", PR_TRUE,
+PR_FALSE, PR_TRUE, PR_FALSE);</i></p>
 <p>The valid editor types are:<br>
 </p>
 <ul>
   <li>"text" (similar to NotePad or a textarea; does not allow for html)</li>
   <li>"textmail" (similar to "text" but html can be inserted; intended
 for plaintext mail usage and handling of citations)</li>
   <li>"html" (this is the default type if no type is specified; it
 allows for all html tags to be inserted)<br>
--- a/editor/idl/nsIEditor.idl
+++ b/editor/idl/nsIEditor.idl
@@ -57,17 +57,17 @@ interface nsIInlineSpellChecker;
 %{C++
 class nsIPresShell;
 typedef short EDirection;
 %}
 
 
 [ptr] native nsIPresShellPtr(nsIPresShell);
 
-[scriptable, uuid(470e18e4-2e82-48de-8850-7474cdbbd97d)]
+[scriptable, uuid(dc81f464-89dd-47bf-bf21-10df3b65b956)]
 
 interface nsIEditor  : nsISupports
 {
 %{C++
   typedef short EDirection;
 %}
   const short eNone = 0;
   const short eNext = 1;
@@ -557,9 +557,12 @@ interface nsIEditor  : nsISupports
    */
   void dumpContentTree();
 
   /** Dumps a text representation of the content tree to standard out */
   void debugDumpContent() ;
 
   /* Run unit tests. Noop in optimized builds */
   void debugUnitTests(out long outNumTests, out long  outNumTestsFailed);
+
+  /* checks if a node is read-only or not */
+  [notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
 };
--- a/editor/idl/nsIPlaintextEditor.idl
+++ b/editor/idl/nsIPlaintextEditor.idl
@@ -34,44 +34,33 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
  
 #include "nsISupports.idl"
 
 interface nsIDOMKeyEvent;
 
-[scriptable, uuid(28dbb4d0-5fea-43f4-aca7-344fc2ecc4f9)]
+[scriptable, uuid(35d74f2b-3d03-43c5-8ace-9d90c3e31244)]
 interface nsIPlaintextEditor : nsISupports
 {
 
-  /* the bits in an editor behavior mask. */
-  const short eEditorPlaintextBit = 0;                  /* only plain text entry is allowed via events */
-  const short eEditorSingleLineBit = 1;                 /* enter key and CR-LF handled specially */
-  const short eEditorPasswordBit = 2;                   /* text is not entered into content, only a representative character */
-  const short eEditorReadonlyBit = 3;                   /* editing events are disabled.  Editor may still accept focus. */
-  const short eEditorDisabledBit = 4;                   /* all events are disabled (like scrolling).  Editor will not accept focus. */
-  const short eEditorFilterInputBit = 5;                /* text input is limited to certain character types, use mFilter */
-  const short eEditorMailBit = 6;                       /* use mail-compose editting rules */
-  const short eEditorUseAsyncUpdatesBit = 7;            /* prevent immediate reflows and view refreshes */
-  const short eEditorEnableWrapHackBit = 8;             /* allow the editor to set font: monospace on the root node */
-  const short eEditorWidgetBit = 9;                     /* bit for widgets */
-  const short eEditorNoCSSBit = 10;                     /* this HTML editor should not create css styles */
-
-  const long eEditorPlaintextMask            = 1;
-  const long eEditorSingleLineMask           = 2;
-  const long eEditorPasswordMask             = 4;
-  const long eEditorReadonlyMask             = 8;
-  const long eEditorDisabledMask             = 16;
-  const long eEditorFilterInputMask          = 32;
-  const long eEditorMailMask                 = 64;
-  const long eEditorUseAsyncUpdatesMask      = 128;
-  const long eEditorEnableWrapHackMask       = 256;
-  const long eEditorWidgetMask               = 512;
-  const long eEditorNoCSSMask                = 1024;
+  // XXX Why aren't these in nsIEditor?
+  const long eEditorPlaintextMask       = 0x0001; /* only plain text entry is allowed via events */
+  const long eEditorSingleLineMask      = 0x0002; /* enter key and CR-LF handled specially */
+  const long eEditorPasswordMask        = 0x0004; /* text is not entered into content, only a representative character */
+  const long eEditorReadonlyMask        = 0x0008; /* editing events are disabled.  Editor may still accept focus. */
+  const long eEditorDisabledMask        = 0x0010; /* all events are disabled (like scrolling).  Editor will not accept focus. */
+  const long eEditorFilterInputMask     = 0x0020; /* text input is limited to certain character types, use mFilter */
+  const long eEditorMailMask            = 0x0040; /* use mail-compose editing rules */
+  const long eEditorUseAsyncUpdatesMask = 0x0080; /* prevent immediate reflows and view refreshes */
+  const long eEditorEnableWrapHackMask  = 0x0100; /* allow the editor to set font: monospace on the root node */
+  const long eEditorWidgetMask          = 0x0200; /* bit for widgets */
+  const long eEditorNoCSSMask           = 0x0400; /* this HTML editor should not create css styles */
+  const long eEditorAllowInteraction    = 0x0800; /*  */
 
   /*
    * The valid values for newlines handling.
    * Can't change the values unless we remove
    * use of the pref.
    */
   const long eNewlinesPasteIntact                = 0;
   const long eNewlinesPasteToFirst               = 1;
--- a/editor/libeditor/base/DeleteElementTxn.cpp
+++ b/editor/libeditor/base/DeleteElementTxn.cpp
@@ -53,36 +53,44 @@ DeleteElementTxn::DeleteElementTxn()
 : EditTxn()
 ,mElement()
 ,mParent()
 ,mRefNode()
 ,mRangeUpdater(nsnull)
 {
 }
 
-NS_IMETHODIMP DeleteElementTxn::Init(nsIDOMNode *aElement,
+NS_IMETHODIMP DeleteElementTxn::Init(nsIEditor *aEditor,
+                                     nsIDOMNode *aElement,
                                      nsRangeUpdater *aRangeUpdater)
 {
-  if (!aElement) return NS_ERROR_NULL_POINTER;
+  if (!aEditor || !aElement) return NS_ERROR_NULL_POINTER;
+  mEditor = aEditor;
   mElement = do_QueryInterface(aElement);
+  nsresult result = mElement->GetParentNode(getter_AddRefs(mParent));
+  if (NS_FAILED(result)) { return result; }
+
+  // do nothing if the parent is read-only
+  if (mParent && !mEditor->IsModifiableNode(mParent)) {
+    return NS_ERROR_FAILURE;
+  }
+
   mRangeUpdater = aRangeUpdater;
   return NS_OK;
 }
 
 
 NS_IMETHODIMP DeleteElementTxn::DoTransaction(void)
 {
 #ifdef NS_DEBUG
   if (gNoisy) { printf("%p Do Delete Element element = %p\n", this, mElement.get()); }
 #endif
 
   if (!mElement) return NS_ERROR_NOT_INITIALIZED;
 
-  nsresult result = mElement->GetParentNode(getter_AddRefs(mParent));
-  if (NS_FAILED(result)) { return result; }
   if (!mParent) { return NS_OK; }  // this is a no-op, there's no parent to delete mElement from
 
 #ifdef NS_DEBUG
   // begin debug output
   nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mElement);
   nsAutoString elementTag(NS_LITERAL_STRING("text node"));
   if (element)
     element->GetTagName(elementTag);
@@ -100,17 +108,17 @@ NS_IMETHODIMP DeleteElementTxn::DoTransa
 
     NS_Free(c);
     NS_Free(p);
   }
   // end debug output
 #endif
 
   // remember which child mElement was (by remembering which child was next)
-  result = mElement->GetNextSibling(getter_AddRefs(mRefNode));  // can return null mRefNode
+  nsresult result = mElement->GetNextSibling(getter_AddRefs(mRefNode));  // can return null mRefNode
 
   // give range updater a chance.  SelAdjDeleteNode() needs to be called *before*
   // we do the action, unlike some of the other nsRangeStore update methods.
   if (mRangeUpdater) 
     mRangeUpdater->SelAdjDeleteNode(mElement);
 
   nsCOMPtr<nsIDOMNode> resultNode;
   return mParent->RemoveChild(mElement, getter_AddRefs(resultNode));
--- a/editor/libeditor/base/DeleteElementTxn.h
+++ b/editor/libeditor/base/DeleteElementTxn.h
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef DeleteElementTxn_h__
 #define DeleteElementTxn_h__
 
 #include "EditTxn.h"
 
 #include "nsIDOMNode.h"
+#include "nsIEditor.h"
 #include "nsCOMPtr.h"
 
 #define DELETE_ELEMENT_TXN_CID \
 {/* 6fd77770-ac49-11d2-86d8-000064657374 */ \
 0x6fd77770, 0xac49, 0x11d2, \
 {0x86, 0xd8, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74} }
 
 class nsRangeUpdater;
@@ -57,17 +58,17 @@ class DeleteElementTxn : public EditTxn
 {
 public:
 
   static const nsIID& GetCID() { static const nsIID iid = DELETE_ELEMENT_TXN_CID; return iid; }
  
   /** initialize the transaction.
     * @param aElement the node to delete
     */
-  NS_IMETHOD Init(nsIDOMNode *aElement, nsRangeUpdater *aRangeUpdater);
+  NS_IMETHOD Init(nsIEditor *aEditor, nsIDOMNode *aElement, nsRangeUpdater *aRangeUpdater);
 
 private:
   DeleteElementTxn();
 
 public:
   NS_DECL_EDITTXN
 
   NS_IMETHOD RedoTransaction();
@@ -78,16 +79,19 @@ protected:
   nsCOMPtr<nsIDOMNode> mElement;
 
   /** parent of node to delete */
   nsCOMPtr<nsIDOMNode> mParent;
 
   /** next sibling to remember for undo/redo purposes */
   nsCOMPtr<nsIDOMNode> mRefNode;
 
+  /** the editor for this transaction */
+  nsIEditor* mEditor;
+
   /** range updater object */
   nsRangeUpdater *mRangeUpdater;
   
   friend class TransactionFactory;
 
 };
 
 #endif
--- a/editor/libeditor/base/DeleteRangeTxn.cpp
+++ b/editor/libeditor/base/DeleteRangeTxn.cpp
@@ -83,16 +83,27 @@ NS_IMETHODIMP DeleteRangeTxn::Init(nsIEd
   NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndParent failed.");
   result = aRange->GetStartOffset(&mStartOffset);
   NS_ASSERTION((NS_SUCCEEDED(result)), "GetStartOffset failed.");
   result = aRange->GetEndOffset(&mEndOffset);
   NS_ASSERTION((NS_SUCCEEDED(result)), "GetEndOffset failed.");
   result = aRange->GetCommonAncestorContainer(getter_AddRefs(mCommonParent));
   NS_ASSERTION((NS_SUCCEEDED(result)), "GetCommonParent failed.");
 
+  if (!mEditor->IsModifiableNode(mStartParent)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (mStartParent!=mEndParent &&
+      (!mEditor->IsModifiableNode(mEndParent) ||
+       !mEditor->IsModifiableNode(mCommonParent)))
+  {
+      return NS_ERROR_FAILURE;
+  }
+
 #ifdef NS_DEBUG
   {
     PRUint32 count;
     nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(mStartParent);
     if (textNode)
       textNode->GetLength(&count);
     else
     {
@@ -231,18 +242,19 @@ DeleteRangeTxn::CreateTxnsToDeleteBetwee
     if (NS_FAILED(result)) return result;
     if (!txn) return NS_ERROR_NULL_POINTER;
 
     PRInt32 numToDel;
     if (aStartOffset==aEndOffset)
       numToDel = 1;
     else
       numToDel = aEndOffset-aStartOffset;
-    txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
-    AppendChild(txn);
+    result = txn->Init(mEditor, textNode, aStartOffset, numToDel, mRangeUpdater);
+    if (NS_SUCCEEDED(result))
+      AppendChild(txn);
     NS_RELEASE(txn);
   }
   else
   {
     nsCOMPtr<nsIDOMNodeList> children;
     result = aStartParent->GetChildNodes(getter_AddRefs(children));
     if (NS_FAILED(result)) return result;
     if (!children) return NS_ERROR_NULL_POINTER;
@@ -260,18 +272,19 @@ DeleteRangeTxn::CreateTxnsToDeleteBetwee
       if (NS_FAILED(result)) return result;
       if (!child) return NS_ERROR_NULL_POINTER;
 
       DeleteElementTxn *txn;
       result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn);
       if (NS_FAILED(result)) return result;
       if (!txn) return NS_ERROR_NULL_POINTER;
 
-      txn->Init(child, mRangeUpdater);
-      AppendChild(txn);
+      result = txn->Init(mEditor, child, mRangeUpdater);
+      if (NS_SUCCEEDED(result))
+        AppendChild(txn);
       NS_RELEASE(txn);
     }
   }
   return result;
 }
 
 NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteContent(nsIDOMNode *aParent, 
                                                         PRUint32    aOffset, 
@@ -297,44 +310,46 @@ NS_IMETHODIMP DeleteRangeTxn::CreateTxns
     
     if (numToDelete)
     {
       DeleteTextTxn *txn;
       result = TransactionFactory::GetNewTransaction(DeleteTextTxn::GetCID(), (EditTxn **)&txn);
       if (NS_FAILED(result)) return result;
       if (!txn) return NS_ERROR_NULL_POINTER;
 
-      txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
-      AppendChild(txn);
+      result = txn->Init(mEditor, textNode, start, numToDelete, mRangeUpdater);
+      if (NS_SUCCEEDED(result))
+        AppendChild(txn);
       NS_RELEASE(txn);
     }
   }
 
   return result;
 }
 
 NS_IMETHODIMP DeleteRangeTxn::CreateTxnsToDeleteNodesBetween()
 {
   nsCOMPtr<nsIContentIterator> iter = do_CreateInstance("@mozilla.org/content/subtree-content-iterator;1");
   if (!iter) return NS_ERROR_NULL_POINTER;
 
   nsresult result = iter->Init(mRange);
   if (NS_FAILED(result)) return result;
 
-  while (!iter->IsDone())
+  while (!iter->IsDone() && NS_SUCCEEDED(result))
   {
     nsCOMPtr<nsIDOMNode> node = do_QueryInterface(iter->GetCurrentNode());
     if (!node)
       return NS_ERROR_NULL_POINTER;
 
     DeleteElementTxn *txn;
     result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)&txn);
     if (NS_FAILED(result)) return result;
     if (!txn) return NS_ERROR_NULL_POINTER;
 
-    txn->Init(node, mRangeUpdater);
-    AppendChild(txn);
+    result = txn->Init(mEditor, node, mRangeUpdater);
+    if (NS_SUCCEEDED(result))
+      AppendChild(txn);
     NS_RELEASE(txn);
     iter->Next();
   }
   return result;
 }
 
--- a/editor/libeditor/base/DeleteTextTxn.cpp
+++ b/editor/libeditor/base/DeleteTextTxn.cpp
@@ -60,16 +60,21 @@ NS_IMETHODIMP DeleteTextTxn::Init(nsIEdi
                                   PRUint32 aNumCharsToDelete,
                                   nsRangeUpdater *aRangeUpdater)
 {
   NS_ASSERTION(aEditor&&aElement, "bad arg");
   if (!aEditor || !aElement) { return NS_ERROR_NULL_POINTER; }
 
   mEditor = aEditor;
   mElement = do_QueryInterface(aElement);
+  // do nothing if the node is read-only
+  if (!mEditor->IsModifiableNode(mElement)) {
+    return NS_ERROR_FAILURE;
+  }
+
   mOffset = aOffset;
   mNumCharsToDelete = aNumCharsToDelete;
   NS_ASSERTION(0!=aNumCharsToDelete, "bad arg, numCharsToDelete");
   PRUint32 count;
   aElement->GetLength(&count);
   NS_ASSERTION(count>=aNumCharsToDelete, "bad arg, numCharsToDelete.  Not enough characters in node");
   NS_ASSERTION(count>=aOffset+aNumCharsToDelete, "bad arg, numCharsToDelete.  Not enough characters in node");
   mDeletedText.Truncate();
--- a/editor/libeditor/base/JoinElementTxn.cpp
+++ b/editor/libeditor/base/JoinElementTxn.cpp
@@ -52,16 +52,22 @@ JoinElementTxn::JoinElementTxn()
 NS_IMETHODIMP JoinElementTxn::Init(nsEditor   *aEditor,
                                    nsIDOMNode *aLeftNode,
                                    nsIDOMNode *aRightNode)
 {
   NS_PRECONDITION((aEditor && aLeftNode && aRightNode), "null arg");
   if (!aEditor || !aLeftNode || !aRightNode) { return NS_ERROR_NULL_POINTER; }
   mEditor = aEditor;
   mLeftNode = do_QueryInterface(aLeftNode);
+  nsCOMPtr<nsIDOMNode>leftParent;
+  nsresult result = mLeftNode->GetParentNode(getter_AddRefs(leftParent));
+  if (NS_FAILED(result)) return result;
+  if (!mEditor->IsModifiableNode(leftParent)) {
+    return NS_ERROR_FAILURE;
+  }
   mRightNode = do_QueryInterface(aRightNode);
   mOffset=0;
   return NS_OK;
 }
 
 // After DoTransaction() and RedoTransaction(), the left node is removed from the content tree and right node remains.
 NS_IMETHODIMP JoinElementTxn::DoTransaction(void)
 {
--- a/editor/libeditor/base/nsEditPropertyAtomList.h
+++ b/editor/libeditor/base/nsEditPropertyAtomList.h
@@ -161,16 +161,17 @@ EDITOR_ATOM(cssRight, "right")
 EDITOR_ATOM(cssTextAlign, "text-align")
 EDITOR_ATOM(cssTextDecoration, "text-decoration")
 EDITOR_ATOM(cssTop, "top")
 EDITOR_ATOM(cssVerticalAlign, "vertical-align")
 EDITOR_ATOM(cssWhitespace, "white-space")
 EDITOR_ATOM(cssWidth, "width")
 EDITOR_ATOM(cssZIndex, "z-index")
 
+EDITOR_ATOM(cssMozUserModify, "-moz-user-modify")
 EDITOR_ATOM(cssMozUserSelect, "-moz-user-select")
 EDITOR_ATOM(mozdirty, "_moz_dirty")
 
 EDITOR_ATOM(cssPxUnit, "px")
 EDITOR_ATOM(cssEmUnit, "em")
 EDITOR_ATOM(cssCmUnit, "cm")
 EDITOR_ATOM(cssPercentUnit, "%")
 EDITOR_ATOM(cssInUnit, "in")
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -3788,18 +3788,18 @@ nsEditor::IsTextInDirtyFrameVisible(nsID
 PRBool 
 nsEditor::IsEditable(nsIDOMNode *aNode)
 {
   if (!aNode) return PR_FALSE;
   nsCOMPtr<nsIPresShell> shell;
   GetPresShell(getter_AddRefs(shell));
   if (!shell)  return PR_FALSE;
 
-  if (IsMozEditorBogusNode(aNode)) return PR_FALSE;
-  
+  if (IsMozEditorBogusNode(aNode) || !IsModifiableNode(aNode)) return PR_FALSE;
+
   // see if it has a frame.  If so, we'll edit it.
   // special case for textnodes: frame must have width.
   nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
   if (content)
   {
     nsIFrame *resultFrame = shell->GetPrimaryFrameFor(content);
     if (!resultFrame)   // if it has no frame, it is not editable
       return PR_FALSE;
@@ -4745,17 +4745,17 @@ NS_IMETHODIMP nsEditor::CreateTxnForInse
 NS_IMETHODIMP nsEditor::CreateTxnForDeleteElement(nsIDOMNode * aElement,
                                              DeleteElementTxn ** aTxn)
 {
   nsresult result = NS_ERROR_NULL_POINTER;
   if (nsnull != aElement)
   {
     result = TransactionFactory::GetNewTransaction(DeleteElementTxn::GetCID(), (EditTxn **)aTxn);
     if (NS_SUCCEEDED(result)) {
-      result = (*aTxn)->Init(aElement, &mRangeUpdater);
+      result = (*aTxn)->Init(this, aElement, &mRangeUpdater);
     }
   }
   return result;
 }
 
 NS_IMETHODIMP 
 nsEditor::CreateTxnForIMEText(const nsAString& aStringToInsert,
                               IMETextTxn ** aTxn)
@@ -5366,8 +5366,14 @@ nsEditor::DumpNode(nsIDOMNode *aNode, PR
     textNode->GetData(str);
     nsCAutoString cstr;
     LossyCopyUTF16toASCII(str, cstr);
     cstr.ReplaceChar('\n', ' ');
     printf("<textnode> %s\n", cstr.get());
   }
 }
 #endif
+
+PRBool
+nsEditor::IsModifiableNode(nsIDOMNode *aNode)
+{
+  return PR_TRUE;
+}
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -580,16 +580,61 @@ nsHTMLEditRules::WillDoAction(nsISelecti
   printf("nsHTMLEditRules::WillDoAction action = %d\n", aInfo->action);
 #endif
 
   *aCancel = PR_FALSE;
   *aHandled = PR_FALSE;
     
   // my kingdom for dynamic cast
   nsTextRulesInfo *info = NS_STATIC_CAST(nsTextRulesInfo*, aInfo);
+
+  // Deal with actions for which we don't need to check whether the selection is
+  // editable.
+  if (info->action == kOutputText) {
+    return nsTextEditRules::WillDoAction(aSelection, aInfo, aCancel, aHandled);
+  }
+
+  nsCOMPtr<nsIDOMRange> domRange;
+  nsresult rv = aSelection->GetRangeAt(0, getter_AddRefs(domRange));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> selStartNode;
+  rv = domRange->GetStartContainer(getter_AddRefs(selStartNode));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!mHTMLEditor->IsModifiableNode(selStartNode))
+  {
+    *aCancel = PR_TRUE;
+
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMNode> selEndNode;
+  rv = domRange->GetEndContainer(getter_AddRefs(selEndNode));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (selStartNode != selEndNode)
+  {
+    if (!mHTMLEditor->IsModifiableNode(selEndNode))
+    {
+      *aCancel = PR_TRUE;
+
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIRange> range = do_QueryInterface(domRange);
+    nsCOMPtr<nsIDOMNode> ancestor =
+      do_QueryInterface(range->GetCommonAncestor());
+    if (!mHTMLEditor->IsModifiableNode(ancestor))
+    {
+      *aCancel = PR_TRUE;
+
+      return NS_OK;
+    }
+  }
     
   switch (info->action)
   {
     case kInsertText:
     case kInsertTextIME:
       return WillInsertText(info->action,
                             aSelection, 
                             aCancel, 
@@ -1564,16 +1609,23 @@ nsHTMLEditRules::WillInsertBreak(nsISele
   
   if (IsBlockNode(node)) 
     blockParent = node;
   else 
     blockParent = mHTMLEditor->GetBlockNodeParent(node);
     
   if (!blockParent) return NS_ERROR_FAILURE;
   
+  // do nothing if the node is read-only
+  if (!mHTMLEditor->IsModifiableNode(blockParent))
+  {
+    *aCancel = PR_TRUE;
+    return NS_OK;
+  }
+
   // if block is empty, populate with br.
   // (for example, imagine a div that contains the word "text".  the user selects
   // "text" and types return.  "text" is deleted leaving an empty block.  we want
   // to put in one br to make block have a line.  then code further below will put 
   // in a second br.)
   PRBool isEmpty;
   res = IsEmptyBlock(blockParent, &isEmpty);
   if (isEmpty)
--- a/editor/libeditor/html/nsHTMLEditor.cpp
+++ b/editor/libeditor/html/nsHTMLEditor.cpp
@@ -125,16 +125,17 @@
 #include "nsEditorUtils.h"
 #include "nsWSRunObject.h"
 #include "nsHTMLObjectResizer.h"
 
 #include "nsIFrame.h"
 #include "nsIView.h"
 #include "nsIWidget.h"
 #include "nsIParserService.h"
+#include "nsIEventStateManager.h"
 
 // Some utilities to handle annoying overloading of "A" tag for link and named anchor
 static char hrefText[] = "href";
 static char anchorTxt[] = "anchor";
 static char namedanchorText[] = "namedanchor";
 
 nsIRangeUtils* nsHTMLEditor::sRangeHelper;
 
@@ -297,33 +298,35 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc,
       delete mHTMLCSSUtils;
     result = NS_NewHTMLCSSUtils(&mHTMLCSSUtils);
     if (NS_FAILED(result)) { return result; }
     mHTMLCSSUtils->Init(this);
 
     // disable links
     nsPresContext *context = aPresShell->GetPresContext();
     if (!context) return NS_ERROR_NULL_POINTER;
-    if (!(mFlags & eEditorPlaintextMask)) {
+    if (!(mFlags & (eEditorPlaintextMask | eEditorAllowInteraction))) {
       mLinkHandler = context->GetLinkHandler();
 
       context->SetLinkHandler(nsnull);
     }
 
     // init the type-in state
     mTypeInState = new TypeInState();
     if (!mTypeInState) {return NS_ERROR_NULL_POINTER;}
     NS_ADDREF(mTypeInState);
 
     // init the selection listener for image resizing
     mSelectionListenerP = new ResizerSelectionListener(this);
     if (!mSelectionListenerP) {return NS_ERROR_NULL_POINTER;}
 
-    // ignore any errors from this in case the file is missing
-    AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/EditorOverride.css"));
+    if (!(mFlags & eEditorAllowInteraction)) {
+      // ignore any errors from this in case the file is missing
+      AddOverrideStyleSheet(NS_LITERAL_STRING("resource:/res/EditorOverride.css"));
+    }
 
     nsCOMPtr<nsISelection>selection;
     result = GetSelection(getter_AddRefs(selection));
     if (NS_FAILED(result)) { return result; }
     if (selection) 
     {
       nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(selection));
       nsCOMPtr<nsISelectionListener>listener;
@@ -3879,38 +3882,60 @@ nsHTMLEditor::GetEmbeddedObjects(nsISupp
 #ifdef XP_MAC
 #pragma mark -
 #pragma mark  nsIEditor overrides 
 #pragma mark -
 #endif
 
 NS_IMETHODIMP nsHTMLEditor::DeleteNode(nsIDOMNode * aNode)
 {
+  // do nothing if the node is read-only
+  if (!IsModifiableNode(aNode)) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsCOMPtr<nsIDOMNode> selectAllNode = FindUserSelectAllNode(aNode);
   
   if (selectAllNode)
   {
     return nsEditor::DeleteNode(selectAllNode);
   }
   return nsEditor::DeleteNode(aNode);
 }
 
 NS_IMETHODIMP nsHTMLEditor::DeleteText(nsIDOMCharacterData *aTextNode,
                                        PRUint32             aOffset,
                                        PRUint32             aLength)
 {
+  // do nothing if the node is read-only
+  if (!IsModifiableNode(aTextNode)) {
+    return NS_ERROR_FAILURE;
+  }
+
   nsCOMPtr<nsIDOMNode> selectAllNode = FindUserSelectAllNode(aTextNode);
   
   if (selectAllNode)
   {
     return nsEditor::DeleteNode(selectAllNode);
   }
   return nsEditor::DeleteText(aTextNode, aOffset, aLength);
 }
 
+NS_IMETHODIMP nsHTMLEditor::InsertTextImpl(const nsAString& aStringToInsert, 
+                                           nsCOMPtr<nsIDOMNode> *aInOutNode, 
+                                           PRInt32 *aInOutOffset,
+                                           nsIDOMDocument *aDoc)
+{
+  // do nothing if the node is read-only
+  if (!IsModifiableNode(*aInOutNode)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return nsEditor::InsertTextImpl(aStringToInsert, aInOutNode, aInOutOffset, aDoc);
+}
 
 #ifdef XP_MAC
 #pragma mark -
 #pragma mark  support utils
 #pragma mark -
 #endif
 
 /* This routine examines aNode and it's ancestors looking for any node which has the
@@ -3942,16 +3967,24 @@ nsCOMPtr<nsIDOMNode> nsHTMLEditor::FindU
     {
       node = nsnull;
     }
   } 
 
   return resultNode;
 }
 
+NS_IMETHODIMP_(PRBool)
+nsHTMLEditor::IsModifiableNode(nsIDOMNode *aNode)
+{
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+
+  return !content || !(content->IntrinsicState() & NS_EVENT_STATE_MOZ_READONLY);
+}
+
 static nsresult SetSelectionAroundHeadChildren(nsCOMPtr<nsISelection> aSelection, nsWeakPtr aDocWeak)
 {
   nsresult res = NS_OK;
   // Set selection around <head> node
   nsCOMPtr<nsIDOMDocument> doc = do_QueryReferent(aDocWeak);
   if (!doc) return NS_ERROR_NOT_INITIALIZED;
 
   nsCOMPtr<nsIDOMNodeList>nodeList; 
@@ -4199,16 +4232,64 @@ nsHTMLEditor::SelectEntireDocument(nsISe
   {
     // if its empty dont select entire doc - that would select the bogus node
     return aSelection->Collapse(rootElement, 0);
   }
 
   return nsEditor::SelectEntireDocument(aSelection);
 }
 
+static nsIContent*
+FindEditableRoot(nsIContent *aContent)
+{
+  nsIDocument *document = aContent->GetCurrentDoc();
+  if (!document || document->HasFlag(NODE_IS_EDITABLE) ||
+      !aContent->HasFlag(NODE_IS_EDITABLE)) {
+    return nsnull;
+  }
+
+  nsIContent *parent, *content = aContent;
+  while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+    content = parent;
+  }
+
+  return content;
+}
+
+NS_IMETHODIMP
+nsHTMLEditor::SelectAll()
+{
+  ForceCompositionEnd();
+
+  nsresult rv;
+  nsCOMPtr<nsISelectionController> selCon = do_QueryReferent(mSelConWeak, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsISelection> selection;
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                            getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMNode> anchorNode;
+  rv = selection->GetAnchorNode(getter_AddRefs(anchorNode));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsIContent *rootContent = FindEditableRoot(anchorContent);
+  if (!rootContent) {
+    return SelectEntireDocument(selection);
+  }
+
+  nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return selection->SelectAllChildren(rootElement);
+}
 
 
 #ifdef XP_MAC
 #pragma mark -
 #pragma mark  Random methods 
 #pragma mark -
 #endif
 
--- a/editor/libeditor/html/nsHTMLEditor.h
+++ b/editor/libeditor/html/nsHTMLEditor.h
@@ -358,16 +358,23 @@ public:
   NS_IMETHOD CollapseAdjacentTextNodes(nsIDOMRange *aInRange);
 
   virtual PRBool NodesSameType(nsIDOMNode *aNode1, nsIDOMNode *aNode2);
 
   NS_IMETHODIMP DeleteNode(nsIDOMNode * aNode);
   NS_IMETHODIMP DeleteText(nsIDOMCharacterData *aTextNode,
                            PRUint32             aOffset,
                            PRUint32             aLength);
+  NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, 
+                            nsCOMPtr<nsIDOMNode> *aInOutNode, 
+                            PRInt32 *aInOutOffset,
+                            nsIDOMDocument *aDoc);
+  NS_IMETHOD_(PRBool) IsModifiableNode(nsIDOMNode *aNode);
+
+  NS_IMETHOD SelectAll();
 
   /* ------------ nsICSSLoaderObserver -------------- */
   NS_IMETHOD StyleSheetLoaded(nsICSSStyleSheet*aSheet, PRBool aWasAlternate,
                               nsresult aStatus);
 
   /* ------------ Utility Routines, not part of public API -------------- */
   NS_IMETHOD TypedText(const nsAString& aString, PRInt32 aAction);
   nsresult InsertNodeAtPoint( nsIDOMNode *aNode, 
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -63,16 +63,17 @@
 #include "nsIDragService.h"
 #include "nsIDragSession.h"
 #include "nsIContent.h"
 #include "nsISupportsPrimitives.h"
 #include "nsIDOMNSRange.h"
 #include "nsEditorUtils.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIEventStateManager.h"
+#include "nsISelectionPrivate.h"
 
 //#define DEBUG_IME
 
 /*
  * nsTextEditorKeyListener implementation
  */
 
 NS_IMPL_ISUPPORTS2(nsTextEditorKeyListener, nsIDOMEventListener, nsIDOMKeyListener)
@@ -218,17 +219,18 @@ nsTextEditorKeyListener::KeyPress(nsIDOM
         mEditor->DeleteSelection(nsIEditor::eNext);
         aKeyEvent->PreventDefault(); // consumed
         return NS_OK; 
         break;
  
       case nsIDOMKeyEvent::DOM_VK_TAB:
         if ((flags & nsIPlaintextEditor::eEditorSingleLineMask) ||
             (flags & nsIPlaintextEditor::eEditorPasswordMask)   ||
-            (flags & nsIPlaintextEditor::eEditorWidgetMask))
+            (flags & nsIPlaintextEditor::eEditorWidgetMask)     ||
+            (flags & nsIPlaintextEditor::eEditorAllowInteraction))
           return NS_OK; // let it be used for focus switching
 
         if (isAnyModifierKeyButShift)
           return NS_OK;
 
         // else we insert the tab straight through
         textEditor->HandleKeyPress(keyEvent);
         // let HandleKeyPress consume the event
@@ -1027,16 +1029,40 @@ IsTargetFocused(nsIDOMEventTarget* aTarg
     GetFocusedContent(getter_AddRefs(focusedContent));
 
   // focusedContent will be null in the case where the document has focus,
   // and so will content.
 
   return (focusedContent == content);
 }
 
+static nsIContent*
+FindEditableRoot(nsIContent *aContent)
+{
+  nsIDocument *document = aContent->GetCurrentDoc();
+  if (!document) {
+    return nsnull;
+  }
+
+  if (document->HasFlag(NODE_IS_EDITABLE)) {
+    return document->GetRootContent();
+  }
+
+  if (!aContent->HasFlag(NODE_IS_EDITABLE)) {
+    return nsnull;
+  }
+
+  nsIContent *parent, *content = aContent;
+  while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
+    content = parent;
+  }
+
+  return content;
+}
+
 nsresult
 nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
 {
   NS_ENSURE_ARG(aEvent);
   // It's possible for us to receive a focus when we're really not focused.
   // This happens, for example, when an onfocus handler that's hooked up
   // before this listener focuses something else.  In that case, all of the
   // onblur handlers will be fired synchronously, then the remaining focus
@@ -1054,28 +1080,48 @@ nsTextEditorFocusListener::Focus(nsIDOME
   if (mEditor)
   {
     aEvent->StopPropagation();
 
     PRUint32 flags;
     mEditor->GetFlags(&flags);
     if (! (flags & nsIPlaintextEditor::eEditorDisabledMask))
     { // only enable caret and selection if the editor is not disabled
-      nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
-      if (editor)
+      nsCOMPtr<nsIContent> content = do_QueryInterface(target);
+
+      nsIContent *editableRoot = content ? FindEditableRoot(content) : nsnull;
+
+      nsCOMPtr<nsISelectionController> selCon;
+      mEditor->GetSelectionController(getter_AddRefs(selCon));
+      nsCOMPtr<nsIPresShell> presShell = do_QueryInterface(selCon);
+      if (selCon && editableRoot)
       {
-        nsCOMPtr<nsISelectionController>selCon;
-        editor->GetSelectionController(getter_AddRefs(selCon));
-        if (selCon)
+        nsCOMPtr<nsISelection> selection;
+        selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                             getter_AddRefs(selection));
+
+        if (presShell && selection) {
+          nsCOMPtr<nsICaret> caret;
+          presShell->GetCaret(getter_AddRefs(caret));
+          if (caret) {
+            caret->SetCaretDOMSelection(selection);
+          }
+        }
+
+        const PRBool kIsReadonly = (flags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
+        selCon->SetCaretReadOnly(kIsReadonly);
+        selCon->SetCaretEnabled(PR_TRUE);
+        selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+        selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+
+        nsCOMPtr<nsISelectionPrivate> selectionPrivate =
+          do_QueryInterface(selection);
+        if (selectionPrivate)
         {
-          const PRBool kIsReadonly = (flags & nsIPlaintextEditor::eEditorReadonlyMask) != 0;
-          selCon->SetCaretReadOnly(kIsReadonly);
-          selCon->SetCaretEnabled(PR_TRUE);
-          selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
-          selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+          selectionPrivate->SetAncestorLimiter(editableRoot);
         }
       }
     }
 
     nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
     if (imeEditor)
       imeEditor->NotifyIMEOnFocus();
   }
@@ -1100,16 +1146,26 @@ nsTextEditorFocusListener::Blur(nsIDOMEv
 
     nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
     if (editor)
     {
       nsCOMPtr<nsISelectionController>selCon;
       editor->GetSelectionController(getter_AddRefs(selCon));
       if (selCon)
       {
+        nsCOMPtr<nsISelection> selection;
+        selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                             getter_AddRefs(selection));
+
+        nsCOMPtr<nsISelectionPrivate> selectionPrivate =
+          do_QueryInterface(selection);
+        if (selectionPrivate) {
+          selectionPrivate->SetAncestorLimiter(nsnull);
+        }
+
         selCon->SetCaretEnabled(PR_FALSE);
 
         PRUint32 flags;
         mEditor->GetFlags(&flags);
         if((flags & nsIPlaintextEditor::eEditorWidgetMask)  ||
           (flags & nsIPlaintextEditor::eEditorPasswordMask) ||
           (flags & nsIPlaintextEditor::eEditorReadonlyMask) ||
           (flags & nsIPlaintextEditor::eEditorDisabledMask) ||
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -1291,17 +1291,19 @@ nsTextEditRules::CreateBogusNodeIfNeeded
   // now we've got the body tag.
   // iterate the body tag, looking for editable content
   // if no editable content is found, insert the bogus node
   PRBool needsBogusContent=PR_TRUE;
   nsCOMPtr<nsIDOMNode> bodyChild;
   nsresult res = mBody->GetFirstChild(getter_AddRefs(bodyChild));        
   while ((NS_SUCCEEDED(res)) && bodyChild)
   { 
-    if (mEditor->IsMozEditorBogusNode(bodyChild) || mEditor->IsEditable(bodyChild))
+    if (mEditor->IsMozEditorBogusNode(bodyChild) ||
+        !mEditor->IsEditable(mBody) ||
+        mEditor->IsEditable(bodyChild))
     {
       needsBogusContent = PR_FALSE;
       break;
     }
     nsCOMPtr<nsIDOMNode>temp;
     bodyChild->GetNextSibling(getter_AddRefs(temp));
     bodyChild = do_QueryInterface(temp);
   }
--- a/editor/ui/composer/content/editor.js
+++ b/editor/ui/composer/content/editor.js
@@ -49,16 +49,17 @@ const kDisplayModeNormal = 0;
 const kDisplayModeAllTags = 1;
 const kDisplayModeSource = 2;
 const kDisplayModePreview = 3;
 const kDisplayModeMenuIDs = ["viewNormalMode", "viewAllTagsMode", "viewSourceMode", "viewPreviewMode"];
 const kDisplayModeTabIDS = ["NormalModeButton", "TagModeButton", "SourceModeButton", "PreviewModeButton"];
 const kNormalStyleSheet = "chrome://editor/content/EditorContent.css";
 const kAllTagsStyleSheet = "chrome://editor/content/EditorAllTags.css";
 const kParagraphMarksStyleSheet = "chrome://editor/content/EditorParagraphMarks.css";
+const kContentEditableStyleSheet = "resource:/res/contenteditable.css";
 
 const kTextMimeType = "text/plain";
 const kHTMLMimeType = "text/html";
 
 const nsIWebNavigation = Components.interfaces.nsIWebNavigation;
 
 var gPreviousNonSourceDisplayMode = 1;
 var gEditorDisplayMode = -1;
@@ -394,16 +395,20 @@ var gEditorDocumentObserver =
         if (!("InsertCharWindow" in window))
           window.InsertCharWindow = null;
 
         try {
           editor.QueryInterface(nsIEditorStyleSheets);
 
           //  and extra styles for showing anchors, table borders, smileys, etc
           editor.addOverrideStyleSheet(kNormalStyleSheet);
+
+          // remove contenteditable stylesheets if they were applied by the
+          // editingSession
+          editor.removeOverrideStyleSheet(kContentEditableStyleSheet);
         } catch (e) {}
 
         // Things for just the Web Composer application
         if (IsWebComposer())
         {
           InlineSpellCheckerUI.init(editor);
           document.getElementById('menu_inlinespellcheck').setAttribute('disabled', !InlineSpellCheckerUI.canSpellCheck);
 
--- a/embedding/browser/activex/src/control/MozillaBrowser.cpp
+++ b/embedding/browser/activex/src/control/MozillaBrowser.cpp
@@ -1286,17 +1286,18 @@ HRESULT CMozillaBrowser::SetEditorMode(B
     if (!mEditingSession || !mCommandManager)
         return E_FAIL;
   
     nsCOMPtr<nsIDOMWindow> domWindow;
     nsresult rv = GetDOMWindow(getter_AddRefs(domWindow));
     if (NS_FAILED(rv))
         return E_FAIL;
 
-    rv = mEditingSession->MakeWindowEditable(domWindow, "html", PR_FALSE);
+    rv = mEditingSession->MakeWindowEditable(domWindow, "html", PR_FALSE,
+                                             PR_FALSE);
  
     return S_OK;
 }
 
 
 HRESULT CMozillaBrowser::OnEditorCommand(DWORD nCmdID)
 {
     NG_TRACE_METHOD(CMozillaBrowser::OnEditorCommand);
--- a/embedding/config/basebrowser-installer-win.pkg
+++ b/embedding/config/basebrowser-installer-win.pkg
@@ -238,16 +238,18 @@ components\xmlextras.dll
 ; res:
 ; 
 res\html.css   
 res\quirk.css
 res\viewsource.css
 res\hiddenWindow.html
 res\ua.css
 res\forms.css
+res\contenteditable.css
+res\designmode.css
 res\arrow.gif
 res\arrowd.gif
 res\loading-image.gif
 res\broken-image.gif
 res\entityTables\html40Special.properties
 res\entityTables\htmlEntityVersions.properties
 res\entityTables\html40Latin1.properties
 res\entityTables\html40Symbols.properties
--- a/embedding/config/basebrowser-mac-macho
+++ b/embedding/config/basebrowser-mac-macho
@@ -233,16 +233,18 @@ chrome/embed.jar
 ; res:
 ; 
 res/html.css   
 res/quirk.css
 res/viewsource.css
 res/hiddenWindow.html
 res/ua.css
 res/forms.css
+res/contenteditable.css
+res/designmode.css
 res/arrow.gif
 res/arrowd.gif
 res/loading-image.gif
 res/broken-image.gif
 res/entityTables/html40Special.properties
 res/entityTables/htmlEntityVersions.properties
 res/entityTables/html40Latin1.properties
 res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-qnx
+++ b/embedding/config/basebrowser-qnx
@@ -233,16 +233,18 @@ chrome/embed.jar
 ; res:
 ; 
 res/html.css   
 res/quirk.css
 res/viewsource.css
 res/hiddenWindow.html
 res/ua.css
 res/forms.css
+res/contenteditable.css
+res/designmode.css
 res/arrow.gif
 res/arrowd.gif
 res/loading-image.gif
 res/broken-image.gif
 res/entityTables/html40Special.properties
 res/entityTables/htmlEntityVersion.properties
 res/entityTables/html40Latin1.properties
 res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-unix
+++ b/embedding/config/basebrowser-unix
@@ -235,16 +235,18 @@ chrome/embed.jar
 ; res:
 ; 
 res/html.css   
 res/quirk.css
 res/viewsource.css
 res/hiddenWindow.html
 res/ua.css
 res/forms.css
+res/contenteditable.css
+res/designmode.css
 res/arrow.gif
 res/arrowd.gif
 res/loading-image.gif
 res/broken-image.gif
 res/entityTables/html40Special.properties
 res/entityTables/htmlEntityVersion.properties
 res/entityTables/html40Latin1.properties
 res/entityTables/html40Symbols.properties
--- a/embedding/config/basebrowser-win
+++ b/embedding/config/basebrowser-win
@@ -254,16 +254,18 @@ components\xmlextras.dll
 ; res:
 ; 
 res\html.css   
 res\quirk.css
 res\viewsource.css
 res\hiddenWindow.html
 res\ua.css
 res\forms.css
+res/contenteditable.css
+res/designmode.css
 res\arrow.gif
 res\arrowd.gif
 res\loading-image.gif
 res\broken-image.gif
 res\entityTables\html40Special.properties
 res\entityTables\htmlEntityVersions.properties
 res\entityTables\html40Latin1.properties
 res\entityTables\html40Symbols.properties
--- a/embedding/config/minimo-qnx
+++ b/embedding/config/minimo-qnx
@@ -180,16 +180,18 @@ chrome/embed.jar
 ; 
 ; res:
 ; 
 res/html.css   
 res/quirk.css
 res/viewsource.css
 res/ua.css
 res/forms.css
+res/contenteditable.css
+res/designmode.css
 res/arrow.gif
 res/arrowd.gif
 res/loading-image.gif
 res/broken-image.gif
 res/entityTables/html40Special.properties
 res/entityTables/htmlEntityVersion.properties
 res/entityTables/html40Latin1.properties
 res/entityTables/html40Symbols.properties
--- a/embedding/config/minimo-unix
+++ b/embedding/config/minimo-unix
@@ -178,16 +178,18 @@ chrome/embed.jar
 ; 
 ; res:
 ; 
 res/html.css   
 res/quirk.css
 res/viewsource.css
 res/ua.css
 res/forms.css
+res/contenteditable.css
+res/designmode.css
 res/arrow.gif
 res/arrowd.gif
 res/loading-image.gif
 res/broken-image.gif
 res/entityTables/html40Special.properties
 res/entityTables/htmlEntityVersion.properties
 res/entityTables/html40Latin1.properties
 res/entityTables/html40Symbols.properties
--- a/embedding/qa/testembed/nsIEditSession.cpp
+++ b/embedding/qa/testembed/nsIEditSession.cpp
@@ -96,17 +96,18 @@ void CnsIEditSession::InitTest(PRInt16 d
 		QAOutput("Didn't get editingSession object for InitTest() test. Test failed.", displayMode);
 }
 
 void CnsIEditSession::MakeWinEditTest(PRBool afterUriLoad, PRInt16 displayMode)
 {
 	editingSession = GetEditSessionObject();
 	domWindow = GetTheDOMWindow(qaWebBrowser);
 	if (editingSession) {
-		rv= editingSession->MakeWindowEditable(domWindow, "text", afterUriLoad);
+		rv= editingSession->MakeWindowEditable(domWindow, "text", afterUriLoad,
+		                                       PR_TRUE, PR_FALSE);
 		RvTestResult(rv, "MakeWindowEditable() test", displayMode);
 		if (displayMode == 1)
 			RvTestResultDlg(rv, "MakeWindowEditable() test");
 		if (!domWindow)
 			QAOutput("Didn't get domWindow object for MakeWindowEditable() test. Test failed.", displayMode);
 	}
 	else
 		QAOutput("Didn't get editingSession object for MakeWindowEditable() test. Test failed.", 1);
@@ -166,17 +167,17 @@ void CnsIEditSession::SetEditorWinTest(P
 		QAOutput("Didn't get object(s) for SetEditorWinTest() test. Test failed.", 1);
 }
 
 void CnsIEditSession::TearEditorWinTest(PRInt16 displayMode)
 {
 	editingSession = GetEditSessionObject();
 	domWindow = GetTheDOMWindow(qaWebBrowser);
 	if (editingSession) {
-		rv = editingSession->TearDownEditorOnWindow(domWindow);
+		rv = editingSession->TearDownEditorOnWindow(domWindow, PR_FALSE);
 		RvTestResult(rv, "TearDownEditorOnWindow() test", displayMode);
 		if (displayMode == 1)
 			RvTestResultDlg(rv, "TearDownEditorOnWindow() test");
 		if (!domWindow)
 			QAOutput("Didn't get domWindow object for TearDownEditorOnWindow() test. Test failed.", displayMode);
 	}
 	else
 		QAOutput("Didn't get object(s) for TearEditorWinTest() test. Test failed.", 1);
--- a/embedding/tests/wxEmbed/EditorFrame.cpp
+++ b/embedding/tests/wxEmbed/EditorFrame.cpp
@@ -79,17 +79,17 @@ EditorFrame::EditorFrame(wxWindow* aPare
 void EditorFrame::MakeEditable()
 {
     nsCOMPtr<nsIDOMWindow> domWindow;
     mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
 
     nsCOMPtr<nsIEditingSession> editingSession = do_GetInterface(mWebBrowser);
     if (!editingSession)
         return;// NS_ERROR_FAILURE;
-    editingSession->MakeWindowEditable(domWindow, NULL, PR_TRUE);
+    editingSession->MakeWindowEditable(domWindow, NULL, PR_TRUE, PR_FALSE);
 }
 
 nsresult EditorFrame::DoCommand(const char *aCommand, nsICommandParams *aCommandParams)
 {
     if (mCommandManager)
     {
         nsCOMPtr<nsIDOMWindow> domWindow;
         mWebBrowser->GetContentDOMWindow(getter_AddRefs(domWindow));
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -88,16 +88,18 @@
 #include "nsISelection.h"
 #include "nsISelection2.h"
 #include "nsISelectionController.h"
 #include "nsIServiceManager.h"
 #include "nsITextServicesFilter.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsUnicharUtils.h"
+#include "nsIContent.h"
+#include "nsIEventStateManager.h"
 
 // Set to spew messages to the console about what is happening.
 //#define DEBUG_INLINESPELL
 
 // the number of milliseconds that we will take at once to do spellchecking
 #define INLINESPELL_CHECK_TIMEOUT 50
 
 // The number of words to check before we look at the time to see if
@@ -1103,16 +1105,21 @@ mozInlineSpellChecker::SkipSpellCheckFor
           *checkSpelling = PR_FALSE;
       }
 
       nsCOMPtr<nsIDOMNode> nextParent;
       parent->GetParentNode(getter_AddRefs(nextParent));
       parent = nextParent;
     }
   }
+  else {
+    // XXX Do we really want this for all read-write content?
+    nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+    *checkSpelling = content->IntrinsicState() & NS_EVENT_STATE_MOZ_READWRITE;
+  }
 
   return NS_OK;
 }
 
 // mozInlineSpellChecker::ScheduleSpellCheck
 //
 //    This is called by code to do the actual spellchecking. We will set up
 //    the proper structures for calls to DoSpellCheck.
--- a/layout/base/nsCaret.cpp
+++ b/layout/base/nsCaret.cpp
@@ -579,24 +579,20 @@ nsCaret::DrawAtPositionWithHint(nsIDOMNo
 
   nsIFrame* theFrame = nsnull;
   PRInt32   theFrameOffset = 0;
 
   nsresult rv = GetCaretFrameForNodeOffset(contentNode, aOffset, aFrameHint, aBidiLevel,
                                            &theFrame, &theFrameOffset);
   if (NS_FAILED(rv) || !theFrame)
     return PR_FALSE;
-  
+
   // now we have a frame, check whether it's appropriate to show the caret here
   const nsStyleUserInterface* userinterface = theFrame->GetStyleUserInterface();
-  if (
-#ifdef SUPPORT_USER_MODIFY
-        // editable content still defaults to NS_STYLE_USER_MODIFY_READ_ONLY at present. See bug 15284
-      (userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
-#endif          
+  if ((userinterface->mUserModify == NS_STYLE_USER_MODIFY_READ_ONLY) ||
       (userinterface->mUserInput == NS_STYLE_USER_INPUT_NONE) ||
       (userinterface->mUserInput == NS_STYLE_USER_INPUT_DISABLED))
   {
     return PR_FALSE;
   }  
 
   if (!mDrawn)
   {
--- a/layout/build/layout.pkg
+++ b/layout/build/layout.pkg
@@ -30,16 +30,18 @@ dist/bin/components/@SHARED_LIBRARY@
 #if MOZ_XUL
 !xpt dist/bin/components/xultmpl.xpt
 !xpt dist/bin/components/layout_xul_tree.xpt
 #endif
 dist/bin/res/html.css
 dist/bin/res/quirk.css
 dist/bin/res/viewsource.css
 dist/bin/res/forms.css
+dist/bin/res/contenteditable.css
+dist/bin/res/designmode.css
 dist/bin/res/arrow.gif
 dist/bin/res/arrowd.gif
 dist/bin/res/ua.css
 dist/bin/res/broken-image.gif
 dist/bin/res/loading-image.gif
 dist/bin/res/html/gopher-audio.gif
 dist/bin/res/html/gopher-binary.gif
 dist/bin/res/html/gopher-find.gif
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5732,27 +5732,16 @@ nsIFrame::IsFocusable(PRInt32 *aTabIndex
   }
   PRBool isFocusable = PR_FALSE;
 
   if (mContent && mContent->IsNodeOfType(nsINode::eELEMENT) &&
       AreAncestorViewsVisible()) {
     const nsStyleVisibility* vis = GetStyleVisibility();
     if (vis->mVisible != NS_STYLE_VISIBILITY_COLLAPSE &&
         vis->mVisible != NS_STYLE_VISIBILITY_HIDDEN) {
-      if (mContent->IsNodeOfType(nsINode::eHTML)) {
-        nsCOMPtr<nsISupports> container(PresContext()->GetContainer());
-        nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(container));
-        if (editorDocShell) {
-          PRBool isEditable;
-          editorDocShell->GetEditable(&isEditable);
-          if (isEditable) {
-            return NS_OK;  // Editor content is not focusable
-          }
-        }
-      }
       const nsStyleUserInterface* ui = GetStyleUserInterface();
       if (ui->mUserFocus != NS_STYLE_USER_FOCUS_IGNORE &&
           ui->mUserFocus != NS_STYLE_USER_FOCUS_NONE) {
         // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
         tabIndex = 0;
       }
       isFocusable = mContent->IsFocusable(&tabIndex);
       if (!isFocusable && !aWithMouse &&
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -452,16 +452,19 @@ public:
 
   /** Get the content node that limits the selection
    *  When searching up a nodes for parents, as in a text edit field
    *    in an browser page, we must stop at this node else we reach into the 
    *    parent page, which is very bad!
    */
   nsIContent* GetLimiter() { return mLimiter; }
 
+  nsIContent* GetAncestorLimiter() { return mAncestorLimiter; }
+  void SetAncestorLimiter(nsIContent *aLimiter);
+
   /** This will tell the frame selection that a double click has been pressed 
    *  so it can track abort future drags if inside the same selection
    *  @aDoubleDown has the double click down happened
    */
   void SetMouseDoubleDown(PRBool aDoubleDown) { mMouseDoubleDownState = aDoubleDown; }
   
   /** This will return whether the double down flag was set.
    *  @return whether the double down flag was set
@@ -626,16 +629,18 @@ private:
   // maintain selection
   nsCOMPtr<nsIDOMRange> mMaintainRange;
   nsSelectionAmount mMaintainedAmount;
 
   //batching
   PRInt32 mBatching;
     
   nsIContent *mLimiter;     //limit selection navigation to a child of this node.
+  nsIContent *mAncestorLimiter; // Limit selection navigation to a descendant of
+                                // this node.
   nsIPresShell *mShell;
 
   PRInt16 mSelectionChangeReason; // reason for notifications of selection changing
   PRInt16 mDisplaySelection; //for visual display purposes.
 
   HINT  mHint;   //hint to tell if the selection is at the end of this line or beginning of next
 #ifdef IBMBIDI
   PRInt8 mCaretBidiLevel;
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -659,22 +659,32 @@ or composer)
 */
 PRBool       
 IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsIContent *aContent)
 {
   if (!aFrameSel || !aContent)
     return PR_FALSE;
   if (aFrameSel)
   {
-    nsCOMPtr<nsIContent> tLimiter = aFrameSel->GetLimiter();
-    if (tLimiter && tLimiter != aContent)
+    nsIContent *limiter = aFrameSel->GetLimiter();
+    if (limiter)
     {
-      if (tLimiter != aContent->GetParent()) //if newfocus == the limiter. that's ok. but if not there and not parent bad
+      if (limiter != aContent && limiter != aContent->GetParent()) //if newfocus == the limiter. that's ok. but if not there and not parent bad
         return PR_FALSE; //not in the right content. tLimiter said so
     }
+    limiter = aFrameSel->GetAncestorLimiter();
+    if (limiter)
+    {
+      nsIContent *content = aContent;
+      while (content && content != limiter)
+      {
+        content = content->GetParent();
+      }
+      return content != nsnull;
+    }
   }
   return PR_TRUE;
 }
 
 
 NS_IMPL_ADDREF(nsSelectionIterator)
 NS_IMPL_RELEASE(nsSelectionIterator)
 
@@ -820,16 +830,17 @@ nsFrameSelection::nsFrameSelection()
       return;
     NS_ADDREF(mDomSelections[i]);
     mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
   }
   mBatching = 0;
   mChangesDuringBatching = PR_FALSE;
   mNotifyFrames = PR_TRUE;
   mLimiter = nsnull; //no default limiter.
+  mAncestorLimiter = nsnull;
   
   mMouseDoubleDownState = PR_FALSE;
   
   mHint = HINTLEFT;
 #ifdef IBMBIDI
   mCaretBidiLevel = BIDI_LEVEL_UNDEFINED;
 #endif
   mDragSelectingCells = PR_FALSE;
@@ -2215,18 +2226,20 @@ nsFrameSelection::HandleClick(nsIContent
                               PRBool      aMultipleSelection,
                               PRBool      aHint) 
 {
   if (!aNewFocus)
     return NS_ERROR_INVALID_ARG;
 
   InvalidateDesiredX();
 
-  if (!aContinueSelection)
+  if (!aContinueSelection) {
     mMaintainRange = nsnull;
+    mAncestorLimiter = nsnull;
+  }
 
   mHint = HINT(aHint);
   // Don't take focus when dragging off of a table
   if (!mDragSelectingCells)
   {
     BidiLevelFromClick(aNewFocus, aContentOffset);
     PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
     if (aContinueSelection &&
@@ -2781,28 +2794,31 @@ nsFrameSelection::IntraLineMove(PRBool a
 nsresult
 nsFrameSelection::SelectAll()
 {
   nsCOMPtr<nsIContent> rootContent;
   if (mLimiter)
   {
     rootContent = mLimiter;//addrefit
   }
+  else if (mAncestorLimiter) {
+    rootContent = mAncestorLimiter;
+  }
   else
   {
     nsIDocument *doc = mShell->GetDocument();
     if (!doc)
       return NS_ERROR_FAILURE;
     rootContent = doc->GetRootContent();
     if (!rootContent)
       return NS_ERROR_FAILURE;
   }
   PRInt32 numChildren = rootContent->GetChildCount();
   PostReason(nsISelectionListener::NO_REASON);
-  return TakeFocus(mLimiter, 0, numChildren, PR_FALSE, PR_FALSE);
+  return TakeFocus(rootContent, 0, numChildren, PR_FALSE, PR_FALSE);
 }
 
 //////////END FRAMESELECTION
 
 void
 nsFrameSelection::StartBatchChanges()
 {
   mBatching++;
@@ -3818,16 +3834,33 @@ nsFrameSelection::CreateAndAddRange(nsID
   if (NS_FAILED(result)) return result;
   
   PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
   return mDomSelections[index]->AddRange(range);
 }
 
 // End of Table Selection
 
+void
+nsFrameSelection::SetAncestorLimiter(nsIContent *aLimiter)
+{
+  if (mAncestorLimiter != aLimiter) {
+    mAncestorLimiter = aLimiter;
+    PRInt8 index =
+      GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    if (!IsValidSelectionPoint(this, mDomSelections[index]->FetchFocusNode())) {
+      ClearNormalSelection();
+      if (mAncestorLimiter) {
+        PostReason(nsISelectionListener::NO_REASON);
+        TakeFocus(mAncestorLimiter, 0, 0, PR_FALSE, PR_FALSE);
+      }
+    }
+  }
+}
+
 //END nsFrameSelection methods
 
 
 #ifdef XP_MAC
 #pragma mark -
 #endif
 
 //BEGIN nsISelection interface implementations
@@ -5286,16 +5319,23 @@ nsTypedSelection::GetCachedFrameOffset(n
 NS_IMETHODIMP
 nsTypedSelection::GetFrameSelection(nsFrameSelection **aFrameSelection) {
   NS_ENSURE_ARG_POINTER(aFrameSelection);
   *aFrameSelection = mFrameSelection;
   NS_IF_ADDREF(*aFrameSelection);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsTypedSelection::SetAncestorLimiter(nsIContent *aContent)
+{
+  mFrameSelection->SetAncestorLimiter(aContent);
+  return NS_OK;
+}
+
 nsresult
 nsTypedSelection::StartAutoScrollTimer(nsPresContext *aPresContext,
                                        nsIView *aView,
                                        nsPoint& aPoint,
                                        PRUint32 aDelay)
 {
   NS_PRECONDITION(aView, "Need a view");
 
--- a/layout/style/Makefile.in
+++ b/layout/style/Makefile.in
@@ -173,16 +173,18 @@ LOCAL_INCLUDES	= \
 
 _FILES	= \
 	ua.css \
 	html.css \
 	quirk.css \
 	viewsource.css \
 	arrow.gif \
 	arrowd.gif \
+	contenteditable.css \
+	designmode.css \
 	$(NULL)
 
 GARBAGE		+= $(addprefix $(DIST)/bin/res/,$(_FILES))
 
 FORMS_CSS_SRC = $(srcdir)/forms.css
 PREPROCESS_FORMS_CSS = $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py --marker=% $(DEFINES) $(ACDEFINES) $(FORMS_CSS_SRC)
 
 $(DIST)/bin/res/forms.css $(DESTDIR)$(mozappdir)/res/forms.css: $(FORMS_CSS_SRC) Makefile
new file mode 100644
--- /dev/null
+++ b/layout/style/contenteditable.css
@@ -0,0 +1,367 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Disruptive Innovations.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Peter Van der Beken <peterv@propagandism.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+::-moz-canvas {
+  cursor: text;
+}
+
+:focus:-moz-read-write :-moz-read-only {
+  -moz-user-select: all !important;
+}
+
+input:-moz-read-write > .anonymous-div:-moz-read-only,
+textarea:-moz-read-write > .anonymous-div:-moz-read-only {
+  -moz-user-select: text !important;
+}
+
+/* Use default arrow over objects with size that 
+   are selected when clicked on.
+   Override the browser's pointer cursor over links
+*/
+
+img:-moz-read-write, img:-moz-read-write[usemap], area:-moz-read-write,
+object:-moz-read-write, object:-moz-read-write[usemap], 
+applet:-moz-read-write, hr:-moz-read-write, button:-moz-read-write,
+isindex:-moz-read-write, select:-moz-read-write,
+a:link img, a:visited img, a:active img,
+a[name]:-moz-only-whitespace {
+  cursor: default;
+}
+
+:-moz-any-link:-moz-read-write {
+  cursor: text;
+  color : inherit;
+}
+
+/* Prevent clicking on links from going to link */
+a:link img, a:visited img {
+  -moz-user-input: none;
+}
+
+/* We suppress user/author's prefs for link underline, 
+   so we must set explicitly. This isn't good!
+*/
+a:link:-moz-read-write {
+  text-decoration: underline -moz-anchor-decoration;
+  color: -moz-hyperlinktext;
+}
+
+/* Allow double-clicks on these widgets to open properties dialogs
+   XXX except when the widget has disabled attribute */
+:-moz-read-write > input:-moz-read-only,
+:-moz-read-write > button:-moz-read-only,
+:-moz-read-write > textarea:-moz-read-only {
+  -moz-user-select: all !important;
+  -moz-user-input: auto !important;
+  -moz-user-focus: none !important;
+}
+
+/* XXX Still need a better way of blocking other events to these widgets */
+select:-moz-read-write,
+:-moz-read-write > input[disabled],
+:-moz-read-write > input[type="checkbox"],
+:-moz-read-write > input[type="radio"],
+:-moz-read-write > input[type="file"],
+input[contenteditable="true"][disabled],
+input[contenteditable="true"][type="checkbox"],
+input[contenteditable="true"][type="radio"],
+input[contenteditable="true"][type="file"] {
+  -moz-user-select: all !important;
+  -moz-user-input: none !important;
+  -moz-user-focus: none !important;
+}
+
+isindex:-moz-read-write[prompt]
+{
+  -moz-user-select: none !important;
+  -moz-user-input: none !important;
+  -moz-user-focus: none !important;
+}
+
+:-moz-read-write > input[type="hidden"],
+input[contenteditable="true"][type="hidden"] {
+  border: 1px solid black !important;
+  visibility: visible !important;
+}
+
+label:-moz-read-write {
+    -moz-user-select: all !important;
+}
+
+::-moz-display-comboboxcontrol-frame {
+  -moz-user-select: text !important;
+}
+
+option:-moz-read-write {
+  -moz-user-select: text !important;
+}
+
+/* the following rules are for Image Resizing */
+
+span[\_moz_anonclass="mozResizer"] {
+  width: 5px;
+  height: 5px;
+  position: absolute;
+  border: 1px black solid;
+  background-color: white;
+  -moz-user-select: none;
+  z-index: 2147483646; /* max value -1 for this property */
+}
+
+/* we can't use :active below */
+span[\_moz_anonclass="mozResizer"][\_moz_activated],
+span[\_moz_anonclass="mozResizer"]:hover {
+  background-color: black;
+}
+
+span[\_moz_anonclass="mozResizer"].hidden,
+span[\_moz_anonclass="mozResizingShadow"].hidden,
+img[\_moz_anonclass="mozResizingShadow"].hidden,
+span[\_moz_anonclass="mozGrabber"].hidden,
+span[\_moz_anonclass="mozResizingInfo"].hidden,
+a[\_moz_anonclass="mozTableRemoveRow"].hidden,
+a[\_moz_anonclass="mozTableRemoveColumn"].hidden {
+  display: none !important;
+}
+
+span[\_moz_anonclass="mozResizer"][anonlocation="nw"] {
+  cursor: nw-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="n"] {
+  cursor: n-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="ne"] {
+  cursor: ne-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="w"] {
+  cursor: w-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="e"] {
+  cursor: e-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="sw"] {
+  cursor: sw-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="s"] {
+  cursor: s-resize;
+}
+span[\_moz_anonclass="mozResizer"][anonlocation="se"] {
+  cursor: se-resize;
+}
+
+span[\_moz_anonclass="mozResizingShadow"],
+img[\_moz_anonclass="mozResizingShadow"] {
+  outline: thin dashed black;
+  -moz-user-select: none;
+  opacity: 0.5;
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+}
+
+span[\_moz_anonclass="mozResizingInfo"] {
+  font-family: sans-serif;
+  font-size: x-small;
+  color: black;
+  background-color: #d0d0d0;
+  border: ridge 2px #d0d0d0;
+  padding: 2px;
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+}
+
+img[\_moz_resizing] {
+  outline: thin solid black;
+}
+
+*[\_moz_abspos] {
+  outline: silver ridge 2px;
+  z-index: 2147483645 !important; /* max value -2 for this property */
+}
+*[\_moz_abspos="white"] {
+  background-color: white !important;
+}
+*[\_moz_abspos="black"] {
+  background-color: black !important;
+}
+
+span[\_moz_anonclass="mozGrabber"] {
+  outline: ridge 2px silver;
+  padding: 2px;
+  position: absolute;
+  width: 12px;
+  height: 12px;
+  background-image: url("resource:/res/grabber.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none;
+  cursor: move;
+}
+
+/* INLINE TABLE EDITING */
+
+a[\_moz_anonclass="mozTableAddColumnBefore"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 4px;
+  height: 8px;
+  background-image: url("resource:/res/table-add-column-before.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddColumnBefore"]:hover {
+  background-image: url("resource:/res/table-add-column-before-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnBefore"]:active {
+  background-image: url("resource:/res/table-add-column-before-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 4px;
+  height: 8px;
+  background-image: url("resource:/res/table-add-column-after.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"]:hover {
+  background-image: url("resource:/res/table-add-column-after-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddColumnAfter"]:active {
+  background-image: url("resource:/res/table-add-column-after-active.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 8px;
+  height: 8px;
+  background-image: url("resource:/res/table-remove-column.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"]:hover {
+  background-image: url("resource:/res/table-remove-column-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveColumn"]:active {
+  background-image: url("resource:/res/table-remove-column-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 8px;
+  height: 4px;
+  background-image: url("resource:/res/table-add-row-before.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"]:hover {
+  background-image: url("resource:/res/table-add-row-before-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowBefore"]:active {
+  background-image: url("resource:/res/table-add-row-before-active.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 8px;
+  height: 4px;
+  background-image: url("resource:/res/table-add-row-after.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"]:hover {
+  background-image: url("resource:/res/table-add-row-after-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableAddRowAfter"]:active {
+  background-image: url("resource:/res/table-add-row-after-active.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"] {
+  position: absolute;
+  z-index: 2147483647; /* max value for this property */
+  text-decoration: none !important;
+  border: none 0px !important;
+  width: 8px;
+  height: 8px;
+  background-image: url("resource:/res/table-remove-row.gif");
+  background-repeat: no-repeat;
+  background-position: center center;
+  -moz-user-select: none !important;
+  -moz-user-focus: none !important;
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"]:hover {
+  background-image: url("resource:/res/table-remove-row-hover.gif");
+}
+
+a[\_moz_anonclass="mozTableRemoveRow"]:active {
+  background-image: url("resource:/res/table-remove-row-active.gif");
+}
new file mode 100644
--- /dev/null
+++ b/layout/style/designmode.css
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Disruptive Innovations.
+ * Portions created by the Initial Developer are Copyright (C) 2004
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Peter Van der Beken <peterv@propagandism.org>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+*|* {
+  -moz-user-modify: read-write;
+}
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -129,16 +129,21 @@ input > .anonymous-div {
   padding: 0px 1px;
   margin: 0px;
   /* XXXldb I'm not sure if we really want the 'text-decoration: inherit',
      but it's needed to make 'text-decoration' "work" on text inputs. */
   text-decoration: inherit;
   ime-mode: inherit;
 }
 
+input:-moz-read-write,
+textarea:-moz-read-write {
+  -moz-user-modify: read-write !important;
+}
+
 select {
   margin: 0;
   border-color: ThreeDFace;
   background-color: -moz-Field;
   color: -moz-FieldText;
   font: -moz-list;
   line-height: normal !important;
   white-space: nowrap !important;
--- a/toolkit/content/widgets/editor.xml
+++ b/toolkit/content/widgets/editor.xml
@@ -61,17 +61,17 @@
           })
         ]]>
       </field>
       <method name="makeEditable">
         <parameter name="editortype"/>
         <parameter name="waitForUrlLoad"/>
         <body>
         <![CDATA[
-          this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad);
+          this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad, true, false);
           this.setAttribute("editortype", editortype);
 
           this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
               .getInterface(Components.interfaces.nsIURIContentListener)
               .parentContentListener = this._editorContentListener;
         ]]>
         </body>
       </method>
--- a/xpfe/global/resources/content/bindings/editor.xml
+++ b/xpfe/global/resources/content/bindings/editor.xml
@@ -61,17 +61,17 @@
           })
         ]]>
       </field>
       <method name="makeEditable">
         <parameter name="editortype"/>
         <parameter name="waitForUrlLoad"/>
         <body>
         <![CDATA[
-          this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad);
+          this.editingSession.makeWindowEditable(this.contentWindow, editortype, waitForUrlLoad, true, false);
           this.setAttribute("editortype", editortype);
 
           this.docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
               .getInterface(Components.interfaces.nsIURIContentListener)
               .parentContentListener = this._editorContentListener;
         ]]>
         </body>
       </method>
--- a/xpinstall/packager/packages-os2
+++ b/xpinstall/packager/packages-os2
@@ -370,16 +370,18 @@ bin/greprefs/*
 bin/defaults/pref/*
 bin/defaults/autoconfig/*
 bin/res/hiddenWindow.html
 bin/res/ua.css
 bin/res/html.css
 bin/res/quirk.css
 bin/res/forms.css
 bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
 bin/res/grabber.gif
 bin/res/table-add-column-after-active.gif
 bin/res/table-add-column-after-hover.gif
 bin/res/table-add-column-after.gif
 bin/res/table-add-column-before-active.gif
 bin/res/table-add-column-before-hover.gif
 bin/res/table-add-column-before.gif
 bin/res/table-add-row-after-active.gif
--- a/xpinstall/packager/packages-static-unix
+++ b/xpinstall/packager/packages-static-unix
@@ -252,16 +252,18 @@ bin/res/unixcharset.properties
 bin/res/charsetData.properties
 bin/res/langGroups.properties
 bin/res/language.properties
 bin/res/ua.css
 bin/res/html.css
 bin/res/quirk.css
 bin/res/forms.css
 bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
 bin/res/grabber.gif
 bin/res/table-add-column-after-active.gif
 bin/res/table-add-column-after-hover.gif
 bin/res/table-add-column-after.gif
 bin/res/table-add-column-before-active.gif
 bin/res/table-add-column-before-hover.gif
 bin/res/table-add-column-before.gif
 bin/res/table-add-row-after-active.gif
--- a/xpinstall/packager/packages-static-win
+++ b/xpinstall/packager/packages-static-win
@@ -253,16 +253,18 @@ bin\defaults\pref\*
 bin\defaults\autoconfig\*
 bin\res\hiddenWindow.html
 bin\res\ua.css
 bin\res\html.css
 bin\res\quirk.css
 bin\res\forms.css
 bin\res\platform-forms.css
 bin\res\EditorOverride.css
+bin\res\contenteditable.css
+bin\res\designmode.css
 bin\res\viewsource.css
 bin\res\mathml.css
 bin\res\arrow.gif
 bin\res\arrowd.gif
 bin\res\loading-image.gif
 bin\res\broken-image.gif
 bin\res\html\*
 bin\res\fonts\*
--- a/xpinstall/packager/packages-unix
+++ b/xpinstall/packager/packages-unix
@@ -388,16 +388,18 @@ bin/res/unixcharset.properties
 bin/res/charsetData.properties
 bin/res/langGroups.properties
 bin/res/language.properties
 bin/res/ua.css
 bin/res/html.css
 bin/res/quirk.css
 bin/res/forms.css
 bin/res/EditorOverride.css
+bin/res/contenteditable.css
+bin/res/designmode.css
 bin/res/grabber.gif
 bin/res/table-add-column-after-active.gif
 bin/res/table-add-column-after-hover.gif
 bin/res/table-add-column-after.gif
 bin/res/table-add-column-before-active.gif
 bin/res/table-add-column-before-hover.gif
 bin/res/table-add-column-before.gif
 bin/res/table-add-row-after-active.gif