Bug 641706: Make SpecialPowers able to create a XHR object with full system powers. r=smaug
authorJonas Sicking <jonas@sicking.cc>
Thu, 17 Mar 2011 09:19:13 -0700
changeset 63932 132e89233cfa
parent 63931 fb0f7cc68d0e
child 63933 ef57f679db3f
push id19284
push usersicking@mozilla.com
push date2011-03-25 22:21 +0000
treeherdermozilla-central@132e89233cfa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs641706
milestone2.2a1pre
Bug 641706: Make SpecialPowers able to create a XHR object with full system powers. r=smaug
content/base/src/nsXMLHttpRequest.cpp
content/base/src/nsXMLHttpRequest.h
content/base/test/test_bug426308.html
content/base/test/test_bug431701.html
testing/mochitest/specialpowers/content/specialpowers.js
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -121,29 +121,26 @@
 #define XML_HTTP_REQUEST_COMPLETED      (1 << 4)  // 4
 #define XML_HTTP_REQUEST_SENT           (1 << 5)  // Internal, LOADING in IE and external view
 #define XML_HTTP_REQUEST_STOPPED        (1 << 6)  // Internal, INTERACTIVE in IE and external view
 // The above states are mutually exclusive, change with ChangeState() only.
 // The states below can be combined.
 #define XML_HTTP_REQUEST_ABORTED        (1 << 7)  // Internal
 #define XML_HTTP_REQUEST_ASYNC          (1 << 8)  // Internal
 #define XML_HTTP_REQUEST_PARSEBODY      (1 << 9)  // Internal
-#define XML_HTTP_REQUEST_XSITEENABLED   (1 << 10) // Internal, Is any cross-site request allowed?
-                                                  //           Even if this is false the
-                                                  //           access-control spec is supported
-#define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 11) // Internal
-#define XML_HTTP_REQUEST_MULTIPART      (1 << 12) // Internal
-#define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 13) // Internal
-#define XML_HTTP_REQUEST_BACKGROUND     (1 << 14) // Internal
+#define XML_HTTP_REQUEST_SYNCLOOPING    (1 << 10) // Internal
+#define XML_HTTP_REQUEST_MULTIPART      (1 << 11) // Internal
+#define XML_HTTP_REQUEST_GOT_FINAL_STOP (1 << 12) // Internal
+#define XML_HTTP_REQUEST_BACKGROUND     (1 << 13) // Internal
 // This is set when we've got the headers for a multipart XMLHttpRequest,
 // but haven't yet started to process the first part.
-#define XML_HTTP_REQUEST_MPART_HEADERS  (1 << 15) // Internal
-#define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 16) // Internal
-#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 17) // Internal
-#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 18) // Internal
+#define XML_HTTP_REQUEST_MPART_HEADERS  (1 << 14) // Internal
+#define XML_HTTP_REQUEST_USE_XSITE_AC   (1 << 15) // Internal
+#define XML_HTTP_REQUEST_NEED_AC_PREFLIGHT (1 << 16) // Internal
+#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 17) // Internal
 
 #define XML_HTTP_REQUEST_LOADSTATES         \
   (XML_HTTP_REQUEST_UNINITIALIZED |         \
    XML_HTTP_REQUEST_OPENED |                \
    XML_HTTP_REQUEST_LOADED |                \
    XML_HTTP_REQUEST_INTERACTIVE |           \
    XML_HTTP_REQUEST_COMPLETED |             \
    XML_HTTP_REQUEST_SENT |                  \
@@ -1634,24 +1631,22 @@ nsXMLHttpRequest::GetCurrentHttpChannel(
   }
 
   return httpChannel;
 }
 
 nsresult
 nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
 {
-  // First check if cross-site requests are enabled
-  if ((mState & XML_HTTP_REQUEST_XSITEENABLED)) {
+  // First check if cross-site requests are enabled...
+  if (IsSystemXHR()) {
     return NS_OK;
   }
 
-  // or if this is a same-origin request.
-  NS_ASSERTION(!nsContentUtils::IsSystemPrincipal(mPrincipal),
-               "Shouldn't get here!");
+  // ...or if this is a same-origin request.
   if (nsContentUtils::CheckMayLoad(mPrincipal, aChannel)) {
     return NS_OK;
   }
 
   // This is a cross-site request
   mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
 
   // Check if we need to do a preflight request.
@@ -1792,22 +1787,16 @@ nsXMLHttpRequest::OpenRequest(const nsAC
                      uri,
                      nsnull,                    // ioService
                      loadGroup,
                      nsnull,                    // callbacks
                      nsIRequest::LOAD_BACKGROUND,
                      channelPolicy);
   if (NS_FAILED(rv)) return rv;
 
-  // Check if we're doing a cross-origin request.
-  if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
-    // Chrome callers are always allowed to read from different origins.
-    mState |= XML_HTTP_REQUEST_XSITEENABLED;
-  }
-
   mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
               XML_HTTP_REQUEST_NEED_AC_PREFLIGHT);
 
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
   if (httpChannel) {
     rv = httpChannel->SetRequestMethod(method);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -1818,27 +1807,16 @@ nsXMLHttpRequest::OpenRequest(const nsAC
 }
 
 /* void open (in AUTF8String method, in AUTF8String url); */
 NS_IMETHODIMP
 nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
                        PRBool async, const nsAString& user,
                        const nsAString& password, PRUint8 optional_argc)
 {
-  if (nsContentUtils::GetCurrentJSContext()) {
-    // We're (likely) called from JS
-
-    // Find out if UniversalBrowserRead privileges are enabled
-    if (nsContentUtils::IsCallerTrustedForRead()) {
-      mState |= XML_HTTP_REQUEST_XSITEENABLED;
-    } else {
-      mState &= ~XML_HTTP_REQUEST_XSITEENABLED;
-    }
-  }
-
   if (!optional_argc) {
     // No optional arguments were passed in. Default async to true.
     async = PR_TRUE;
   }
 
   return OpenRequest(method, url, async, user, password);
 }
 
@@ -1949,25 +1927,27 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     NS_ERROR("Ugh, still getting data on an aborted XMLHttpRequest!");
 
     return NS_ERROR_UNEXPECTED;
   }
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(request));
   NS_ENSURE_TRUE(channel, NS_ERROR_UNEXPECTED);
 
-  nsCOMPtr<nsIPrincipal> documentPrincipal = mPrincipal;
-  if (nsContentUtils::IsSystemPrincipal(documentPrincipal)) {
+  nsCOMPtr<nsIPrincipal> documentPrincipal;
+  if (IsSystemXHR()) {
     // Don't give this document the system principal.  We need to keep track of
     // mPrincipal being system because we use it for various security checks
     // that should be passing, but the document data shouldn't get a system
     // principal.
     nsresult rv;
     documentPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
     NS_ENSURE_SUCCESS(rv, rv);
+  } else {
+    documentPrincipal = mPrincipal;
   }
 
   channel->SetOwner(documentPrincipal);
 
   mReadRequest = request;
   mContext = ctxt;
   mState |= XML_HTTP_REQUEST_PARSEBODY;
   mState &= ~XML_HTTP_REQUEST_MPART_HEADERS;
@@ -2035,17 +2015,17 @@ nsXMLHttpRequest::OnStartRequest(nsIRequ
     nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(mOwner);
     rv = nsContentUtils::CreateDocument(emptyStr, emptyStr, nsnull, docURI,
                                         baseURI, mPrincipal, global,
                                         getter_AddRefs(mResponseXML));
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIDocument> responseDoc = do_QueryInterface(mResponseXML);
     responseDoc->SetPrincipal(documentPrincipal);
 
-    if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+    if (IsSystemXHR()) {
       responseDoc->ForceEnableXULXBL();
     }
 
     if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
       nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
       if (htmlDoc) {
         htmlDoc->DisableCookieAccess();
       }
@@ -2415,17 +2395,17 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   // Ignore argument if method is GET, there is no point in trying to
   // upload anything
   nsCAutoString method;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
 
   if (httpChannel) {
     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
 
-    if (!nsContentUtils::IsSystemPrincipal(mPrincipal)) {
+    if (!IsSystemXHR()) {
       // Get the referrer for the request.
       //
       // If it weren't for history.push/replaceState, we could just use the
       // principal's URI here.  But since we want changes to the URI effected
       // by push/replaceState to be reflected in the XHR referrer, we have to
       // be more clever.
       //
       // If the document's original URI (before any push/replaceStates) matches
@@ -2651,17 +2631,17 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   nsCOMPtr<nsIStreamListener> listener = this;
   if (mState & XML_HTTP_REQUEST_MULTIPART) {
     listener = new nsMultipartProxyListener(listener);
     if (!listener) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
-  if (!(mState & XML_HTTP_REQUEST_XSITEENABLED)) {
+  if (!IsSystemXHR()) {
     // Always create a nsCrossSiteListenerProxy here even if it's
     // a same-origin request right now, since it could be redirected.
     listener = new nsCrossSiteListenerProxy(listener, mPrincipal, mChannel,
                                             withCredentials, &rv);
     NS_ENSURE_TRUE(listener, NS_ERROR_OUT_OF_MEMORY);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
@@ -2848,26 +2828,26 @@ nsXMLHttpRequest::SetRequestHeader(const
                          nsCaseInsensitiveCStringComparator()) ||
         StringBeginsWith(header, NS_LITERAL_CSTRING("sec-"),
                          nsCaseInsensitiveCStringComparator())) {
       NS_WARNING("refusing to set request header");
       return NS_OK;
     }
 
     // Check for dangerous cross-site headers
-    PRBool safeHeader = !!(mState & XML_HTTP_REQUEST_XSITEENABLED);
+    bool safeHeader = IsSystemXHR();
     if (!safeHeader) {
       // Content-Type isn't always safe, but we'll deal with it in Send()
       const char *kCrossOriginSafeHeaders[] = {
         "accept", "accept-language", "content-language", "content-type",
         "last-event-id"
       };
       for (i = 0; i < NS_ARRAY_LENGTH(kCrossOriginSafeHeaders); ++i) {
         if (header.LowerCaseEqualsASCII(kCrossOriginSafeHeaders[i])) {
-          safeHeader = PR_TRUE;
+          safeHeader = true;
           break;
         }
       }
     }
 
     if (!safeHeader) {
       mACUnsafeHeaders.AppendElement(header);
     }
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -64,16 +64,17 @@
 #include "nsHashKeys.h"
 #include "prclist.h"
 #include "prtime.h"
 #include "nsIDOMNSEvent.h"
 #include "nsITimer.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsDOMProgressEvent.h"
 #include "nsDOMEventTargetWrapperCache.h"
+#include "nsContentUtils.h"
 
 class nsILoadGroup;
 class AsyncVerifyRedirectCallbackForwarder;
 
 class nsAccessControlLRUCache
 {
 public:
   struct TokenTime
@@ -328,16 +329,20 @@ protected:
                                   nsRefPtr<nsDOMEventListenerWrapper>& aCurrent,
                                   nsIDOMEventListener* aNew);
 
   nsresult GetInnerEventListener(nsRefPtr<nsDOMEventListenerWrapper>& aWrapper,
                                  nsIDOMEventListener** aListener);
 
   already_AddRefed<nsIHttpChannel> GetCurrentHttpChannel();
 
+  bool IsSystemXHR() {
+    return !!nsContentUtils::IsSystemPrincipal(mPrincipal);
+  }
+
   /**
    * Check if aChannel is ok for a cross-site request by making sure no
    * inappropriate headers are set, and no username/password is set.
    *
    * Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
    */
   nsresult CheckChannelForCrossSiteRequest(nsIChannel* aChannel);
 
--- a/content/base/test/test_bug426308.html
+++ b/content/base/test/test_bug426308.html
@@ -17,19 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 426308 **/
 
 const SJS_URL = "http://example.org:80/tests/content/base/test/bug426308-redirect.sjs";
 
-netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
-
-var req = new XMLHttpRequest();
+var req = SpecialPowers.createSystemXHR();
 req.open("GET", SJS_URL + "?" + window.location.href, false);
 req.send(null);
 
 is(req.status, 200, "Redirect did not happen");
 
 </script>
 </pre>
 </body>
--- a/content/base/test/test_bug431701.html
+++ b/content/base/test/test_bug431701.html
@@ -47,18 +47,17 @@ function frameDoc(id) {
 
 function createDoc() {
   return document.implementation.createDocument('', 'html', null);
 }
 
 function xhrDoc(idx) {
   return function() {
     // Defy same-origin restrictions!
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    var xhr = new XMLHttpRequest();
+    var xhr = SpecialPowers.createSystemXHR();
     xhr.open("GET", docSources[idx], false);
     xhr.send();
     return xhr.responseXML;
   };
 }
 
 // Each row has the document getter function, then the characterSet,
 // inputEncoding, xmlEncoding expected for that document.
--- a/testing/mochitest/specialpowers/content/specialpowers.js
+++ b/testing/mochitest/specialpowers/content/specialpowers.js
@@ -32,23 +32,28 @@
  * 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 *****/
 /* This code is loaded in every child process that is started by mochitest in
  * order to be used as a replacement for UniversalXPConnect
  */
+
+var Ci = Components.interfaces;
+var Cc = Components.classes;
+
 function SpecialPowers(window) {
   this.window = window;
   bindDOMWindowUtils(this, window);
 }
 
 function bindDOMWindowUtils(sp, window) {
-  var util = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils);
+  var util = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIDOMWindowUtils);
   // This bit of magic brought to you by the letters
   // B Z, and E, S and the number 5.
   //
   // Take all of the properties on the nsIDOMWindowUtils-implementing
   // object, and rebind them onto a new object with a stub that uses
   // apply to call them from this privileged scope. This way we don't
   // have to explicitly stub out new methods that appear on
   // nsIDOMWindowUtils.
@@ -130,17 +135,16 @@ SpecialPowers.prototype = {
       msg = {'op':'set', 'prefName': aPrefName, 'prefType': aPrefType, 'prefValue': aValue};
     }
     return(sendSyncMessage('SPPrefService', msg)[0]);
   },
 
   //XXX: these APIs really ought to be removed, they're not e10s-safe.
   // (also they're pretty Firefox-specific)
   _getTopChromeWindow: function(window) {
-    var Ci = Components.interfaces;
     return window.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIWebNavigation)
                  .QueryInterface(Ci.nsIDocShellTreeItem)
                  .rootTreeItem
                  .QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIDOMWindow)
                  .QueryInterface(Ci.nsIDOMChromeWindow);
   },
@@ -164,16 +168,21 @@ SpecialPowers.prototype = {
                                       .hasAttribute("disabled");
   },
 
   addChromeEventListener: function(type, listener, capture, allowUntrusted) {
     addEventListener(type, listener, capture, allowUntrusted);
   },
   removeChromeEventListener: function(type, listener, capture) {
     removeEventListener(type, listener, capture);
+  },
+
+  createSystemXHR: function() {
+    return Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+             .createInstance(Ci.nsIXMLHttpRequest);
   }
 };
 
 // Expose everything but internal APIs (starting with underscores) to
 // web content.
 SpecialPowers.prototype.__exposedProps__ = {};
 for each (i in Object.keys(SpecialPowers.prototype).filter(function(v) {return v.charAt(0) != "_";})) {
   SpecialPowers.prototype.__exposedProps__[i] = "r";