Bug 479093 - Text sent to services includes body of <script> tags. r=smaug, r=josh, sr=roc
authorTom Dyas <tdyas@zecador.org>
Tue, 17 Mar 2009 19:04:01 -0700
changeset 26757 ca4d8bb54e7f1895626094199e113070a2cec135
parent 26756 023cb4350567b17d87e5b841f4ccec78103ce95b
child 26758 7074d91a795ace11fb50940b97e908d3e0a60e1d
push id6210
push useropettay@mozilla.com
push dateTue, 31 Mar 2009 09:33:12 +0000
treeherdermozilla-central@ca4d8bb54e7f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, josh, roc
bugs479093
milestone1.9.2a1pre
Bug 479093 - Text sent to services includes body of <script> tags. r=smaug, r=josh, sr=roc
content/base/public/nsCopySupport.h
content/base/src/nsCopySupport.cpp
content/events/src/nsContentEventHandler.cpp
content/events/src/nsContentEventHandler.h
content/events/src/nsEventStateManager.cpp
widget/public/nsGUIEvent.h
widget/src/cocoa/nsChildView.mm
--- a/content/base/public/nsCopySupport.h
+++ b/content/base/public/nsCopySupport.h
@@ -65,11 +65,17 @@ class nsCopySupport
     
     static nsresult ImageCopy(nsIImageLoadingContent* aImageElement,
                               PRInt32 aCopyFlags);
 
     // Given the current selection, find the target that
     // before[copy,cut,paste] and [copy,cut,paste] events will fire on.
     static nsresult GetClipboardEventTarget(nsISelection *aSel,
                                             nsIDOMNode **aEventTarget);
+
+    // Get the selection as a transferable. Similar to HTMLCopy except does
+    // not deal with the clipboard.
+    static nsresult GetTransferableForSelection(nsISelection * aSelection,
+                                                nsIDocument * aDocument,
+                                                nsITransferable ** aTransferable);
 };
 
 #endif
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -81,23 +81,33 @@ static NS_DEFINE_CID(kHTMLConverterCID, 
 static nsresult AppendString(nsITransferable *aTransferable,
                              const nsAString& aString,
                              const char* aFlavor);
 
 // copy HTML node data
 static nsresult AppendDOMNode(nsITransferable *aTransferable,
                               nsIDOMNode *aDOMNode);
 
-nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID)
+// Helper used for HTMLCopy and GetTransferableForSelection since both routines
+// share common code.
+static nsresult
+SelectionCopyHelper(nsISelection *aSel, nsIDocument *aDoc,
+                    PRBool doPutOnClipboard, PRInt16 aClipboardID,
+                    nsITransferable ** aTransferable)
 {
+  // Clear the output parameter for the transferable, if provided.
+  if (aTransferable) {
+    *aTransferable = nsnull;
+  }
+
   nsresult rv = NS_OK;
   
   PRBool bIsPlainTextContext = PR_FALSE;
 
-  rv = IsPlainTextContext(aSel, aDoc, &bIsPlainTextContext);
+  rv = nsCopySupport::IsPlainTextContext(aSel, aDoc, &bIsPlainTextContext);
   if (NS_FAILED(rv)) 
     return rv;
 
   PRBool bIsHTMLCopy = !bIsPlainTextContext;
   nsAutoString mimeType;
 
   nsCOMPtr<nsIDocumentEncoder> docEncoder;
 
@@ -109,16 +119,17 @@ nsresult nsCopySupport::HTMLCopy(nsISele
   PRUint32 flags = nsIDocumentEncoder::OutputPreformatted;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(aDoc);
   NS_ASSERTION(domDoc, "Need a document");
 
   rv = docEncoder->Init(domDoc, mimeType, flags);
   if (NS_FAILED(rv)) 
     return rv;
+
   rv = docEncoder->SetSelection(aSel);
   if (NS_FAILED(rv)) 
     return rv;
 
   nsAutoString buffer, parents, info, textBuffer, plaintextBuffer;
 
   rv = docEncoder->EncodeToString(textBuffer);
   if (NS_FAILED(rv)) 
@@ -155,52 +166,50 @@ nsresult nsCopySupport::HTMLCopy(nsISele
     NS_ENSURE_SUCCESS(rv, rv);
 
     // encode the selection as html with contextual info
     rv = docEncoder->EncodeToStringWithContext(parents, info, buffer);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   
   // Get the Clipboard
-  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
-  if (NS_FAILED(rv)) 
-    return rv;
+  nsCOMPtr<nsIClipboard> clipboard;
+  if (doPutOnClipboard) {
+    clipboard = do_GetService(kCClipboardCID, &rv);
+    if (NS_FAILED(rv))
+      return rv;
+  }
 
-  if ( clipboard ) 
-  {
+  if ((doPutOnClipboard && clipboard) || aTransferable != nsnull) {
     // Create a transferable for putting data on the Clipboard
     nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID);
-    if ( trans ) 
-    {
-      if (bIsHTMLCopy)
-      {
+    if (trans) {
+      if (bIsHTMLCopy) {
         // set up the data converter
         trans->SetConverter(htmlConverter);
 
-        if (!buffer.IsEmpty())
-        {
+        if (!buffer.IsEmpty()) {
           // Add the html DataFlavor to the transferable
           rv = AppendString(trans, buffer, 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, parents, kHTMLContext);
-          NS_ENSURE_SUCCESS(rv, rv);
-        }
-        if (!info.IsEmpty())
-        {
+
+        // Add the htmlcontext DataFlavor to the transferable
+        // Even if parents is empty string, this flavor should
+        // be attached to the transferable
+        rv = AppendString(trans, parents, kHTMLContext);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!info.IsEmpty()) {
           // Add the htmlinfo DataFlavor to the transferable
           rv = AppendString(trans, info, kHTMLInfo);
           NS_ENSURE_SUCCESS(rv, rv);
         }
-        if (!plaintextBuffer.IsEmpty())
-        {
+
+        if (!plaintextBuffer.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, plaintextBuffer, kUnicodeMime);
           NS_ENSURE_SUCCESS(rv, rv);
         }
 
@@ -218,38 +227,55 @@ nsresult nsCopySupport::HTMLCopy(nsISele
             // 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 (!textBuffer.IsEmpty())
-        {
-         // Add the unicode DataFlavor to the transferable
+      } else {
+        if (!textBuffer.IsEmpty()) {
+          // Add the unicode DataFlavor to the transferable
           rv = AppendString(trans, textBuffer, kUnicodeMime);
           NS_ENSURE_SUCCESS(rv, rv);
         }
       }
 
-      PRBool doPutOnClipboard = PR_TRUE;
-      DoHooks(aDoc, trans, &doPutOnClipboard);
+      if (doPutOnClipboard && clipboard) {
+        PRBool actuallyPutOnClipboard = PR_TRUE;
+        nsCopySupport::DoHooks(aDoc, trans, &actuallyPutOnClipboard);
 
-      // put the transferable on the clipboard
-      if (doPutOnClipboard)
-        clipboard->SetData(trans, nsnull, aClipboardID);
+        // put the transferable on the clipboard
+        if (actuallyPutOnClipboard)
+          clipboard->SetData(trans, nsnull, aClipboardID);
+      }
+
+      // Return the transferable to the caller if requested.
+      if (aTransferable != nsnull) {
+        trans.swap(*aTransferable);
+      }
     }
   }
   return rv;
 }
 
+nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 aClipboardID)
+{
+  return SelectionCopyHelper(aSel, aDoc, PR_TRUE, aClipboardID, nsnull);
+}
+
+nsresult
+nsCopySupport::GetTransferableForSelection(nsISelection * aSel,
+                                           nsIDocument * aDoc,
+                                           nsITransferable ** aTransferable)
+{
+  return SelectionCopyHelper(aSel, aDoc, PR_FALSE, 0, aTransferable);
+}
+
 nsresult nsCopySupport::DoHooks(nsIDocument *aDoc, nsITransferable *aTrans,
                                 PRBool *aDoPutOnClipboard)
 {
   NS_ENSURE_ARG(aDoc);
 
   *aDoPutOnClipboard = PR_TRUE;
 
   nsCOMPtr<nsISupports> container = aDoc->GetContainer();
--- a/content/events/src/nsContentEventHandler.cpp
+++ b/content/events/src/nsContentEventHandler.cpp
@@ -43,16 +43,17 @@
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsISelection.h"
 #include "nsIDOMText.h"
 #include "nsIDOMRange.h"
 #include "nsRange.h"
 #include "nsGUIEvent.h"
 #include "nsCaret.h"
+#include "nsCopySupport.h"
 #include "nsFrameSelection.h"
 #include "nsIFrame.h"
 #include "nsIView.h"
 #include "nsIContentIterator.h"
 #include "nsTextFragment.h"
 #include "nsTextFrame.h"
 #include "nsISelectionController.h"
 #include "nsISelectionPrivate.h"
@@ -87,16 +88,22 @@ nsContentEventHandler::Init(nsQueryConte
   if (!mPresShell)
     return NS_ERROR_NOT_AVAILABLE;
 
   nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(mSelection,
                "GetSelectionForCopy succeeded, but the result is null");
 
+  PRBool isCollapsed;
+  rv = mSelection->GetIsCollapsed(&isCollapsed);
+  if (NS_FAILED(rv))
+    return NS_ERROR_NOT_AVAILABLE;
+  aEvent->mReply.mHasSelection = !isCollapsed;
+
   nsCOMPtr<nsIDOMRange> firstRange;
   rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
   // This shell doesn't support selection.
   if (NS_FAILED(rv))
     return NS_ERROR_NOT_AVAILABLE;
   mFirstSelectedRange = do_QueryInterface(firstRange);
   NS_ENSURE_TRUE(mFirstSelectedRange, NS_ERROR_FAILURE);
 
@@ -106,17 +113,16 @@ nsContentEventHandler::Init(nsQueryConte
   NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
 
   aEvent->mReply.mContentsRoot = mRootContent.get();
 
   nsRefPtr<nsCaret> caret;
   rv = mPresShell->GetCaret(getter_AddRefs(caret));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ASSERTION(caret, "GetCaret succeeded, but the result is null");
-  PRBool isCollapsed;
   nsRect r;
   nsIView* view = nsnull;
   rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
                                   mSelection, &r, &isCollapsed, &view);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(view, NS_ERROR_FAILURE);
   aEvent->mReply.mFocusedWidget = view->GetWidget();
 
@@ -680,16 +686,51 @@ nsContentEventHandler::OnQueryCaretRect(
 
   aEvent->mReply.mRect =
       nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
   aEvent->mSucceeded = PR_TRUE;
   return NS_OK;
 }
 
 nsresult
+nsContentEventHandler::OnQueryContentState(nsQueryContentEvent * aEvent)
+{
+  nsresult rv = Init(aEvent);
+  if (NS_FAILED(rv))
+    return rv;
+  
+  aEvent->mSucceeded = PR_TRUE;
+
+  return NS_OK;
+}
+
+nsresult
+nsContentEventHandler::OnQuerySelectionAsTransferable(nsQueryContentEvent* aEvent)
+{
+  nsresult rv = Init(aEvent);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (!aEvent->mReply.mHasSelection) {
+    aEvent->mSucceeded = PR_TRUE;
+    aEvent->mReply.mTransferable = nsnull;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mPresShell->GetDocument();
+  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+
+  rv = nsCopySupport::GetTransferableForSelection(mSelection, doc, getter_AddRefs(aEvent->mReply.mTransferable));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  aEvent->mSucceeded = PR_TRUE;
+  return NS_OK;
+}
+
+nsresult
 nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
                                                 nsINode* aNode,
                                                 PRInt32 aNodeOffset,
                                                 PRUint32* aNativeOffset)
 {
   NS_ASSERTION(aNativeOffset, "param is invalid");
 
   nsCOMPtr<nsIRange> prev = new nsRange();
--- a/content/events/src/nsContentEventHandler.h
+++ b/content/events/src/nsContentEventHandler.h
@@ -72,16 +72,20 @@ public:
   // NS_QUERY_TEXT_CONTENT event handler
   nsresult OnQueryTextContent(nsQueryContentEvent* aEvent);
   // NS_QUERY_CARET_RECT event handler
   nsresult OnQueryCaretRect(nsQueryContentEvent* aEvent);
   // NS_QUERY_TEXT_RECT event handler
   nsresult OnQueryTextRect(nsQueryContentEvent* aEvent);
   // NS_QUERY_EDITOR_RECT event handler
   nsresult OnQueryEditorRect(nsQueryContentEvent* aEvent);
+  // NS_QUERY_CONTENT_STATE event handler
+  nsresult OnQueryContentState(nsQueryContentEvent* aEvent);
+  // NS_QUERY_SELECTION_AS_TRANSFERABLE event handler
+  nsresult OnQuerySelectionAsTransferable(nsQueryContentEvent* aEvent);
 
   // NS_SELECTION_* event
   nsresult OnSelectionEvent(nsSelectionEvent* aEvent);
 
 protected:
   nsPresContext* mPresContext;
   nsIPresShell* mPresShell;
   nsCOMPtr<nsISelection> mSelection;
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1683,16 +1683,28 @@ nsEventStateManager::PreHandleEvent(nsPr
     }
     break;
   case NS_QUERY_EDITOR_RECT:
     {
       nsContentEventHandler handler(mPresContext);
       handler.OnQueryEditorRect((nsQueryContentEvent*)aEvent);
     }
     break;
+  case NS_QUERY_CONTENT_STATE:
+    {
+      nsContentEventHandler handler(mPresContext);
+      handler.OnQueryContentState(static_cast<nsQueryContentEvent*>(aEvent));
+    }
+    break;
+  case NS_QUERY_SELECTION_AS_TRANSFERABLE:
+    {
+      nsContentEventHandler handler(mPresContext);
+      handler.OnQuerySelectionAsTransferable(static_cast<nsQueryContentEvent*>(aEvent));
+    }
+    break;
   case NS_SELECTION_SET:
     {
       nsContentEventHandler handler(mPresContext);
       handler.OnSelectionEvent((nsSelectionEvent*)aEvent);
     }
     break;
   }
   return NS_OK;
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -49,16 +49,17 @@
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMDataTransfer.h"
 #include "nsWeakPtr.h"
 #include "nsIWidget.h"
 #include "nsTArray.h"
 #include "nsTraceRefcnt.h"
+#include "nsITransferable.h"
 
 class nsIRenderingContext;
 class nsIRegion;
 class nsIMenuItem;
 class nsIAccessible;
 class nsIContent;
 class nsIURI;
 class nsIDOMEvent;
@@ -357,16 +358,21 @@ class nsHashKey;
 #define NS_QUERY_CARET_RECT             (NS_QUERY_CONTENT_EVENT_START + 3)
 // Query for the bounding rect of a range of characters. This works on any
 // valid character range given offset and length. Result is relative to top
 // level widget coordinates
 #define NS_QUERY_TEXT_RECT              (NS_QUERY_CONTENT_EVENT_START + 4)
 // Query for the bounding rect of the current focused frame. Result is relative
 // to top level widget coordinates
 #define NS_QUERY_EDITOR_RECT             (NS_QUERY_CONTENT_EVENT_START + 5)
+// Query for the current state of the content. The particular members of
+// mReply that are set for each query content event will be valid on success.
+#define NS_QUERY_CONTENT_STATE           (NS_QUERY_CONTENT_EVENT_START + 6)
+// Query for the selection in the form of a nsITransferable.
+#define NS_QUERY_SELECTION_AS_TRANSFERABLE (NS_QUERY_CONTENT_EVENT_START + 7)
 
 // Video events
 #ifdef MOZ_MEDIA
 #define NS_MEDIA_EVENT_START            3300
 #define NS_LOADSTART           (NS_MEDIA_EVENT_START)
 #define NS_PROGRESS            (NS_MEDIA_EVENT_START+1)
 #define NS_SUSPEND             (NS_MEDIA_EVENT_START+2)
 #define NS_EMPTIED             (NS_MEDIA_EVENT_START+3)
@@ -1007,16 +1013,19 @@ public:
   struct {
     void* mContentsRoot;
     PRUint32 mOffset;
     nsString mString;
     nsIntRect mRect; // Finally, the coordinates is system coordinates.
     // The return widget has the caret. This is set at all query events.
     nsIWidget* mFocusedWidget;
     PRPackedBool mReversed; // true if selection is reversed (end < start)
+    PRPackedBool mHasSelection; // true if the selection exists
+    // used by NS_QUERY_SELECTION_AS_TRANSFERABLE
+    nsCOMPtr<nsITransferable> mTransferable;
   } mReply;
 };
 
 class nsSelectionEvent : public nsGUIEvent
 {
 public:
   nsSelectionEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
     nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_SELECTION_EVENT),
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -63,16 +63,17 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIServiceManager.h"
 #include "nsILocalFile.h"
 #include "nsILocalFileMac.h"
 #include "nsGfxCIID.h"
 #include "nsIMenuRollup.h"
 
 #include "nsDragService.h"
+#include "nsClipboard.h"
 #include "nsCursorManager.h"
 #include "nsWindowMap.h"
 #include "nsCocoaUtils.h"
 #include "nsMenuUtilsX.h"
 #include "nsMenuBarX.h"
 
 #include "nsIDOMSimpleGestureEvent.h"
 
@@ -2386,20 +2387,25 @@ NSEvent* gLastDragEvent = nil;
 
 + (void)initialize
 {
   static BOOL initialized = NO;
 
   if (!initialized) {
     // Inform the OS about the types of services (from the "Services" menu)
     // that we can handle.
-    NSArray *sendTypes = [NSArray arrayWithObject:NSStringPboardType];
-    NSArray *returnTypes = [NSArray array];
+
+    NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
+    NSArray *returnTypes = [[NSArray alloc] init];
+
     [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes];
 
+    [sendTypes release];
+    [returnTypes release];
+
     initialized = YES;
   }
 }
 
 
 // initWithFrame:geckoChild:
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
 {
@@ -6422,82 +6428,112 @@ static BOOL keyUpAlreadySentKeyDown = NO
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
 #pragma mark -
 
 // Support for the "Services" menu. We currently only support sending strings
-// to services.
+// and HTML to system services.
 
 - (id)validRequestorForSendType:(NSString *)sendType
                      returnType:(NSString *)returnType
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   // sendType contains the type of data that the service would like this
   // application to send to it.  sendType is nil if the service is not
   // requesting any data.
   //
   // returnType contains the type of data the the service would like to
   // return to this application (e.g., to overwrite the selection).
   // returnType is nil if the service will not return any data.
   //
   // The following condition thus triggers when the service expects a string
-  // from us or no data at all AND when the service will not send back any
-  // data to us.
-
-  if ((!sendType || [sendType isEqual:NSStringPboardType]) && !returnType) {
-    // Query Gecko window to determine if there is a current selection.
-    bool hasSelection = false;
+  // or HTML from us or no data at all AND when the service will not send back
+  // any data to us.
+
+  if ((!sendType || [sendType isEqual:NSStringPboardType] ||
+       [sendType isEqual:NSHTMLPboardType]) && !returnType) {
+    // Query the Gecko window to determine if there is a current selection.
     if (mGeckoChild) {
       nsAutoRetainCocoaObject kungFuDeathGrip(self);
-      nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT,
-                                    mGeckoChild);
-      mGeckoChild->DispatchWindowEvent(selection);
-      if (selection.mSucceeded && !selection.mReply.mString.IsEmpty())
-        hasSelection = true;
+
+      nsQueryContentEvent event(PR_TRUE, NS_QUERY_CONTENT_STATE, mGeckoChild);
+      mGeckoChild->DispatchWindowEvent(event);
+
+      // Return this object if it can handle the request.
+      if ((!sendType || (event.mSucceeded && event.mReply.mHasSelection)) &&
+          !returnType)
+        return self;
     }
-
-    // Return this object if it can handle the request.
-    if ((!sendType || hasSelection) && !returnType)
-      return self;
   }
 
   return [super validRequestorForSendType:sendType returnType:returnType];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 
 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
                              types:(NSArray *)types
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
- 
-  // Ensure that the service will accept strings. (We only support strings.)
-  if ([types containsObject:NSStringPboardType] == NO)
+
+  // Make sure that the service will accept strings or HTML.
+  if ([types containsObject:NSStringPboardType] == NO &&
+      [types containsObject:NSHTMLPboardType] == NO)
+    return NO;
+
+  // Bail out if there is no Gecko object.
+  if (!mGeckoChild)
     return NO;
 
   // Obtain the current selection.
-  if (!mGeckoChild)
+  nsQueryContentEvent event(PR_TRUE,
+                            NS_QUERY_SELECTION_AS_TRANSFERABLE,
+                            mGeckoChild);
+  mGeckoChild->DispatchWindowEvent(event);
+  if (!event.mSucceeded || !event.mReply.mTransferable)
     return NO;
-  nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
-  mGeckoChild->DispatchWindowEvent(selection);
-  if (!selection.mSucceeded || selection.mReply.mString.IsEmpty())
+
+  // Transform the transferable to an NSDictionary.
+  NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(event.mReply.mTransferable);
+  if (!pasteboardOutputDict)
     return NO;
 
-  // Copy the current selection to the pasteboard.
-  NSArray *typesDeclared = [NSArray arrayWithObject:NSStringPboardType];
-  [pboard declareTypes:typesDeclared owner:nil];
-  return [pboard setString:ToNSString(selection.mReply.mString)
-                   forType:NSStringPboardType];
+  // Declare the pasteboard types.
+  unsigned int typeCount = [pasteboardOutputDict count];
+  NSMutableArray * types = [NSMutableArray arrayWithCapacity:typeCount];
+  [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
+  [pboard declareTypes:types owner:nil];
+
+  // Write the data to the pasteboard.
+  for (unsigned int i = 0; i < typeCount; i++) {
+    NSString* currentKey = [types objectAtIndex:i];
+    id currentValue = [pasteboardOutputDict valueForKey:currentKey];
+
+    if (currentKey == NSStringPboardType ||
+        currentKey == kCorePboardType_url ||
+        currentKey == kCorePboardType_urld ||
+        currentKey == kCorePboardType_urln) {
+      [pboard setString:currentValue forType:currentKey];
+    } else if (currentKey == NSHTMLPboardType) {
+      [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey];
+    } else if (currentKey == NSTIFFPboardType) {
+      [pboard setData:currentValue forType:currentKey];
+    } else if (currentKey == NSFilesPromisePboardType) {
+      [pboard setPropertyList:currentValue forType:currentKey];        
+    }
+  }
+
+  return YES;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
 
 
 // Called if the service wants us to replace the current selection. We do
 // not currently support replacing the current selection so just return NO.
 - (BOOL)readSelectionFromPasteboard:(NSPasteboard *)pboard