Bug 1261299 - Update the selection cache on repaint to handle when a pre-existing selection becomes active aka the current selection. draft
authorJimmy Wang <jimmyw22@gmail.com>
Wed, 27 Jul 2016 16:05:37 -0400
changeset 401295 5e75a99fe0a173d99b30dc9936928216eb301a39
parent 401294 52923cdfa21ff9482e74e52d5de4e454fe4e8856
child 401296 ae408eb774ffdabc07ed711e146d99679e835554
push id26424
push userjimmyw22@gmail.com
push dateTue, 16 Aug 2016 21:05:59 +0000
bugs1261299
milestone51.0a1
Bug 1261299 - Update the selection cache on repaint to handle when a pre-existing selection becomes active aka the current selection. MozReview-Commit-ID: HrTQnHd12rg
layout/generic/nsFrameSelection.h
layout/generic/nsSelection.cpp
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -368,17 +368,17 @@ public:
   nsresult ScrollSelectionIntoView(mozilla::SelectionType aSelectionType,
                                    SelectionRegion aRegion,
                                    int16_t aFlags) const;
 
   /** RepaintSelection repaints the selected frames that are inside the selection
    *  specified by aSelectionType.
    * @param aSelectionType The selection type what you want to repaint.
    */
-  nsresult RepaintSelection(mozilla::SelectionType aSelectionType) const;
+  nsresult RepaintSelection(mozilla::SelectionType aSelectionType);
 
   /** GetFrameForNodeOffset given a node and its child offset, return the nsIFrame and
    *  the offset into that frame. 
    * @param aNode input parameter for the node to look at
    * @param aOffset offset into above node.
    * @param aReturnOffset will contain offset into frame.
    */
   virtual nsIFrame* GetFrameForNodeOffset(nsIContent*        aNode,
@@ -676,16 +676,20 @@ private:
 
   uint32_t     GetBatching() const {return mBatching; }
   bool         GetNotifyFrames() const { return mNotifyFrames; }
   void         SetDirty(bool aDirty=true){if (mBatching) mChangesDuringBatching = aDirty;}
 
   // nsFrameSelection may get deleted when calling this,
   // so remember to use nsCOMPtr when needed.
   nsresult     NotifySelectionListeners(mozilla::SelectionType aSelectionType);
+  // Update the selection cache on repaint when the
+  // selection being repainted is not empty.
+  nsresult     UpdateSelectionCacheOnRepaintSelection(mozilla::dom::
+                                                      Selection* aSel);
 
   RefPtr<mozilla::dom::Selection>
     mDomSelections[mozilla::kPresentSelectionTypeCount];
 
   // Table selection support.
   nsITableCellLayout* GetCellLayout(nsIContent *aCellContent) const;
 
   nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1959,24 +1959,32 @@ nsFrameSelection::ScrollSelectionIntoVie
   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
   return mDomSelections[index]->ScrollIntoView(aRegion,
                                                verticalScroll,
                                                nsIPresShell::ScrollAxis(),
                                                flags);
 }
 
 nsresult
-nsFrameSelection::RepaintSelection(SelectionType aSelectionType) const
+nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
 {
   int8_t index = GetIndexFromSelectionType(aSelectionType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
   if (!mDomSelections[index])
     return NS_ERROR_NULL_POINTER;
   NS_ENSURE_STATE(mShell);
+
+// On macOS, update the selection cache to the new active selection
+// aka the current selection.
+#ifdef XP_MACOSX
+  if (aSelectionType == SelectionType::eNormal) {
+    UpdateSelectionCacheOnRepaintSelection(mDomSelections[index]);
+  }
+#endif
   return mDomSelections[index]->Repaint(mShell->GetPresContext());
 }
  
 nsIFrame*
 nsFrameSelection::GetFrameForNodeOffset(nsIContent*        aNode,
                                         int32_t            aOffset,
                                         CaretAssociateHint aHint,
                                         int32_t*           aReturnOffset) const
@@ -6507,16 +6515,54 @@ nsAutoCopyListener::NotifySelectionChang
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   // call the copy code
   return nsCopySupport::HTMLCopy(aSel, doc,
                                  mCachedClipboard, false);
 }
 
+/**
+ * See Bug 1288453.
+ *
+ * Update the selection cache on repaint to handle when a pre-existing
+ * selection becomes active aka the current selection.
+ *
+ * 1. Change the current selection by click n dragging another selection.
+ *   - Make a selection on content page. Make a selection in a text editor.
+ *   - You can click n drag the content selection to make it active again.
+ * 2. Change the current selection when switching to a tab with a selection.
+ *   - Make selection in tab.
+ *   - Switching tabs will make its respective selection active.
+ *
+ * Therefore, we only update the selection cache on a repaint
+ * if the current selection being repainted is not an empty selection.
+ *
+ * If the current selection is empty. The current selection cache
+ * would be cleared by nsAutoCopyListener::NotifySelectionChanged.
+ */
+nsresult
+nsFrameSelection::UpdateSelectionCacheOnRepaintSelection(Selection* aSel)
+{
+  nsIPresShell* ps = aSel->GetPresShell();
+  if (!ps) {
+    return NS_OK;
+  }
+  nsCOMPtr<nsIDocument> aDoc = ps->GetDocument();
+
+  bool collapsed;
+  if (aDoc && aSel &&
+      NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
+    return nsCopySupport::HTMLCopy(aSel, aDoc,
+                                   nsIClipboard::kSelectionCache, false);
+  }
+
+  return NS_OK;
+}
+
 // SelectionChangeListener
 
 SelectionChangeListener::RawRangeData::RawRangeData(const nsRange* aRange)
 {
   mozilla::ErrorResult rv;
   mStartParent = aRange->GetStartContainer(rv);
   rv.SuppressException();
   mEndParent = aRange->GetEndContainer(rv);