From fb41f028badb4dfddfc47fb2a1a68c1aa90dcef5 Mon Sep 17 00:00:00 2001
From: Robert Bragg <robert@linux.intel.com>
Date: Fri, 8 May 2009 13:57:22 +0100
Subject: [PATCH] Adds initial Gtk clipboard support to moz-headless

If build with MOZ_X11 enabled then this now builds the clipboard code taken
from the gtk2 backend.  This doesn't provide any embedding API yet to expose
the clipboard.
---
 widget/src/headless/Makefile.in         |    6 +
 widget/src/headless/nsClipboard.cpp     |  948 +++++++++++++++++++++++++++++++
 widget/src/headless/nsClipboard.h       |   93 +++
 widget/src/headless/nsIImageToPixbuf.h  |   62 ++
 widget/src/headless/nsImageToPixbuf.cpp |  196 +++++++
 widget/src/headless/nsImageToPixbuf.h   |   71 +++
 widget/src/headless/nsWidgetFactory.cpp |   18 +
 7 files changed, 1394 insertions(+), 0 deletions(-)
 create mode 100644 widget/src/headless/nsClipboard.cpp
 create mode 100644 widget/src/headless/nsClipboard.h
 create mode 100644 widget/src/headless/nsIImageToPixbuf.h
 create mode 100644 widget/src/headless/nsImageToPixbuf.cpp
 create mode 100644 widget/src/headless/nsImageToPixbuf.h

Index: offscreen/widget/src/headless/Makefile.in
===================================================================
--- offscreen.orig/widget/src/headless/Makefile.in	2009-05-16 18:23:25.000000000 +0100
+++ offscreen/widget/src/headless/Makefile.in	2009-06-12 14:14:05.000000000 +0100
@@ -95,6 +95,12 @@
 		nsScreenManagerHeadless.cpp \
 		$(NULL)
 
+ifdef MOZ_X11
+CPPSRCS +=      nsClipboard.cpp \
+		nsImageToPixbuf.cpp \
+                $(NULL)
+endif
+
 # build our subdirs, too
 SHARED_LIBRARY_LIBS = ../xpwidgets/libxpwidgets_s.a
 
Index: offscreen/widget/src/headless/nsClipboard.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ offscreen/widget/src/headless/nsClipboard.cpp	2009-06-12 14:14:05.000000000 +0100
@@ -0,0 +1,948 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Christopher Blizzard
+ * <blizzard@mozilla.org>.  Portions created by the Initial Developer
+ * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsClipboard.h"
+#include "nsSupportsPrimitives.h"
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsXPIDLString.h"
+#include "nsPrimitiveHelpers.h"
+#include "nsICharsetConverterManager.h"
+#include "nsIServiceManager.h"
+#include "nsIImage.h"
+#include "nsImageToPixbuf.h"
+#include "nsStringStream.h"
+
+#include <gtk/gtk.h>
+
+// For manipulation of the X event queue
+#include <X11/Xlib.h>
+#include <gdk/gdkx.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifdef POLL_WITH_XCONNECTIONNUMBER
+#include <poll.h>
+#endif
+
+// Callback when someone asks us for the selection
+void
+invisible_selection_get_cb (GtkWidget          *aWidget,
+                            GtkSelectionData   *aSelectionData,
+                            guint               aTime,
+                            guint               aInfo,
+                            nsClipboard        *aClipboard);
+
+gboolean
+selection_clear_event_cb   (GtkWidget          *aWidget,
+                            GdkEventSelection  *aEvent,
+                            nsClipboard        *aClipboard);
+
+static void
+ConvertHTMLtoUCS2          (guchar             *data,
+                            PRInt32             dataLength,
+                            PRUnichar         **unicodeData,
+                            PRInt32            &outUnicodeLen);
+
+static void
+GetHTMLCharset             (guchar * data, PRInt32 dataLength, nsCString& str);
+
+
+// Our own versions of gtk_clipboard_wait_for_contents and
+// gtk_clipboard_wait_for_text, which don't run the event loop while
+// waiting for the data.  This prevents a lot of problems related to
+// dispatching events at unexpected times.
+
+static GtkSelectionData *
+wait_for_contents          (GtkClipboard *clipboard, GdkAtom target);
+
+static gchar *
+wait_for_text              (GtkClipboard *clipboard);
+
+static Bool
+checkEventProc(Display *display, XEvent *event, XPointer arg);
+
+struct retrieval_context
+{
+    PRBool   completed;
+    void    *data;
+
+    retrieval_context() : completed(PR_FALSE), data(nsnull) { }
+};
+
+static void
+wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *transferData);
+
+static void
+clipboard_contents_received(GtkClipboard     *clipboard,
+                            GtkSelectionData *selection_data,
+                            gpointer          data);
+
+static void
+clipboard_text_received(GtkClipboard *clipboard,
+                        const gchar  *text,
+                        gpointer      data);
+
+nsClipboard::nsClipboard()
+{
+    mWidget = nsnull;
+}
+
+nsClipboard::~nsClipboard()
+{
+    if (mWidget)
+        gtk_widget_destroy(mWidget);
+}
+
+NS_IMPL_ISUPPORTS1(nsClipboard, nsIClipboard)
+
+nsresult
+nsClipboard::Init(void)
+{
+    mWidget = gtk_invisible_new();
+    if (!mWidget)
+        return NS_ERROR_FAILURE;
+
+    g_signal_connect(G_OBJECT(mWidget), "selection_get",
+                     G_CALLBACK(invisible_selection_get_cb), this);
+
+    g_signal_connect(G_OBJECT(mWidget), "selection_clear_event",
+                     G_CALLBACK(selection_clear_event_cb), this);
+
+    // XXX make sure to set up the selection_clear event
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::SetData(nsITransferable *aTransferable,
+                     nsIClipboardOwner *aOwner, PRInt32 aWhichClipboard)
+{
+    // See if we can short cut
+    if ((aWhichClipboard == kGlobalClipboard &&
+         aTransferable == mGlobalTransferable.get() &&
+         aOwner == mGlobalOwner.get()) ||
+        (aWhichClipboard == kSelectionClipboard &&
+         aTransferable == mSelectionTransferable.get() &&
+         aOwner == mSelectionOwner.get())) {
+        return NS_OK;
+    }
+
+    nsresult rv;
+    if (!mPrivacyHandler) {
+        rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
+        NS_ENSURE_SUCCESS(rv, rv);
+    }
+    rv = mPrivacyHandler->PrepareDataForClipboard(aTransferable);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Clear out the clipboard in order to set the new data
+    EmptyClipboard(aWhichClipboard);
+
+    if (aWhichClipboard == kSelectionClipboard) {
+        mSelectionOwner = aOwner;
+        mSelectionTransferable = aTransferable;
+    }
+    else {
+        mGlobalOwner = aOwner;
+        mGlobalTransferable = aTransferable;
+    }
+
+    // Which selection are we about to claim, CLIPBOARD or PRIMARY?
+    GdkAtom selectionAtom = GetSelectionAtom(aWhichClipboard);
+
+    // Make ourselves the owner.  If we fail to, return.
+    if (!gtk_selection_owner_set(mWidget, selectionAtom, GDK_CURRENT_TIME))
+        return NS_ERROR_FAILURE;
+
+    // Clear the old selection target list.
+    gtk_selection_clear_targets(mWidget, selectionAtom);
+
+    // Get the types of supported flavors
+    nsCOMPtr<nsISupportsArray> flavors;
+
+    rv = aTransferable->FlavorsTransferableCanExport(getter_AddRefs(flavors));
+    if (!flavors || NS_FAILED(rv))
+        return NS_ERROR_FAILURE;
+
+    // Add all the flavors to this widget's supported type.
+    PRUint32 count;
+    flavors->Count(&count);
+    for (PRUint32 i=0; i < count; i++) {
+        nsCOMPtr<nsISupports> tastesLike;
+        flavors->GetElementAt(i, getter_AddRefs(tastesLike));
+        nsCOMPtr<nsISupportsCString> flavor = do_QueryInterface(tastesLike);
+
+        if (flavor) {
+            nsXPIDLCString flavorStr;
+            flavor->ToString(getter_Copies(flavorStr));
+
+            // special case text/unicode since we can handle all of
+            // the string types
+            if (!strcmp(flavorStr, kUnicodeMime)) {
+                AddTarget(gdk_atom_intern("UTF8_STRING", FALSE),
+                          selectionAtom);
+                AddTarget(gdk_atom_intern("COMPOUND_TEXT", FALSE),
+                          selectionAtom);
+                AddTarget(gdk_atom_intern("TEXT", FALSE), selectionAtom);
+                AddTarget(GDK_SELECTION_TYPE_STRING, selectionAtom);
+                // next loop iteration
+                continue;
+            }
+
+            // very special case for this one. since our selection mechanism doesn't work for images,
+            // we must use GTK's clipboard utility functions
+            if (!strcmp(flavorStr, kNativeImageMime) || !strcmp(flavorStr, kPNGImageMime) ||
+                !strcmp(flavorStr, kJPEGImageMime) || !strcmp(flavorStr, kGIFImageMime)) {
+                nsCOMPtr<nsISupports> item;
+                PRUint32 len;
+                rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(item), &len);
+                nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive(do_QueryInterface(item));
+                if (!ptrPrimitive)
+                    continue;
+
+                nsCOMPtr<nsISupports> primitiveData;
+                ptrPrimitive->GetData(getter_AddRefs(primitiveData));
+                nsCOMPtr<nsIImage> image(do_QueryInterface(primitiveData));
+                if (!image) // Not getting an image for an image mime type!?
+                    continue;
+
+                if (NS_FAILED(image->LockImagePixels(PR_FALSE)))
+                    continue;
+                GdkPixbuf* pixbuf = nsImageToPixbuf::ImageToPixbuf(image);
+                if (!pixbuf) {
+                    image->UnlockImagePixels(PR_FALSE);
+                    continue;
+                }
+
+                GtkClipboard *aClipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
+                gtk_clipboard_set_image(aClipboard, pixbuf);
+                g_object_unref(pixbuf);
+                image->UnlockImagePixels(PR_FALSE);
+                continue;
+            }
+
+            // Add this to our list of valid targets
+            GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
+            AddTarget(atom, selectionAtom);
+        }
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::GetData(nsITransferable *aTransferable, PRInt32 aWhichClipboard)
+{
+    if (!aTransferable)
+        return NS_ERROR_FAILURE;
+
+    GtkClipboard *clipboard;
+    clipboard = gtk_clipboard_get(GetSelectionAtom(aWhichClipboard));
+
+    guchar        *data = NULL;
+    gint           length = 0;
+    PRBool         foundData = PR_FALSE;
+    nsCAutoString  foundFlavor;
+
+    // Get a list of flavors this transferable can import
+    nsCOMPtr<nsISupportsArray> flavors;
+    nsresult rv;
+    rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavors));
+    if (!flavors || NS_FAILED(rv))
+        return NS_ERROR_FAILURE;
+
+    PRUint32 count;
+    flavors->Count(&count);
+    for (PRUint32 i=0; i < count; i++) {
+        nsCOMPtr<nsISupports> genericFlavor;
+        flavors->GetElementAt(i, getter_AddRefs(genericFlavor));
+
+        nsCOMPtr<nsISupportsCString> currentFlavor;
+        currentFlavor = do_QueryInterface(genericFlavor);
+
+        if (currentFlavor) {
+            nsXPIDLCString flavorStr;
+            currentFlavor->ToString(getter_Copies(flavorStr));
+
+            // Special case text/unicode since we can convert any
+            // string into text/unicode
+            if (!strcmp(flavorStr, kUnicodeMime)) {
+                gchar* new_text = wait_for_text(clipboard);
+                if (new_text) {
+                    // Convert utf-8 into our unicode format.
+                    NS_ConvertUTF8toUTF16 ucs2string(new_text);
+                    data = (guchar *)ToNewUnicode(ucs2string);
+                    length = ucs2string.Length() * 2;
+                    g_free(new_text);
+                    foundData = PR_TRUE;
+                    foundFlavor = kUnicodeMime;
+                    break;
+                }
+                // If the type was text/unicode and we couldn't get
+                // text off the clipboard, run the next loop
+                // iteration.
+                continue;
+            }
+
+            // For images, we must wrap the data in an nsIInputStream then return instead of break,
+            // because that code below won't help us.
+            if (!strcmp(flavorStr, kJPEGImageMime) || !strcmp(flavorStr, kPNGImageMime) || !strcmp(flavorStr, kGIFImageMime)) {
+                GdkAtom atom;
+                if (!strcmp(flavorStr, kJPEGImageMime)) // This is image/jpg, but X only understands image/jpeg
+                    atom = gdk_atom_intern("image/jpeg", FALSE);
+                else
+                    atom = gdk_atom_intern(flavorStr, FALSE);
+
+                GtkSelectionData *selectionData = wait_for_contents(clipboard, atom);
+                if (!selectionData)
+                    continue;
+
+                nsCOMPtr<nsIInputStream> byteStream;
+                NS_NewByteInputStream(getter_AddRefs(byteStream), (const char*)selectionData->data,
+                                      selectionData->length, NS_ASSIGNMENT_COPY);
+                aTransferable->SetTransferData(flavorStr, byteStream, sizeof(nsIInputStream*));
+                gtk_selection_data_free(selectionData);
+                return NS_OK;
+            }
+
+            // Get the atom for this type and try to request it off
+            // the clipboard.
+            GdkAtom atom = gdk_atom_intern(flavorStr, FALSE);
+            GtkSelectionData *selectionData;
+            selectionData = wait_for_contents(clipboard, atom);
+            if (selectionData) {
+                length = selectionData->length;
+                // Special case text/html since we can convert into UCS2
+                if (!strcmp(flavorStr, kHTMLMime)) {
+                    PRUnichar* htmlBody= nsnull;
+                    PRInt32 htmlBodyLen = 0;
+                    // Convert text/html into our unicode format
+                    ConvertHTMLtoUCS2((guchar *)selectionData->data, length,
+                                      &htmlBody, htmlBodyLen);
+                    if (!htmlBodyLen)
+                        break;
+                    data = (guchar *)htmlBody;
+                    length = htmlBodyLen * 2;
+                } else {
+                    data = (guchar *)nsMemory::Alloc(length);
+                    if (!data)
+                        break;
+                    memcpy(data, selectionData->data, length);
+                }
+                foundData = PR_TRUE;
+                foundFlavor = flavorStr;
+                break;
+            }
+        }
+    }
+
+    if (foundData) {
+        nsCOMPtr<nsISupports> wrapper;
+        nsPrimitiveHelpers::CreatePrimitiveForData(foundFlavor.get(),
+                                                   data, length,
+                                                   getter_AddRefs(wrapper));
+        aTransferable->SetTransferData(foundFlavor.get(),
+                                       wrapper, length);
+    }
+
+    if (data)
+        nsMemory::Free(data);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::EmptyClipboard(PRInt32 aWhichClipboard)
+{
+    if (aWhichClipboard == kSelectionClipboard) {
+        if (mSelectionOwner) {
+            mSelectionOwner->LosingOwnership(mSelectionTransferable);
+            mSelectionOwner = nsnull;
+        }
+        mSelectionTransferable = nsnull;
+    }
+    else {
+        if (mGlobalOwner) {
+            mGlobalOwner->LosingOwnership(mGlobalTransferable);
+            mGlobalOwner = nsnull;
+        }
+        mGlobalTransferable = nsnull;
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::HasDataMatchingFlavors(const char** aFlavorList, PRUint32 aLength,
+                                    PRInt32 aWhichClipboard, PRBool *_retval)
+{
+    if (!aFlavorList || !_retval)
+        return NS_ERROR_NULL_POINTER;
+
+    *_retval = PR_FALSE;
+
+    GtkSelectionData *selection_data =
+        GetTargets(GetSelectionAtom(aWhichClipboard));
+    if (!selection_data)
+        return NS_OK;
+
+    gint n_targets = 0;
+    GdkAtom *targets = NULL;
+
+    if (!gtk_selection_data_get_targets(selection_data, 
+                                        &targets, &n_targets) ||
+        !n_targets)
+        return NS_OK;
+
+    // Walk through the provided types and try to match it to a
+    // provided type.
+    for (PRUint32 i = 0; i < aLength && !*_retval; i++) {
+        // We special case text/unicode here.
+        if (!strcmp(aFlavorList[i], kUnicodeMime) && 
+            gtk_selection_data_targets_include_text(selection_data)) {
+            *_retval = PR_TRUE;
+            break;
+        }
+
+        for (PRInt32 j = 0; j < n_targets; j++) {
+            gchar *atom_name = gdk_atom_name(targets[j]);
+            if (!strcmp(atom_name, aFlavorList[i]))
+                *_retval = PR_TRUE;
+
+            // X clipboard wants image/jpeg, not image/jpg
+            if (!strcmp(aFlavorList[i], kJPEGImageMime) && !strcmp(atom_name, "image/jpeg"))
+                *_retval = PR_TRUE;
+
+            g_free(atom_name);
+
+            if (*_retval)
+                break;
+        }
+    }
+    gtk_selection_data_free(selection_data);
+    g_free(targets);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsClipboard::SupportsSelectionClipboard(PRBool *_retval)
+{
+    *_retval = PR_TRUE; // yeah, unix supports the selection clipboard
+    return NS_OK;
+}
+
+/* static */
+GdkAtom
+nsClipboard::GetSelectionAtom(PRInt32 aWhichClipboard)
+{
+    if (aWhichClipboard == kGlobalClipboard)
+        return GDK_SELECTION_CLIPBOARD;
+
+    return GDK_SELECTION_PRIMARY;
+}
+
+/* static */
+GtkSelectionData *
+nsClipboard::GetTargets(GdkAtom aWhichClipboard)
+{
+    GtkClipboard *clipboard = gtk_clipboard_get(aWhichClipboard);
+    return wait_for_contents(clipboard, gdk_atom_intern("TARGETS", FALSE));
+}
+
+nsITransferable *
+nsClipboard::GetTransferable(PRInt32 aWhichClipboard)
+{
+    nsITransferable *retval;
+
+    if (aWhichClipboard == kSelectionClipboard)
+        retval = mSelectionTransferable.get();
+    else
+        retval = mGlobalTransferable.get();
+        
+    return retval;
+}
+
+void
+nsClipboard::AddTarget(GdkAtom aName, GdkAtom aClipboard)
+{
+    gtk_selection_add_target(mWidget, aClipboard, aName, 0);
+}
+
+void
+nsClipboard::SelectionGetEvent (GtkWidget        *aWidget,
+                                GtkSelectionData *aSelectionData,
+                                guint             aTime)
+{
+    // Someone has asked us to hand them something.  The first thing
+    // that we want to do is see if that something includes text.  If
+    // it does, try to give it text/unicode after converting it to
+    // utf-8.
+
+    PRInt32 whichClipboard;
+
+    // which clipboard?
+    if (aSelectionData->selection == GDK_SELECTION_PRIMARY)
+        whichClipboard = kSelectionClipboard;
+    else if (aSelectionData->selection == GDK_SELECTION_CLIPBOARD)
+        whichClipboard = kGlobalClipboard;
+    else
+        return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
+
+    nsCOMPtr<nsITransferable> trans = GetTransferable(whichClipboard);
+    
+    nsresult rv;
+    nsCOMPtr<nsISupports> item;
+    PRUint32 len;
+
+    // Check to see if the selection data includes any of the string
+    // types that we support.
+    if (aSelectionData->target == gdk_atom_intern ("STRING", FALSE) ||
+        aSelectionData->target == gdk_atom_intern ("TEXT", FALSE) ||
+        aSelectionData->target == gdk_atom_intern ("COMPOUND_TEXT", FALSE) ||
+        aSelectionData->target == gdk_atom_intern ("UTF8_STRING", FALSE)) {
+        // Try to convert our internal type into a text string.  Get
+        // the transferable for this clipboard and try to get the
+        // text/unicode type for it.
+        rv = trans->GetTransferData("text/unicode", getter_AddRefs(item),
+                                    &len);
+        if (!item || NS_FAILED(rv))
+            return;
+        
+        nsCOMPtr<nsISupportsString> wideString;
+        wideString = do_QueryInterface(item);
+        if (!wideString)
+            return;
+
+        nsAutoString ucs2string;
+        wideString->GetData(ucs2string);
+        char *utf8string = ToNewUTF8String(ucs2string);
+        if (!utf8string)
+            return;
+        
+        gtk_selection_data_set_text (aSelectionData, utf8string,
+                                     strlen(utf8string));
+
+        nsMemory::Free(utf8string);
+        return;
+    }
+
+    // Try to match up the selection data target to something our
+    // transferable provides.
+    gchar *target_name = gdk_atom_name(aSelectionData->target);
+    if (!target_name)
+        return;
+
+    rv = trans->GetTransferData(target_name, getter_AddRefs(item), &len);
+    // nothing found?
+    if (!item || NS_FAILED(rv)) {
+        g_free(target_name);
+        return;
+    }
+
+    void *primitive_data = nsnull;
+    nsPrimitiveHelpers::CreateDataFromPrimitive(target_name, item,
+                                                &primitive_data, len);
+
+    if (primitive_data) {
+        // Check to see if the selection data is text/html
+        if (aSelectionData->target == gdk_atom_intern (kHTMLMime, FALSE)) {
+            /*
+             * "text/html" can be encoded UCS2. It is recommended that
+             * documents transmitted as UCS2 always begin with a ZERO-WIDTH
+             * NON-BREAKING SPACE character (hexadecimal FEFF, also called
+             * Byte Order Mark (BOM)). Adding BOM can help other app to
+             * detect mozilla use UCS2 encoding when copy-paste.
+             */
+            guchar *buffer = (guchar *)
+                    nsMemory::Alloc((len * sizeof(guchar)) + sizeof(PRUnichar));
+            if (!buffer)
+                return;
+            PRUnichar prefix = 0xFEFF;
+            memcpy(buffer, &prefix, sizeof(prefix));
+            memcpy(buffer + sizeof(prefix), primitive_data, len);
+            nsMemory::Free((guchar *)primitive_data);
+            primitive_data = (guchar *)buffer;
+            len += sizeof(prefix);
+        }
+  
+        gtk_selection_data_set(aSelectionData, aSelectionData->target,
+                               8, /* 8 bits in a unit */
+                               (const guchar *)primitive_data, len);
+        nsMemory::Free(primitive_data);
+    }
+
+    g_free(target_name);
+                           
+}
+
+void
+nsClipboard::SelectionClearEvent (GtkWidget         *aWidget,
+                                  GdkEventSelection *aEvent)
+{
+    PRInt32 whichClipboard;
+
+    // which clipboard?
+    if (aEvent->selection == GDK_SELECTION_PRIMARY)
+        whichClipboard = kSelectionClipboard;
+    else if (aEvent->selection == GDK_SELECTION_CLIPBOARD)
+        whichClipboard = kGlobalClipboard;
+    else
+        return; // THAT AIN'T NO CLIPBOARD I EVER HEARD OF
+
+    EmptyClipboard(whichClipboard);
+}
+
+void
+invisible_selection_get_cb (GtkWidget          *aWidget,
+                            GtkSelectionData   *aSelectionData,
+                            guint               aTime,
+                            guint               aInfo,
+                            nsClipboard        *aClipboard)
+{
+    aClipboard->SelectionGetEvent(aWidget, aSelectionData, aTime);
+}
+
+gboolean
+selection_clear_event_cb   (GtkWidget          *aWidget,
+                            GdkEventSelection  *aEvent,
+                            nsClipboard        *aClipboard)
+{
+    aClipboard->SelectionClearEvent(aWidget, aEvent);
+    return TRUE;
+}
+
+/*
+ * when copy-paste, mozilla wants data encoded using UCS2,
+ * other app such as StarOffice use "text/html"(RFC2854).
+ * This function convert data(got from GTK clipboard)
+ * to data mozilla wanted.
+ *
+ * data from GTK clipboard can be 3 forms:
+ *  1. From current mozilla
+ *     "text/html", charset = utf-16
+ *  2. From old version mozilla or mozilla-based app
+ *     content("body" only), charset = utf-16
+ *  3. From other app who use "text/html" when copy-paste
+ *     "text/html", has "charset" info
+ *
+ * data      : got from GTK clipboard
+ * dataLength: got from GTK clipboard
+ * body      : pass to Mozilla
+ * bodyLength: pass to Mozilla
+ */
+void ConvertHTMLtoUCS2(guchar * data, PRInt32 dataLength,
+                       PRUnichar** unicodeData, PRInt32& outUnicodeLen)
+{
+    nsCAutoString charset;
+    GetHTMLCharset(data, dataLength, charset);// get charset of HTML
+    if (charset.EqualsLiteral("UTF-16")) {//current mozilla
+        outUnicodeLen = (dataLength / 2) - 1;
+        *unicodeData = reinterpret_cast<PRUnichar*>
+                                       (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
+                       sizeof(PRUnichar)));
+        if (*unicodeData) {
+            memcpy(*unicodeData, data + sizeof(PRUnichar),
+                   outUnicodeLen * sizeof(PRUnichar));
+            (*unicodeData)[outUnicodeLen] = '\0';
+        }
+    } else if (charset.EqualsLiteral("UNKNOWN")) {
+        outUnicodeLen = 0;
+        return;
+    } else {
+        // app which use "text/html" to copy&paste
+        nsCOMPtr<nsIUnicodeDecoder> decoder;
+        nsresult rv;
+        // get the decoder
+        nsCOMPtr<nsICharsetConverterManager> ccm =
+            do_GetService(NS_CHARSETCONVERTERMANAGER_CONTRACTID, &rv);
+        if (NS_FAILED(rv)) {
+#ifdef DEBUG_CLIPBOARD
+            g_print("        can't get CHARSET CONVERTER MANAGER service\n");
+#endif
+            outUnicodeLen = 0;
+            return;
+        }
+        rv = ccm->GetUnicodeDecoder(charset.get(), getter_AddRefs(decoder));
+        if (NS_FAILED(rv)) {
+#ifdef DEBUG_CLIPBOARD
+            g_print("        get unicode decoder error\n");
+#endif
+            outUnicodeLen = 0;
+            return;
+        }
+        // converting
+        decoder->GetMaxLength((const char *)data, dataLength, &outUnicodeLen);
+        // |outUnicodeLen| is number of chars
+        if (outUnicodeLen) {
+            *unicodeData = reinterpret_cast<PRUnichar*>
+                                           (nsMemory::Alloc((outUnicodeLen + sizeof('\0')) *
+                           sizeof(PRUnichar)));
+            if (*unicodeData) {
+                PRInt32 numberTmp = dataLength;
+                decoder->Convert((const char *)data, &numberTmp,
+                                 *unicodeData, &outUnicodeLen);
+#ifdef DEBUG_CLIPBOARD
+                if (numberTmp != dataLength)
+                    printf("didn't consume all the bytes\n");
+#endif
+                // null terminate. Convert() doesn't do it for us
+                (*unicodeData)[outUnicodeLen] = '\0';
+            }
+        } // if valid length
+    }
+}
+
+/*
+ * get "charset" information from clipboard data
+ * return value can be:
+ *  1. "UTF-16":      mozilla or "text/html" with "charset=utf-16"
+ *  2. "UNKNOWN":     mozilla can't detect what encode it use
+ *  3. other:         "text/html" with other charset than utf-16
+ */
+void GetHTMLCharset(guchar * data, PRInt32 dataLength, nsCString& str)
+{
+    // if detect "FFFE" or "FEFF", assume UTF-16
+    PRUnichar* beginChar =  (PRUnichar*)data;
+    if ((beginChar[0] == 0xFFFE) || (beginChar[0] == 0xFEFF)) {
+        str.AssignLiteral("UTF-16");
+        return;
+    }
+    // no "FFFE" and "FEFF", assume ASCII first to find "charset" info
+    const nsDependentCString htmlStr((const char *)data, dataLength);
+    nsACString::const_iterator start, end;
+    htmlStr.BeginReading(start);
+    htmlStr.EndReading(end);
+    nsACString::const_iterator valueStart(start), valueEnd(start);
+
+    if (CaseInsensitiveFindInReadable(
+        NS_LITERAL_CSTRING("CONTENT=\"text/html;"),
+        start, end)) {
+        start = end;
+        htmlStr.EndReading(end);
+
+        if (CaseInsensitiveFindInReadable(
+            NS_LITERAL_CSTRING("charset="),
+            start, end)) {
+            valueStart = end;
+            start = end;
+            htmlStr.EndReading(end);
+          
+            if (FindCharInReadable('"', start, end))
+                valueEnd = start;
+        }
+    }
+    // find "charset" in HTML
+    if (valueStart != valueEnd) {
+        str = Substring(valueStart, valueEnd);
+        ToUpperCase(str);
+#ifdef DEBUG_CLIPBOARD
+        printf("Charset of HTML = %s\n", charsetUpperStr.get());
+#endif
+        return;
+    }
+    str.AssignLiteral("UNKNOWN");
+}
+
+static void
+DispatchSelectionNotifyEvent(GtkWidget *widget, XEvent *xevent)
+{
+    GdkEvent event;
+    event.selection.type = GDK_SELECTION_NOTIFY;
+    event.selection.window = widget->window;
+    event.selection.selection = gdk_x11_xatom_to_atom(xevent->xselection.selection);
+    event.selection.target = gdk_x11_xatom_to_atom(xevent->xselection.target);
+    event.selection.property = gdk_x11_xatom_to_atom(xevent->xselection.property);
+    event.selection.time = xevent->xselection.time;
+
+    gtk_widget_event(widget, &event);
+}
+
+static void
+DispatchPropertyNotifyEvent(GtkWidget *widget, XEvent *xevent)
+{
+    if (((GdkWindowObject *) widget->window)->event_mask & GDK_PROPERTY_CHANGE_MASK) {
+        GdkEvent event;
+        event.property.type = GDK_PROPERTY_NOTIFY;
+        event.property.window = widget->window;
+        event.property.atom = gdk_x11_xatom_to_atom(xevent->xproperty.atom);
+        event.property.time = xevent->xproperty.time;
+        event.property.state = xevent->xproperty.state;
+
+        gtk_widget_event(widget, &event);
+    }
+}
+
+struct checkEventContext
+{
+    GtkWidget *cbWidget;
+    Atom       selAtom;
+};
+
+static Bool
+checkEventProc(Display *display, XEvent *event, XPointer arg)
+{
+    checkEventContext *context = (checkEventContext *) arg;
+
+    if (event->xany.type == SelectionNotify ||
+        (event->xany.type == PropertyNotify &&
+         event->xproperty.atom == context->selAtom)) {
+
+        GdkWindow *cbWindow = gdk_window_lookup(event->xany.window);
+        if (cbWindow) {
+            GtkWidget *cbWidget = NULL;
+            gdk_window_get_user_data(cbWindow, (gpointer *)&cbWidget);
+            if (cbWidget && GTK_IS_WIDGET(cbWidget)) {
+                context->cbWidget = cbWidget;
+                return True;
+            }
+        }
+    }
+
+    return False;
+}
+
+// Idle timeout for receiving selection and property notify events (microsec)
+static const int kClipboardTimeout = 500000;
+
+static void
+wait_for_retrieval(GtkClipboard *clipboard, retrieval_context *r_context)
+{
+    if (r_context->completed)  // the request completed synchronously
+        return;
+
+    Display *xDisplay = GDK_DISPLAY();
+    checkEventContext context;
+    context.cbWidget = NULL;
+    context.selAtom = gdk_x11_atom_to_xatom(gdk_atom_intern("GDK_SELECTION",
+                                                            FALSE));
+
+    // Send X events which are relevant to the ongoing selection retrieval
+    // to the clipboard widget.  Wait until either the operation completes, or
+    // we hit our timeout.  All other X events remain queued.
+
+    int select_result;
+
+#ifdef POLL_WITH_XCONNECTIONNUMBER
+    struct pollfd fds[1];
+    fds[0].fd = XConnectionNumber(xDisplay);
+    fds[0].events = POLLIN;
+#else
+    int cnumber = ConnectionNumber(xDisplay);
+    fd_set select_set;
+    FD_ZERO(&select_set);
+    FD_SET(cnumber, &select_set);
+    ++cnumber;
+    struct timeval tv;
+#endif
+
+    do {
+        XEvent xevent;
+
+        while (XCheckIfEvent(xDisplay, &xevent, checkEventProc,
+                             (XPointer) &context)) {
+
+            if (xevent.xany.type == SelectionNotify)
+                DispatchSelectionNotifyEvent(context.cbWidget, &xevent);
+            else
+                DispatchPropertyNotifyEvent(context.cbWidget, &xevent);
+
+            if (r_context->completed)
+                return;
+        }
+
+#ifdef POLL_WITH_XCONNECTIONNUMBER
+        select_result = poll(fds, 1, kClipboardTimeout / 1000);
+#else
+        tv.tv_sec = 0;
+        tv.tv_usec = kClipboardTimeout;
+        select_result = select(cnumber, &select_set, NULL, NULL, &tv);
+#endif
+    } while (select_result == 1);
+
+#ifdef DEBUG_CLIPBOARD
+    printf("exceeded clipboard timeout\n");
+#endif
+}
+
+static void
+clipboard_contents_received(GtkClipboard     *clipboard,
+                            GtkSelectionData *selection_data,
+                            gpointer          data)
+{
+    retrieval_context *context = static_cast<retrieval_context *>(data);
+    context->completed = PR_TRUE;
+
+    if (selection_data->length >= 0)
+        context->data = gtk_selection_data_copy(selection_data);
+}
+
+
+static GtkSelectionData *
+wait_for_contents(GtkClipboard *clipboard, GdkAtom target)
+{
+    retrieval_context context;
+    gtk_clipboard_request_contents(clipboard, target,
+                                   clipboard_contents_received,
+                                   &context);
+
+    wait_for_retrieval(clipboard, &context);
+    return static_cast<GtkSelectionData *>(context.data);
+}
+
+static void
+clipboard_text_received(GtkClipboard *clipboard,
+                        const gchar  *text,
+                        gpointer      data)
+{
+    retrieval_context *context = static_cast<retrieval_context *>(data);
+    context->completed = PR_TRUE;
+    context->data = g_strdup(text);
+}
+
+static gchar *
+wait_for_text(GtkClipboard *clipboard)
+{
+    retrieval_context context;
+    gtk_clipboard_request_text(clipboard, clipboard_text_received, &context);
+
+    wait_for_retrieval(clipboard, &context);
+    return static_cast<gchar *>(context.data);
+}
Index: offscreen/widget/src/headless/nsClipboard.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ offscreen/widget/src/headless/nsClipboard.h	2009-06-12 14:14:05.000000000 +0100
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Christopher Blizzard
+ * <blizzard@mozilla.org>.  Portions created by the Initial Developer
+ * are Copyright (C) 2001 the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef __nsClipboard_h_
+#define __nsClipboard_h_
+
+#include "nsIClipboard.h"
+#include "nsClipboardPrivacyHandler.h"
+#include "nsAutoPtr.h"
+#include <gtk/gtk.h>
+
+class nsClipboard : public nsIClipboard
+{
+public:
+    nsClipboard();
+    virtual ~nsClipboard();
+    
+    NS_DECL_ISUPPORTS
+    
+    NS_DECL_NSICLIPBOARD
+
+    // Make sure we are initialized, called from the factory
+    // constructor
+    nsresult  Init                (void);
+    // Someone requested the selection from the hidden widget
+    void      SelectionGetEvent   (GtkWidget         *aWidget,
+                                   GtkSelectionData  *aSelectionData,
+                                   guint              aTime);
+    void      SelectionClearEvent (GtkWidget         *aWidget,
+                                   GdkEventSelection *aEvent);
+
+
+private:
+    // Utility methods
+    static GdkAtom               GetSelectionAtom (PRInt32 aWhichClipboard);
+    static GtkSelectionData     *GetTargets       (GdkAtom aWhichClipboard);
+
+    // Get our hands on the correct transferable, given a specific
+    // clipboard
+    nsITransferable             *GetTransferable  (PRInt32 aWhichClipboard);
+
+    // Add a target type to the hidden widget
+    void                         AddTarget        (GdkAtom aName,
+                                                   GdkAtom aClipboard);
+
+    // The hidden widget where we do all of our operations
+    GtkWidget                   *mWidget;
+    // Hang on to our owners and transferables so we can transfer data
+    // when asked.
+    nsCOMPtr<nsIClipboardOwner>  mSelectionOwner;
+    nsCOMPtr<nsIClipboardOwner>  mGlobalOwner;
+    nsCOMPtr<nsITransferable>    mSelectionTransferable;
+    nsCOMPtr<nsITransferable>    mGlobalTransferable;
+    nsRefPtr<nsClipboardPrivacyHandler> mPrivacyHandler;
+
+};
+
+#endif /* __nsClipboard_h_ */
Index: offscreen/widget/src/headless/nsIImageToPixbuf.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ offscreen/widget/src/headless/nsIImageToPixbuf.h	2009-06-12 14:14:05.000000000 +0100
@@ -0,0 +1,62 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org widget code.
+ *
+ * The Initial Developer of the Original Code is
+ * Christian Biesinger <cbiesinger@web.de>.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NSIIMAGETOPIXBUF_H_
+#define NSIIMAGETOPIXBUF_H_
+
+#include "nsISupports.h"
+
+// dfa4ac93-83f2-4ab8-9b2a-0ff7022aebe2
+#define NSIIMAGETOPIXBUF_IID \
+{ 0xdfa4ac93, 0x83f2, 0x4ab8, \
+  { 0x9b, 0x2a, 0x0f, 0xf7, 0x02, 0x2a, 0xeb, 0xe2 } }
+
+class nsIImage;
+typedef struct _GdkPixbuf GdkPixbuf;
+
+/**
+ * An interface that allows converting an nsIImage to a GdkPixbuf*.
+ */
+class nsIImageToPixbuf : public nsISupports {
+    public:
+        NS_DECLARE_STATIC_IID_ACCESSOR(NSIIMAGETOPIXBUF_IID)
+
+        NS_IMETHOD_(GdkPixbuf*) ConvertImageToPixbuf(nsIImage* aImage) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIImageToPixbuf, NSIIMAGETOPIXBUF_IID)
+
+#endif
Index: offscreen/widget/src/headless/nsImageToPixbuf.cpp
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ offscreen/widget/src/headless/nsImageToPixbuf.cpp	2009-06-12 14:14:05.000000000 +0100
@@ -0,0 +1,196 @@
+/* vim:set sw=4 sts=4 et cin: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org widget code.
+ *
+ * The Initial Developer of the Original Code is
+ * Christian Biesinger <cbiesinger@web.de>.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+#include "gfxContext.h"
+
+#include "nsIImage.h"
+
+#include "nsAutoPtr.h"
+
+#include "nsImageToPixbuf.h"
+
+NS_IMPL_ISUPPORTS1(nsImageToPixbuf, nsIImageToPixbuf)
+
+inline unsigned char
+unpremultiply (unsigned char color,
+               unsigned char alpha)
+{
+    if (alpha == 0)
+        return 0;
+    // plus alpha/2 to round instead of truncate
+    return (color * 255 + alpha / 2) / alpha;
+}
+
+NS_IMETHODIMP_(GdkPixbuf*)
+nsImageToPixbuf::ConvertImageToPixbuf(nsIImage* aImage)
+{
+    return ImageToPixbuf(aImage);
+}
+
+GdkPixbuf*
+nsImageToPixbuf::ImageToPixbuf(nsIImage* aImage)
+{
+    PRInt32 width = aImage->GetWidth(),
+            height = aImage->GetHeight();
+
+    nsRefPtr<gfxPattern> pattern;
+    aImage->GetPattern(getter_AddRefs(pattern));
+
+    return PatternToPixbuf(pattern, width, height);
+}
+
+GdkPixbuf*
+nsImageToPixbuf::ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface, PRInt32 aWidth, PRInt32 aHeight)
+{
+    GdkPixbuf* pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, PR_TRUE, 8,
+                                       aWidth, aHeight);
+    if (!pixbuf)
+        return nsnull;
+
+    PRUint32 rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+    guchar* pixels = gdk_pixbuf_get_pixels (pixbuf);
+
+    long cairoStride = aImgSurface->Stride();
+    unsigned char* cairoData = aImgSurface->Data();
+
+    gfxASurface::gfxImageFormat format = aImgSurface->Format();
+
+    for (PRInt32 row = 0; row < aHeight; ++row) {
+        for (PRInt32 col = 0; col < aWidth; ++col) {
+            guchar* pixel = pixels + row * rowstride + 4 * col;
+
+            PRUint32* cairoPixel = reinterpret_cast<PRUint32*>
+                                                   ((cairoData + row * cairoStride + 4 * col));
+
+            if (format == gfxASurface::ImageFormatARGB32) {
+                const PRUint8 a = (*cairoPixel >> 24) & 0xFF;
+                const PRUint8 r = unpremultiply((*cairoPixel >> 16) & 0xFF, a);
+                const PRUint8 g = unpremultiply((*cairoPixel >>  8) & 0xFF, a);
+                const PRUint8 b = unpremultiply((*cairoPixel >>  0) & 0xFF, a);
+
+                *pixel++ = r;
+                *pixel++ = g;
+                *pixel++ = b;
+                *pixel++ = a;
+            } else {
+                NS_ASSERTION(format == gfxASurface::ImageFormatRGB24,
+                             "unexpected format");
+                const PRUint8 r = (*cairoPixel >> 16) & 0xFF;
+                const PRUint8 g = (*cairoPixel >>  8) & 0xFF;
+                const PRUint8 b = (*cairoPixel >>  0) & 0xFF;
+
+                *pixel++ = r;
+                *pixel++ = g;
+                *pixel++ = b;
+                *pixel++ = 0xFF; // A
+            }
+        }
+    }
+
+    return pixbuf;
+}
+
+GdkPixbuf*
+nsImageToPixbuf::SurfaceToPixbuf(gfxASurface* aSurface, PRInt32 aWidth, PRInt32 aHeight)
+{
+    if (aSurface->CairoStatus()) {
+        NS_ERROR("invalid surface");
+        return nsnull;
+    }
+
+    nsRefPtr<gfxImageSurface> imgSurface;
+    if (aSurface->GetType() == gfxASurface::SurfaceTypeImage) {
+        imgSurface = static_cast<gfxImageSurface*>
+                                (static_cast<gfxASurface*>(aSurface));
+    } else {
+        imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
+					 gfxImageSurface::ImageFormatARGB32);
+                                       
+        if (!imgSurface)
+            return nsnull;
+
+        nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
+        if (!context)
+            return nsnull;
+
+        context->SetOperator(gfxContext::OPERATOR_SOURCE);
+        context->SetSource(aSurface);
+        context->Paint();
+    }
+
+    return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
+}
+  
+GdkPixbuf*
+nsImageToPixbuf::PatternToPixbuf(gfxPattern* aPattern, PRInt32 aWidth, PRInt32 aHeight)
+{
+    if (aPattern->CairoStatus()) {
+        NS_ERROR("invalid pattern");
+        return nsnull;
+    }
+
+    nsRefPtr<gfxImageSurface> imgSurface;
+    if (aPattern->GetType() == gfxPattern::PATTERN_SURFACE) {
+        nsRefPtr<gfxASurface> surface = aPattern->GetSurface();
+        if (surface->GetType() == gfxASurface::SurfaceTypeImage) {
+            imgSurface = static_cast<gfxImageSurface*>
+                                    (static_cast<gfxASurface*>(surface.get()));
+        }
+    } 
+    
+    if (!imgSurface) {
+        imgSurface = new gfxImageSurface(gfxIntSize(aWidth, aHeight),
+					 gfxImageSurface::ImageFormatARGB32);
+                                       
+        if (!imgSurface)
+            return nsnull;
+
+        nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
+        if (!context)
+            return nsnull;
+
+        context->SetOperator(gfxContext::OPERATOR_SOURCE);
+        context->SetPattern(aPattern);
+        context->Paint();
+    }
+
+    return ImgSurfaceToPixbuf(imgSurface, aWidth, aHeight);
+}
Index: offscreen/widget/src/headless/nsImageToPixbuf.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ offscreen/widget/src/headless/nsImageToPixbuf.h	2009-06-12 14:14:05.000000000 +0100
@@ -0,0 +1,71 @@
+/* vim:set sw=4 sts=4 et cin: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org widget code.
+ *
+ * The Initial Developer of the Original Code is
+ * Christian Biesinger <cbiesinger@web.de>.
+ * Portions created by the Initial Developer are Copyright (C) 2006
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef NSIMAGETOPIXBUF_H_
+#define NSIMAGETOPIXBUF_H_
+
+#include "nsIImageToPixbuf.h"
+
+class gfxASurface;
+class gfxPattern;
+class gfxImageSurface;
+
+class nsImageToPixbuf : public nsIImageToPixbuf {
+    public:
+        NS_DECL_ISUPPORTS
+        NS_IMETHOD_(GdkPixbuf*) ConvertImageToPixbuf(nsIImage* aImage);
+
+        // Friendlier version of ConvertImageToPixbuf for callers inside of
+        // widget
+        static GdkPixbuf* ImageToPixbuf(nsIImage* aImage);
+        static GdkPixbuf* SurfaceToPixbuf(gfxASurface* aSurface,
+                                          PRInt32 aWidth, PRInt32 aHeight);
+        static GdkPixbuf* PatternToPixbuf(gfxPattern* aPattern,
+                                          PRInt32 aWidth, PRInt32 aHeight);
+    private:
+        static GdkPixbuf* ImgSurfaceToPixbuf(gfxImageSurface* aImgSurface,
+                                             PRInt32 aWidth, PRInt32 aHeight);
+        ~nsImageToPixbuf() {}
+};
+
+
+// fc2389b8-c650-4093-9e42-b05e5f0685b7
+#define NS_IMAGE_TO_PIXBUF_CID \
+{ 0xfc2389b8, 0xc650, 0x4093, \
+  { 0x9e, 0x42, 0xb0, 0x5e, 0x5f, 0x06, 0x85, 0xb7 } }
+
+#endif
Index: offscreen/widget/src/headless/nsWidgetFactory.cpp
===================================================================
--- offscreen.orig/widget/src/headless/nsWidgetFactory.cpp	2009-06-12 14:08:56.000000000 +0100
+++ offscreen/widget/src/headless/nsWidgetFactory.cpp	2009-06-12 14:15:24.000000000 +0100
@@ -46,6 +46,10 @@
 #include "nsWindow.h"
 #include "nsTransferable.h"
 #include "nsHTMLFormatConverter.h"
+#ifdef MOZ_X11
+#include "nsClipboardHelper.h"
+#include "nsClipboard.h"
+#endif
 #include "nsSound.h"
 #include "nsBidiKeyboard.h"
 #include "nsScreenManagerHeadless.h"
@@ -66,6 +70,10 @@
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
+#ifdef MOZ_X11
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init)
+#endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsScreenManagerHeadless)
 //NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
@@ -142,6 +150,16 @@
     NS_TRANSFERABLE_CID,
     "@mozilla.org/widget/transferable;1",
     nsTransferableConstructor },
+#ifdef MOZ_X11
+  { "Gtk Clipboard",
+    NS_CLIPBOARD_CID,
+    "@mozilla.org/widget/clipboard;1",
+    nsClipboardConstructor },
+  { "Clipboard Helper",
+    NS_CLIPBOARDHELPER_CID,
+    "@mozilla.org/widget/clipboardhelper;1",
+    nsClipboardHelperConstructor },
+#endif
   { "HTML Format Converter",
     NS_HTMLFORMATCONVERTER_CID,
     "@mozilla.org/widget/htmlformatconverter;1",
