Bug 936340 - Implement navigator.sendBeacon. r=sicking
☠☠ backed out by adcc61aa0615 ☠ ☠
authorRichard Barnes <rbarnes@mozilla.com>
Tue, 25 Feb 2014 08:40:54 -0500
changeset 170703 0cea4bded6f8088096f07adbe26802b560559337
parent 170702 71a558bf7b6f2029f8b7ea55778b6b15f6716af2
child 170704 cc15ce931273d9aee9139490ca6b781ae4533c3f
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssicking
bugs936340
milestone30.0a1
Bug 936340 - Implement navigator.sendBeacon. r=sicking
content/base/public/nsContentPolicyUtils.h
content/base/public/nsIContentPolicy.idl
content/base/src/contentSecurityPolicy.js
content/base/src/nsMixedContentBlocker.cpp
content/base/test/file_mixed_content_main.html
dom/base/Navigator.cpp
dom/base/Navigator.h
dom/tests/mochitest/beacon/beacon-frame.html
dom/tests/mochitest/beacon/beacon-handler.sjs
dom/tests/mochitest/beacon/beacon-set-cookie.sjs
dom/tests/mochitest/beacon/mochitest.ini
dom/tests/mochitest/beacon/moz.build
dom/tests/mochitest/beacon/test_beacon.html
dom/tests/mochitest/beacon/test_beaconContentPolicy.html
dom/tests/mochitest/beacon/test_beaconCookies.html
dom/tests/mochitest/beacon/test_beaconFrame.html
dom/tests/mochitest/beacon/test_beaconPreflight.html
dom/tests/mochitest/moz.build
dom/webidl/Navigator.webidl
netwerk/mime/nsMimeTypes.h
testing/mochitest/b2g-debug.json
testing/mochitest/b2g-desktop.json
testing/mochitest/b2g.json
--- a/content/base/public/nsContentPolicyUtils.h
+++ b/content/base/public/nsContentPolicyUtils.h
@@ -105,16 +105,17 @@ NS_CP_ContentTypeName(uint32_t contentTy
     CASE_RETURN( TYPE_XMLHTTPREQUEST    );
     CASE_RETURN( TYPE_OBJECT_SUBREQUEST );
     CASE_RETURN( TYPE_DTD               );
     CASE_RETURN( TYPE_FONT              );
     CASE_RETURN( TYPE_MEDIA             );
     CASE_RETURN( TYPE_WEBSOCKET         );
     CASE_RETURN( TYPE_CSP_REPORT        );
     CASE_RETURN( TYPE_XSLT              );
+    CASE_RETURN( TYPE_BEACON            );
    default:
     return "<Unknown Type>";
   }
 }
 
 #endif // defined(PR_LOGGING)
 
 #undef CASE_RETURN
--- a/content/base/public/nsIContentPolicy.idl
+++ b/content/base/public/nsIContentPolicy.idl
@@ -140,16 +140,21 @@ interface nsIContentPolicy : nsISupports
    */
   const nsContentPolicyType TYPE_CSP_REPORT = 17;
 
   /**
    * Indicates a style sheet transformation.
    */
   const nsContentPolicyType TYPE_XSLT = 18;
 
+  /**
+   * Indicates a beacon post.
+   */
+  const nsContentPolicyType TYPE_BEACON = 19;
+
   /* When adding new content types, please update nsContentBlocker,
    * NS_CP_ContentTypeName, contentSecurityPolicy.js, all nsIContentPolicy
    * implementations, and other things that are not listed here that are
    * related to nsIContentPolicy. */
 
   //////////////////////////////////////////////////////////////////////
 
   /**
--- a/content/base/src/contentSecurityPolicy.js
+++ b/content/base/src/contentSecurityPolicy.js
@@ -92,16 +92,17 @@ function ContentSecurityPolicy() {
   csp._MAPPINGS[cp.TYPE_IMAGE]             = cspr_sd_new.IMG_SRC;
   csp._MAPPINGS[cp.TYPE_STYLESHEET]        = cspr_sd_new.STYLE_SRC;
   csp._MAPPINGS[cp.TYPE_OBJECT]            = cspr_sd_new.OBJECT_SRC;
   csp._MAPPINGS[cp.TYPE_OBJECT_SUBREQUEST] = cspr_sd_new.OBJECT_SRC;
   csp._MAPPINGS[cp.TYPE_SUBDOCUMENT]       = cspr_sd_new.FRAME_SRC;
   csp._MAPPINGS[cp.TYPE_MEDIA]             = cspr_sd_new.MEDIA_SRC;
   csp._MAPPINGS[cp.TYPE_FONT]              = cspr_sd_new.FONT_SRC;
   csp._MAPPINGS[cp.TYPE_XSLT]              = cspr_sd_new.SCRIPT_SRC;
+  csp._MAPPINGS[cp.TYPE_BEACON]            = cspr_sd_new.CONNECT_SRC;
 
   /* Our original CSP implementation's mappings for XHR and websocket
    * These should be changed to be = cspr_sd.CONNECT_SRC when we remove
    * the original implementation - NOTE: order in this array is important !!!
    */
   csp._MAPPINGS[cp.TYPE_XMLHTTPREQUEST]    = cspr_sd_old.XHR_SRC;
   csp._MAPPINGS[cp.TYPE_WEBSOCKET]         = cspr_sd_old.XHR_SRC;
 
--- a/content/base/src/nsMixedContentBlocker.cpp
+++ b/content/base/src/nsMixedContentBlocker.cpp
@@ -237,16 +237,18 @@ nsMixedContentBlocker::ShouldLoad(uint32
   // opts into ping, no request will be made.  Categorizing this as Mixed
   // Display Content for now, but this is subject to change.
   //
   // TYPE_STYLESHEET: XSLT stylesheets can insert scripts. CSS positioning
   // and other advanced CSS features can possibly be exploited to cause
   // spoofing attacks (e.g. make a "grant permission" button look like a
   // "refuse permission" button).
   //
+  // TYPE_BEACON: Beacon requests are similar to TYPE_PING, but are default on.
+  //
   // TYPE_WEBSOCKET: The Websockets API requires browsers to
   // reject mixed-content websockets: "If secure is false but the origin of
   // the entry script has a scheme component that is itself a secure protocol,
   // e.g. HTTPS, then throw a SecurityError exception." We already block mixed
   // content websockets within the websockets implementation, so we don't need
   // to do any blocking here, nor do we need to provide a way to undo or
   // override the blocking. Websockets without TLS are very flaky anyway in the
   // face of many HTTP-aware proxies. Compared to psasive content, there is
@@ -280,16 +282,17 @@ nsMixedContentBlocker::ShouldLoad(uint32
 
 
     // Static display content is considered moderate risk for mixed content so
     // these will be blocked according to the mixed display preference
     case TYPE_IMAGE:
     case TYPE_MEDIA:
     case TYPE_OBJECT_SUBREQUEST:
     case TYPE_PING:
+    case TYPE_BEACON:
       classification = eMixedDisplay;
       break;
 
     // Active content (or content with a low value/risk-of-blocking ratio)
     // that has been explicitly evaluated; listed here for documentation
     // purposes and to avoid the assertion and warning for the default case.
     case TYPE_CSP_REPORT:
     case TYPE_DTD:
--- a/content/base/test/file_mixed_content_main.html
+++ b/content/base/test/file_mixed_content_main.html
@@ -25,16 +25,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     Load events for external fonts are not detectable by javascript.
   case nsIContentPolicy::TYPE_WEBSOCKET: - NO TEST:
     websocket connections over https require an encrypted websocket protocol (wss:)
 
   case nsIContentPolicy::TYPE_IMAGE:
   case nsIContentPolicy::TYPE_MEDIA:
   case nsIContentPolicy::TYPE_PING:
     our ping implementation is off by default and does not comply with the current spec (bug 786347)
+  case nsIContentPolicy::TYPE_BEACON:
+
   }
      */
 -->
 
 <script>
   var baseUrl = "http://example.com/tests/content/base/test/file_mixed_content_server.sjs";
 
   //For tests that require setTimeout, set the maximum polling time to 100 x 100ms = 10 seconds.
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -10,16 +10,21 @@
 #include "Navigator.h"
 #include "nsIXULAppInfo.h"
 #include "nsPluginArray.h"
 #include "nsMimeTypeArray.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/DesktopNotification.h"
 #include "nsGeolocation.h"
 #include "nsIHttpProtocolHandler.h"
+#include "nsIContentPolicy.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsContentPolicyUtils.h"
+#include "nsCrossSiteListenerProxy.h"
+#include "nsISupportsPriority.h"
 #include "nsICachingChannel.h"
 #include "nsIWebContentHandlerRegistrar.h"
 #include "nsICookiePermission.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
@@ -40,24 +45,29 @@
 #ifdef MOZ_B2G_RIL
 #include "mozilla/dom/IccManager.h"
 #include "mozilla/dom/CellBroadcast.h"
 #include "mozilla/dom/MobileConnectionArray.h"
 #include "mozilla/dom/Voicemail.h"
 #endif
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
+#include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
+#include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
+#include "mozIThirdPartyUtil.h"
+#include "nsChannelPolicy.h"
 
 #ifdef MOZ_MEDIA_NAVIGATOR
 #include "MediaManager.h"
 #endif
 #ifdef MOZ_B2G_BT
 #include "BluetoothManager.h"
 #endif
 #include "DOMCameraManager.h"
@@ -74,16 +84,19 @@
 #include "nsIDataStoreService.h"
 #include "nsJSUtils.h"
 
 #include "nsScriptNameSpaceManager.h"
 
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/Promise.h"
 
+#include "nsIUploadChannel2.h"
+#include "nsFormData.h"
+
 namespace mozilla {
 namespace dom {
 
 static bool sDoNotTrackEnabled = false;
 static bool sVibratorEnabled   = false;
 static uint32_t sMaxVibrateMS  = 0;
 static uint32_t sMaxVibrateListLen = 0;
 
@@ -983,16 +996,289 @@ Navigator::GetGeolocation(ErrorResult& a
     mGeolocation = nullptr;
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   return mGeolocation;
 }
 
+class BeaconStreamListener MOZ_FINAL : public nsIStreamListener
+{
+  public:
+    BeaconStreamListener() {}
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSISTREAMLISTENER
+    NS_DECL_NSIREQUESTOBSERVER
+};
+
+NS_IMPL_ISUPPORTS2(BeaconStreamListener,
+                   nsIStreamListener,
+                   nsIRequestObserver)
+
+
+NS_IMETHODIMP
+BeaconStreamListener::OnStartRequest(nsIRequest *aRequest,
+                                     nsISupports *aContext)
+{
+  aRequest->Cancel(NS_ERROR_NET_INTERRUPT);
+  return NS_BINDING_ABORTED;
+}
+
+NS_IMETHODIMP
+BeaconStreamListener::OnStopRequest(nsIRequest *aRequest,
+                                    nsISupports *aContext,
+                                    nsresult aStatus)
+{
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BeaconStreamListener::OnDataAvailable(nsIRequest *aRequest,
+                                      nsISupports *ctxt,
+                                      nsIInputStream *inStr,
+                                      uint64_t sourceOffset,
+                                      uint32_t count)
+{
+  MOZ_ASSERT(false);
+  return NS_OK;
+}
+
+bool
+Navigator::SendBeacon(const nsAString& aUrl,
+                      const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
+                      ErrorResult& aRv)
+{
+  if (!mWindow) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsCOMPtr<nsIDocument> doc = mWindow->GetDoc();
+  if (!doc) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsIURI* documentURI = doc->GetDocumentURI();
+  if (!documentURI) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = nsContentUtils::NewURIWithDocumentCharset(
+                  getter_AddRefs(uri),
+                  aUrl,
+                  doc,
+                  doc->GetDocBaseURI());
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_DOM_URL_MISMATCH_ERR);
+    return false;
+  }
+
+  // Check whether this is a sane URI to load
+  // Explicitly disallow things like chrome:, javascript:, and data: URIs
+  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
+  nsCOMPtr<nsIScriptSecurityManager> secMan = nsContentUtils::GetSecurityManager();
+  uint32_t flags = nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL
+                   & nsIScriptSecurityManager::DISALLOW_SCRIPT;
+  rv = secMan->CheckLoadURIWithPrincipal(principal,
+                                         uri,
+                                         flags);
+  if (NS_FAILED(rv)) {
+    // Bad URI
+    aRv.Throw(rv);
+    return false;
+  }
+
+  // Check whether the CSP allows us to load
+  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_BEACON,
+                                 uri,
+                                 principal,
+                                 doc,
+                                 EmptyCString(), //mime guess
+                                 nullptr,         //extra
+                                 &shouldLoad,
+                                 nsContentUtils::GetContentPolicy(),
+                                 nsContentUtils::GetSecurityManager());
+  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
+    // Disallowed by content policy
+    aRv.Throw(NS_ERROR_CONTENT_BLOCKED);
+    return false;
+  }
+
+  nsCOMPtr<nsIChannel> channel;
+  nsCOMPtr<nsIChannelPolicy> channelPolicy;
+  nsCOMPtr<nsIContentSecurityPolicy> csp;
+  rv = principal->GetCsp(getter_AddRefs(csp));
+  if (NS_FAILED(rv)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return false;
+  }
+
+  if (csp) {
+    channelPolicy = do_CreateInstance(NSCHANNELPOLICY_CONTRACTID);
+    channelPolicy->SetContentSecurityPolicy(csp);
+    channelPolicy->SetLoadType(nsIContentPolicy::TYPE_BEACON);
+  }
+  rv = NS_NewChannel(getter_AddRefs(channel),
+                     uri,
+                     nullptr,
+                     nullptr,
+                     nullptr,
+                     nsIRequest::LOAD_NORMAL,
+                     channelPolicy);
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return false;
+  }
+
+  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
+  if (!httpChannel) {
+    // Beacon spec only supports HTTP requests at this time
+    aRv.Throw(NS_ERROR_DOM_BAD_URI);
+    return false;
+  }
+  httpChannel->SetReferrer(documentURI);
+
+  // Anything that will need to refer to the window during the request
+  // will need to be done now.  For example, detection of whether any
+  // cookies set by this request are foreign.  Note that ThirdPartyUtil
+  // (nsIThirdPartyUtil.isThirdPartyChannel) does a secondary check between
+  // the channel URI and the cookie URI even when forceAllowThirdPartyCookie
+  // is set, so this is safe with regard to redirects.
+  nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(do_QueryInterface(channel));
+  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
+  if (!httpChannelInternal) {
+    aRv.Throw(NS_ERROR_DOM_BAD_URI);
+    return false;
+  }
+  bool isForeign = true;
+  thirdPartyUtil->IsThirdPartyWindow(mWindow, uri, &isForeign);
+  httpChannelInternal->SetForceAllowThirdPartyCookie(!isForeign);
+
+  nsCString mimeType;
+  if (!aData.IsNull()) {
+    nsCOMPtr<nsIInputStream> in;
+  
+    if (aData.Value().IsString()) {
+      nsCString stringData = NS_ConvertUTF16toUTF8(aData.Value().GetAsString());
+      nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+      rv = strStream->SetData(stringData.BeginReading(), stringData.Length());
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+      mimeType.AssignLiteral("text/plain;charset=UTF-8");
+      in = strStream;
+  
+    } else if (aData.Value().IsArrayBufferView()) {
+  
+      nsCOMPtr<nsIStringInputStream> strStream = do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID, &rv);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+  
+      rv = strStream->SetData(reinterpret_cast<char*>(aData.Value().GetAsArrayBufferView().Data()),
+                              aData.Value().GetAsArrayBufferView().Length());
+  
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+      mimeType.AssignLiteral("application/octet-stream");
+      in = strStream;
+  
+    } else if (aData.Value().IsBlob()) {
+      nsCOMPtr<nsIDOMBlob> blob = aData.Value().GetAsBlob();
+      rv = blob->GetInternalStream(getter_AddRefs(in));
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+      nsAutoString type;
+      rv = blob->GetType(type);
+      if (NS_FAILED(rv)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return false;
+      }
+      mimeType = NS_ConvertUTF16toUTF8(type);
+  
+    } else if (aData.Value().IsFormData()) {
+      nsFormData& form = aData.Value().GetAsFormData();
+      uint64_t len;
+      nsAutoCString charset;
+      form.GetSendInfo(getter_AddRefs(in),
+                       &len,
+                       mimeType,
+                       charset);
+    } else {
+      MOZ_ASSERT(false, "switch statements not in sync");
+      aRv.Throw(NS_ERROR_FAILURE);
+      return false;
+    }
+  
+    nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(channel);
+    if (!uploadChannel) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return false;
+    }
+    uploadChannel->ExplicitSetUploadStream(in, mimeType, -1,
+                                           NS_LITERAL_CSTRING("POST"),
+                                           false);
+  } else {
+    httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("POST"));
+  }
+
+  nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(channel);
+  if (p) {
+    p->SetPriority(nsISupportsPriority::PRIORITY_LOWEST);
+  }
+
+  nsRefPtr<nsCORSListenerProxy> cors = new nsCORSListenerProxy(new BeaconStreamListener(),
+                                                               principal,
+                                                               true);
+
+  // Start a preflight if cross-origin and content type is not whitelisted
+  rv = secMan->CheckSameOriginURI(documentURI, uri, false);
+  bool crossOrigin = NS_FAILED(rv);
+  nsAutoCString contentType, parsedCharset;
+  rv = NS_ParseContentType(mimeType, contentType, parsedCharset);
+  if (crossOrigin && 
+      contentType.Length() > 0 &&
+      !contentType.Equals(APPLICATION_WWW_FORM_URLENCODED) &&
+      !contentType.Equals(MULTIPART_FORM_DATA) &&
+      !contentType.Equals(TEXT_PLAIN)) {
+    nsCOMPtr<nsIChannel> preflightChannel;
+    nsTArray<nsCString> unsafeHeaders;
+    unsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
+    rv = NS_StartCORSPreflight(channel,
+                               cors,
+                               principal,
+                               true,
+                               unsafeHeaders,
+                               getter_AddRefs(preflightChannel));
+  } else {
+    rv = channel->AsyncOpen(cors, nullptr);
+  }
+  if (NS_FAILED(rv)) {
+    aRv.Throw(rv);
+    return false;
+  }
+  return true;
+}
+
 #ifdef MOZ_MEDIA_NAVIGATOR
 void
 Navigator::MozGetUserMedia(JSContext* aCx,
                            const MediaStreamConstraints& aConstraints,
                            NavigatorUserMediaSuccessCallback& aOnSuccess,
                            NavigatorUserMediaErrorCallback& aOnError,
                            ErrorResult& aRv)
 {
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -3,40 +3,43 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Navigator_h
 #define mozilla_dom_Navigator_h
 
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/dom/Nullable.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "nsString.h"
 #include "nsTArray.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMMozConnection;
 class nsIDOMMozMobileMessageManager;
 class nsIDOMNavigatorSystemMessages;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
+class nsIDOMBlob;
 
 namespace mozilla {
 namespace dom {
 class Geolocation;
 class systemMessageCallback;
 class MediaStreamConstraints;
 class MediaStreamConstraintsInternal;
 class WakeLock;
+class ArrayBufferViewOrBlobOrStringOrFormData;
 }
 }
 
 #ifdef MOZ_B2G_RIL
 class nsIDOMMozIccManager;
 #endif // MOZ_B2G_RIL
 
 //*****************************************************************************
@@ -216,16 +219,21 @@ public:
   bluetooth::BluetoothManager* GetMozBluetooth(ErrorResult& aRv);
 #endif // MOZ_B2G_BT
 #ifdef MOZ_TIME_MANAGER
   time::TimeManager* GetMozTime(ErrorResult& aRv);
 #endif // MOZ_TIME_MANAGER
 #ifdef MOZ_AUDIO_CHANNEL_MANAGER
   system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
 #endif // MOZ_AUDIO_CHANNEL_MANAGER
+
+  bool SendBeacon(const nsAString& aUrl,
+                  const Nullable<ArrayBufferViewOrBlobOrStringOrFormData>& aData,
+                  ErrorResult& aRv);
+
 #ifdef MOZ_MEDIA_NAVIGATOR
   void MozGetUserMedia(JSContext* aCx,
                        const MediaStreamConstraints& aConstraints,
                        NavigatorUserMediaSuccessCallback& aOnSuccess,
                        NavigatorUserMediaErrorCallback& aOnError,
                        ErrorResult& aRv);
   void MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
                               MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-frame.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Inner frame performing a basic sendBeacon from within an iframe</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<script type="text/javascript">
+
+function sendBeacon()
+{
+    var frame = window.parent.document.getElementById("frame");
+    var data = window.parent.beaconConvert(frame.getAttribute("data"));
+
+    var result = navigator.sendBeacon("http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs", data);
+    window.parent.beaconSent(result);
+}
+
+window.addEventListener("load", function() { setTimeout(sendBeacon, 0); }, false);
+
+</script>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-handler.sjs
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const CC = Components.Constructor;
+const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
+                             "nsIBinaryInputStream",
+                             "setInputStream");
+
+function DEBUG(str)
+{
+  // dump("********** " + str);
+}
+
+function handleRequest(request, response) {
+  DEBUG("Entered request handler");
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  if (request.method == "GET") {
+    response.setHeader("Content-Type", "application/json", false);
+    switch (request.queryString) {
+    case "getLastBeacon":
+      var data = getState("beaconData");
+      var mimetype = getState("beaconMimetype");
+      DEBUG("GET was sending : " + data + "\n");
+      DEBUG("GET was sending : " + mimetype + "\n");
+      var result = {
+        "data": data,
+        "mimetype": mimetype,
+      };
+      response.write(JSON.stringify(result));
+      break;
+    default:
+      response.setStatusLine(request.httpVersion, 400, "Bad Request");
+      break;
+    }
+    return;
+  }
+
+  if (request.method == "POST") {
+    var body = new BinaryInputStream(request.bodyInputStream);
+    var avail;
+    var bytes = [];
+
+    while ((avail = body.available()) > 0) {
+      Array.prototype.push.apply(bytes, body.readByteArray(avail));
+    }
+
+    var data = "";
+    for (var i=0; i < bytes.length; i++) {
+      // We are only passing strings at this point.
+      if (bytes[i] < 32) continue;
+      var charcode = String.fromCharCode(bytes[i]);
+      data += charcode;
+    }
+
+    var mimetype = request.getHeader("Content-Type");
+
+    // check to see if this is form data.
+    if (mimetype.indexOf("multipart/form-data") != -1) {
+
+      // trim the mime type to make testing easier.
+      mimetype = "multipart/form-data";
+      // Extract only the form-data name.
+
+      var pattern = /; name=\"(.+)\";/;
+      data = data.split(pattern)[1];
+    }
+
+    DEBUG("**********   POST was sending : " + data + "\n");
+    DEBUG("**********   POST was sending : " + mimetype + "\n");
+    setState("beaconData", data);
+    setState("beaconMimetype", mimetype);
+
+    response.setHeader("Content-Type", "text/plain", false);
+    response.write('ok');
+    return;
+  }
+
+  response.setStatusLine(request.httpVersion, 402, "Bad Request");
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/beacon-set-cookie.sjs
@@ -0,0 +1,8 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Set-Cookie", "cookie="+ request.host + "~" + Math.random());
+  response.setHeader("Content-Type", "text/plain", false);
+  response.setHeader("Cache-Control", "no-cache", false);
+
+  response.setStatusLine(request.httpVersion, 200, "OK");
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/mochitest.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files = beacon-frame.html
+                beacon-handler.sjs
+                beacon-set-cookie.sjs
+
+[test_beacon.html]
+[test_beaconFrame.html]
+[test_beaconPreflight.html]
+[test_beaconCookies.html]
+[test_beaconContentPolicy.html]
+[test_beaconElectrolysis.html]
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ['mochitest.ini']
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beacon.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+  <title>Test whether sendBeacon fails for non-HTTP URIs and syntactically incorrect calls</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function beginTest() {
+  var threw;
+  try {
+    is(false, navigator.sendBeacon("ftp://example.com", "0"));
+    threw = false;
+  } catch (ex) {
+    threw = true;
+  }
+  ok(threw, "sendBeacon not supported for non ftp calls.");
+
+  try {
+    is(false, navigator.sendBeacon());
+    threw = false;
+  } catch (e) {
+    threw = true;
+  }
+  ok(threw, "sendBeacon needs more parameters.");
+
+  SimpleTest.finish()
+}
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconContentPolicy.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+  <title>Test that sendBeacon obeys content policy directives</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs";
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+
+var policy = setupPolicy();
+
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function setupPolicy() {
+  var policyID = SpecialPowers.wrap(SpecialPowers.Components).ID("{b80e19d0-878f-d41b-2654-194714a4115c}");
+  var policyName = "@mozilla.org/testpolicy;1";
+  var policy = {
+    // nsISupports implementation
+    QueryInterface: function(iid) {
+      iid = SpecialPowers.wrap(iid);
+      if (iid.equals(Ci.nsISupports) ||
+        iid.equals(Ci.nsIFactory) ||
+        iid.equals(Ci.nsIContentPolicy))
+        return this;
+      throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
+    },
+    
+    // nsIFactory implementation
+    createInstance: function(outer, iid) {
+      return this.QueryInterface(iid);
+    },
+
+    // nsIContentPolicy implementation
+    shouldLoad: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+      // Remember last content type seen for the test url
+
+      if (SpecialPowers.wrap(contentLocation).spec == beaconUrl) {
+        is(contentType,  Ci.nsIContentPolicy.TYPE_BEACON, "Beacon content type should match expected.  is: " + contentType + " should be: " + Ci.nsIContentPolicy.TYPE_BEACON);
+        teardownPolicy();
+        SimpleTest.finish();
+      }
+
+      return Ci.nsIContentPolicy.ACCEPT;
+    },
+
+    shouldProcess: function(contentType, contentLocation, requestOrigin, context, mimeTypeGuess, extra) {
+      return Ci.nsIContentPolicy.ACCEPT;
+    }
+  }
+  policy = SpecialPowers.wrapCallbackObject(policy);
+
+  // Register content policy
+  var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager.QueryInterface(Ci.nsIComponentRegistrar);
+  componentManager.registerFactory(policyID, "Test content policy", policyName, policy);
+
+  var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+  categoryManager.addCategoryEntry("content-policy", policyName, policyName, false, true);
+
+  return { 'policy': policy, 'policyID': policyID, 'policyName': policyName };
+}
+
+function teardownPolicy() {
+  setTimeout(function() {
+    // policy will not be removed from the category correctly
+    var componentManager = SpecialPowers.wrap(SpecialPowers.Components).manager.QueryInterface(Ci.nsIComponentRegistrar);
+    componentManager.unregisterFactory(policy.policyID, policy.policy);
+    var categoryManager = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+    categoryManager.deleteCategoryEntry("content-policy", policy.policyName, false);
+  }, 0);
+}
+
+function beginTest() {
+  navigator.sendBeacon(beaconUrl, "bacon would have been a better name than beacon");
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconCookies.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+  <title>Test whether sendBeacon sets cookies</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-set-cookie.sjs";
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+const Cc = SpecialPowers.Cc;
+const Ci = SpecialPowers.Ci;
+
+function examiner() {
+  SpecialPowers.addObserver(this, "cookie-changed", false);
+}
+examiner.prototype = {
+  finished: false,
+  observe: function examiner_observe(subject, topic, data) {
+    if (!this.finished) {
+      ok(true, "cookie set by beacon request");
+      SimpleTest.finish();
+    }
+  }
+}
+
+window.examiner = new examiner();
+
+function fail() {
+  examiner.finished = true;
+  ok(false, "cookie event never arrived");
+  SimpleTest.finish();
+}
+
+function beginTest() {
+  navigator.sendBeacon(beaconUrl, "ceci n'est pas une demande");
+  setTimeout(2000, fail);
+}
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconFrame.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+  <title>Test for beacon</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+
+<div id="content">
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, runNextTest);
+
+function getBeaconServerStatus(callback) {
+    var request = new XMLHttpRequest();
+    request.open("GET", "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs?getLastBeacon", true);
+    request.onload = function() {
+        if (request.readyState === request.DONE) {
+            callback(request.responseText);
+        }
+    };
+    request.send(null);
+}
+
+function createIframeWithData(data, mimetype, convert) {
+    beaconConvert = convert;
+
+    var frame = document.createElement("IFRAME");
+    frame.setAttribute("src", "beacon-frame.html");
+    frame.id = "frame";
+    frame.setAttribute("data", data.toString());
+    frame.setAttribute("mimetype", mimetype);
+    var c = document.getElementById("content");
+    c.appendChild(frame);
+}
+
+function beaconSent(result) {
+    // This function gets called from beacon-frame.html in the inner frame
+    // Check that the beacon was actually sent
+    ok(result, "Beacon was not sent")
+    
+    // remove the frame.
+    var frame = document.getElementById("frame");
+    var data = frame.getAttribute("data");
+    var mimetype = frame.getAttribute("mimetype");
+
+    var c = document.getElementById("content");
+    c.removeChild(frame);
+
+    getBeaconServerStatus( function(response) {
+      console.log(response);
+        var result = JSON.parse(response);
+        
+        is(result.data, data, "Beacon status should match expected.  is: " + result.data + " should be: " + data);
+        is(result.mimetype, mimetype, "Beacon mimetype should match expected.  is: " + result.mimetype + " should be: " + mimetype);
+
+        runNextTest();
+    });
+}
+
+function runNextTest() {
+    var test = tests.shift();
+    setTimeout(test, 0);
+}
+
+var beaconConvert = function() {};
+
+function stringToArrayBuffer(input) {
+
+    var buffer = new ArrayBuffer(input.length * 2);
+    var array = new Uint16Array(buffer);
+
+    // dumbly copy over the bytes
+    for (var i = 0, len = input.length; i < len; i++) {
+        array[i] = input.charCodeAt(i);
+    }
+    return array;
+}
+
+function stringToBlob(input) {
+    var blob = new Blob([input], {type : 'text/html'});
+    return blob;
+}
+
+function stringToFormData(input) {
+    var formdata = new FormData();
+    formdata.append(input, new Blob(['hi']));
+    return formdata;
+}
+
+function identity(data) {
+    return data;
+}
+
+var tests = [
+    function() { createIframeWithData("hi!", "text/plain;charset=UTF-8", identity); },
+    function() { createIframeWithData("123", "application/octet-stream", stringToArrayBuffer); },
+    function() { createIframeWithData("abc", "text/html", stringToBlob); },
+    function() { createIframeWithData("qwerty", "multipart/form-data", stringToFormData); },
+    function() { SimpleTest.finish(); },
+];
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/beacon/test_beaconPreflight.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=936340
+-->
+<head>
+  <title>Test for Bug 936340</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=936340">Mozilla Bug 936340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var beaconUrl = "http://mochi.test:8888/tests/dom/tests/mochitest/beacon/beacon-handler.sjs";
+
+// not enabled by default yet.
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({'set': [["beacon.enabled", true]]}, beginTest);
+
+function beginTest() {
+  var abv = new Uint8Array([0,1,2,3]);
+  var sent = navigator.sendBeacon(beaconUrl, abv);
+  ok(sent, "non-standard content type allowed after pre-flight");
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/moz.build
+++ b/dom/tests/mochitest/moz.build
@@ -2,16 +2,17 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += [
     'dom-level0',
     'ajax',
+    'beacon',
     'bugs',
     'crypto',
     'general',
     'geolocation',
     'localstorage',
     'orientation',
     'sessionstorage',
     'storageevent',
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -6,16 +6,17 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
  * http://www.w3.org/TR/tracking-dnt/
  * http://www.w3.org/TR/geolocation-API/#geolocation_interface
  * http://www.w3.org/TR/battery-status/#navigatorbattery-interface
  * http://www.w3.org/TR/vibration/#vibration-interface
  * http://www.w3.org/2012/sysapps/runtime/#extension-to-the-navigator-interface-1
  * https://dvcs.w3.org/hg/gamepad/raw-file/default/gamepad.html#navigator-interface-extension
+ * http://www.w3.org/TR/beacon/#sec-beacon-method
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-navigator-object
 [HeaderFile="Navigator.h", NeedNewResolve]
@@ -342,8 +343,14 @@ partial interface Navigator {
                               MozGetUserMediaDevicesSuccessCallback onsuccess,
                               NavigatorUserMediaErrorCallback onerror,
                               // The originating innerWindowID is needed to
                               // avoid calling the callbacks if the window has
                               // navigated away. It is optional only as legacy.
                               optional unsigned long long innerWindowID = 0);
 };
 #endif // MOZ_MEDIA_NAVIGATOR
+
+partial interface Navigator {
+  [Throws, Pref="beacon.enabled"]
+  boolean sendBeacon(DOMString url,
+                     optional (ArrayBufferView or Blob or DOMString or FormData)? data = null);
+};
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -106,16 +106,17 @@
 
 #define MESSAGE_EXTERNAL_BODY               "message/external-body"
 #define MESSAGE_NEWS                        "message/news"
 #define MESSAGE_RFC822                      "message/rfc822"
 
 #define MULTIPART_ALTERNATIVE               "multipart/alternative"
 #define MULTIPART_APPLEDOUBLE               "multipart/appledouble"
 #define MULTIPART_DIGEST                    "multipart/digest"
+#define MULTIPART_FORM_DATA                 "multipart/form-data"
 #define MULTIPART_HEADER_SET                "multipart/header-set"
 #define MULTIPART_MIXED                     "multipart/mixed"
 #define MULTIPART_PARALLEL                  "multipart/parallel"
 #define MULTIPART_SIGNED                    "multipart/signed"
 #define MULTIPART_RELATED                   "multipart/related"
 #define MULTIPART_MIXED_REPLACE             "multipart/x-mixed-replace"
 #define MULTIPART_BYTERANGES                "multipart/byteranges"
 
--- a/testing/mochitest/b2g-debug.json
+++ b/testing/mochitest/b2g-debug.json
@@ -10,16 +10,21 @@
   },
 "excludetests": {
     "content/xul":"tests that use xul",
     "layout/xul" : "",
     "dom/apps":"bug 972927, nearly perma-fail",
     "dom/datastore":"bug 974270, frequent failures",
     "dom/datastore/tests/test_changes.html":"intermittent failures, bug 961021",
     "dom/tests/mochitest/general/test_focusrings.xul":"",
+    "dom/tests/mochitest/beacon/test_beacon.html":"",
+    "dom/tests/mochitest/beacon/test_beaconContentPolicy.html":"",
+    "dom/tests/mochitest/beacon/test_beaconCookies.html":"",
+    "dom/tests/mochitest/beacon/test_beaconFrame.html":"",
+    "dom/tests/mochitest/beacon/test_beaconPreflight.html":"",
     "layout/base/tests/test_bug465448.xul":"",
 
     "layout/forms/test/test_bug478219.xhtml":"window.closed not working, bug 907795",
     "content/media/test":"bug 918299",
     "content/media/test/test_bug448534.html": "Timed out, bug 894922? Bug 902677 is for the timing out of a lot of media tests",
     "content/media/mediasource/test/test_MediaSource.html": " ReferenceError: MediaSource is not defined",
     "content/media/test/test_autoplay_contentEditable.html": "bug 899074 - timeouts",
     "content/media/test/test_bug465498.html":"",
--- a/testing/mochitest/b2g-desktop.json
+++ b/testing/mochitest/b2g-desktop.json
@@ -9,16 +9,21 @@
     "toolkit/devtools/apps": ""
   },
 "excludetests": {
     "b2g/chrome/content/test/mochitest": "require OOP support for mochitest-b2g-desktop, Bug 957554",
     "content/xul":"tests that use xul",
     "layout/xul" : "",
     "dom/plugins": "tests that use plugins",
     "dom/tests/mochitest/general/test_focusrings.xul":"",
+    "dom/tests/mochitest/beacon/test_beacon.html":"",
+    "dom/tests/mochitest/beacon/test_beaconContentPolicy.html":"",
+    "dom/tests/mochitest/beacon/test_beaconCookies.html":"",
+    "dom/tests/mochitest/beacon/test_beaconFrame.html":"",
+    "dom/tests/mochitest/beacon/test_beaconPreflight.html":"",
     "layout/base/tests/test_bug465448.xul":"",
     "layout/forms/test/test_bug287446.html": "bug 947789",
 
     "layout/forms/test/test_bug478219.xhtml":"window.closed not working, bug 907795",
     "content/media/test":"bug 918299",
     "content/media/test/test_bug448534.html": "Timed out, bug 894922? Bug 902677 is for the timing out of a lot of media tests",
     "content/media/mediasource/test/test_MediaSource.html": " ReferenceError: MediaSource is not defined",
     "content/media/test/test_autoplay_contentEditable.html": "bug 899074 - timeouts",
--- a/testing/mochitest/b2g.json
+++ b/testing/mochitest/b2g.json
@@ -9,16 +9,21 @@
     "toolkit/devtools/apps": ""
   },
 "excludetests": {
     "content/xul":"tests that use xul",
     "layout/xul" : "",
     "dom/apps":"bug 972927, nearly perma-fail",
     "dom/datastore":"bug 974270, frequent failures",
     "dom/tests/mochitest/general/test_focusrings.xul":"",
+    "dom/tests/mochitest/beacon/test_beacon.html":"",
+    "dom/tests/mochitest/beacon/test_beaconContentPolicy.html":"",
+    "dom/tests/mochitest/beacon/test_beaconCookies.html":"",
+    "dom/tests/mochitest/beacon/test_beaconFrame.html":"",
+    "dom/tests/mochitest/beacon/test_beaconPreflight.html":"",
     "layout/base/tests/test_bug465448.xul":"",
 
     "layout/forms/test/test_bug478219.xhtml":"window.closed not working, bug 907795",
     "content/media/test/test_bug448534.html": "Timed out, bug 894922? Bug 902677 is for the timing out of a lot of media tests",
     "content/media/mediasource/test/test_MediaSource.html": " ReferenceError: MediaSource is not defined",
     "content/media/test/test_autoplay_contentEditable.html": "bug 899074 - timeouts",
     "content/media/test/test_bug465498.html":"",
     "content/media/test/test_bug493187.html":"",