Bug 1123480 - Component: widget nsTransferable private browsing correction. r=jdm
authorMichael Schloh von Bennewitz <michael@schloh.com>
Wed, 18 Feb 2015 06:52:00 -0500
changeset 247321 55e4ab92ff9470bfeadb60a999d4833205cb426d
parent 247320 13b2859e5c155a5993e86ffb914b0620cf6f4031
child 247322 b213954a5d30d946088cc8267d7c064753cec0a4
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1123480
milestone38.0a1
Bug 1123480 - Component: widget nsTransferable private browsing correction. r=jdm This patch mitigates violation of private browsing disk access. The DataStruct API and implementation is modified to obey private browsing design when used by objects such as nsTransferable (during clipboard data caching for example.) Without this patch, a user is misled by use of private browsing when copying (or in some case just selecting) large blocks of text. A condition (presently hard coded at one million bytes kLargeDatasetSize) produces a cache file on disk regardless of whether private browsing is in use. This violates Mozilla's design (documented online at https://support.mozilla.org/kb/private-browsing-browse-web-without-saving-info/ and https://wiki.mozilla.org/PrivateBrowsing) This patch simply corrects the violation, discovered and resolved by the Tor Browser community.
widget/nsTransferable.cpp
widget/nsTransferable.h
widget/tests/chrome.ini
widget/tests/test_bug1123480.xul
--- a/widget/nsTransferable.cpp
+++ b/widget/nsTransferable.cpp
@@ -51,20 +51,21 @@ size_t GetDataForFlavor (const nsTArray<
 //-------------------------------------------------------------------------
 DataStruct::~DataStruct() 
 { 
   if (mCacheFileName) free(mCacheFileName); 
 }
 
 //-------------------------------------------------------------------------
 void
-DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen )
+DataStruct::SetData ( nsISupports* aData, uint32_t aDataLen, bool aIsPrivateData )
 {
   // Now, check to see if we consider the data to be "too large"
-  if (aDataLen > kLargeDatasetSize) {
+  // as well as ensuring that private browsing mode is disabled
+  if (aDataLen > kLargeDatasetSize && !aIsPrivateData) {
     // if so, cache it to disk instead of memory
     if ( NS_SUCCEEDED(WriteCache(aData, aDataLen)) )
       return;
     else
 			NS_WARNING("Oh no, couldn't write data to the cache file");   
   } 
 
   mData    = aData;
@@ -394,33 +395,33 @@ nsTransferable::SetTransferData(const ch
   MOZ_ASSERT(mInitialized);
 
   NS_ENSURE_ARG(aFlavor);
 
   // first check our intrinsic flavors to see if one has been registered.
   for (size_t i = 0; i < mDataArray.Length(); ++i) {
     DataStruct& data = mDataArray.ElementAt(i);
     if ( data.GetFlavor().Equals(aFlavor) ) {
-      data.SetData ( aData, aDataLen );
+      data.SetData ( aData, aDataLen, mPrivateData );
       return NS_OK;
     }
   }
 
   // if not, try using a format converter to find a flavor to put the data in
   if ( mFormatConv ) {
     for (size_t i = 0; i < mDataArray.Length(); ++i) {
       DataStruct& data = mDataArray.ElementAt(i);
       bool canConvert = false;
       mFormatConv->CanConvert(aFlavor, data.GetFlavor().get(), &canConvert);
 
       if ( canConvert ) {
         nsCOMPtr<nsISupports> ConvertedData;
         uint32_t ConvertedLen;
         mFormatConv->Convert(aFlavor, aData, aDataLen, data.GetFlavor().get(), getter_AddRefs(ConvertedData), &ConvertedLen);
-        data.SetData(ConvertedData, ConvertedLen);
+        data.SetData(ConvertedData, ConvertedLen, mPrivateData);
         return NS_OK;
       }
     }
   }
 
   // Can't set data neither directly nor through converter. Just add this flavor and try again
   nsresult result = NS_ERROR_FAILURE;
   if ( NS_SUCCEEDED(AddDataFlavor(aFlavor)) )
--- a/widget/nsTransferable.h
+++ b/widget/nsTransferable.h
@@ -23,17 +23,17 @@ class nsDataObj;
 //
 struct DataStruct
 {
   explicit DataStruct ( const char* aFlavor )
     : mDataLen(0), mFlavor(aFlavor), mCacheFileName(nullptr) { }
   ~DataStruct();
   
   const nsCString& GetFlavor() const { return mFlavor; }
-  void SetData( nsISupports* inData, uint32_t inDataLen );
+  void SetData( nsISupports* inData, uint32_t inDataLen, bool aIsPrivateData );
   void GetData( nsISupports** outData, uint32_t *outDataLen );
   already_AddRefed<nsIFile> GetFileSpec(const char* aFileName);
   bool IsDataAvailable() const { return (mData && mDataLen > 0) || (!mData && mCacheFileName); }
   
 protected:
 
   enum {
     // The size of data over which we write the data to disk rather than
--- a/widget/tests/chrome.ini
+++ b/widget/tests/chrome.ini
@@ -78,8 +78,11 @@ skip-if = toolkit != "cocoa"
 [test_chrome_context_menus_win.xul]
 skip-if = toolkit != "windows"
 support-files = chrome_context_menus_win.xul
 [test_plugin_input_event.html]
 skip-if = toolkit != "windows"
 [test_mouse_scroll.xul]
 skip-if = toolkit != "windows"
 support-files = window_mouse_scroll_win.html
+
+# Privacy relevant
+[test_bug1123480.xul]
new file mode 100644
--- /dev/null
+++ b/widget/tests/test_bug1123480.xul
@@ -0,0 +1,79 @@
+<?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=1123480
+-->
+<window title="Mozilla Bug 1123480"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="RunTest();">
+  <title>nsTransferable PBM Overflow Selection Test</title>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+  // Boilerplate constructs
+  var SmallDataset = 100000; // Hundred thousand bytes
+
+  // Create 1 Mo of sample garbage text
+  var Ipsum = ""; // Select text from this
+  for (var Iter = 0; Iter < SmallDataset; Iter++) {
+      Ipsum += Math.random().toString(36) + ' ';
+  }
+
+  function RunTest() {
+    // Construct a nsIFile object for access to file methods
+    Components.utils.import("resource://gre/modules/FileUtils.jsm");
+    var clipboardFile = FileUtils.getFile("TmpD", ["clipboardcache"]);
+
+    // Sanitize environment
+    if (clipboardFile.exists()) {
+        clipboardFile.remove(false);
+    }
+    ok(!clipboardFile.exists(), "failed to presanitize the environment");
+
+    // Overflow a nsTransferable region by using the clipboard helper
+    const gClipboardHelper = Components.classes["@mozilla.org/widget/clipboardhelper;1"].getService(Components.interfaces.nsIClipboardHelper);
+    gClipboardHelper.copyString(Ipsum);
+
+    // Disabled private browsing mode should cache large selections to disk
+    ok(clipboardFile.exists(), "correctly saved memory by caching to disk");
+
+    // Sanitize environment again
+    if (clipboardFile.exists()) {
+        clipboardFile.remove(false);
+    }
+    ok(!clipboardFile.exists(), "failed to postsanitize the environment");
+
+    // Repeat procedure of plain text selection with private browsing enabled
+    Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+    var Winpriv = window.open("about:blank", "_blank", "chrome, width=500, height=200, private");
+    ok(Winpriv, "failed to open private window");
+    ok(PrivateBrowsingUtils.isContentWindowPrivate(Winpriv), "correctly used a private window context");
+
+    // Select plaintext in private channel
+    Components.utils.import('resource://gre/modules/Services.jsm');
+    const nsTransferable = Components.Constructor("@mozilla.org/widget/transferable;1", "nsITransferable");
+    const nsSupportsString = Components.Constructor("@mozilla.org/supports-string;1", "nsISupportsString");
+    var Loadctx = PrivateBrowsingUtils.privacyContextFromWindow(Winpriv);
+    var Transfer = nsTransferable();
+    var Suppstr = nsSupportsString();
+    Suppstr.data = Ipsum;
+    Transfer.init(Loadctx);
+    Transfer.addDataFlavor("text/plain");
+    Transfer.setTransferData("text/plain", Suppstr, Ipsum.length);
+    Services.clipboard.setData(Transfer, null, Services.clipboard.kGlobalClipboard);
+
+    // Enabled private browsing mode should not cache any selection to disk
+    ok(!clipboardFile.exists(), "did not violate private browsing mode");
+  }
+  ]]>
+  </script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1123480"
+     target="_blank">Mozilla Bug 1123480</a>
+  </body>
+</window>