Bug 1261299 - Add a method nsCopySupport::SetSelectionCache instead of relying on the method used to set the os clipboard. draft
authorJimmy Wang <jimmyw22@gmail.com>
Tue, 09 Aug 2016 13:02:14 -0400
changeset 398771 230110b6e13e40066080ec414abd679d1cc78314
parent 398770 19f5240bfcca483d0f75935c7d9634d738cd25f3
child 527744 38cebd14cc0dd20c27573c9e7b55aa77fa8426de
push id25625
push userjimmyw22@gmail.com
push dateTue, 09 Aug 2016 18:29:23 +0000
bugs1261299
milestone51.0a1
Bug 1261299 - Add a method nsCopySupport::SetSelectionCache instead of relying on the method used to set the os clipboard. MozReview-Commit-ID: KvmdD2XqKBh
dom/base/nsCopySupport.cpp
dom/base/nsCopySupport.h
layout/generic/nsSelection.cpp
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -291,28 +291,228 @@ SelectionCopyHelper(nsISelection *aSel, 
       if (aTransferable != nullptr) {
         trans.swap(*aTransferable);
       }
     }
   }
   return rv;
 }
 
+static nsresult
+SelectionCopyHelper2(nsISelection *aSel, nsIDocument *aDoc,
+                    bool doPutOnClipboard, int16_t aClipboardID,
+                    uint32_t aFlags, nsITransferable ** aTransferable)
+{
+  // Clear the output parameter for the transferable, if provided.
+  if (aTransferable) {
+    *aTransferable = nullptr;
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIDocumentEncoder> docEncoder;
+  docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID);
+  NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
+
+  // note that we assign text/unicode as mime type, but in fact nsHTMLCopyEncoder
+  // ignore it and use text/html or text/plain depending where the selection
+  // is. if it is a selection into input/textarea element or in a html content
+  // with pre-wrap style : text/plain. Otherwise text/html.
+  // see nsHTMLCopyEncoder::SetSelection
+  nsAutoString mimeType;
+  mimeType.AssignLiteral(kUnicodeMime);
+
+  // Do the first and potentially trial encoding as preformatted and raw.
+  uint32_t flags = aFlags | nsIDocumentEncoder::OutputPreformatted
+                          | nsIDocumentEncoder::OutputRaw
+                          | nsIDocumentEncoder::OutputForPlainTextClipboardCopy;
+
+  nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
+  NS_ASSERTION(domDoc, "Need a document");
+
+  rv = docEncoder->Init(domDoc, mimeType, flags);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = docEncoder->SetSelection(aSel);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // SetSelection set the mime type to text/plain if the selection is inside a
+  // text widget.
+  rv = docEncoder->GetMimeType(mimeType);
+  NS_ENSURE_SUCCESS(rv, rv);
+  bool selForcedTextPlain = mimeType.EqualsLiteral(kTextMime);
+
+  nsAutoString buf;
+  rv = docEncoder->EncodeToString(buf);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = docEncoder->GetMimeType(mimeType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!selForcedTextPlain && mimeType.EqualsLiteral(kTextMime)) {
+    // SetSelection and EncodeToString use this case to signal that text/plain
+    // was forced because the document is either not an nsIHTMLDocument or it's
+    // XHTML.  We want to pretty print XHTML but not non-nsIHTMLDocuments.
+    nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(aDoc);
+    if (!htmlDoc) {
+      selForcedTextPlain = true;
+    }
+  }
+
+  // The mime type is ultimately text/html if the encoder successfully encoded
+  // the selection as text/html.
+  bool encodedTextHTML = mimeType.EqualsLiteral(kHTMLMime);
+
+  // First, prepare the text/plain clipboard flavor.
+  nsAutoString textPlainBuf;
+  if (selForcedTextPlain) {
+    // Nothing to do.  buf contains the final, preformatted, raw text/plain.
+    textPlainBuf.Assign(buf);
+  } else {
+    // Redo the encoding, but this time use pretty printing.
+    flags =
+      nsIDocumentEncoder::OutputSelectionOnly |
+      nsIDocumentEncoder::OutputAbsoluteLinks |
+      nsIDocumentEncoder::SkipInvisibleContent |
+      nsIDocumentEncoder::OutputDropInvisibleBreak |
+      (aFlags & (nsIDocumentEncoder::OutputNoScriptContent |
+                 nsIDocumentEncoder::OutputRubyAnnotation));
+
+    mimeType.AssignLiteral(kTextMime);
+    rv = docEncoder->Init(domDoc, mimeType, flags);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docEncoder->SetSelection(aSel);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docEncoder->EncodeToString(textPlainBuf);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  // Second, prepare the text/html flavor.
+  nsAutoString textHTMLBuf;
+  nsAutoString htmlParentsBuf;
+  nsAutoString htmlInfoBuf;
+  if (encodedTextHTML) {
+    // Redo the encoding, but this time use the passed-in flags.
+    // Don't allow wrapping of CJK strings.
+    mimeType.AssignLiteral(kHTMLMime);
+    rv = docEncoder->Init(domDoc, mimeType,
+                          aFlags |
+                          nsIDocumentEncoder::OutputDisallowLineBreaking);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docEncoder->SetSelection(aSel);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = docEncoder->EncodeToStringWithContext(htmlParentsBuf, htmlInfoBuf,
+                                               textHTMLBuf);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  if (aTransferable != nullptr) {
+    // Create a transferable for putting data on the Clipboard
+    nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID);
+    if (trans) {
+      trans->Init(aDoc->GetLoadContext());
+      if (encodedTextHTML) {
+        // Set up a format converter so that clipboard flavor queries work.
+        // This converter isn't really used for conversions.
+        nsCOMPtr<nsIFormatConverter> htmlConverter =
+          do_CreateInstance(kHTMLConverterCID);
+        trans->SetConverter(htmlConverter);
+
+        if (!textHTMLBuf.IsEmpty()) {
+          // Add the html DataFlavor to the transferable
+          rv = AppendString(trans, textHTMLBuf, kHTMLMime);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        // Add the htmlcontext DataFlavor to the transferable
+        // Even if parents is empty string, this flavor should
+        // be attached to the transferable
+        rv = AppendString(trans, htmlParentsBuf, kHTMLContext);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!htmlInfoBuf.IsEmpty()) {
+          // Add the htmlinfo DataFlavor to the transferable
+          rv = AppendString(trans, htmlInfoBuf, kHTMLInfo);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        if (!textPlainBuf.IsEmpty()) {
+          // unicode text
+          // Add the unicode DataFlavor to the transferable
+          // If we didn't have this, then nsDataObj::GetData matches text/unicode against
+          // the kURLMime flavour which is not desirable (eg. when pasting into Notepad)
+          rv = AppendString(trans, textPlainBuf, kUnicodeMime);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+
+        // Try and get source URI of the items that are being dragged
+        nsIURI *uri = aDoc->GetDocumentURI();
+        if (uri) {
+          nsAutoCString spec;
+          uri->GetSpec(spec);
+          if (!spec.IsEmpty()) {
+            nsAutoString shortcut;
+            AppendUTF8toUTF16(spec, shortcut);
+
+            // Add the URL DataFlavor to the transferable. Don't use kURLMime, as it will
+            // cause an unnecessary UniformResourceLocator to be added which confuses
+            // some apps eg. Outlook 2000 - (See Bug 315370). Don't use
+            // kURLDataMime, as it will cause a bogus 'url ' flavor to
+            // show up on the Mac clipboard, confusing other apps, like
+            // Terminal (see bug 336012).
+            rv = AppendString(trans, shortcut, kURLPrivateMime);
+            NS_ENSURE_SUCCESS(rv, rv);
+          }
+        }
+      } else {
+        if (!textPlainBuf.IsEmpty()) {
+          // Add the unicode DataFlavor to the transferable
+          rv = AppendString(trans, textPlainBuf, kUnicodeMime);
+          NS_ENSURE_SUCCESS(rv, rv);
+        }
+      }
+
+      // Return the transferable to the caller if requested.
+      if (aTransferable != nullptr) {
+        trans.swap(*aTransferable);
+      }
+    }
+  }
+  return rv;
+}
+
 nsresult
 nsCopySupport::HTMLCopy(nsISelection* aSel, nsIDocument* aDoc,
                         int16_t aClipboardID, bool aWithRubyAnnotation)
 {
   uint32_t flags = nsIDocumentEncoder::SkipInvisibleContent;
   if (aWithRubyAnnotation) {
     flags |= nsIDocumentEncoder::OutputRubyAnnotation;
   }
   return SelectionCopyHelper(aSel, aDoc, true, aClipboardID, flags, nullptr);
 }
 
 nsresult
+nsCopySupport::SetSelectionCache(nsISelection* aSel, nsIDocument* aDoc)
+{
+  nsresult rv;
+  nsCOMPtr<nsITransferable> transferable;
+  rv = SelectionCopyHelper2(aSel, aDoc, false, 0,
+                             nsIDocumentEncoder::SkipInvisibleContent,
+                             getter_AddRefs(transferable));
+  nsCOMPtr<nsIClipboard> clipboard = do_GetService(kCClipboardCID, &rv);
+  clipboard->SetData(transferable, nullptr, nsIClipboard::kSelectionCache);
+  return rv;
+}
+
+nsresult
 nsCopySupport::ClearSelectionCache()
 {
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard = do_GetService(kCClipboardCID, &rv);
   clipboard->EmptyClipboard(nsIClipboard::kSelectionCache);
   return rv;
 }
 
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -21,16 +21,17 @@ class nsAString;
 class nsIPresShell;
 class nsILoadContext;
 
 class nsCopySupport
 {
   // class of static helper functions for copy support
   public:
     static nsresult ClearSelectionCache();
+    static nsresult SetSelectionCache(nsISelection* aSel, nsIDocument* aDoc);
     static nsresult HTMLCopy(nsISelection *aSel, nsIDocument *aDoc,
                              int16_t aClipboardID, bool aWithRubyAnnotation);
     static nsresult DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
                             bool *aDoPutOnClipboard);
 
     // Get the selection, or entire document, in the format specified by the mime type
     // (text/html or text/plain). If aSel is non-null, use it, otherwise get the entire
     // doc.
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -6517,16 +6517,20 @@ nsAutoCopyListener::NotifySelectionChang
     }
     /* clear X clipboard? */
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
+  if (mCachedClipboard == nsIClipboard::kSelectionCache) {
+    return nsCopySupport::SetSelectionCache(aSel, doc);
+  }
+
   // call the copy code
   return nsCopySupport::HTMLCopy(aSel, doc,
                                  mCachedClipboard, false);
 }
 
 /**
  * See Bug 1288453.
  *
@@ -6555,18 +6559,17 @@ nsFrameSelection::UpdateSelectionCacheOn
     aDoc = do_QueryInterface(ps->GetDocument());
   }
 
   bool collapsed;
   if (aDoc && aSel &&
       NS_SUCCEEDED(aSel->GetIsCollapsed(&collapsed)) && !collapsed) {
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
     NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-    return nsCopySupport::HTMLCopy(aSel, doc,
-                                   nsIClipboard::kSelectionCache, false);
+    return nsCopySupport::SetSelectionCache(aSel, doc);
   }
 
   return NS_OK;
 }
 
 // SelectionChangeListener
 
 SelectionChangeListener::RawRangeData::RawRangeData(const nsRange* aRange)