LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Mar 2006 19:28:56 +0000 (19:28 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 30 Mar 2006 19:28:56 +0000 (19:28 +0000)
        Reviewed by darin

        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
        REGRESSION: Plain-text mode needed for contenteditable area used in new text field

        * fast/forms/plaintext-mode-1-expected.txt: Added.
        * fast/forms/plaintext-mode-1.html: Added.
        * fast/forms/plaintext-mode-2-expected.checksum: Added.
        * fast/forms/plaintext-mode-2-expected.png: Added.
        * fast/forms/plaintext-mode-2-expected.txt: Added.
        * fast/forms/plaintext-mode-2.html: Added.

WebCore:

        Reviewed by darin

        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
        REGRESSION: Plain-text mode needed for contenteditable area used in new text field

        * bridge/mac/WebCoreFrameBridge.h:
        * bridge/mac/WebCoreFrameBridge.mm:
        (-[WebCoreFrameBridge isSelectionEditable]):
        (-[WebCoreFrameBridge isSelectionRichlyEditable]):
        * css/CSSComputedStyleDeclaration.cpp:
        (WebCore::CSSComputedStyleDeclaration::getPropertyCSSValue):
        * css/CSSValueKeywords.in:
        * css/cssparser.cpp:
        (WebCore::CSSParser::parseValue):
        * dom/Node.cpp:
        (WebCore::Node::isContentRichlyEditable):
        * dom/Node.h:
        * editing/EditCommand.cpp:
        (WebCore::EditCommand::apply):
        * editing/JSEditor.cpp:
        * editing/ReplaceSelectionCommand.cpp:
        (WebCore::ReplacementFragment::ReplacementFragment):
        (WebCore::ReplaceSelectionCommand::doApply):
        * editing/Selection.h:
        (WebCore::Selection::rootEditableElement):
        (WebCore::Selection::isContentEditable):
        (WebCore::Selection::isContentRichlyEditable):
        * editing/SelectionController.h:
        (WebCore::SelectionController::rootEditableElement):
        (WebCore::SelectionController::isContentEditable):
        (WebCore::SelectionController::isContentRichlyEditable):
        * html/HTMLElement.cpp:
        (WebCore::HTMLElement::isContentEditable):
        (WebCore::HTMLElement::contentEditable):
        (WebCore::HTMLElement::setContentEditable):
        * rendering/RenderTextField.cpp:
        (WebCore::RenderTextField::createDivStyle):
        * rendering/render_style.h:
        (WebCore::):

WebKit:

        Reviewed by darin

        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
        REGRESSION: Plain-text mode needed for contenteditable area used in new text field

        * WebView/WebHTMLView.m:
        (-[WebHTMLView _canEditRichly]): Added.
        (-[WebHTMLView _canIncreaseSelectionListLevel]): Use _canEditRichly
        (-[WebHTMLView _canDecreaseSelectionListLevel]): Ditto.
        (-[WebHTMLView _increaseSelectionListLevel]):
        (-[WebHTMLView _decreaseSelectionListLevel]):
        (-[NSArray validateUserInterfaceItem:]):
        Split rich text editing actions off from ones that can be applied anywhere.
        (-[NSArray _applyStyleToSelection:withUndoAction:]):
        (-[NSArray _applyParagraphStyleToSelection:withUndoAction:]):
        (-[NSArray _alignSelectionUsingCSSValue:withUndoAction:]):
        * WebView/WebHTMLViewPrivate.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@13581 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/forms/plaintext-mode-1-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/plaintext-mode-1.html [new file with mode: 0644]
LayoutTests/fast/forms/plaintext-mode-2-expected.checksum [new file with mode: 0644]
LayoutTests/fast/forms/plaintext-mode-2-expected.png [new file with mode: 0644]
LayoutTests/fast/forms/plaintext-mode-2-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/plaintext-mode-2.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/bridge/mac/WebCoreFrameBridge.h
WebCore/bridge/mac/WebCoreFrameBridge.mm
WebCore/css/CSSComputedStyleDeclaration.cpp
WebCore/css/CSSValueKeywords.in
WebCore/css/cssparser.cpp
WebCore/dom/Node.cpp
WebCore/dom/Node.h
WebCore/editing/EditCommand.cpp
WebCore/editing/JSEditor.cpp
WebCore/editing/ReplaceSelectionCommand.cpp
WebCore/editing/Selection.h
WebCore/editing/SelectionController.h
WebCore/html/HTMLElement.cpp
WebCore/rendering/RenderTextField.cpp
WebCore/rendering/render_style.h
WebKit/ChangeLog
WebKit/WebView/WebHTMLView.m
WebKit/WebView/WebHTMLViewPrivate.h

index d188478..15883df 100644 (file)
@@ -1,3 +1,17 @@
+2006-03-30  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by darin
+        
+        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
+        REGRESSION: Plain-text mode needed for contenteditable area used in new text field
+
+        * fast/forms/plaintext-mode-1-expected.txt: Added.
+        * fast/forms/plaintext-mode-1.html: Added.
+        * fast/forms/plaintext-mode-2-expected.checksum: Added.
+        * fast/forms/plaintext-mode-2-expected.png: Added.
+        * fast/forms/plaintext-mode-2-expected.txt: Added.
+        * fast/forms/plaintext-mode-2.html: Added.
+
 2006-03-30  David Harrison  <harrison@apple.com>
 
         Reviewed by Justin.
diff --git a/LayoutTests/fast/forms/plaintext-mode-1-expected.txt b/LayoutTests/fast/forms/plaintext-mode-1-expected.txt
new file mode 100644 (file)
index 0000000..03bfeb3
--- /dev/null
@@ -0,0 +1,51 @@
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldDeleteDOMRange:range from 0 of #text > DIV to 5 of #text > DIV
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of #text > DIV to 5 of #text > DIV toDOMRange:range from 0 of #text > DIV to 0 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of #text > DIV to 0 of #text > DIV toDOMRange:range from 0 of #text > DIV to 5 of #text > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+Success: document.queryCommandEnabled("BackColor") == true
+Success: document.queryCommandEnabled("Bold") == false
+Success: document.queryCommandEnabled("Copy") == true
+Success: document.queryCommandEnabled("CreateLink") == false
+Success: document.queryCommandEnabled("Cut") == true
+Success: document.queryCommandEnabled("Delete") == true
+Success: document.queryCommandEnabled("FontName") == true
+Success: document.queryCommandEnabled("FontSize") == true
+Success: document.queryCommandEnabled("FontSizeDelta") == true
+Success: document.queryCommandEnabled("ForeColor") == true
+Success: document.queryCommandEnabled("ForwardDelete") == true
+Success: document.queryCommandEnabled("Indent") == false
+Success: document.queryCommandEnabled("InsertHTML") == false
+Success: document.queryCommandEnabled("InsertImage") == false
+Success: document.queryCommandEnabled("InsertLineBreak") == true
+Success: document.queryCommandEnabled("InsertParagraph") == true
+Success: document.queryCommandEnabled("InsertNewlineInQuotedContent") == false
+Success: document.queryCommandEnabled("InsertText") == true
+Success: document.queryCommandEnabled("Italic") == false
+Success: document.queryCommandEnabled("JustifyCenter") == false
+Success: document.queryCommandEnabled("JustifyFull") == false
+Success: document.queryCommandEnabled("JustifyLeft") == false
+Success: document.queryCommandEnabled("JustifyNone") == false
+Success: document.queryCommandEnabled("JustifyRight") == false
+Success: document.queryCommandEnabled("Outdent") == false
+Success: document.queryCommandEnabled("Paste") == true
+Success: document.queryCommandEnabled("PasteAndMatchStyle") == true
+Success: document.queryCommandEnabled("SelectAll") == true
+Success: document.queryCommandEnabled("Strikethrough") == false
+Success: document.queryCommandEnabled("Subscript") == false
+Success: document.queryCommandEnabled("Superscript") == false
+Success: document.queryCommandEnabled("Underline") == false
+Success: document.queryCommandEnabled("Unlink") == false
+Success: document.queryCommandEnabled("Unselect") == true
+Success: document.queryCommandEnabled("Transpose") == true
+Success: document.execCommand("Cut") == true
+Success: document.queryCommandEnabled("Undo") == true
+Success: document.execCommand("Undo") == true
+Success: document.queryCommandEnabled("Redo") == true
+
diff --git a/LayoutTests/fast/forms/plaintext-mode-1.html b/LayoutTests/fast/forms/plaintext-mode-1.html
new file mode 100644 (file)
index 0000000..00feccd
--- /dev/null
@@ -0,0 +1,72 @@
+<script>
+function log(message) {
+    var li = document.createElement("li");
+    li.appendChild(document.createTextNode(message));
+    var console = document.getElementById("console");
+    console.appendChild(li);
+}
+
+function assert(func, args, expected) {
+    var f = func + '("' + args + '")';
+    var result = eval(f);
+    if (result != expected)
+        log('Failure: ' + f + ' was ' + result + ', expected: ' + expected);
+    else
+        log('Success: ' + f + ' == ' + result);
+}
+</script>
+
+<input id="textfield" type="text" style="width: 300" value="hello world">
+<ol id="console"></ol>
+<script>
+if (window.layoutTestController)
+    window.layoutTestController.dumpAsText();
+    
+var e = document.getElementById("textfield");
+e.focus();
+e.setSelectionRange(0, 5);
+
+assert("document.queryCommandEnabled", "BackColor", true);
+assert("document.queryCommandEnabled", "Bold", false);
+assert("document.queryCommandEnabled", "Copy", true);
+assert("document.queryCommandEnabled", "CreateLink", false);
+assert("document.queryCommandEnabled", "Cut", true);
+assert("document.queryCommandEnabled", "Delete", true);
+assert("document.queryCommandEnabled", "FontName", true);
+assert("document.queryCommandEnabled", "FontSize", true);
+assert("document.queryCommandEnabled", "FontSizeDelta", true);
+assert("document.queryCommandEnabled", "ForeColor", true);
+assert("document.queryCommandEnabled", "ForwardDelete", true);
+assert("document.queryCommandEnabled", "Indent", false);
+assert("document.queryCommandEnabled", "InsertHTML", false);
+assert("document.queryCommandEnabled", "InsertImage", false);
+assert("document.queryCommandEnabled", "InsertLineBreak", true);
+assert("document.queryCommandEnabled", "InsertParagraph", true);
+assert("document.queryCommandEnabled", "InsertNewlineInQuotedContent", false);
+assert("document.queryCommandEnabled", "InsertText", true);
+assert("document.queryCommandEnabled", "Italic", false);
+assert("document.queryCommandEnabled", "JustifyCenter", false);
+assert("document.queryCommandEnabled", "JustifyFull", false);
+assert("document.queryCommandEnabled", "JustifyLeft", false);
+assert("document.queryCommandEnabled", "JustifyNone", false);
+assert("document.queryCommandEnabled", "JustifyRight", false);
+assert("document.queryCommandEnabled", "Outdent", false);
+assert("document.queryCommandEnabled", "Paste", true);
+assert("document.queryCommandEnabled", "PasteAndMatchStyle", true);
+assert("document.queryCommandEnabled", "SelectAll", true);
+assert("document.queryCommandEnabled", "Strikethrough", false);
+assert("document.queryCommandEnabled", "Subscript", false);
+assert("document.queryCommandEnabled", "Superscript", false);
+assert("document.queryCommandEnabled", "Underline", false);
+assert("document.queryCommandEnabled", "Unlink", false);
+assert("document.queryCommandEnabled", "Unselect", true);
+
+e.setSelectionRange(3, 3);
+assert("document.queryCommandEnabled", "Transpose", true);
+
+e.setSelectionRange(0, 5);
+assert("document.execCommand", "Cut", true);
+assert("document.queryCommandEnabled", "Undo", true);
+assert("document.execCommand", "Undo", true);
+assert("document.queryCommandEnabled", "Redo", true);
+</script>
\ No newline at end of file
diff --git a/LayoutTests/fast/forms/plaintext-mode-2-expected.checksum b/LayoutTests/fast/forms/plaintext-mode-2-expected.checksum
new file mode 100644 (file)
index 0000000..4b96f57
--- /dev/null
@@ -0,0 +1 @@
+8c4bafcae73964c46a0ffeb93f8c307a
\ No newline at end of file
diff --git a/LayoutTests/fast/forms/plaintext-mode-2-expected.png b/LayoutTests/fast/forms/plaintext-mode-2-expected.png
new file mode 100644 (file)
index 0000000..779563e
Binary files /dev/null and b/LayoutTests/fast/forms/plaintext-mode-2-expected.png differ
diff --git a/LayoutTests/fast/forms/plaintext-mode-2-expected.txt b/LayoutTests/fast/forms/plaintext-mode-2-expected.txt
new file mode 100644 (file)
index 0000000..f9e3aef
--- /dev/null
@@ -0,0 +1,49 @@
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: shouldInsertNode:#document-fragment replacingDOMRange:range from 0 of DIV to 0 of DIV givenAction:WebViewInsertActionPasted
+EDITING DELEGATE: shouldChangeSelectedDOMRange:range from 0 of DIV > DIV to 0 of DIV > DIV toDOMRange:range from 94 of #text > DIV > DIV to 94 of #text > DIV > DIV affinity:NSSelectionAffinityDownstream stillSelecting:FALSE
+EDITING DELEGATE: webViewDidChangeSelection:WebViewDidChangeSelectionNotification
+EDITING DELEGATE: webViewDidChange:WebViewDidChangeNotification
+layer at (0,0) size 800x600
+  RenderCanvas at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x576
+      RenderBlock (anonymous) at (0,0) size 784x23
+        RenderTextField {INPUT} at (0,2) size 600x19 [bgcolor=#FFFFFF] [border: (2px inset #000000)]
+        RenderText {TEXT} at (600,2) size 4x18
+          text run at (600,2) width 4: " "
+        RenderBR {BR} at (0,0) size 0x0
+      RenderBlock {DIV} at (0,23) size 784x18
+        RenderText {TEXT} at (0,0) size 32x18
+          text run at (0,0) width 32: "This "
+        RenderInline {B} at (0,0) size 66x18
+          RenderText {TEXT} at (32,0) size 43x18
+            text run at (32,0) width 43: "styled "
+          RenderInline {I} at (0,0) size 23x18
+            RenderText {TEXT} at (75,0) size 23x18
+              text run at (75,0) width 23: "text"
+        RenderText {TEXT} at (98,0) size 35x18
+          text run at (98,0) width 35: ", and "
+        RenderInline {A} at (0,0) size 24x18 [color=#0000EE]
+          RenderText {TEXT} at (133,0) size 24x18
+            text run at (133,0) width 24: "link"
+        RenderText {TEXT} at (157,0) size 403x18
+          text run at (157,0) width 205: " will be pasted into the textfield. "
+          text run at (362,0) width 198: "All richness should be stripped."
+      RenderBlock {OL} at (0,57) size 784x36
+        RenderListItem {LI} at (40,0) size 744x18
+          RenderListMarker at (0,0) size 0x14
+          RenderText {TEXT} at (0,0) size 328x18
+            text run at (0,0) width 328: "Success: document.execCommand(\"Copy\") == true"
+        RenderListItem {LI} at (40,18) size 744x18
+          RenderListMarker at (0,0) size 0x14
+          RenderText {TEXT} at (0,0) size 326x18
+            text run at (0,0) width 326: "Success: document.execCommand(\"Paste\") == true"
+layer at (11,13) size 594x13
+  RenderBlock {DIV} at (3,3) size 594x13
+    RenderBlock {DIV} at (1,0) size 592x13
+      RenderText {TEXT} at (0,0) size 480x13
+        text run at (0,0) width 480: "This styled text, and link will be pasted into the textfield. All richness should be stripped."
+caret: position 94 of child 0 {TEXT} of child 0 {DIV} of document
diff --git a/LayoutTests/fast/forms/plaintext-mode-2.html b/LayoutTests/fast/forms/plaintext-mode-2.html
new file mode 100644 (file)
index 0000000..884d1e3
--- /dev/null
@@ -0,0 +1,42 @@
+<script>
+function log(message) {
+    var li = document.createElement("li");
+    li.appendChild(document.createTextNode(message));
+    var console = document.getElementById("console");
+    console.appendChild(li);
+}
+
+function assert(func, args, expected) {
+    var f = func + '("' + args + '")';
+    var result = eval(f);
+    if (result != expected)
+        log('Failure: ' + f + ' was ' + result + ', expected: ' + expected);
+    else
+        log('Success: ' + f + ' == ' + result);
+}
+</script>
+
+<input id="textfield" type="text" style="width: 600">
+<br>
+<div id="richcontent">This <b>styled <i>text</i></b>, and <a href="http://www.google.com"> link</a> will be pasted into the textfield.  All richness should be stripped.</div>
+<ol id="console"></ol>
+<script>
+    if (window.layoutTestController)
+        window.layoutTestController.waitUntilDone();
+
+var richcontent = document.getElementById("richcontent");
+var s = window.getSelection();
+s.setBaseAndExtent(richcontent, 0, richcontent, richcontent.childNodes.length);
+assert("document.execCommand", "Copy", true);
+
+var e = document.getElementById("textfield");
+e.focus();
+e.setSelectionRange(0, 0);
+
+window.setTimeout(paste, 500);
+function paste() {
+    assert("document.execCommand", "Paste", true);
+    if (window.layoutTestController)
+        window.layoutTestController.notifyDone();
+}
+</script>
\ No newline at end of file
index eb256ad..64b2ea9 100644 (file)
@@ -1,3 +1,45 @@
+2006-03-30  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by darin
+        
+        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
+        REGRESSION: Plain-text mode needed for contenteditable area used in new text field
+
+        * bridge/mac/WebCoreFrameBridge.h:
+        * bridge/mac/WebCoreFrameBridge.mm:
+        (-[WebCoreFrameBridge isSelectionEditable]):
+        (-[WebCoreFrameBridge isSelectionRichlyEditable]):
+        * css/CSSComputedStyleDeclaration.cpp:
+        (WebCore::CSSComputedStyleDeclaration::getPropertyCSSValue):
+        * css/CSSValueKeywords.in:
+        * css/cssparser.cpp:
+        (WebCore::CSSParser::parseValue):
+        * dom/Node.cpp:
+        (WebCore::Node::isContentRichlyEditable):
+        * dom/Node.h:
+        * editing/EditCommand.cpp:
+        (WebCore::EditCommand::apply):
+        * editing/JSEditor.cpp:
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::ReplacementFragment::ReplacementFragment):
+        (WebCore::ReplaceSelectionCommand::doApply):
+        * editing/Selection.h:
+        (WebCore::Selection::rootEditableElement):
+        (WebCore::Selection::isContentEditable):
+        (WebCore::Selection::isContentRichlyEditable):
+        * editing/SelectionController.h:
+        (WebCore::SelectionController::rootEditableElement):
+        (WebCore::SelectionController::isContentEditable):
+        (WebCore::SelectionController::isContentRichlyEditable):
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::isContentEditable):
+        (WebCore::HTMLElement::contentEditable):
+        (WebCore::HTMLElement::setContentEditable):
+        * rendering/RenderTextField.cpp:
+        (WebCore::RenderTextField::createDivStyle):
+        * rendering/render_style.h:
+        (WebCore::):
+        
 2006-03-30  David Harrison  <harrison@apple.com>
 
         Reviewed by Justin.
index 7225779..10a3b48 100644 (file)
@@ -320,6 +320,7 @@ typedef enum {
 - (DOMHTMLElement *)frameElement;
 
 - (BOOL)isSelectionEditable;
+- (BOOL)isSelectionRichlyEditable;
 - (WebSelectionState)selectionState;
 
 - (NSAttributedString *)selectedAttributedString;
index a5d0adc..c221c23 100644 (file)
@@ -744,9 +744,12 @@ static inline WebCoreFrameBridge *bridge(Frame *frame)
 
 - (BOOL)isSelectionEditable
 {
-    // EDIT FIXME: This needs to consider the entire selected range
-    Node *startNode = m_frame->selection().start().node();
-    return startNode ? startNode->isContentEditable() : NO;
+    return m_frame->selection().isContentEditable();
+}
+
+- (BOOL)isSelectionRichlyEditable
+{
+    return m_frame->selection().isContentRichlyEditable();
 }
 
 - (WebSelectionState)selectionState
index 56640d8..c62d964 100644 (file)
@@ -828,6 +828,8 @@ PassRefPtr<CSSValue> CSSComputedStyleDeclaration::getPropertyCSSValue(int proper
                 return new CSSPrimitiveValue(CSS_VAL_READ_ONLY);
             case READ_WRITE:
                 return new CSSPrimitiveValue(CSS_VAL_READ_WRITE);
+            case READ_WRITE_PLAINTEXT_ONLY:
+                return new CSSPrimitiveValue(CSS_VAL_READ_WRITE_PLAINTEXT_ONLY);
         }
         ASSERT_NOT_REACHED();
         return 0;
index b0af53f..41d8e59 100644 (file)
@@ -379,6 +379,7 @@ unfurl
 #
 read-only
 read-write
+read-write-plaintext-only
 
 #
 # CSS_PROP__KHTML_USER_DRAG
index bd6c9bf..77ee461 100644 (file)
@@ -994,7 +994,7 @@ bool CSSParser::parseValue(int propId, bool important)
             valid_primitive = true;
         break;
     case CSS_PROP__KHTML_USER_MODIFY: // read-only | read-write
-        if (id == CSS_VAL_READ_ONLY || id == CSS_VAL_READ_WRITE)
+        if (id == CSS_VAL_READ_ONLY || id == CSS_VAL_READ_WRITE || CSS_VAL_READ_WRITE_PLAINTEXT_ONLY)
             valid_primitive = true;
         break;
     case CSS_PROP__KHTML_USER_SELECT: // auto | none | text
index c842d1c..606ad59 100644 (file)
@@ -326,6 +326,12 @@ bool Node::isContentEditable() const
     return parent() && parent()->isContentEditable();
 }
 
+bool Node::isContentRichlyEditable() const
+{
+    Node* root = rootEditableElement();
+    return root && root->renderer() && root->renderer()->style()->userModify() != READ_WRITE_PLAINTEXT_ONLY;
+}
+
 IntRect Node::getRect() const
 {
     int _x, _y;
index 6e086fc..773fc3c 100644 (file)
@@ -243,6 +243,7 @@ public:
     virtual bool isIndeterminate() const { return false; }
 
     virtual bool isContentEditable() const;
+    virtual bool isContentRichlyEditable() const;
     virtual IntRect getRect() const;
 
     enum StyleChange { NoChange, NoInherit, Inherit, Detach, Force };
index a629abd..bf7585c 100644 (file)
@@ -206,7 +206,25 @@ void EditCommand::apply()
     ASSERT(state() == NotApplied);
  
     Frame *frame = m_document->frame();
-
+    
+    bool topLevel = !isCompositeStep();
+    if (topLevel) {
+        if (!endingSelection().isContentRichlyEditable()) {
+            switch (editingAction()) {
+                case EditActionTyping:
+                case EditActionPaste:
+                case EditActionDrag:
+                case EditActionSetWritingDirection:
+                case EditActionCut:
+                case EditActionUnspecified:
+                    break;
+                default:
+                    ASSERT_NOT_REACHED();
+                    return;
+            }
+        }
+    }
+    
     doApply();
     
     m_state = Applied;
@@ -216,7 +234,7 @@ void EditCommand::apply()
     if (!preservesTypingStyle())
         setTypingStyle(0);
 
-    if (!isCompositeStep()) {
+    if (topLevel) {
         updateLayout();
         EditCommandPtr cmd(this);
         frame->appliedEditing(cmd);
index 65b9f91..eb5e4b6 100644 (file)
@@ -435,11 +435,26 @@ bool enabled(Frame *frame)
     return true;
 }
 
+bool enabledAnyCaret(Frame *frame)
+{
+    return frame->selection().isCaret() && frame->selection().isContentEditable();
+}
+
 bool enabledAnySelection(Frame *frame)
 {
     return frame->selection().isCaretOrRange();
 }
 
+bool enabledAnyEditableSelection(Frame *frame)
+{
+    return frame->selection().isCaretOrRange() && frame->selection().isContentEditable();
+}
+
+bool enabledAnyRichlyEditableSelection(Frame *frame)
+{
+    return frame->selection().isCaretOrRange() && frame->selection().isContentRichlyEditable();
+}
+
 bool enabledPaste(Frame *frame)
 {
     return supportsPasteCommand && frame->canPaste();
@@ -450,11 +465,21 @@ bool enabledPasteAndMatchStyle(Frame *frame)
     return supportsPasteCommand && frame->canPaste();
 }
 
-bool enabledRangeSelection(Frame *frame)
+bool enabledAnyRangeSelection(Frame *frame)
 {
     return frame->selection().isRange();
 }
 
+bool enabledAnyEditableRangeSelection(Frame *frame)
+{
+    return frame->selection().isRange() && frame->selection().isContentEditable();
+}
+
+bool enabledAnyRichlyEditableRangeSelection(Frame *frame)
+{
+    return frame->selection().isRange() && frame->selection().isContentRichlyEditable();
+}
+
 bool enabledRedo(Frame *frame)
 {
     return frame->canRedo();
@@ -560,43 +585,43 @@ CommandMap *createCommandDictionary()
 
     static const EditorCommand commands[] = {
 
-        { "BackColor", { execBackColor, enabled, stateNone, valueBackColor } },
-        { "Bold", { execBold, enabledAnySelection, stateBold, valueNull } },
-        { "Copy", { execCopy, enabledRangeSelection, stateNone, valueNull } },
-        { "CreateLink", { execCreateLink, enabledRangeSelection, stateNone, valueNull } },
-        { "Cut", { execCut, enabledRangeSelection, stateNone, valueNull } },
-        { "Delete", { execDelete, enabledAnySelection, stateNone, valueNull } },
+        { "BackColor", { execBackColor, enabledAnyRangeSelection, stateNone, valueBackColor } },
+        { "Bold", { execBold, enabledAnyRichlyEditableSelection, stateBold, valueNull } },
+        { "Copy", { execCopy, enabledAnyRangeSelection, stateNone, valueNull } },
+        { "CreateLink", { execCreateLink, enabledAnyRichlyEditableRangeSelection, stateNone, valueNull } },
+        { "Cut", { execCut, enabledAnyEditableRangeSelection, stateNone, valueNull } },
+        { "Delete", { execDelete, enabledAnyEditableSelection, stateNone, valueNull } },
         { "FontName", { execFontName, enabledAnySelection, stateNone, valueFontName } },
         { "FontSize", { execFontSize, enabledAnySelection, stateNone, valueFontSize } },
         { "FontSizeDelta", { execFontSizeDelta, enabledAnySelection, stateNone, valueFontSizeDelta } },
         { "ForeColor", { execForeColor, enabledAnySelection, stateNone, valueForeColor } },
-        { "ForwardDelete", { execForwardDelete, enabledAnySelection, stateNone, valueNull } },
-        { "Indent", { execIndent, enabledAnySelection, stateNone, valueNull } },
-        { "InsertHTML", { execInsertHTML, enabledAnySelection, stateNone, valueNull } },
-        { "InsertImage", { execInsertImage, enabledAnySelection, stateNone, valueNull } },
-        { "InsertLineBreak", { execInsertLineBreak, enabledAnySelection, stateNone, valueNull } },
-        { "InsertParagraph", { execInsertParagraph, enabledAnySelection, stateNone, valueNull } },
-        { "InsertNewlineInQuotedContent", { execInsertNewlineInQuotedContent, enabledAnySelection, stateNone, valueNull } },
-        { "InsertText", { execInsertText, enabledAnySelection, stateNone, valueNull } },
-        { "Italic", { execItalic, enabledAnySelection, stateItalic, valueNull } },
-        { "JustifyCenter", { execJustifyCenter, enabledAnySelection, stateNone, valueNull } },
-        { "JustifyFull", { execJustifyFull, enabledAnySelection, stateNone, valueNull } },
-        { "JustifyLeft", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
-        { "JustifyNone", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
-        { "JustifyRight", { execJustifyRight, enabledAnySelection, stateNone, valueNull } },
-        { "Outdent", { execOutdent, enabledAnySelection, stateNone, valueNull } },
+        { "ForwardDelete", { execForwardDelete, enabledAnyEditableSelection, stateNone, valueNull } },
+        { "Indent", { execIndent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "InsertHTML", { execInsertHTML, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "InsertImage", { execInsertImage, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "InsertLineBreak", { execInsertLineBreak, enabledAnyEditableSelection, stateNone, valueNull } },
+        { "InsertParagraph", { execInsertParagraph, enabledAnyEditableSelection, stateNone, valueNull } },
+        { "InsertNewlineInQuotedContent", { execInsertNewlineInQuotedContent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "InsertText", { execInsertText, enabledAnyEditableSelection, stateNone, valueNull } },
+        { "Italic", { execItalic, enabledAnyRichlyEditableSelection, stateItalic, valueNull } },
+        { "JustifyCenter", { execJustifyCenter, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "JustifyFull", { execJustifyFull, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "JustifyLeft", { execJustifyLeft, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "JustifyNone", { execJustifyLeft, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "JustifyRight", { execJustifyRight, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
+        { "Outdent", { execOutdent, enabledAnyRichlyEditableSelection, stateNone, valueNull } },
         { "Paste", { execPaste, enabledPaste, stateNone, valueNull } },
         { "PasteAndMatchStyle", { execPasteAndMatchStyle, enabledPasteAndMatchStyle, stateNone, valueNull } },
         { "Print", { execPrint, enabled, stateNone, valueNull } },
         { "Redo", { execRedo, enabledRedo, stateNone, valueNull } },
         { "SelectAll", { execSelectAll, enabled, stateNone, valueNull } },
-        { "Strikethrough", { execStrikethrough, enabledAnySelection, stateStrikethrough, valueNull } },
-        { "Subscript", { execSubscript, enabledAnySelection, stateSubscript, valueNull } },
-        { "Superscript", { execSuperscript, enabledAnySelection, stateSuperscript, valueNull } },
-        { "Transpose", { execTranspose, enabled, stateNone, valueNull } },
-        { "Underline", { execUnderline, enabledAnySelection, stateUnderline, valueNull } },
+        { "Strikethrough", { execStrikethrough, enabledAnyRichlyEditableSelection, stateStrikethrough, valueNull } },
+        { "Subscript", { execSubscript, enabledAnyRichlyEditableSelection, stateSubscript, valueNull } },
+        { "Superscript", { execSuperscript, enabledAnyRichlyEditableSelection, stateSuperscript, valueNull } },
+        { "Transpose", { execTranspose, enabledAnyCaret, stateNone, valueNull } },
+        { "Underline", { execUnderline, enabledAnyRichlyEditableSelection, stateUnderline, valueNull } },
         { "Undo", { execUndo, enabledUndo, stateNone, valueNull } },
-        { "Unlink", { execUnlink, enabledRangeSelection, stateNone, valueNull } },
+        { "Unlink", { execUnlink, enabledAnyRichlyEditableRangeSelection, stateNone, valueNull } },
         { "Unselect", { execUnselect, enabledAnySelection, stateNone, valueNull } }
 
         //
index 8936dd0..afd25fc 100644 (file)
@@ -43,6 +43,7 @@
 #include "HTMLInterchange.h"
 #include "htmlediting.h"
 #include "HTMLNames.h"
+#include "markup.h"
 #include "RenderObject.h"
 #include "SelectionController.h"
 #include "visible_units.h"
@@ -119,26 +120,28 @@ ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *f
         
     RefPtr<Node> holder = insertFragmentForTestRendering();
     
-    // Send khtmlBeforeTextInsertedEvent.  The event handler will update text if necessary.
-    if (m_document->frame()) {
-        Node* selectionStartNode = m_document->frame()->selection().start().node();
-        if (selectionStartNode && selectionStartNode->rootEditableElement()) {
-            RefPtr<Range> range = new Range(holder->document());
-            ExceptionCode ec = 0;
-            range->selectNodeContents(holder.get(), ec);
-            String text = plainText(range.get());
-            String newText = text.copy();
-            RefPtr<Event> evt = new BeforeTextInsertedEvent(newText);
-            selectionStartNode->rootEditableElement()->dispatchEvent(evt, ec, true);
-            if (text != newText) {
-                // If the event handler has changed the text, create a new holder node for test rendering
-                m_fragment->removeChildren();
-                m_fragment->appendChild(new Text(m_document.get(), newText), ec);
-                removeNode(holder);
-                holder = insertFragmentForTestRendering();
-            }
-        }
-    }
+    Element* editableRoot = document->frame() ? document->frame()->selection().rootEditableElement() : 0;
+    ASSERT(editableRoot);
+    if (!editableRoot)
+        return;
+
+    RefPtr<Range> range = new Range(holder->document());
+    ExceptionCode ec = 0;
+    range->selectNodeContents(holder.get(), ec);
+    ASSERT(ec == 0);
+    String text = plainText(range.get());
+    String newText = text.copy();
+    // Give the root a chance to change the text.
+    RefPtr<Event> evt = new BeforeTextInsertedEvent(newText);
+    editableRoot->dispatchEvent(evt, ec, true);
+    if (text != newText || !editableRoot->isContentRichlyEditable()) {
+        removeNode(holder);
+        m_fragment = createFragmentFromText(document, newText.deprecatedString());
+        holder = insertFragmentForTestRendering();
+     }
+    
+    if (!editableRoot->isContentRichlyEditable())
+        m_matchStyle = true;
     
     saveRenderingInfo(holder.get());
     removeUnrenderedNodes(holder.get());
@@ -497,6 +500,9 @@ void ReplaceSelectionCommand::doApply()
     Selection selection = endingSelection();
     ASSERT(selection.isCaretOrRange());
     
+    if (!selection.isContentRichlyEditable())
+        m_matchStyle = true;
+    
     if (m_matchStyle)
         m_insertionStyle = styleAtPosition(selection.start());
     
index 3bcb87a..cbbe091 100644 (file)
@@ -70,6 +70,10 @@ public:
     TextGranularity granularity() const { return m_granularity; }
 
     PassRefPtr<Range> toRange() const;
+    
+    Element* rootEditableElement() const { return start().node() ? start().node()->rootEditableElement() : 0; }
+    bool isContentEditable() const { return start().node() ? start().node()->isContentEditable() : false; }
+    bool isContentRichlyEditable() const { return start().node() ? start().node()->isContentRichlyEditable() : false; }
 
     void debugPosition() const;
 
index c47f16d..2bdfdbc 100644 (file)
@@ -74,6 +74,10 @@ public:
     SelectionController &operator=(const SelectionController &o);
     SelectionController &operator=(const VisiblePosition &r) { moveTo(r); return *this; }
 
+    Element* rootEditableElement() const { return selection().rootEditableElement(); }
+    bool isContentEditable() const { return selection().isContentEditable(); }
+    bool isContentRichlyEditable() const { return selection().isContentRichlyEditable(); }
+
     void moveTo(const Range *, EAffinity affinity);
     void moveTo(const VisiblePosition &);
     void moveTo(const VisiblePosition &, const VisiblePosition &);
index f2f5ce9..0a29f34 100644 (file)
@@ -449,7 +449,7 @@ bool HTMLElement::isContentEditable() const
             return false;
     }
     
-    return renderer()->style()->userModify() == READ_WRITE;
+    return renderer()->style()->userModify() == READ_WRITE || renderer()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY;
 }
 
 String HTMLElement::contentEditable() const 
@@ -464,6 +464,8 @@ String HTMLElement::contentEditable() const
             return "true";
         case READ_ONLY:
             return "false";
+        case READ_WRITE_PLAINTEXT_ONLY:
+            return "plaintext-only";
         default:
             return "inherit";
     }
@@ -488,6 +490,11 @@ void HTMLElement::setContentEditable(MappedAttribute* attr)
         attr->decl()->removeProperty(CSS_PROP_WORD_WRAP, false);
         attr->decl()->removeProperty(CSS_PROP__KHTML_NBSP_MODE, false);
         attr->decl()->removeProperty(CSS_PROP__KHTML_LINE_BREAK, false);
+    } else if (equalIgnoringCase(enabled, "plaintext-only")) {
+        addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_WRITE_PLAINTEXT_ONLY);
+        addCSSProperty(attr, CSS_PROP_WORD_WRAP, CSS_VAL_BREAK_WORD);
+        addCSSProperty(attr, CSS_PROP__KHTML_NBSP_MODE, CSS_VAL_SPACE);
+        addCSSProperty(attr, CSS_PROP__KHTML_LINE_BREAK, CSS_VAL_AFTER_WHITE_SPACE);
     }
 }
 
index 6109739..a80674f 100644 (file)
@@ -70,7 +70,7 @@ RenderStyle* RenderTextField::createDivStyle(RenderStyle* startStyle)
     divStyle->setDisplay(BLOCK);
     divStyle->setOverflow(OHIDDEN);
     divStyle->setWhiteSpace(NOWRAP);
-    divStyle->setUserModify(READ_WRITE);
+    divStyle->setUserModify(READ_WRITE_PLAINTEXT_ONLY);
     // We're adding this extra pixel of padding to match WinIE.
     divStyle->setPaddingLeft(Length(1, Fixed));
     divStyle->setPaddingRight(Length(1, Fixed));
index 641fa03..7cf0b5c 100644 (file)
@@ -638,7 +638,7 @@ struct BindingURI {
 // CSS3 User Modify Properties
 
 enum EUserModify {
-    READ_ONLY, READ_WRITE
+    READ_ONLY, READ_WRITE, READ_WRITE_PLAINTEXT_ONLY
 };
 
 // CSS3 User Drag Values
index b3ef630..af4a300 100644 (file)
@@ -1,3 +1,23 @@
+2006-03-30  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by darin
+        
+        http://bugzilla.opendarwin.org/show_bug.cgi?id=6989
+        REGRESSION: Plain-text mode needed for contenteditable area used in new text field
+
+        * WebView/WebHTMLView.m:
+        (-[WebHTMLView _canEditRichly]): Added.
+        (-[WebHTMLView _canIncreaseSelectionListLevel]): Use _canEditRichly
+        (-[WebHTMLView _canDecreaseSelectionListLevel]): Ditto.
+        (-[WebHTMLView _increaseSelectionListLevel]):
+        (-[WebHTMLView _decreaseSelectionListLevel]):
+        (-[NSArray validateUserInterfaceItem:]): 
+        Split rich text editing actions off from ones that can be applied anywhere.
+        (-[NSArray _applyStyleToSelection:withUndoAction:]):
+        (-[NSArray _applyParagraphStyleToSelection:withUndoAction:]):
+        (-[NSArray _alignSelectionUsingCSSValue:withUndoAction:]):
+        * WebView/WebHTMLViewPrivate.h:
+
 2006-03-29  Tim Omernick  <timo@apple.com>
 
         Reviewed by John Sullivan.
index cfd10dc..02c916c 100644 (file)
@@ -1451,6 +1451,11 @@ static WebHTMLView *lastHitView = nil;
     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
 }
 
+- (BOOL)_canEditRichly
+{
+    return [self _canEdit] && [[self _bridge] isSelectionRichlyEditable];
+}
+
 - (BOOL)_canAlterCurrentSelection
 {
     return [self _hasSelectionOrInsertionPoint] && [self _isEditable];
@@ -1581,17 +1586,17 @@ static WebHTMLView *lastHitView = nil;
 
 - (BOOL)_canIncreaseSelectionListLevel
 {
-    return ([self _canEdit] && [[self _bridge] canIncreaseSelectionListLevel]);
+    return ([self _canEditRichly] && [[self _bridge] canIncreaseSelectionListLevel]);
 }
 
 - (BOOL)_canDecreaseSelectionListLevel
 {
-    return ([self _canEdit] && [[self _bridge] canDecreaseSelectionListLevel]);
+    return ([self _canEditRichly] && [[self _bridge] canDecreaseSelectionListLevel]);
 }
 
 - (void)_increaseSelectionListLevel
 {
-    if (![self _canEdit])
+    if (![self _canEditRichly])
         return;
         
     WebFrameBridge *bridge = [self _bridge];
@@ -1600,7 +1605,7 @@ static WebHTMLView *lastHitView = nil;
 
 - (void)_decreaseSelectionListLevel
 {
-    if (![self _canEdit])
+    if (![self _canEditRichly])
         return;
         
     WebFrameBridge *bridge = [self _bridge];
@@ -1872,15 +1877,9 @@ static WebHTMLView *lastHitView = nil;
 {
     SEL action = [item action];
     WebFrameBridge *bridge = [self _bridge];
-    
-    if (action == @selector(alignCenter:)
-            || action == @selector(alignLeft:)
-            || action == @selector(alignJustified:)
-            || action == @selector(alignRight:)
-            || action == @selector(changeAttributes:)
-            || action == @selector(changeBaseWritingDirection:) // FIXME: check menu item based on writing direction
-            || action == @selector(changeColor:)
-            || action == @selector(changeFont:)
+  
+
+    if (action == @selector(changeBaseWritingDirection:) // FIXME: check menu item based on writing direction
             || action == @selector(changeSpelling:)
             || action == @selector(_changeSpellingFromMenu:)
             || action == @selector(checkSpelling:)
@@ -1947,6 +1946,14 @@ static WebHTMLView *lastHitView = nil;
             || action == @selector(yank:)
             || action == @selector(yankAndSelect:)) {
         return [self _canEdit];
+    } else if (action == @selector(alignCenter:)
+            || action == @selector(alignLeft:)
+            || action == @selector(alignJustified:)
+            || action == @selector(alignRight:)
+            || action == @selector(changeAttributes:)
+            || action == @selector(changeColor:)        
+            || action == @selector(changeFont:)) {
+        return [self _canEditRichly];
     } else if (action == @selector(capitalizeWord:)
                || action == @selector(lowercaseWord:)
                || action == @selector(uppercaseWord:)) {
@@ -1957,7 +1964,7 @@ static WebHTMLView *lastHitView = nil;
                || action == @selector(setMark:)) {
         return [self _hasSelection] || ([self _isEditable] && [self _hasInsertionPoint]);
     } else if (action == @selector(changeDocumentBackgroundColor:)) {
-        return [[self _webView] isEditable];
+        return [[self _webView] isEditable] && [self _canEditRichly];
     } else if (action == @selector(copy:)) {
         return [bridge mayDHTMLCopy] || [self _canCopy];
     } else if (action == @selector(cut:)) {
@@ -1983,7 +1990,7 @@ static WebHTMLView *lastHitView = nil;
             [style setVerticalAlign:@"sub"];
             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
         }
-        return [self _canEdit];
+        return [self _canEditRichly];
     } else if (action == @selector(superscript:)) {
         NSMenuItem *menuItem = (NSMenuItem *)item;
         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
@@ -1991,7 +1998,7 @@ static WebHTMLView *lastHitView = nil;
             [style setVerticalAlign:@"super"];
             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
         }
-        return [self _canEdit];
+        return [self _canEditRichly];
     } else if (action == @selector(underline:)) {
         NSMenuItem *menuItem = (NSMenuItem *)item;
         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
@@ -1999,7 +2006,7 @@ static WebHTMLView *lastHitView = nil;
             [style setProperty:@"-khtml-text-decorations-in-effect" :@"underline" :@""];
             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
         }
-        return [self _canEdit];
+        return [self _canEditRichly];
     } else if (action == @selector(unscript:)) {
         NSMenuItem *menuItem = (NSMenuItem *)item;
         if ([menuItem isKindOfClass:[NSMenuItem class]]) {
@@ -2007,7 +2014,7 @@ static WebHTMLView *lastHitView = nil;
             [style setVerticalAlign:@"baseline"];
             [menuItem setState:[[self _bridge] selectionHasStyle:style]];
         }
-        return [self _canEdit];
+        return [self _canEditRichly];
     } else if (action == @selector(_lookUpInDictionaryFromMenu:)) {
         return [self _hasSelection];
     }
@@ -3808,7 +3815,7 @@ done:
 
 - (void)_applyStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(WebUndoAction)undoAction
 {
-    if (style == nil || [style length] == 0 || ![self _canEdit])
+    if (style == nil || [style length] == 0 || ![self _canEditRichly])
         return;
     WebView *webView = [self _webView];
     WebFrameBridge *bridge = [self _bridge];
@@ -3819,7 +3826,7 @@ done:
 
 - (void)_applyParagraphStyleToSelection:(DOMCSSStyleDeclaration *)style withUndoAction:(WebUndoAction)undoAction
 {
-    if (style == nil || [style length] == 0 || ![self _canEdit])
+    if (style == nil || [style length] == 0 || ![self _canEditRichly])
         return;
     WebView *webView = [self _webView];
     WebFrameBridge *bridge = [self _bridge];
@@ -4208,7 +4215,7 @@ NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground co
 
 - (void)_alignSelectionUsingCSSValue:(NSString *)CSSAlignmentValue withUndoAction:(WebUndoAction)undoAction
 {
-    if (![self _canEdit])
+    if (![self _canEditRichly])
         return;
         
     DOMCSSStyleDeclaration *style = [self _emptyStyle];
index 5b06dca..fd4b156 100644 (file)
@@ -77,6 +77,7 @@
 - (BOOL)_canDelete;
 - (BOOL)_canPaste;
 - (BOOL)_canEdit;
+- (BOOL)_canEditRichly;
 - (BOOL)_canAlterCurrentSelection;
 - (BOOL)_hasSelection;
 - (BOOL)_hasSelectionOrInsertionPoint;