Bug 1177621 - SharedWorkers should not be shared between a private and a non-private documents, r=nsm
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 26 Jun 2015 11:18:18 -0700
changeset 250338 fafc26e98274676337d1def350a4a98760642c3b
parent 250337 ca58200f87f40d3931368b84d79808b822e658c5
child 250339 f68aef04ed94dda40bdf77731470791d64f81fc5
push id61518
push useramarchesini@mozilla.com
push dateFri, 26 Jun 2015 18:19:15 +0000
treeherdermozilla-inbound@fafc26e98274 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnsm
bugs1177621
milestone41.0a1
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 1177621 - SharedWorkers should not be shared between a private and a non-private documents, r=nsm
docshell/base/nsDocShell.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/webidl/Navigator.webidl
dom/webidl/ServiceWorkerContainer.webidl
dom/workers/RuntimeService.cpp
dom/workers/ServiceWorkerContainer.cpp
dom/workers/ServiceWorkerContainer.h
dom/workers/ServiceWorkerManager.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/Workers.h
dom/workers/test/chrome.ini
dom/workers/test/serviceworkers/chrome.ini
dom/workers/test/serviceworkers/test_privateBrowsing.html
dom/workers/test/sharedWorker_privateBrowsing.js
dom/workers/test/test_sharedWorker_privateBrowsing.html
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -14022,16 +14022,21 @@ nsDocShell::ShouldPrepareForIntercept(ns
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
   // Preffed off.
   if (!sInterceptionEnabled) {
     return NS_OK;
   }
 
+  // No in private browsing
+  if (mInPrivateBrowsing) {
+    return NS_OK;
+  }
+
   if (mSandboxFlags) {
     // If we're sandboxed, don't intercept.
     return NS_OK;
   }
 
   nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
   if (!swm) {
     return NS_OK;
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -2984,28 +2984,39 @@ nsContentUtils::CanLoadImage(nsIURI* aUR
 
 // static
 bool
 nsContentUtils::IsInPrivateBrowsing(nsIDocument* aDoc)
 {
   if (!aDoc) {
     return false;
   }
-  bool isPrivate = false;
+
   nsCOMPtr<nsILoadGroup> loadGroup = aDoc->GetDocumentLoadGroup();
-  nsCOMPtr<nsIInterfaceRequestor> callbacks;
   if (loadGroup) {
-    loadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
-    if (callbacks) {
-      nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
-      isPrivate = loadContext && loadContext->UsePrivateBrowsing();
-    }
-  } else {
-    nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
-    isPrivate = channel && NS_UsePrivateBrowsing(channel);
+    return IsInPrivateBrowsing(loadGroup);
+  }
+
+  nsCOMPtr<nsIChannel> channel = aDoc->GetChannel();
+  return channel && NS_UsePrivateBrowsing(channel);
+}
+
+// static
+bool
+nsContentUtils::IsInPrivateBrowsing(nsILoadGroup* aLoadGroup)
+{
+  if (!aLoadGroup) {
+    return false;
+  }
+  bool isPrivate = false;
+  nsCOMPtr<nsIInterfaceRequestor> callbacks;
+  aLoadGroup->GetNotificationCallbacks(getter_AddRefs(callbacks));
+  if (callbacks) {
+    nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+    isPrivate = loadContext && loadContext->UsePrivateBrowsing();
   }
   return isPrivate;
 }
 
 bool
 nsContentUtils::DocumentInactiveForImageLoads(nsIDocument* aDocument)
 {
   if (aDocument && !IsChromeDoc(aDocument) && !aDocument->IsResourceDoc()) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -64,16 +64,17 @@ class nsIDOMWindow;
 class nsIDragSession;
 class nsIEditor;
 class nsIFragmentContentSink;
 class nsIFrame;
 class nsIImageLoadingContent;
 class nsIInterfaceRequestor;
 class nsIIOService;
 class nsILineBreaker;
+class nsILoadGroup;
 class nsIMessageBroadcaster;
 class nsNameSpaceManager;
 class nsIObserver;
 class nsIParser;
 class nsIParserService;
 class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
@@ -726,16 +727,21 @@ public:
                                uint32_t *aArgCount, const char*** aArgNames);
 
   /**
    * Returns true if this document is in a Private Browsing window.
    */
   static bool IsInPrivateBrowsing(nsIDocument* aDoc);
 
   /**
+   * Returns true if this loadGroup uses Private Browsing.
+   */
+  static bool IsInPrivateBrowsing(nsILoadGroup* aLoadGroup);
+
+  /**
    * If aNode is not an element, return true exactly when aContent's binding
    * parent is null.
    *
    * If aNode is an element, return true exactly when aContent's binding parent
    * is the same as aNode's.
    *
    * This method is particularly useful for callers who are trying to ensure
    * that they are working with a non-anonymous descendant of a given node.  If
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -400,17 +400,17 @@ partial interface Navigator {
                               // 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
 
 // Service Workers/Navigation Controllers
 partial interface Navigator {
-  [Pref="dom.serviceWorkers.enabled"]
+  [Func="ServiceWorkerContainer::IsEnabled"]
   readonly attribute ServiceWorkerContainer serviceWorker;
 };
 
 partial interface Navigator {
   [Throws, Pref="beacon.enabled"]
   boolean sendBeacon(DOMString url,
                      optional (ArrayBufferView or Blob or DOMString or FormData)? data = null);
 };
--- a/dom/webidl/ServiceWorkerContainer.webidl
+++ b/dom/webidl/ServiceWorkerContainer.webidl
@@ -3,17 +3,17 @@
  * 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/.
  *
  * The origin of this IDL file is
  * http://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html
  *
  */
 
-[Pref="dom.serviceWorkers.enabled",
+[Func="ServiceWorkerContainer::IsEnabled",
  Exposed=Window]
 interface ServiceWorkerContainer : EventTarget {
   // FIXME(nsm):
   // https://github.com/slightlyoff/ServiceWorker/issues/198
   // and discussion at https://etherpad.mozilla.org/serviceworker07apr
   [Unforgeable] readonly attribute ServiceWorker? controller;
 
   [Throws]
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -271,30 +271,32 @@ GetWorkerPref(const nsACString& aPref,
 }
 
 // This function creates a key for a SharedWorker composed by "shared|name|scriptSpec"
 // and a key for a ServiceWorker composed by "service|scope|cache|scriptSpec".
 // If the name contains a '|', this will be replaced by '||'.
 void
 GenerateSharedWorkerKey(const nsACString& aScriptSpec, const nsACString& aName,
                         const nsACString& aCacheName, WorkerType aWorkerType,
-                        nsCString& aKey)
+                        bool aPrivateBrowsing, nsCString& aKey)
 {
   aKey.Truncate();
   NS_NAMED_LITERAL_CSTRING(sharedPrefix, "shared|");
   NS_NAMED_LITERAL_CSTRING(servicePrefix, "service|");
   MOZ_ASSERT(servicePrefix.Length() > sharedPrefix.Length());
   MOZ_ASSERT(aWorkerType == WorkerTypeShared ||
              aWorkerType == WorkerTypeService);
   MOZ_ASSERT_IF(aWorkerType == WorkerTypeShared, aCacheName.IsEmpty());
   MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aCacheName.IsEmpty());
+  MOZ_ASSERT_IF(aWorkerType == WorkerTypeService, !aPrivateBrowsing);
   aKey.SetCapacity(servicePrefix.Length() + aScriptSpec.Length() +
-                   aName.Length() + aCacheName.Length() + 1);
+                   aName.Length() + aCacheName.Length() + 3);
 
   aKey.Append(aWorkerType == WorkerTypeService ? servicePrefix : sharedPrefix);
+  aKey.Append(aPrivateBrowsing ? "1|" : "0|");
 
   nsACString::const_iterator start, end;
   aName.BeginReading(start);
   aName.EndReading(end);
   for (; start != end; ++start) {
     if (*start == '|') {
       aKey.AppendASCII("||");
     } else {
@@ -1512,17 +1514,18 @@ RuntimeService::RegisterWorker(JSContext
       const nsCString& sharedWorkerName = aWorkerPrivate->SharedWorkerName();
       const nsCString& cacheName =
         aWorkerPrivate->IsServiceWorker() ?
           NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
           EmptyCString();
 
       nsAutoCString key;
       GenerateSharedWorkerKey(sharedWorkerScriptSpec, sharedWorkerName,
-                              cacheName, aWorkerPrivate->Type(), key);
+                              cacheName, aWorkerPrivate->Type(),
+                              aWorkerPrivate->IsInPrivateBrowsing(), key);
       MOZ_ASSERT(!domainInfo->mSharedWorkerInfos.Get(key));
 
       SharedWorkerInfo* sharedWorkerInfo =
         new SharedWorkerInfo(aWorkerPrivate, sharedWorkerScriptSpec,
                              sharedWorkerName);
       domainInfo->mSharedWorkerInfos.Put(key, sharedWorkerInfo);
     }
   }
@@ -1626,17 +1629,18 @@ RuntimeService::UnregisterWorker(JSConte
       if (match.mSharedWorkerInfo) {
         nsAutoCString key;
         const nsCString& cacheName =
           aWorkerPrivate->IsServiceWorker() ?
             NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
             EmptyCString();
         GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
                                 match.mSharedWorkerInfo->mName,
-                                cacheName, aWorkerPrivate->Type(), key);
+                                cacheName, aWorkerPrivate->Type(),
+                                aWorkerPrivate->IsInPrivateBrowsing(), key);
         domainInfo->mSharedWorkerInfos.Remove(key);
       }
     }
 
     // See if there's a queued worker we can schedule.
     if (domainInfo->ActiveWorkerCount() < gMaxWorkersPerDomain &&
         !domainInfo->mQueuedWorkers.IsEmpty()) {
       queuedWorker = domainInfo->mQueuedWorkers[0];
@@ -2382,17 +2386,17 @@ RuntimeService::CreateSharedWorkerFromLo
 
     nsCString scriptSpec;
     nsresult rv = aLoadInfo->mResolvedScriptURI->GetSpec(scriptSpec);
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsAutoCString key;
     GenerateSharedWorkerKey(scriptSpec, aName,
                             NS_ConvertUTF16toUTF8(aLoadInfo->mServiceWorkerCacheName),
-                            aType, key);
+                            aType, aLoadInfo->mPrivateBrowsing, key);
 
     if (mDomainMap.Get(aLoadInfo->mDomain, &domainInfo) &&
         domainInfo->mSharedWorkerInfos.Get(key, &sharedWorkerInfo)) {
       workerPrivate = sharedWorkerInfo->mWorkerPrivate;
     }
   }
 
   // Keep a reference to the window before spawning the worker. If the worker is
@@ -2462,17 +2466,18 @@ RuntimeService::ForgetSharedWorker(Worke
     if (match.mSharedWorkerInfo) {
       nsAutoCString key;
       const nsCString& cacheName =
         aWorkerPrivate->IsServiceWorker() ?
           NS_ConvertUTF16toUTF8(aWorkerPrivate->ServiceWorkerCacheName()) :
           EmptyCString();
       GenerateSharedWorkerKey(match.mSharedWorkerInfo->mScriptSpec,
                               match.mSharedWorkerInfo->mName,
-                              cacheName, aWorkerPrivate->Type(), key);
+                              cacheName, aWorkerPrivate->Type(),
+                              aWorkerPrivate->IsInPrivateBrowsing(), key);
       domainInfo->mSharedWorkerInfos.Remove(key);
     }
   }
 }
 
 void
 RuntimeService::NoteIdleThread(WorkerThread* aThread)
 {
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -10,16 +10,17 @@
 #include "nsIServiceWorkerManager.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "mozilla/Services.h"
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsServiceManagerUtils.h"
 
+#include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
 #include "mozilla/dom/workers/bindings/ServiceWorker.h"
 
 #include "ServiceWorker.h"
 
 namespace mozilla {
 namespace dom {
@@ -28,16 +29,35 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
                                    mControllerWorker, mReadyPromise)
 
+/* static */ bool
+ServiceWorkerContainer::IsEnabled(JSContext* aCx, JSObject* aGlobal)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  JS::Rooted<JSObject*> global(aCx, aGlobal);
+  nsCOMPtr<nsPIDOMWindow> window = Navigator::GetWindowFromGlobal(global);
+  if (!window) {
+    return false;
+  }
+
+  nsIDocument* doc = window->GetExtantDoc();
+  if (!doc || nsContentUtils::IsInPrivateBrowsing(doc)) {
+    return false;
+  }
+
+  return Preferences::GetBool("dom.serviceWorkers.enabled", false);
+}
+
 ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindow* aWindow)
   : DOMEventTargetHelper(aWindow)
 {
 }
 
 ServiceWorkerContainer::~ServiceWorkerContainer()
 {
   RemoveReadyPromise();
--- a/dom/workers/ServiceWorkerContainer.h
+++ b/dom/workers/ServiceWorkerContainer.h
@@ -28,16 +28,18 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(controllerchange)
   IMPL_EVENT_HANDLER(reloadpage)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(message)
 
+  static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
+
   explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   already_AddRefed<Promise>
   Register(const nsAString& aScriptURL,
            const RegistrationOptions& aOptions,
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3607,29 +3607,30 @@ ServiceWorkerManager::DispatchFetchEvent
   if (NS_WARN_IF(!event->Dispatch(api.cx()))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 }
 
 bool
 ServiceWorkerManager::IsAvailable(const OriginAttributes& aOriginAttributes,
-                                        nsIURI* aURI)
+                                  nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
 
   nsRefPtr<ServiceWorkerRegistrationInfo> registration =
     GetServiceWorkerRegistrationInfo(aOriginAttributes, aURI);
   return registration && registration->mActiveWorker;
 }
 
 bool
 ServiceWorkerManager::IsControlled(nsIDocument* aDoc, ErrorResult& aRv)
 {
   MOZ_ASSERT(aDoc);
+  MOZ_ASSERT(!nsContentUtils::IsInPrivateBrowsing(aDoc));
 
   nsRefPtr<ServiceWorkerRegistrationInfo> registration;
   nsresult rv = GetDocumentRegistration(aDoc, getter_AddRefs(registration));
   if (NS_WARN_IF(NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE)) {
     // It's OK to ignore the case where we don't have a registration.
     aRv.Throw(rv);
     return false;
   }
@@ -3742,16 +3743,17 @@ ServiceWorkerManager::CreateServiceWorke
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   info.mPrincipal = aPrincipal;
 
   info.mIndexedDBAllowed =
     indexedDB::IDBFactory::AllowedForPrincipal(aPrincipal);
+   info.mPrivateBrowsing = false;
 
   nsCOMPtr<nsIContentSecurityPolicy> csp;
   rv = aPrincipal->GetCsp(getter_AddRefs(csp));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   info.mCSP = csp;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2331,16 +2331,17 @@ WorkerLoadInfo::WorkerLoadInfo()
   , mFromWindow(false)
   , mEvalAllowed(false)
   , mReportCSPViolations(false)
   , mXHRParamsAllowed(false)
   , mPrincipalIsSystem(false)
   , mIsInPrivilegedApp(false)
   , mIsInCertifiedApp(false)
   , mIndexedDBAllowed(false)
+  , mPrivateBrowsing(true)
 {
   MOZ_COUNT_CTOR(WorkerLoadInfo);
 }
 
 WorkerLoadInfo::~WorkerLoadInfo()
 {
   MOZ_COUNT_DTOR(WorkerLoadInfo);
 }
@@ -2385,16 +2386,17 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo
   mFromWindow = aOther.mFromWindow;
   mEvalAllowed = aOther.mEvalAllowed;
   mReportCSPViolations = aOther.mReportCSPViolations;
   mXHRParamsAllowed = aOther.mXHRParamsAllowed;
   mPrincipalIsSystem = aOther.mPrincipalIsSystem;
   mIsInPrivilegedApp = aOther.mIsInPrivilegedApp;
   mIsInCertifiedApp = aOther.mIsInCertifiedApp;
   mIndexedDBAllowed = aOther.mIndexedDBAllowed;
+  mPrivateBrowsing = aOther.mPrivateBrowsing;
 }
 
 template <class Derived>
 class WorkerPrivateParent<Derived>::EventTarget final
   : public nsIEventTarget
 {
   // This mutex protects mWorkerPrivate and must be acquired *before* the
   // WorkerPrivate's mutex whenever they must both be held.
@@ -4158,16 +4160,17 @@ WorkerPrivateParent<Derived>::SetPrincip
   mLoadInfo.mIsInPrivilegedApp =
     (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
      appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
   mLoadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
 
   mLoadInfo.mLoadGroup = aLoadGroup;
 
   mLoadInfo.mPrincipalInfo = new PrincipalInfo();
+  mLoadInfo.mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(aLoadGroup);
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
     PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
 }
 
 template <class Derived>
 JSContext*
 WorkerPrivateParent<Derived>::ParentJSContext() const
@@ -4959,16 +4962,17 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
       }
       return NS_ERROR_FAILURE;
     }
 
     loadInfo.mDomain = aParent->Domain();
     loadInfo.mFromWindow = aParent->IsFromWindow();
     loadInfo.mWindowID = aParent->WindowID();
     loadInfo.mIndexedDBAllowed = aParent->IsIndexedDBAllowed();
+    loadInfo.mPrivateBrowsing = aParent->IsInPrivateBrowsing();
   } else {
     AssertIsOnMainThread();
 
     nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
     MOZ_ASSERT(ssm);
 
     bool isChrome = nsContentUtils::IsCallerChrome();
 
@@ -5078,16 +5082,17 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
       uint16_t appStatus = loadInfo.mPrincipal->GetAppStatus();
       loadInfo.mIsInPrivilegedApp =
         (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED ||
          appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED);
       loadInfo.mIsInCertifiedApp = (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
       loadInfo.mFromWindow = true;
       loadInfo.mWindowID = globalWindow->WindowID();
       loadInfo.mIndexedDBAllowed = IDBFactory::AllowedForWindow(globalWindow);
+      loadInfo.mPrivateBrowsing = nsContentUtils::IsInPrivateBrowsing(document);
     } else {
       // Not a window
       MOZ_ASSERT(isChrome);
 
       // We're being created outside of a window. Need to figure out the script
       // that is creating us in order for us to use relative URIs later on.
       JS::AutoFilename fileName;
       if (JS::DescribeScriptedCaller(aCx, &fileName)) {
@@ -5119,16 +5124,17 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
         if (NS_FAILED(rv)) {
           return rv;
         }
       }
       loadInfo.mXHRParamsAllowed = true;
       loadInfo.mFromWindow = false;
       loadInfo.mWindowID = UINT64_MAX;
       loadInfo.mIndexedDBAllowed = true;
+      loadInfo.mPrivateBrowsing = false;
     }
 
     MOZ_ASSERT(loadInfo.mPrincipal);
     MOZ_ASSERT(isChrome || !loadInfo.mDomain.IsEmpty());
 
     if (!nsContentUtils::GetContentSecurityPolicy(getter_AddRefs(loadInfo.mCSP))) {
       NS_WARNING("Failed to get CSP!");
       return NS_ERROR_FAILURE;
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -748,16 +748,22 @@ public:
   }
 
   bool
   IsIndexedDBAllowed() const
   {
     return mLoadInfo.mIndexedDBAllowed;
   }
 
+  bool
+  IsInPrivateBrowsing() const
+  {
+    return mLoadInfo.mPrivateBrowsing;
+  }
+
   void
   GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers);
 
   void
   CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow);
 
   void
   UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup);
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -254,16 +254,17 @@ struct WorkerLoadInfo
   bool mFromWindow;
   bool mEvalAllowed;
   bool mReportCSPViolations;
   bool mXHRParamsAllowed;
   bool mPrincipalIsSystem;
   bool mIsInPrivilegedApp;
   bool mIsInCertifiedApp;
   bool mIndexedDBAllowed;
+  bool mPrivateBrowsing;
 
   WorkerLoadInfo();
   ~WorkerLoadInfo();
 
   void StealFrom(WorkerLoadInfo& aOther);
 };
 
 // All of these are implemented in RuntimeService.cpp
--- a/dom/workers/test/chrome.ini
+++ b/dom/workers/test/chrome.ini
@@ -43,16 +43,18 @@ support-files =
   fileReaderSync_worker.js
   fileSlice_worker.js
   fileSubWorker_worker.js
   file_worker.js
   jsm_url_worker.js
   workersDisabled_worker.js
   file_url.jsm
   bug1062920_worker.js
+  empty.html
+  sharedWorker_privateBrowsing.js
 
 [test_WorkerDebugger.xul]
 [test_WorkerDebugger.initialize.xul]
 [test_WorkerDebugger.isFrozen.xul]
 [test_WorkerDebugger.postMessage.xul]
 [test_WorkerDebuggerGlobalScope.createSandbox.xul]
 [test_WorkerDebuggerGlobalScope.enterEventLoop.xul]
 [test_WorkerDebuggerGlobalScope.reportError.xul]
@@ -71,8 +73,9 @@ support-files =
 [test_fileReadSlice.xul]
 [test_fileReaderSync.xul]
 [test_fileReaderSyncErrors.xul]
 [test_fileSlice.xul]
 [test_fileSubWorker.xul]
 [test_workersDisabled.xul]
 [test_url.xul]
 [test_bug1062920.xul]
+[test_sharedWorker_privateBrowsing.html]
--- a/dom/workers/test/serviceworkers/chrome.ini
+++ b/dom/workers/test/serviceworkers/chrome.ini
@@ -1,6 +1,7 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g'
 support-files =
   app/*
 
 [test_app_installation.html]
+[test_privateBrowsing.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_privateBrowsing.html
@@ -0,0 +1,93 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for ServiceWorker - Private Browsing</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+
+<script type="application/javascript">
+
+const Ci = Components.interfaces;
+var mainWindow;
+
+var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
+                           .getService(Components.interfaces.nsIPrefBranch);
+prefBranch.setIntPref("browser.startup.page", 0);
+prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
+
+var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
+
+function testOnWindow(aIsPrivate, aCallback) {
+  var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+      if (win.content.location.href != contentPage) {
+        win.gBrowser.loadURI(contentPage);
+        return;
+      }
+
+      win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+      SimpleTest.executeSoon(function() { aCallback(win); });
+    }, true);
+
+    if (!aIsPrivate) {
+      win.gBrowser.loadURI(contentPage);
+    }
+  }, true);
+}
+
+function setupWindow() {
+  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIWebNavigation)
+                     .QueryInterface(Ci.nsIDocShellTreeItem)
+                     .rootTreeItem
+                     .QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindow);
+  runTest();
+}
+
+var wN;
+var wP;
+
+function doTests() {
+  testOnWindow(false, function(aWin) {
+    wN = aWin;
+    ok("serviceWorker" in wN.content.navigator, "ServiceWorkers are available for normal windows");
+
+    testOnWindow(true, function(aWin) {
+      wP = aWin;
+      ok(!("serviceWorker" in wP.content.navigator), "ServiceWorkers are not available for private windows");
+      SimpleTest.finish();
+    });
+  });
+}
+
+var steps = [
+  setupWindow,
+  doTests
+];
+
+function runTest() {
+  if (!steps.length) {
+    wN.close();
+    wP.close();
+
+    prefBranch.clearUserPref("browser.startup.page")
+    prefBranch.clearUserPref("browser.startup.homepage_override.mstone");
+
+    SimpleTest.finish();
+    return;
+  }
+
+  var step = steps.shift();
+  step();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.serviceWorkers.enabled", true]]}, runTest);
+
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/sharedWorker_privateBrowsing.js
@@ -0,0 +1,5 @@
+var counter = 0;
+onconnect = function(evt) {
+  evt.ports[0].postMessage(++counter);
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_sharedWorker_privateBrowsing.html
@@ -0,0 +1,106 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <title>Test for SharedWorker - Private Browsing</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+
+<script type="application/javascript">
+
+const Ci = Components.interfaces;
+var mainWindow;
+
+var prefBranch = Components.classes["@mozilla.org/preferences-service;1"]
+                           .getService(Components.interfaces.nsIPrefBranch);
+prefBranch.setIntPref("browser.startup.page", 0);
+prefBranch.setCharPref("browser.startup.homepage_override.mstone", "ignore");
+
+var contentPage = "http://mochi.test:8888/chrome/dom/workers/test/empty.html";
+
+function testOnWindow(aIsPrivate, aCallback) {
+  var win = mainWindow.OpenBrowserWindow({private: aIsPrivate});
+  win.addEventListener("load", function onLoad() {
+    win.removeEventListener("load", onLoad, false);
+    win.addEventListener("DOMContentLoaded", function onInnerLoad() {
+      if (win.content.location.href != contentPage) {
+        win.gBrowser.loadURI(contentPage);
+        return;
+      }
+
+      win.removeEventListener("DOMContentLoaded", onInnerLoad, true);
+      SimpleTest.executeSoon(function() { aCallback(win); });
+    }, true);
+
+    if (!aIsPrivate) {
+      win.gBrowser.loadURI(contentPage);
+    }
+  }, true);
+}
+
+function setupWindow() {
+  mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIWebNavigation)
+                     .QueryInterface(Ci.nsIDocShellTreeItem)
+                     .rootTreeItem
+                     .QueryInterface(Ci.nsIInterfaceRequestor)
+                     .getInterface(Ci.nsIDOMWindow);
+  runTest();
+}
+
+var wN;
+var wP;
+
+function doTests() {
+  testOnWindow(false, function(aWin) {
+    wN = aWin;
+
+    testOnWindow(true, function(aWin) {
+      wP = aWin;
+
+      var sharedWorker1 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
+      sharedWorker1.port.onmessage = function(event) {
+        is(event.data, 1, "Only 1 sharedworker expected in the private window");
+
+        var sharedWorker2 = new wN.content.SharedWorker('sharedWorker_privateBrowsing.js');
+        sharedWorker2.port.onmessage = function(event) {
+          is(event.data, 1, "Only 1 sharedworker expected in the normal window");
+
+          var sharedWorker3 = new wP.content.SharedWorker('sharedWorker_privateBrowsing.js');
+          sharedWorker3.port.onmessage = function(event) {
+            is(event.data, 2, "Only 2 sharedworker expected in the private window");
+            SimpleTest.finish();
+          }
+        }
+      }
+    });
+  });
+}
+
+var steps = [
+  setupWindow,
+  doTests
+];
+
+function runTest() {
+  if (!steps.length) {
+    wN.close();
+    wP.close();
+
+    prefBranch.clearUserPref("browser.startup.page")
+    prefBranch.clearUserPref("browser.startup.homepage_override.mstone");
+
+    SimpleTest.finish();
+    return;
+  }
+
+  var step = steps.shift();
+  step();
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [["dom.workers.sharedWorkers.enabled", true]]}, runTest);
+
+</script>
+</body>
+</html>