Bug 462106 - Clear the data copied to clipboard inside the private browsing mode after leaving it (2nd try); r,sr=roc
authorEhsan Akhgari <ehsan.akhgari@gmail.com>
Fri, 30 Jan 2009 11:43:17 +0330
changeset 24436 9c777aa29014700fc6954e7e5dc1a289c7193c65
parent 24435 8c2b629e1bb8c65a29a1cb1b9ff82202f7d9e1e0
child 24437 3470b0b3a98d28a6a14236f461d31ab6ec0e6af5
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs462106
milestone1.9.2a1pre
Bug 462106 - Clear the data copied to clipboard inside the private browsing mode after leaving it (2nd try); r,sr=roc
widget/src/gtk2/nsClipboard.cpp
widget/src/gtk2/nsClipboard.h
widget/src/photon/nsClipboard.cpp
widget/src/photon/nsClipboard.h
widget/src/qt/nsClipboard.cpp
widget/src/qt/nsClipboard.h
widget/src/xpwidgets/Makefile.in
widget/src/xpwidgets/nsBaseClipboard.cpp
widget/src/xpwidgets/nsBaseClipboard.h
widget/src/xpwidgets/nsClipboardPrivacyHandler.cpp
widget/src/xpwidgets/nsClipboardPrivacyHandler.h
widget/tests/Makefile.in
widget/tests/test_bug462106.xul
--- a/widget/src/gtk2/nsClipboard.cpp
+++ b/widget/src/gtk2/nsClipboard.cpp
@@ -159,16 +159,24 @@ nsClipboard::SetData(nsITransferable *aT
          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 {
@@ -182,17 +190,16 @@ nsClipboard::SetData(nsITransferable *aT
     // 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
-    nsresult rv;
     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;
--- a/widget/src/gtk2/nsClipboard.h
+++ b/widget/src/gtk2/nsClipboard.h
@@ -35,16 +35,18 @@
  * 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();
     
@@ -79,12 +81,13 @@ private:
     // 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_ */
--- a/widget/src/photon/nsClipboard.cpp
+++ b/widget/src/photon/nsClipboard.cpp
@@ -125,16 +125,24 @@ NS_IMETHODIMP nsClipboard::SetData(nsITr
 
 	if ((aTransferable == mGlobalTransferable.get() && anOwner == mGlobalOwner.get() && 
 			aWhichClipboard == kGlobalClipboard ) || (aTransferable == mSelectionTransferable.get() && 
 			anOwner == mSelectionOwner.get() && aWhichClipboard == kSelectionClipboard))
 	{
 		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);
+
 	EmptyClipboard(aWhichClipboard);
 
 	switch (aWhichClipboard) 
 	{
 		case kSelectionClipboard:
 			mSelectionOwner = anOwner;
 			mSelectionTransferable = aTransferable;
 			break;
--- a/widget/src/photon/nsClipboard.h
+++ b/widget/src/photon/nsClipboard.h
@@ -41,16 +41,18 @@
 #ifndef nsClipboard_h__
 #define nsClipboard_h__
 
 #include <Pt.h>
 
 #include "nsIClipboard.h"
 #include "nsITransferable.h"
 #include "nsIClipboardOwner.h"
+#include "nsClipboardPrivacyHandler.h"
+#include "nsAutoPtr.h"
 #include <nsCOMPtr.h>
 
 class nsITransferable;
 class nsIClipboardOwner;
 class nsIWidget;
 
 /**
  * Native Clipboard wrapper
@@ -86,16 +88,17 @@ nsresult GetFormat(const char* aMimeStr,
 	inline nsITransferable *GetTransferable(PRInt32 aWhichClipboard);
 
 private:
   unsigned long GetFlavourTimestamp( char *type );
   nsCOMPtr<nsIClipboardOwner> mSelectionOwner;
   nsCOMPtr<nsIClipboardOwner> mGlobalOwner;
   nsCOMPtr<nsITransferable>   mSelectionTransferable;
   nsCOMPtr<nsITransferable>   mGlobalTransferable;
+  nsRefPtr<nsClipboardPrivacyHandler> mPrivacyHandler;
 
   // Used for communicating pasted data
   // from the asynchronous X routines back to a blocking paste:
   PRBool mBlocking;
   // Used for keeping track of the current input group
   PRInt32 mInputGroup;
 };
 
--- a/widget/src/qt/nsClipboard.cpp
+++ b/widget/src/qt/nsClipboard.cpp
@@ -303,16 +303,24 @@ nsClipboard::SetData(nsITransferable *aT
          && 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);
+
     EmptyClipboard(aWhichClipboard);
 
     QClipboard::Mode mode;
 
     if (kGlobalClipboard == aWhichClipboard)
     {
         mGlobalOwner = aOwner;
         mGlobalTransferable = aTransferable;
--- a/widget/src/qt/nsClipboard.h
+++ b/widget/src/qt/nsClipboard.h
@@ -36,16 +36,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 #ifndef nsClipboard_h__
 #define nsClipboard_h__
 
 #include "nsIClipboard.h"
 #include "nsITransferable.h"
 #include "nsIClipboardOwner.h"
+#include "nsClipboardPrivacyHandler.h"
+#include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 
 #include <qclipboard.h>
 
 /* Native Qt Clipboard wrapper */
 class nsClipboard : public nsIClipboard
 {
 public:
@@ -63,11 +65,12 @@ protected:
                                       QClipboard::Mode cbMode);
     NS_IMETHOD GetNativeClipboardData(nsITransferable *aTransferable,
                                       QClipboard::Mode cbMode);
 
     nsCOMPtr<nsIClipboardOwner> mSelectionOwner;
     nsCOMPtr<nsIClipboardOwner> mGlobalOwner;
     nsCOMPtr<nsITransferable>   mSelectionTransferable;
     nsCOMPtr<nsITransferable>   mGlobalTransferable;
+    nsRefPtr<nsClipboardPrivacyHandler> mPrivacyHandler;
 };
 
 #endif // nsClipboard_h__
--- a/widget/src/xpwidgets/Makefile.in
+++ b/widget/src/xpwidgets/Makefile.in
@@ -76,16 +76,17 @@ CPPSRCS		= \
 		nsPrimitiveHelpers.cpp \
 		nsXPLookAndFeel.cpp \
 		nsClipboardHelper.cpp \
 		nsPrintOptionsImpl.cpp \
 		nsPrintSettingsImpl.cpp \
 		nsPrintSession.cpp \
 		nsWidgetAtoms.cpp \
 		nsIdleService.cpp \
+		nsClipboardPrivacyHandler.cpp \
 		$(NULL)
 
 ifneq (,$(filter beos os2 cocoa windows,$(MOZ_WIDGET_TOOLKIT)))
 CPPSRCS += nsBaseClipboard.cpp
 endif
 
 ifneq (,$(filter beos qt gtk2 os2 cocoa photon windows,$(MOZ_WIDGET_TOOLKIT)))
 CPPSRCS += nsBaseFilePicker.cpp
--- a/widget/src/xpwidgets/nsBaseClipboard.cpp
+++ b/widget/src/xpwidgets/nsBaseClipboard.cpp
@@ -81,16 +81,22 @@ NS_IMETHODIMP nsBaseClipboard::SetData(n
     NS_ADDREF(mClipboardOwner);
 
   mTransferable = aTransferable;
   
   nsresult rv = NS_ERROR_FAILURE;
 
   if ( mTransferable ) {
     NS_ADDREF(mTransferable);
+    if (!mPrivacyHandler) {
+      rv = NS_NewClipboardPrivacyHandler(getter_AddRefs(mPrivacyHandler));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+    rv = mPrivacyHandler->PrepareDataForClipboard(mTransferable);
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = SetNativeClipboardData(aWhichClipboard);
   }
   
   return rv;
 }
 
 /**
   * Gets the transferable object
--- a/widget/src/xpwidgets/nsBaseClipboard.h
+++ b/widget/src/xpwidgets/nsBaseClipboard.h
@@ -35,16 +35,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsBaseClipboard_h__
 #define nsBaseClipboard_h__
 
 #include "nsIClipboard.h"
 #include "nsITransferable.h"
+#include "nsClipboardPrivacyHandler.h"
+#include "nsAutoPtr.h"
 
 class nsITransferable;
 class nsDataObj;
 class nsIClipboardOwner;
 class nsIWidget;
 
 /**
  * Native Win32 BaseClipboard wrapper
@@ -66,13 +68,14 @@ public:
 protected:
 
   NS_IMETHOD SetNativeClipboardData ( PRInt32 aWhichClipboard ) = 0;
   NS_IMETHOD GetNativeClipboardData ( nsITransferable * aTransferable, PRInt32 aWhichClipboard ) = 0;
 
   PRBool              mIgnoreEmptyNotification;
   nsIClipboardOwner * mClipboardOwner;
   nsITransferable   * mTransferable;
+  nsRefPtr<nsClipboardPrivacyHandler> mPrivacyHandler;
 
 };
 
 #endif // nsBaseClipboard_h__
 
new file mode 100644
--- /dev/null
+++ b/widget/src/xpwidgets/nsClipboardPrivacyHandler.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original author)
+ *
+ * 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 "nsClipboardPrivacyHandler.h"
+#include "nsITransferable.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIObserverService.h"
+#include "nsIClipboard.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsLiteralString.h"
+#include "nsNetCID.h"
+#include "nsXPCOM.h"
+
+#define NS_MOZ_DATA_FROM_PRIVATEBROWSING "application/x-moz-private-browsing"
+
+NS_IMPL_ISUPPORTS2(nsClipboardPrivacyHandler, nsIObserver, nsISupportsWeakReference)
+
+nsresult
+nsClipboardPrivacyHandler::Init()
+{
+  nsresult rv;
+  nsCOMPtr<nsIObserverService> observerService =
+    do_GetService("@mozilla.org/observer-service;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = observerService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, PR_TRUE);
+  return rv;
+}
+
+/**
+  * Prepare the transferable object to be inserted into the clipboard
+  *
+  */
+nsresult
+nsClipboardPrivacyHandler::PrepareDataForClipboard(nsITransferable * aTransferable)
+{
+  NS_ASSERTION(aTransferable, "clipboard given a null transferable");
+
+  nsresult rv = NS_OK;
+  if (InPrivateBrowsing()) {
+    nsCOMPtr<nsISupportsPRBool> data = do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+    if (data) {
+      rv = data->SetData(PR_TRUE);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = aTransferable->AddDataFlavor(NS_MOZ_DATA_FROM_PRIVATEBROWSING);
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      rv = aTransferable->SetTransferData(NS_MOZ_DATA_FROM_PRIVATEBROWSING, data, sizeof(PRBool));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return rv;
+}
+
+NS_IMETHODIMP
+nsClipboardPrivacyHandler::Observe(nsISupports *aSubject, char const *aTopic, PRUnichar const *aData)
+{
+  if (NS_LITERAL_STRING(NS_PRIVATE_BROWSING_LEAVE).Equals(aData)) {
+    nsresult rv;
+    nsCOMPtr<nsIClipboard> clipboard =
+      do_GetService("@mozilla.org/widget/clipboard;1", &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    const char * flavors[] = { NS_MOZ_DATA_FROM_PRIVATEBROWSING };
+    PRBool haveFlavors;
+    rv = clipboard->HasDataMatchingFlavors(flavors,
+                                           NS_ARRAY_LENGTH(flavors),
+                                           nsIClipboard::kGlobalClipboard,
+                                           &haveFlavors);
+    if (NS_SUCCEEDED(rv) && haveFlavors) {
+      // Empty the native clipboard by copying an empty transferable
+      nsCOMPtr<nsITransferable> nullData =
+        do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+      NS_ENSURE_SUCCESS(rv, rv);
+      rv = clipboard->SetData(nullData, nsnull,
+                              nsIClipboard::kGlobalClipboard);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+PRBool
+nsClipboardPrivacyHandler::InPrivateBrowsing()
+{
+  PRBool inPrivateBrowsingMode = PR_FALSE;
+  if (!mPBService)
+    mPBService = do_GetService(NS_PRIVATE_BROWSING_SERVICE_CONTRACTID);
+  if (mPBService)
+    mPBService->GetPrivateBrowsingEnabled(&inPrivateBrowsingMode);
+  return inPrivateBrowsingMode;
+}
+
+nsresult
+NS_NewClipboardPrivacyHandler(nsClipboardPrivacyHandler ** aHandler)
+{
+  NS_PRECONDITION(aHandler != nsnull, "null ptr");
+  if (!aHandler)
+    return NS_ERROR_NULL_POINTER;
+
+  *aHandler = new nsClipboardPrivacyHandler();
+  if (!*aHandler)
+    return NS_ERROR_OUT_OF_MEMORY;
+
+  NS_ADDREF(*aHandler);
+  nsresult rv = (*aHandler)->Init();
+  if (NS_FAILED(rv))
+    NS_RELEASE(*aHandler);
+
+  return rv;
+}
new file mode 100644
--- /dev/null
+++ b/widget/src/xpwidgets/nsClipboardPrivacyHandler.h
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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
+ * Ehsan Akhgari.
+ * Portions created by the Initial Developer are Copyright (C) 2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Ehsan Akhgari <ehsan.akhgari@gmail.com> (Original author)
+ *
+ * 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 nsClipboardPrivacyHandler_h__
+#define nsClipboardPrivacyHandler_h__
+
+#include "nsIObserver.h"
+#include "nsIPrivateBrowsingService.h"
+#include "nsWeakReference.h"
+#include "nsCOMPtr.h"
+
+class nsITransferable;
+
+// nsClipboardPrivacyHandler makes sure that clipboard data copied during
+// the private browsing mode does not leak after exiting this mode.
+// In order to ensure this, callers should store an object of this class
+// for their lifetime, and call PrepareDataForClipboard in their
+// nsIClipboard::SetData implementation before starting to use the
+// nsITransferable object in any way.
+
+class nsClipboardPrivacyHandler : public nsIObserver,
+                                  public nsSupportsWeakReference
+{
+
+public:
+
+  // nsISupports
+  NS_DECL_ISUPPORTS
+
+  // nsIObserver  
+  NS_DECL_NSIOBSERVER
+
+  nsresult Init();
+  nsresult PrepareDataForClipboard(nsITransferable * aTransferable);
+
+private:
+
+  PRBool InPrivateBrowsing();
+
+  nsCOMPtr<nsIPrivateBrowsingService> mPBService;
+
+};
+
+nsresult NS_NewClipboardPrivacyHandler(nsClipboardPrivacyHandler ** aHandler);
+
+#endif // nsClipboardPrivacyHandler_h__
--- a/widget/tests/Makefile.in
+++ b/widget/tests/Makefile.in
@@ -41,16 +41,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = widget/test 
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES =	test_bug343416.xul \
 		test_bug444800.xul \
+		test_bug462106.xul \
 		test_keycodes.xul \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += native_menus_window.xul \
                test_native_menus.xul \
                test_bug428405.xul \
                test_bug466599.xul \
new file mode 100644
--- /dev/null
+++ b/widget/tests/test_bug462106.xul
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462106
+-->
+<window title="Mozilla Bug 462106"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="initAndRunTests()">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/MochiKit/packed.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display: none"></div>
+    <pre id="test"></pre>
+  </body>
+
+  <!-- test code goes here -->
+  <script class="testbody" type="application/javascript">
+  <![CDATA[
+
+  /** Test for Bug 462106 **/
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+function copy(str) {
+  Cc["@mozilla.org/widget/clipboardhelper;1"].
+  getService(Ci.nsIClipboardHelper).
+  copyString(str);
+}
+
+function paste() {
+  let trans = Cc["@mozilla.org/widget/transferable;1"].
+              createInstance(Ci.nsITransferable);
+  trans.addDataFlavor("text/unicode");
+  let clip = Cc["@mozilla.org/widget/clipboard;1"].
+             getService(Ci.nsIClipboard);
+  clip.getData(trans, Ci.nsIClipboard.kGlobalClipboard);
+  let str = {}, length = {};
+  try {
+    trans.getTransferData("text/unicode", str, length);
+  } catch (e) {
+    str = null;
+  }
+  if (str) {
+    str = str.value.QueryInterface(Ci.nsISupportsString);
+    if (str)
+      str = str.data.substring(0, length.value / 2);
+  }
+  return str;
+}
+
+function initAndRunTests() {
+  let pb;
+  try {
+    pb = Cc["@mozilla.org/privatebrowsing;1"].
+         getService(Ci.nsIPrivateBrowsingService);
+  } catch (e) {
+    return;
+  }
+
+  SimpleTest.waitForExplicitFinish();
+
+  let prefBranch = Cc["@mozilla.org/preferences-service;1"].
+                   getService(Ci.nsIPrefBranch);
+  prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true);
+
+  const data = "random number: " + Math.random();
+  copy(data);
+  is(data, paste(), "Data successfully copied before entering the private browsing mode");
+  pb.privateBrowsingEnabled = true;
+  pb.privateBrowsingEnabled = false;
+  // the data should still be on the clipboard because it was copied before
+  // entering the private browsing mode
+  is(data, paste(), "Copied data persisted after leaving the private browsing mode");
+
+  const data2 = "another random number: " + Math.random();
+  pb.privateBrowsingEnabled = true;
+  copy(data2); // copy the data inside the private browsing mode
+  setTimeout(function() { // without this, the test fails sporadically
+    // the data should be on the clipboard inside the private browsing mode
+    is(data2, paste(), "Data successfully copied inside the private browsing mode");
+    pb.privateBrowsingEnabled = false;
+    // the data should no longer be on the clipboard at this stage
+    isnot(data2, paste(), "Data no longer available after leaving the private browsing mode");
+
+    prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session");
+    SimpleTest.finish();
+  }, 0);
+}
+
+  ]]>
+  </script>
+</window>