Bug 608170 - 'ChromeWorkers should have atob, btoa'. r=jst, a=blocking+
authorBen Turner <bent.mozilla@gmail.com>
Mon, 15 Nov 2010 17:39:28 -0800
changeset 57556 2192654e75c511c17a5bb6d32f409d7b1d65c113
parent 57555 91465c3b341b6697b248bb28c6a4276953e59c80
child 57557 759b21bbb64bbb1a5b06fa666f28c7158e0d2c4f
push id16978
push userbturner@mozilla.com
push dateTue, 16 Nov 2010 01:51:03 +0000
treeherdermozilla-central@2192654e75c5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst, blocking
bugs608170
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 608170 - 'ChromeWorkers should have atob, btoa'. r=jst, a=blocking+
dom/base/Makefile.in
dom/base/nsGlobalWindow.cpp
dom/src/threads/Makefile.in
dom/src/threads/nsDOMWorker.cpp
dom/src/threads/test/Makefile.in
dom/src/threads/test/atob_worker.js
dom/src/threads/test/test_atob.html
js/src/xpconnect/loader/Makefile.in
js/src/xpconnect/loader/mozJSComponentLoader.cpp
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcprivate.h
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -126,15 +126,16 @@ endif
 ifdef MOZ_IPC
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
+		-I$(srcdir)/../../js/src/xpconnect/src \
 		-I$(srcdir)/../../js/src/xpconnect/wrappers \
 		$(NULL)
 
 ifdef MOZ_X11
 CXXFLAGS += $(TK_CFLAGS)
 LDFLAGS += $(TK_LIBS)
 endif
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -183,17 +183,17 @@
 #include "nsIJSON.h"
 #include "nsIXULWindow.h"
 #ifdef MOZ_XUL
 #include "nsXULPopupManager.h"
 #include "nsIDOMXULControlElement.h"
 #include "nsIFrame.h"
 #endif
 
-#include "plbase64.h"
+#include "xpcprivate.h"
 
 #ifdef NS_PRINTING
 #include "nsIPrintSettings.h"
 #include "nsIPrintSettingsService.h"
 #include "nsIWebBrowserPrint.h"
 #endif
 
 #include "nsWindowRoot.h"
@@ -6807,94 +6807,38 @@ Is8bit(const nsAString& aString)
 
   return PR_TRUE;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Atob(const nsAString& aAsciiBase64String,
                      nsAString& aBinaryData)
 {
-  aBinaryData.Truncate();
-
   if (!Is8bit(aAsciiBase64String)) {
+    aBinaryData.Truncate();
     return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   }
 
-  PRUint32 dataLen = aAsciiBase64String.Length();
-
-  NS_LossyConvertUTF16toASCII base64(aAsciiBase64String);
-  if (base64.Length() != dataLen) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  PRInt32 resultLen = dataLen;
-  if (!base64.IsEmpty() && base64[dataLen - 1] == '=') {
-    if (base64.Length() > 1 && base64[dataLen - 2] == '=') {
-      resultLen = dataLen - 2;
-    } else {
-      resultLen = dataLen - 1;
-    }
-  }
-
-  resultLen = ((resultLen * 3) / 4);
-  // Add 4 extra bytes (one is needed for sure for null termination)
-  // to the malloc size just to make sure we don't end up writing past
-  // the allocated memory (the PL_Base64Decode API should really
-  // provide a guaranteed way to figure this out w/o needing to do the
-  // above yourself).
-  char *dest = static_cast<char *>(nsMemory::Alloc(resultLen + 4));
-  if (!dest) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  char *bin_data = PL_Base64Decode(base64.get(), dataLen, dest);
-
-  nsresult rv = NS_OK;
-
-  if (bin_data) {
-    CopyASCIItoUTF16(Substring(bin_data, bin_data + resultLen), aBinaryData);
-  } else {
-    rv = NS_ERROR_DOM_INVALID_CHARACTER_ERR;
-  }
-
-  nsMemory::Free(dest);
-
+  nsresult rv = nsXPConnect::Base64Decode(aAsciiBase64String, aBinaryData);
+  if (NS_FAILED(rv) && rv == NS_ERROR_INVALID_ARG) {
+    return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
+  }
   return rv;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::Btoa(const nsAString& aBinaryData,
                      nsAString& aAsciiBase64String)
 {
-  aAsciiBase64String.Truncate();
-
   if (!Is8bit(aBinaryData)) {
+    aAsciiBase64String.Truncate();
     return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
   }
 
-  char *bin_data = ToNewCString(aBinaryData);
-  if (!bin_data) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  PRInt32 resultLen = ((aBinaryData.Length() + 2) / 3) * 4;
-
-  char *base64 = PL_Base64Encode(bin_data, aBinaryData.Length(), nsnull);
-  if (!base64) {
-    nsMemory::Free(bin_data);
-
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-
-  CopyASCIItoUTF16(nsDependentCString(base64, resultLen), aAsciiBase64String);
-
-  PR_Free(base64);
-  nsMemory::Free(bin_data);
-
-  return NS_OK;
+  return nsXPConnect::Base64Encode(aBinaryData, aAsciiBase64String);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIDOMEventTarget
 //*****************************************************************************
 
 NS_IMETHODIMP
 nsGlobalWindow::AddEventListener(const nsAString& aType,
--- a/dom/src/threads/Makefile.in
+++ b/dom/src/threads/Makefile.in
@@ -59,19 +59,20 @@ CPPSRCS = \
   nsDOMWorkerScriptLoader.cpp \
   nsDOMWorkerSecurityManager.cpp \
   nsDOMWorkerTimeout.cpp \
   nsDOMWorkerXHR.cpp \
   nsDOMWorkerXHRProxy.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
+  -I$(topsrcdir)/content/base/src \
+  -I$(topsrcdir)/content/events/src \
   -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/dom/src/json \
-  -I$(topsrcdir)/content/base/src \
-  -I$(topsrcdir)/content/events/src \
+  -I$(topsrcdir)/js/src/xpconnect/src \
   $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -31,39 +31,39 @@
  * 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 "jscntxt.h"
-
 #include "nsDOMWorker.h"
-#include "nsAtomicRefcnt.h"
 
 #include "nsIDOMEvent.h"
 #include "nsIEventTarget.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIXPConnect.h"
 
+#include "jscntxt.h"
 #ifdef MOZ_SHARK
 #include "jsdbgapi.h"
 #endif
+#include "nsAtomicRefcnt.h"
 #include "nsAutoLock.h"
 #include "nsAXPCNativeCallContext.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
 #include "nsJSON.h"
 #include "nsJSUtils.h"
 #include "nsProxyRelease.h"
 #include "nsThreadUtils.h"
 #include "nsNativeCharsetUtils.h"
+#include "xpcprivate.h"
 
 #include "nsDOMThreadService.h"
 #include "nsDOMWorkerEvents.h"
 #include "nsDOMWorkerLocation.h"
 #include "nsDOMWorkerNavigator.h"
 #include "nsDOMWorkerPool.h"
 #include "nsDOMWorkerScriptLoader.h"
 #include "nsDOMWorkerTimeout.h"
@@ -100,16 +100,22 @@ public:
   static JSBool
   NewXMLHttpRequest(JSContext* aCx, uintN aArgc, jsval* aVp);
 
   static JSBool
   NewWorker(JSContext* aCx, uintN aArgc, jsval* aVp) {
     return MakeNewWorker(aCx, aArgc, aVp, nsDOMWorker::CONTENT);
   }
 
+  static JSBool
+  AtoB(JSContext* aCx, uintN aArgc, jsval* aVp);
+
+  static JSBool
+  BtoA(JSContext* aCx, uintN aArgc, jsval* aVp);
+
   // Chrome-only functions
   static JSBool
   NewChromeWorker(JSContext* aCx, uintN aArgc, jsval* aVp);
 
 #ifdef BUILD_CTYPES
   static JSBool
   CTypesLazyGetter(JSContext* aCx, JSObject* aObj, jsid aId, jsval* aVp);
 #endif
@@ -340,16 +346,108 @@ nsDOMWorkerFunctions::NewXMLHttpRequest(
     return JS_FALSE;
   }
 
   JS_SET_RVAL(aCs, aVp, v);
   return JS_TRUE;
 }
 
 JSBool
+nsDOMWorkerFunctions::AtoB(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
+  if (!str) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  // We want the bytes here, not the jschars.
+  const char* bytes = JS_GetStringBytesZ(aCx, str);
+  if (!bytes) {
+    return JS_FALSE;
+  }
+
+  nsDependentCString string(bytes, JS_GetStringLength(str));
+  nsCAutoString result;
+
+  if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
+    JS_ReportError(aCx, "Failed to decode base64 string!");
+    return JS_FALSE;
+  }
+
+  str = JS_NewStringCopyN(aCx, result.get(), result.Length());
+  if (!str) {
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
+  return JS_TRUE;
+}
+
+JSBool
+nsDOMWorkerFunctions::BtoA(JSContext* aCx,
+                           uintN aArgc,
+                           jsval* aVp)
+{
+  nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
+  NS_ASSERTION(worker, "This should be set by the DOM thread service!");
+
+  if (worker->IsCanceled()) {
+    return JS_FALSE;
+  }
+
+  if (!aArgc) {
+    JS_ReportError(aCx, "Function requires at least 1 parameter");
+    return JS_FALSE;
+  }
+
+  JSString* str = JS_ValueToString(aCx, JS_ARGV(aCx, aVp)[0]);
+  if (!str) {
+    NS_ASSERTION(JS_IsExceptionPending(aCx), "Need to set an exception!");
+    return JS_FALSE;
+  }
+
+  // We want the bytes here, not the jschars.
+  const char* bytes = JS_GetStringBytesZ(aCx, str);
+  if (!bytes) {
+    return JS_FALSE;
+  }
+
+  nsDependentCString string(bytes, JS_GetStringLength(str));
+  nsCAutoString result;
+
+  if (NS_FAILED(nsXPConnect::Base64Encode(string, result))) {
+    JS_ReportError(aCx, "Failed to encode base64 data!");
+    return JS_FALSE;
+  }
+
+  str = JS_NewStringCopyN(aCx, result.get(), result.Length());
+  if (!str) {
+    return JS_FALSE;
+  }
+
+  JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(str));
+  return JS_TRUE;
+}
+
+JSBool
 nsDOMWorkerFunctions::NewChromeWorker(JSContext* aCx,
                                       uintN aArgc,
                                       jsval* aVp)
 {
   nsDOMWorker* worker = static_cast<nsDOMWorker*>(JS_GetContextPrivate(aCx));
   NS_ASSERTION(worker, "This should be set by the DOM thread service!");
 
   if (!worker->IsPrivileged()) {
@@ -484,16 +582,18 @@ JSFunctionSpec gDOMWorkerFunctions[] = {
   { "dump",                nsDOMWorkerFunctions::Dump,                1, 0 },
   { "setTimeout",          nsDOMWorkerFunctions::SetTimeout,          1, 0 },
   { "clearTimeout",        nsDOMWorkerFunctions::KillTimeout,         1, 0 },
   { "setInterval",         nsDOMWorkerFunctions::SetInterval,         1, 0 },
   { "clearInterval",       nsDOMWorkerFunctions::KillTimeout,         1, 0 },
   { "importScripts",       nsDOMWorkerFunctions::LoadScripts,         1, 0 },
   { "XMLHttpRequest",      nsDOMWorkerFunctions::NewXMLHttpRequest,   0, 0 },
   { "Worker",              nsDOMWorkerFunctions::NewWorker,           1, 0 },
+  { "atob",                nsDOMWorkerFunctions::AtoB,                1, 0 },
+  { "btoa",                nsDOMWorkerFunctions::BtoA,                1, 0 },
 #ifdef MOZ_SHARK
   { "startShark",          js_StartShark,                             0, 0 },
   { "stopShark",           js_StopShark,                              0, 0 },
   { "connectShark",        js_ConnectShark,                           0, 0 },
   { "disconnectShark",     js_DisconnectShark,                        0, 0 },
 #endif
   { nsnull,                nsnull,                                    0, 0 }
 };
--- a/dom/src/threads/test/Makefile.in
+++ b/dom/src/threads/test/Makefile.in
@@ -43,16 +43,18 @@ VPATH            = @srcdir@
 
 relativesrcdir   = dom/src/threads/test
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
   test_404.html \
+  test_atob.html \
+  atob_worker.js \
   test_close.html \
   close_worker.js \
   test_closeOnGC.html \
   closeOnGC_worker.js \
   closeOnGC_server.sjs \
   test_errorPropagation.html \
   errorPropagation_worker1.js \
   errorPropagation_worker2.js \
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/atob_worker.js
@@ -0,0 +1,44 @@
+var data = [ -1, 0, 1, 1.5, undefined, true, false ];
+
+// XXXbent window.atob treats |null| as the empty string, whereas worker.atob
+//         and the js component loader treat it as the string 'null'. Meh.
+
+var str = "";
+for (var i = 0; i < 30; i++) {
+  data.push(str);
+  str += i % 2 ? "b" : "a";
+}
+
+function onmessage(event) {
+
+  data.forEach(function(string) {
+    postMessage({ type: "btoa", value: btoa(string) });
+    postMessage({ type: "atob", value: atob(btoa(string)) });
+  });
+
+  var threw;
+  try {
+    atob();
+  }
+  catch(e) {
+    threw = true;
+  }
+
+  if (!threw) {
+    throw "atob didn't throw when called without an argument!";
+  }
+  threw = false;
+
+  try {
+    btoa();
+  }
+  catch(e) {
+    threw = true;
+  }
+
+  if (!threw) {
+    throw "btoa didn't throw when called without an argument!";
+  }
+
+  postMessage({ type: "done" });
+}
new file mode 100644
--- /dev/null
+++ b/dom/src/threads/test/test_atob.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for DOM Worker Threads</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script src="atob_worker.js" language="javascript"></script>
+<script class="testbody" type="text/javascript">
+
+  var dataIndex = 0;
+
+  var worker = new Worker("atob_worker.js");
+  worker.onmessage = function(event) {
+    switch (event.data.type) {
+      case "done":
+        is(dataIndex, data.length, "Saw all values");
+        SimpleTest.finish();
+        return;
+      case "btoa":
+        is(btoa(data[dataIndex]), event.data.value,
+           "Good btoa value " + dataIndex);
+        break;
+      case "atob":
+        is(atob(btoa(data[dataIndex])) + "", event.data.value,
+           "Good round trip value " + dataIndex);
+        dataIndex++;
+        break;
+      default:
+        ok(false, "Worker posted a bad message: " + event.message);
+        worker.terminate();
+        SimpleTest.finish();
+    }
+  }
+
+  worker.onerror = function(event) {
+    ok(false, "Worker threw an error: " + event.message);
+    worker.terminate();
+    SimpleTest.finish();
+  }
+
+  worker.postMessage("go");
+
+  SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
--- a/js/src/xpconnect/loader/Makefile.in
+++ b/js/src/xpconnect/loader/Makefile.in
@@ -40,17 +40,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= jsloader
 LIBRARY_NAME	= jsloader_s
 FORCE_STATIC_LIB = 1
 LIBXUL_LIBRARY = 1
-
+LOCAL_INCLUDES += -I$(srcdir)/../src
 
 CPPSRCS		= mozJSComponentLoader.cpp mozJSSubScriptLoader.cpp
 
 EXTRA_JS_MODULES = XPCOMUtils.jsm ISO8601DateUtils.jsm
 
 include $(topsrcdir)/config/rules.mk
 
 DEFINES		+= -DJSFILE -DJS_THREADSAFE
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp
@@ -77,20 +77,20 @@
 #include "jscompartment.h"
 #include "jsprf.h"
 // For reporting errors with the console service
 #include "nsIScriptError.h"
 #include "nsIConsoleService.h"
 #include "nsIStorageStream.h"
 #include "nsIStringStream.h"
 #include "prmem.h"
-#include "plbase64.h"
 #if defined(XP_WIN)
 #include "nsILocalFileWin.h"
 #endif
+#include "xpcprivate.h"
 
 #ifdef MOZ_ENABLE_LIBXUL
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #endif
 
 #if defined(MOZ_SHARK) || defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) || defined(MOZ_TRACEVIS)
 #include "jsdbgapi.h"
@@ -213,70 +213,66 @@ Debug(JSContext *cx, uintN argc, jsval *
 #else
     return JS_TRUE;
 #endif
 }
 
 static JSBool
 Atob(JSContext *cx, uintN argc, jsval *vp)
 {
-    JSString *str;
     if (!argc)
         return JS_TRUE;
 
-    str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
+    JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
     if (!str)
         return JS_FALSE;
 
-    size_t base64StrLength = JS_GetStringLength(str);
-    char *base64Str = JS_GetStringBytes(str);
-
-    PRUint32 bin_dataLength = (PRUint32)base64StrLength;
-    if (base64StrLength >= 1 && base64Str[base64StrLength - 1] == '=') {
-        if (base64StrLength >= 2 && base64Str[base64StrLength - 2] == '=')
-            bin_dataLength -= 2;
-        else
-            --bin_dataLength;
-    }
-    bin_dataLength = (PRUint32)((PRUint64)bin_dataLength * 3) / 4;
-
-    char *bin_data = PL_Base64Decode(base64Str, base64StrLength, nsnull);
-    if (!bin_data)
+    const char* bytes = JS_GetStringBytesZ(cx, str);
+    if (!bytes)
         return JS_FALSE;
 
-    str = JS_NewStringCopyN(cx, bin_data, bin_dataLength);
-    PR_Free(bin_data);
+    nsDependentCString string(bytes, JS_GetStringLength(str));
+    nsCAutoString result;
+
+    if (NS_FAILED(nsXPConnect::Base64Decode(string, result))) {
+        JS_ReportError(cx, "Failed to decode base64 string!");
+        return JS_FALSE;
+    }
+
+    str = JS_NewStringCopyN(cx, result.get(), result.Length());
     if (!str)
         return JS_FALSE;
 
     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
     return JS_TRUE;
 }
 
 static JSBool
 Btoa(JSContext *cx, uintN argc, jsval *vp)
 {
-    JSString *str;
     if (!argc)
         return JS_TRUE;
 
-    str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
+    JSString *str = JS_ValueToString(cx, JS_ARGV(cx, vp)[0]);
     if (!str)
         return JS_FALSE;
 
-    char *bin_data = JS_GetStringBytes(str);
-    size_t bin_dataLength = JS_GetStringLength(str);
-
-    char *base64 = PL_Base64Encode(bin_data, bin_dataLength, nsnull);
-    if (!base64)
+    const char* bytes = JS_GetStringBytesZ(cx, str);
+    if (!bytes)
         return JS_FALSE;
 
-    PRUint32 base64Length = ((bin_dataLength + 2) / 3) * 4;
-    str = JS_NewStringCopyN(cx, base64, base64Length);
-    PR_Free(base64);
+    nsDependentCString data(bytes, JS_GetStringLength(str));
+    nsCAutoString result;
+
+    if (NS_FAILED(nsXPConnect::Base64Encode(data, result))) {
+        JS_ReportError(cx, "Failed to encode base64 data!");
+        return JS_FALSE;
+    }
+
+    str = JS_NewStringCopyN(cx, result.get(), result.Length());
     if (!str)
         return JS_FALSE;
 
     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
     return JS_TRUE;
 }
 
 static JSFunctionSpec gGlobalFun[] = {
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -50,16 +50,17 @@
 #include "jsobj.h"
 #include "jsfun.h"
 #include "jsscript.h"
 #include "nsThreadUtilsInternal.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
+#include "plbase64.h"
 #include "jstypedarray.h"
 
 #include "XrayWrapper.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
 
 #include "jsdIDebuggerService.h"
 
@@ -2584,16 +2585,118 @@ nsXPConnect::GetCaller(JSContext **aJSCo
 {
     XPCCallContext *ccx = XPCPerThreadData::GetData(nsnull)->GetCallContext();
     *aJSContext = ccx->GetJSContext();
 
     // Set to the caller in XPC_WN_Helper_{Call,Construct}
     *aObj = ccx->GetFlattenedJSObject();
 }
 
+// static
+nsresult
+nsXPConnect::Base64Encode(const nsACString &aBinaryData,
+                          nsACString &aString)
+{
+  // Check for overflow.
+  if(aBinaryData.Length() > (PR_UINT32_MAX / 4) * 3)
+      return NS_ERROR_FAILURE;
+
+  PRUint32 stringLen = ((aBinaryData.Length() + 2) / 3) * 4;
+
+  char *buffer;
+
+  // Add one byte for null termination.
+  if(aString.SetCapacity(stringLen + 1) &&
+     (buffer = aString.BeginWriting()) &&
+     PL_Base64Encode(aBinaryData.BeginReading(), aBinaryData.Length(), buffer))
+  {
+      // PL_Base64Encode doesn't null terminate the buffer for us when we pass
+      // the buffer in. Do that manually.
+      buffer[stringLen] = '\0';
+
+      aString.SetLength(stringLen);
+      return NS_OK;
+  }
+
+  aString.Truncate();
+  return NS_ERROR_INVALID_ARG;
+}
+
+// static
+nsresult
+nsXPConnect::Base64Encode(const nsAString &aString,
+                          nsAString &aBinaryData)
+{
+    NS_LossyConvertUTF16toASCII string(aString);
+    nsCAutoString binaryData;
+
+    nsresult rv = Base64Encode(string, binaryData);
+    if(NS_SUCCEEDED(rv))
+        CopyASCIItoUTF16(binaryData, aBinaryData);
+    else
+        aBinaryData.Truncate();
+
+    return rv;
+}
+
+// static
+nsresult
+nsXPConnect::Base64Decode(const nsACString &aString,
+                          nsACString &aBinaryData)
+{
+  // Check for overflow.
+  if(aString.Length() > PR_UINT32_MAX / 3)
+      return NS_ERROR_FAILURE;
+
+  PRUint32 binaryDataLen = ((aString.Length() * 3) / 4);
+
+  char *buffer;
+
+  // Add one byte for null termination.
+  if(aBinaryData.SetCapacity(binaryDataLen + 1) &&
+     (buffer = aBinaryData.BeginWriting()) &&
+     PL_Base64Decode(aString.BeginReading(), aString.Length(), buffer))
+  {
+      // PL_Base64Decode doesn't null terminate the buffer for us when we pass
+      // the buffer in. Do that manually, taking into account the number of '='
+      // characters we were passed.
+      if(!aString.IsEmpty() && aString[aString.Length() - 1] == '=')
+      {
+          if(aString.Length() > 1 && aString[aString.Length() - 2] == '=')
+              binaryDataLen -= 2;
+          else
+              binaryDataLen -= 1;
+      }
+      buffer[binaryDataLen] = '\0';
+
+      aBinaryData.SetLength(binaryDataLen);
+      return NS_OK;
+  }
+
+  aBinaryData.Truncate();
+  return NS_ERROR_INVALID_ARG;
+}
+
+// static
+nsresult
+nsXPConnect::Base64Decode(const nsAString &aBinaryData,
+                          nsAString &aString)
+{
+    NS_LossyConvertUTF16toASCII binaryData(aBinaryData);
+    nsCAutoString string;
+
+    nsresult rv = Base64Decode(binaryData, string);
+    if(NS_SUCCEEDED(rv))
+        CopyASCIItoUTF16(string, aString);
+    else
+        aString.Truncate();
+
+    return rv;
+}
+
 NS_IMETHODIMP
 nsXPConnect::SetDebugModeWhenPossible(PRBool mode)
 {
     gDesiredDebugMode = mode;
     return NS_OK;
 }
 
 /* These are here to be callable from a debugger */
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -144,20 +144,27 @@
  * something that works.
  */
 #include <windows.h>
 static FARPROC GetProcAddressA(HMODULE hMod, wchar_t *procName);
 #endif /* WINCE */
 
 #include <atlbase.h>
 #include "oaidl.h"
+#endif
+
+#ifdef XP_WIN
 // Nasty MS defines
+#ifdef GetClassInfo
 #undef GetClassInfo
+#endif
+#ifdef GetClassName
 #undef GetClassName
 #endif
+#endif /* XP_WIN */
 
 #include "nsINode.h"
 
 /***************************************************************************/
 // Compile time switches for instrumentation and stuff....
 
 // Note that one would not normally turn *any* of these on in a non-DEBUG build.
 
@@ -550,16 +557,28 @@ public:
     // The JS GC marks objects gray that are held alive directly or indirectly
     // by an XPConnect root. The cycle collector explores only this subset
     // of the JS heap.
     static JSBool IsGray(void *thing);
 
     nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
+    static nsresult Base64Encode(const nsACString &aString,
+                                 nsACString &aBinary);
+
+    static nsresult Base64Encode(const nsAString &aString,
+                                 nsAString &aBinaryData);
+
+    static nsresult Base64Decode(const nsACString &aBinaryData,
+                                 nsACString &aString);
+
+    static nsresult Base64Decode(const nsAString &aBinaryData,
+                                 nsAString &aString);
+
     // nsCycleCollectionParticipant
     NS_IMETHOD RootAndUnlinkJSObjects(void *p);
     NS_IMETHOD Unlink(void *p);
     NS_IMETHOD Unroot(void *p);
     NS_IMETHOD Traverse(void *p,
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime