Bug 824080 HTML editor shouldn't set selection state as inactive at losing focus but the document has focus r=ehsan
authorMasayuki Nakano <masayuki@d-toybox.com>
Sat, 18 May 2013 09:07:46 +0900
changeset 132293 8a53b2daa50e60c1b736af2ca43012cd152f40cb
parent 132292 584cfff7da90920ed3a212fc470def2310b9efd4
child 132294 e4d4cbfcbfeacb36a41481302b9f6390795b9576
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersehsan
bugs824080
milestone24.0a1
Bug 824080 HTML editor shouldn't set selection state as inactive at losing focus but the document has focus r=ehsan
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/base/nsEditorEventListener.cpp
editor/reftests/824080-1-ref.html
editor/reftests/824080-1.html
editor/reftests/824080-2-ref.html
editor/reftests/824080-2.html
editor/reftests/824080-3-ref.html
editor/reftests/824080-3.html
editor/reftests/824080-4-ref.html
editor/reftests/824080-4.html
editor/reftests/824080-5-ref.html
editor/reftests/824080-5.html
editor/reftests/824080-6-ref.html
editor/reftests/824080-6.html
editor/reftests/824080-7-ref.html
editor/reftests/824080-7.html
editor/reftests/reftest.list
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -4952,16 +4952,73 @@ nsEditor::InitializeSelection(nsIDOMEven
     if (rangeCount == 0) {
       BeginningOfDocument();
     }
   }
 
   return NS_OK;
 }
 
+void
+nsEditor::FinalizeSelection()
+{
+  nsCOMPtr<nsISelectionController> selCon;
+  nsresult rv = GetSelectionController(getter_AddRefs(selCon));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsISelection> selection;
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                            getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  nsCOMPtr<nsISelectionPrivate> selectionPrivate = do_QueryInterface(selection);
+  NS_ENSURE_TRUE_VOID(selectionPrivate);
+
+  selectionPrivate->SetAncestorLimiter(nullptr);
+
+  nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+  NS_ENSURE_TRUE_VOID(presShell);
+
+  nsRefPtr<nsCaret> caret = presShell->GetCaret();
+  if (caret) {
+    caret->SetIgnoreUserModify(true);
+  }
+
+  selCon->SetCaretEnabled(false);
+
+  if (!HasIndependentSelection()) {
+    // If this editor doesn't have an independent selection, i.e., it must
+    // mean that it is an HTML editor, the selection controller is shared with
+    // presShell.  So, even this editor loses focus, other part of the document
+    // may still have focus.
+    nsCOMPtr<nsIDocument> doc = GetDocument();
+    ErrorResult ret;
+    if (!doc || !doc->HasFocus(ret)) {
+      // If the document already lost focus, mark the selection as disabled.
+      selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
+    } else {
+      // Otherwise, mark selection as normal because outside of a
+      // contenteditable element should be selected with normal selection
+      // color after here.
+      selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
+    }
+  } else if (IsFormWidget() || IsPasswordEditor() ||
+             IsReadonly() || IsDisabled() || IsInputFiltered()) {
+    // In <input> or <textarea>, the independent selection should be hidden
+    // while this editor doesn't have focus.
+    selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
+  } else {
+    // Otherwise, although we're not sure how this case happens, the
+    // independent selection should be marked as disabled.
+    selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
+  }
+
+  selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
+}
+
 dom::Element *
 nsEditor::GetRoot()
 {
   if (!mRootElement)
   {
     nsCOMPtr<nsIDOMElement> root;
 
     // Let GetRootElement() do the work
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -735,16 +735,21 @@ public:
   }
 
   bool IsTabbable() const
   {
     return IsSingleLineEditor() || IsPasswordEditor() || IsFormWidget() ||
            IsInteractionAllowed();
   }
 
+  bool HasIndependentSelection() const
+  {
+    return !!mSelConWeak;
+  }
+
   // Get the input event target. This might return null.
   virtual already_AddRefed<nsIContent> GetInputEventTargetContent() = 0;
 
   // Get the focused content, if we're focused.  Returns null otherwise.
   virtual already_AddRefed<nsIContent> GetFocusedContent();
 
   // Get the focused content for the argument of some nsIMEStateManager's
   // methods.
@@ -768,16 +773,19 @@ public:
   // element in the document has focus.
   virtual already_AddRefed<nsIContent> FindSelectionRoot(nsINode* aNode);
 
   // Initializes selection and caret for the editor.  If aEventTarget isn't
   // a host of the editor, i.e., the editor doesn't get focus, this does
   // nothing.
   nsresult InitializeSelection(nsIDOMEventTarget* aFocusEventTarget);
 
+  // Finalizes selection and caret for the editor.
+  void FinalizeSelection();
+
   // This method has to be called by nsEditorEventListener::Focus.
   // All actions that have to be done when the editor is focused needs to be
   // added here.
   void OnFocus(nsIDOMEventTarget* aFocusEventTarget);
 
   // Used to insert content from a data transfer into the editable area.
   // This is called for each item in the data transfer, with the index of
   // each item passed as aIndex.
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -892,55 +892,17 @@ nsEditorEventListener::Blur(nsIDOMEvent*
   nsIFocusManager* fm = nsFocusManager::GetFocusManager();
   NS_ENSURE_TRUE(fm, NS_OK);
 
   nsCOMPtr<nsIDOMElement> element;
   fm->GetFocusedElement(getter_AddRefs(element));
   if (element)
     return NS_OK;
 
-  // turn off selection and caret
-  nsCOMPtr<nsISelectionController>selCon;
-  mEditor->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(nullptr);
-    }
-
-    nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-    if (presShell) {
-      nsRefPtr<nsCaret> caret = presShell->GetCaret();
-      if (caret) {
-        caret->SetIgnoreUserModify(true);
-      }
-    }
-
-    selCon->SetCaretEnabled(false);
-
-    if(mEditor->IsFormWidget() || mEditor->IsPasswordEditor() ||
-       mEditor->IsReadonly() || mEditor->IsDisabled() ||
-       mEditor->IsInputFiltered())
-    {
-      selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off
-    }
-    else
-    {
-      selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
-    }
-
-    selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
-  }
-
+  mEditor->FinalizeSelection();
   return NS_OK;
 }
 
 void
 nsEditorEventListener::SpellCheckIfNeeded() {
   // If the spell check skip flag is still enabled from creation time,
   // disable it because focused editors are allowed to spell check.
   uint32_t currentFlags = 0;
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor = document.getElementById("editor");
+      document.getSelection().selectAllChildren(document.body);
+    }
+  </script>
+</head>
+<body onload="doTest();">
+<p>normal text</p>
+<div id="editor">editable text</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor = document.getElementById("editor");
+      editor.focus();
+      editor.blur();
+      document.getSelection().selectAllChildren(document.body);
+    }
+  </script>
+</head>
+<body onload="doTest();">
+<p>normal text</p>
+<div id="editor" contenteditable>editable text</div>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-2-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.getElementById("text"));
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor.focus();
+      editor.blur();
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-2.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.getElementById("text"));
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor.focus();
+      editor.blur();
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<script>document.designMode='on';</script><body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-3-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.getElementById("text"));
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor.focus();
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-3.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.getElementById("text"));
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor.focus();
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<script>document.designMode='on';</script><body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-4-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.body);
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<div>content editable</div>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-4.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor1 = document.getElementById("editor1");
+      editor1.focus();
+      editor1.blur();
+      document.getSelection().selectAllChildren(document.body);
+      var editor2 = document.getElementById("editor2");
+      var editorBody = editor2.contentDocument.body;
+      editor2.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor2.focus();
+      editor2.blur();
+    }
+  </script>
+</head>
+<body>
+<p>normal text</p>
+<div id="editor1" contenteditable>content editable</div>
+<iframe id="editor2" onload="doTest();"
+  src="data:text/html,<script>document.designMode='on';</script><body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-5-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.body);
+      var editor = document.getElementById("editor");
+      var editorBody = editor.contentDocument.body;
+      editor.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor.focus();
+    }
+  </script>
+</head>
+<body>
+<p id="text">normal text</p>
+<div>content editable</div>
+<iframe id="editor" onload="doTest();"
+  src="data:text/html,<body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-5.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor1 = document.getElementById("editor1");
+      editor1.focus();
+      editor1.blur();
+      document.getSelection().selectAllChildren(document.body);
+      var editor2 = document.getElementById("editor2");
+      var editorBody = editor2.contentDocument.body;
+      editor2.contentDocument.getSelection().selectAllChildren(editorBody);
+      editor2.focus();
+    }
+  </script>
+</head>
+<body>
+<p>normal text</p>
+<div id="editor1" contenteditable>content editable</div>
+<iframe id="editor2" onload="doTest();"
+  src="data:text/html,<script>document.designMode='on';</script><body>editable text</body>"></iframe>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-6-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor = document.getElementById("editor");
+      editor.focus();
+      editor.blur();
+    }
+  </script>
+</head>
+<body onload="doTest()">
+<p>normal text</p>
+<textarea id="editor">textarea</textarea>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-6.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.body);
+      var editor = document.getElementById("editor");
+      editor.focus();
+      editor.select();
+      editor.blur();
+    }
+  </script>
+</head>
+<body onload="doTest()">
+<p>normal text</p>
+<textarea id="editor">textarea</textarea>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-7-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      var editor = document.getElementById("editor");
+      editor.focus();
+      editor.selectionStart = 2;
+      editor.selectionEnd = 4;
+    }
+  </script>
+</head>
+<body onload="doTest()">
+<p>normal text</p>
+<textarea id="editor">textarea</textarea>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/editor/reftests/824080-7.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script type="text/javascript">
+    function doTest()
+    {
+      document.getSelection().selectAllChildren(document.body);
+      var editor = document.getElementById("editor");
+      editor.focus();
+      editor.selectionStart = 2;
+      editor.selectionEnd = 4;
+      editor.blur();
+      editor.focus();
+    }
+  </script>
+</head>
+<body onload="doTest()">
+<p>normal text</p>
+<textarea id="editor">textarea</textarea>
+</body>
+</html>
+
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -93,8 +93,18 @@ skip-if(Android||B2G) needs-focus == 462
 == readonly-editable.html readonly-editable-ref.html
 == dynamic-overflow-change.html dynamic-overflow-change-ref.html
 == 694880-1.html 694880-ref.html
 == 694880-2.html 694880-ref.html
 == 694880-3.html 694880-ref.html
 == 388980-1.html 388980-1-ref.html
 needs-focus == spellcheck-superscript-1.html spellcheck-superscript-1-ref.html
 skip-if(B2G) fails-if(Android) needs-focus != spellcheck-superscript-2.html spellcheck-superscript-2-ref.html # bug 783658
+== 824080-1.html 824080-1-ref.html
+== 824080-2.html 824080-2-ref.html
+== 824080-3.html 824080-3-ref.html
+!= 824080-2.html 824080-3.html
+== 824080-4.html 824080-4-ref.html
+== 824080-5.html 824080-5-ref.html
+!= 824080-4.html 824080-5.html
+== 824080-6.html 824080-6-ref.html
+== 824080-7.html 824080-7-ref.html
+!= 824080-6.html 824080-7.html