Bug 1061468 - Notify the editor when removing the focused element is its ancestor limiter. r=ehsan
authorMats Palmgren <mats@mozilla.com>
Tue, 09 Sep 2014 23:27:56 +0000
changeset 227657 004917420079b488f6d7c7ee1f8cc03ac9c99b5a
parent 227656 96a507248c53660de032c3e821ba7a612afa1cf6
child 227658 0ba3143c153cdfd967f5ed7e26bf2554ec5fc6e0
push idunknown
push userunknown
push dateunknown
reviewersehsan
bugs1061468
milestone35.0a1
Bug 1061468 - Notify the editor when removing the focused element is its ancestor limiter. r=ehsan
content/base/public/nsISelectionPrivate.idl
dom/base/nsFocusManager.cpp
editor/libeditor/nsEditor.cpp
editor/libeditor/nsEditor.h
editor/nsIEditor.idl
layout/generic/nsSelection.cpp
--- a/content/base/public/nsISelectionPrivate.idl
+++ b/content/base/public/nsISelectionPrivate.idl
@@ -23,23 +23,24 @@ template<class T> class nsTArray;
 
 [ptr] native nsIFrame(nsIFrame);
 [ptr] native RangeArray(nsTArray<nsRange*>);
 [ref] native constTextRangeStyleRef(const mozilla::TextRangeStyle);
 [ref] native nsPointRef(nsPoint);
 native nsDirection(nsDirection);
 native ScrollAxis(nsIPresShell::ScrollAxis);
 
-[scriptable, builtinclass, uuid(52629837-7b3f-4434-940d-a14de7ef9b7a)]
+[scriptable, builtinclass, uuid(5a82ee9a-35ce-11e4-8c3e-b7043d68ad70)]
 interface nsISelectionPrivate : nsISelection
  {
     const short ENDOFPRECEDINGLINE=0;
     const short STARTOFNEXTLINE=1;
 
     attribute boolean interlinePosition;
+    [noscript] attribute nsIContent ancestorLimiter;
 
     /* startBatchChanges
        match this up with endbatchChanges. will stop ui updates while multiple selection methods are called
     */
     [noscript] void  startBatchChanges();
 
     /* endBatchChanges
        match this up with startBatchChanges
@@ -76,18 +77,16 @@ interface nsISelectionPrivate : nsISelec
      */
     [noscript] attribute boolean canCacheFrameOffset;
 
     /* GetCachedOffsetForFrame
      * Returns cached value for nsTextFrame::GetPointFromOffset.
      */
     [noscript] void getCachedFrameOffset(in nsIFrame aFrame, in int32_t inOffset, in nsPointRef aPoint);
 
-    [noscript] void setAncestorLimiter(in nsIContent aContent);
-
     /**
      * Set the painting style for the range. The range must be a range in
      * the selection. The textRangeStyle will be used by text frame
      * when it is painting the selection.
      */
     [noscript] void setTextRangeStyle(in nsIDOMRange range,
                       in constTextRangeStyleRef textRangeStyle);
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -7,16 +7,17 @@
 
 #include "nsFocusManager.h"
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsGkAtoms.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "nsIDOMWindow.h"
+#include "nsIEditor.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsIHTMLDocument.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsLayoutUtils.h"
@@ -810,18 +811,17 @@ nsFocusManager::ContentRemoved(nsIDocume
   if (content && nsContentUtils::ContentIsDescendantOf(content, aContent)) {
     bool shouldShowFocusRing = window->ShouldShowFocusRing();
     window->SetFocusedNode(nullptr);
 
     // if this window is currently focused, clear the global focused
     // element as well, but don't fire any events.
     if (window == mFocusedWindow) {
       mFocusedContent = nullptr;
-    }
-    else {
+    } else {
       // Check if the node that was focused is an iframe or similar by looking
       // if it has a subdocument. This would indicate that this focused iframe
       // and its descendants will be going away. We will need to move the
       // focus somewhere else, so just clear the focus in the toplevel window
       // so that no element is focused.
       nsIDocument* subdoc = aDocument->GetSubDocumentFor(content);
       if (subdoc) {
         nsCOMPtr<nsIDocShell> docShell = subdoc->GetDocShell();
@@ -829,16 +829,37 @@ nsFocusManager::ContentRemoved(nsIDocume
           nsCOMPtr<nsPIDOMWindow> childWindow = docShell->GetWindow();
           if (childWindow && IsSameOrAncestor(childWindow, mFocusedWindow)) {
             ClearFocus(mActiveWindow);
           }
         }
       }
     }
 
+    // Notify the editor in case we removed its ancestor limiter.
+    if (content->IsEditable()) {
+      nsCOMPtr<nsIDocShell> docShell = aDocument->GetDocShell();
+      if (docShell) {
+        nsCOMPtr<nsIEditor> editor;
+        docShell->GetEditor(getter_AddRefs(editor));
+        if (editor) {
+          nsCOMPtr<nsISelection> s;
+          editor->GetSelection(getter_AddRefs(s));
+          nsCOMPtr<nsISelectionPrivate> selection = do_QueryInterface(s);
+          if (selection) {
+            nsCOMPtr<nsIContent> limiter;
+            selection->GetAncestorLimiter(getter_AddRefs(limiter));
+            if (limiter == content) {
+              editor->FinalizeSelection();
+            }
+          }
+        }
+      }
+    }
+
     NotifyFocusStateChange(content, shouldShowFocusRing, false);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -4801,40 +4801,40 @@ nsEditor::InitializeSelection(nsIDOMEven
     if (rangeCount == 0) {
       BeginningOfDocument();
     }
   }
 
   return NS_OK;
 }
 
-void
+NS_IMETHODIMP
 nsEditor::FinalizeSelection()
 {
   nsCOMPtr<nsISelectionController> selCon;
   nsresult rv = GetSelectionController(getter_AddRefs(selCon));
-  NS_ENSURE_SUCCESS_VOID(rv);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISelection> selection;
   rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
                             getter_AddRefs(selection));
-  NS_ENSURE_SUCCESS_VOID(rv);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsISelectionPrivate> selectionPrivate = do_QueryInterface(selection);
-  NS_ENSURE_TRUE_VOID(selectionPrivate);
+  NS_ENSURE_TRUE(selectionPrivate, rv);
 
   selectionPrivate->SetAncestorLimiter(nullptr);
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
-  NS_ENSURE_TRUE_VOID(presShell);
+  NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_INITIALIZED);
 
   selCon->SetCaretEnabled(false);
 
   nsFocusManager* fm = nsFocusManager::GetFocusManager();
-  NS_ENSURE_TRUE_VOID(fm);
+  NS_ENSURE_TRUE(fm, NS_ERROR_NOT_INITIALIZED);
   fm->UpdateCaretForCaretBrowsingMode();
 
   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();
@@ -4855,16 +4855,17 @@ nsEditor::FinalizeSelection()
     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);
+  return NS_OK;
 }
 
 dom::Element *
 nsEditor::GetRoot()
 {
   if (!mRootElement)
   {
     nsCOMPtr<nsIDOMElement> root;
--- a/editor/libeditor/nsEditor.h
+++ b/editor/libeditor/nsEditor.h
@@ -796,19 +796,16 @@ 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/nsIEditor.idl
+++ b/editor/nsIEditor.idl
@@ -16,17 +16,17 @@ interface nsIDocumentStateListener;
 interface nsIOutputStream;
 interface nsITransactionManager;
 interface nsITransaction;
 interface nsIEditorObserver;
 interface nsIEditActionListener;
 interface nsIInlineSpellChecker;
 interface nsITransferable;
 
-[scriptable, uuid(04714a01-e02f-4ef5-a388-612451d0db16)]
+[scriptable, uuid(a1ddae68-35d0-11e4-9329-cb55463f21c9)]
 
 interface nsIEditor  : nsISupports
 {
 %{C++
   typedef short EDirection;
   typedef short EStripWrappers;
 %}
   const short eNone = 0;
@@ -38,16 +38,21 @@ interface nsIEditor  : nsISupports
   const short eToEndOfLine = 6;
 
   const short eStrip = 0;
   const short eNoStrip = 1;
 
   readonly attribute nsISelection selection;
 
   /**
+   * Finalizes selection and caret for the editor.
+   */
+  [noscript] void finalizeSelection();
+
+  /**
    * Init is to tell the implementation of nsIEditor to begin its services
    * @param aDoc          The dom document interface being observed
    * @param aRoot         This is the root of the editable section of this
    *                      document. If it is null then we get root
    *                      from document body.
    * @param aSelCon       this should be used to get the selection location
    *                      (will be null for HTML editors)
    * @param aFlags        A bitmask of flags for specifying the behavior
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -4261,16 +4261,26 @@ Selection::GetCachedFrameOffset(nsIFrame
        mCachedOffsetForFrame->mLastContentOffset = inOffset; 
      }
   }
 
   return rv;
 }
 
 NS_IMETHODIMP
+Selection::GetAncestorLimiter(nsIContent** aContent)
+{
+  if (mFrameSelection) {
+    nsCOMPtr<nsIContent> c = mFrameSelection->GetAncestorLimiter();
+    c.forget(aContent);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 Selection::SetAncestorLimiter(nsIContent* aContent)
 {
   if (mFrameSelection)
     mFrameSelection->SetAncestorLimiter(aContent);
   return NS_OK;
 }
 
 RangeData*