Bug 1165466 - Fix up docshell and loadcontext inheriting code in nsIScriptSecurityManager. r=bholley
authorYoshi Huang <allstars.chh@mozilla.com>
Wed, 23 Sep 2015 16:10:21 +0800
changeset 296678 86a7be21dfc8ef0a3c3080d58ef508732ce2d154
parent 296677 fb6f36ba8eb1909f23e1ba5b6c1c8f3e3200bbfc
child 296679 d0e88c95f3c5abb537793b88c16a778c56050cae
push id5880
push userarmenzg@mozilla.com
push dateMon, 28 Sep 2015 13:39:44 +0000
reviewersbholley
bugs1165466
milestone44.0a1
Bug 1165466 - Fix up docshell and loadcontext inheriting code in nsIScriptSecurityManager. r=bholley
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/moz.build
caps/nsScriptSecurityManager.cpp
caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
docshell/base/LoadContext.cpp
docshell/base/LoadContext.h
docshell/base/SerializedLoadContext.cpp
docshell/base/SerializedLoadContext.h
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsILoadContext.idl
dom/browser-element/BrowserElementParent.js
dom/ipc/TabParent.cpp
image/test/unit/test_private_channel.js
netwerk/cookie/CookieServiceParent.cpp
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/protocol/http/HttpChannelParent.cpp
netwerk/test/unit/head_channels.js
netwerk/test/unit/test_cache_jar.js
netwerk/test/unit/test_cacheflags.js
netwerk/test/unit/test_predictor.js
netwerk/test/unit_ipc/child_app_offline.js
uriloader/prefetch/OfflineCacheUpdateParent.cpp
uriloader/prefetch/OfflineCacheUpdateParent.h
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 sw=2 et 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/. */
 
 #include "mozilla/BasePrincipal.h"
 
+#include "nsDocShell.h"
 #include "nsIAddonPolicyService.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 
 #include "nsPrincipal.h"
 #include "nsNetUtil.h"
 #include "nsIURIWithPrincipal.h"
@@ -22,16 +23,38 @@
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/URLSearchParams.h"
 
 namespace mozilla {
 
 using dom::URLParams;
 
+void OriginAttributes::InheritFromDocShellParent(const OriginAttributes& aParent)
+{
+  mAppId = aParent.mAppId;
+  mInBrowser = aParent.mInBrowser;
+  mUserContextId = aParent.mUserContextId;
+  mSignedPkg = aParent.mSignedPkg;
+}
+
+bool OriginAttributes::CopyFromLoadContext(nsILoadContext* aLoadContext)
+{
+  OriginAttributes attrs;
+  bool result = aLoadContext->GetOriginAttributes(attrs);
+  NS_ENSURE_TRUE(result, false);
+
+  mAppId = attrs.mAppId;
+  mInBrowser = attrs.mInBrowser;
+  mAddonId = attrs.mAddonId;
+  mUserContextId = attrs.mUserContextId;
+  mSignedPkg = attrs.mSignedPkg;
+  return true;
+}
+
 void
 OriginAttributes::CreateSuffix(nsACString& aStr) const
 {
   MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   UniquePtr<URLParams> params(new URLParams());
   nsAutoString value;
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -9,16 +9,17 @@
 
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsJSPrincipals.h"
 
 #include "mozilla/dom/ChromeUtilsBinding.h"
 
 class nsIContentSecurityPolicy;
+class nsILoadContext;
 class nsIObjectOutputStream;
 class nsIObjectInputStream;
 
 namespace mozilla {
 
 class OriginAttributes : public dom::OriginAttributesDictionary
 {
 public:
@@ -39,16 +40,38 @@ public:
            mUserContextId == aOther.mUserContextId &&
            mSignedPkg == aOther.mSignedPkg;
   }
   bool operator!=(const OriginAttributes& aOther) const
   {
     return !(*this == aOther);
   }
 
+  // The docshell often influences the origin attributes of content loaded
+  // inside of it, and in some cases also influences the origin attributes of
+  // content loaded in child docshells. We say that a given attribute "lives on
+  // the docshell" to indicate that this attribute is specified by the docshell
+  // (if any) associated with a given content document.
+  //
+  // In practice, this usually means that we need to store a copy of those
+  // attributes on each docshell, or provide methods on the docshell to compute
+  // them on-demand.
+  // We could track each of these attributes individually, but since the
+  // majority of the existing origin attributes currently live on the docshell,
+  // it's cleaner to simply store an entire OriginAttributes struct on each
+  // docshell, and selectively copy them to child docshells and content
+  // principals in a manner that implements our desired semantics.
+  //
+  // This method is used to propagate attributes from parent to child
+  // docshells.
+  void InheritFromDocShellParent(const OriginAttributes& aParent);
+
+  // Copy from the origin attributes of the nsILoadContext.
+  bool CopyFromLoadContext(nsILoadContext* aLoadContext);
+
   // Serializes/Deserializes non-default values into the suffix format, i.e.
   // |!key1=value1&key2=value2|. If there are no non-default attributes, this
   // returns an empty string.
   void CreateSuffix(nsACString& aStr) const;
   bool PopulateFromSuffix(const nsACString& aStr);
 
   // Populates the attributes from a string like
   // |uri!key1=value1&key2=value2| and returns the uri without the suffix.
--- a/caps/moz.build
+++ b/caps/moz.build
@@ -39,15 +39,16 @@ UNIFIED_SOURCES += [
     'nsNullPrincipal.cpp',
     'nsNullPrincipalURI.cpp',
     'nsPrincipal.cpp',
     'nsScriptSecurityManager.cpp',
     'nsSystemPrincipal.cpp',
 ]
 
 LOCAL_INCLUDES += [
+    '/docshell/base',
     '/dom/base',
     '/js/xpconnect/src',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -1088,34 +1088,37 @@ nsScriptSecurityManager::GetAppCodebaseP
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::
   GetLoadContextCodebasePrincipal(nsIURI* aURI,
                                   nsILoadContext* aLoadContext,
                                   nsIPrincipal** aPrincipal)
 {
-  // XXXbholley - Make this more general in bug 1165466.
   OriginAttributes attrs;
-  aLoadContext->GetAppId(&attrs.mAppId);
-  aLoadContext->GetIsInBrowserElement(&attrs.mInBrowser);
+  bool result = attrs.CopyFromLoadContext(aLoadContext);
+  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
+
   nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
                                                       nsIDocShell* aDocShell,
                                                       nsIPrincipal** aPrincipal)
 {
-  // XXXbholley - Make this more general in bug 1165466.
-  OriginAttributes attrs(aDocShell->GetAppId(), aDocShell->GetIsInBrowserElement());
+  OriginAttributes attrs;
+  nsDocShell* docShell= nsDocShell::Cast(aDocShell);
+  bool result = attrs.CopyFromLoadContext(docShell);
+  NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
+
   nsresult rv = MaybeSetAddonIdFromURI(attrs, aURI);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateCodebasePrincipal(aURI, attrs);
   prin.forget(aPrincipal);
   return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
 }
 
 // static
--- a/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
+++ b/caps/tests/mochitest/test_principal_jarprefix_origin_appid_appstatus.html
@@ -189,28 +189,16 @@ var gData = [
     isapp: true,
     child: {
       src: "http://example.org/chrome/",
       isapp: false,
       browser: true,
     },
     test: [ "child-has-different-eo", "child-has-different-appstatus", "child-has-same-appid" ],
   },
-  // app inside a browser is an app.
-  {
-    src: "http://example.org/",
-    isapp: false,
-    browser: true,
-    child: {
-      app: "http://example.org/manifest.webapp",
-      src: "http://example.org/chrome/",
-      isapp: true,
-    },
-    test: [ "child-has-different-eo", "child-has-different-appstatus", "child-has-different-appid" ],
-  },
   // browser inside a browser are two browsers
   {
     src: "http://example.org/",
     isapp: false,
     browser: true,
     child: {
       src: "http://example.org/chrome/",
       isapp: false,
--- a/docshell/base/LoadContext.cpp
+++ b/docshell/base/LoadContext.cpp
@@ -1,15 +1,16 @@
 /* -*- 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/. */
 
 #include "mozilla/Assertions.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/LoadContext.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(LoadContext, nsILoadContext, nsIInterfaceRequestor)
 
 LoadContext::LoadContext(nsIPrincipal* aPrincipal,
                          nsILoadContext* aOptionalBase)
@@ -17,19 +18,17 @@ LoadContext::LoadContext(nsIPrincipal* a
   , mNestedFrameId(0)
   , mIsContent(true)
   , mUsePrivateBrowsing(false)
   , mUseRemoteTabs(false)
 #ifdef DEBUG
   , mIsNotNull(true)
 #endif
 {
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aPrincipal->GetAppId(&mAppId)));
-  MOZ_ALWAYS_TRUE(
-    NS_SUCCEEDED(aPrincipal->GetIsInBrowserElement(&mIsInBrowserElement)));
+  mOriginAttributes = BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
 
   if (!aOptionalBase) {
     return;
   }
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aOptionalBase->GetIsContent(&mIsContent)));
   MOZ_ALWAYS_TRUE(
     NS_SUCCEEDED(aOptionalBase->GetUsePrivateBrowsing(&mUsePrivateBrowsing)));
@@ -146,28 +145,39 @@ LoadContext::SetRemoteTabs(bool aUseRemo
 
 NS_IMETHODIMP
 LoadContext::GetIsInBrowserElement(bool* aIsInBrowserElement)
 {
   MOZ_ASSERT(mIsNotNull);
 
   NS_ENSURE_ARG_POINTER(aIsInBrowserElement);
 
-  *aIsInBrowserElement = mIsInBrowserElement;
+  *aIsInBrowserElement = mOriginAttributes.mInBrowser;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 LoadContext::GetAppId(uint32_t* aAppId)
 {
   MOZ_ASSERT(mIsNotNull);
 
   NS_ENSURE_ARG_POINTER(aAppId);
 
-  *aAppId = mAppId;
+  *aAppId = mOriginAttributes.mAppId;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadContext::GetOriginAttributes(JS::MutableHandleValue aAttrs)
+{
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  MOZ_ASSERT(cx);
+
+  bool ok = ToJSValue(cx, mOriginAttributes, aAttrs);
+  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // LoadContext::nsIInterfaceRequestor
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 LoadContext::GetInterface(const nsIID& aIID, void** aResult)
--- a/docshell/base/LoadContext.h
+++ b/docshell/base/LoadContext.h
@@ -4,16 +4,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/. */
 
 #ifndef LoadContext_h
 #define LoadContext_h
 
 #include "SerializedLoadContext.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/BasePrincipal.h"
 #include "nsIWeakReferenceUtils.h"
 #include "mozilla/dom/Element.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadContext.h"
 
 namespace mozilla {
 
 /**
@@ -37,97 +38,91 @@ public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSILOADCONTEXT
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // AppId/inBrowser arguments override those in SerializedLoadContext provided
   // by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               dom::Element* aTopFrameElement,
-              uint32_t aAppId, bool aInBrowser)
+              OriginAttributes& aAttrs)
     : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
     , mNestedFrameId(0)
-    , mAppId(aAppId)
     , mIsContent(aToCopy.mIsContent)
     , mUsePrivateBrowsing(aToCopy.mUsePrivateBrowsing)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
-    , mIsInBrowserElement(aInBrowser)
+    , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
   }
 
   // AppId/inBrowser arguments override those in SerializedLoadContext provided
   // by child process.
   LoadContext(const IPC::SerializedLoadContext& aToCopy,
               uint64_t aNestedFrameId,
-              uint32_t aAppId, bool aInBrowser)
+              OriginAttributes& aAttrs)
     : mTopFrameElement(nullptr)
     , mNestedFrameId(aNestedFrameId)
-    , mAppId(aAppId)
     , mIsContent(aToCopy.mIsContent)
     , mUsePrivateBrowsing(aToCopy.mUsePrivateBrowsing)
     , mUseRemoteTabs(aToCopy.mUseRemoteTabs)
-    , mIsInBrowserElement(aInBrowser)
+    , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(aToCopy.mIsNotNull)
 #endif
   {
   }
 
   LoadContext(dom::Element* aTopFrameElement,
-              uint32_t aAppId,
               bool aIsContent,
               bool aUsePrivateBrowsing,
               bool aUseRemoteTabs,
-              bool aIsInBrowserElement)
+              OriginAttributes& aAttrs)
     : mTopFrameElement(do_GetWeakReference(aTopFrameElement))
     , mNestedFrameId(0)
-    , mAppId(aAppId)
     , mIsContent(aIsContent)
     , mUsePrivateBrowsing(aUsePrivateBrowsing)
     , mUseRemoteTabs(aUseRemoteTabs)
-    , mIsInBrowserElement(aIsInBrowserElement)
+    , mOriginAttributes(aAttrs)
 #ifdef DEBUG
     , mIsNotNull(true)
 #endif
   {
   }
 
   // Constructor taking reserved appId for the safebrowsing cookie.
   explicit LoadContext(uint32_t aAppId)
     : mTopFrameElement(nullptr)
     , mNestedFrameId(0)
-    , mAppId(aAppId)
     , mIsContent(false)
     , mUsePrivateBrowsing(false)
     , mUseRemoteTabs(false)
-    , mIsInBrowserElement(false)
+    , mOriginAttributes(aAppId, false)
 #ifdef DEBUG
     , mIsNotNull(true)
 #endif
   {
   }
 
   // Constructor for creating a LoadContext with a given principal's appId and
   // browser flag.
   explicit LoadContext(nsIPrincipal* aPrincipal,
                        nsILoadContext* aOptionalBase = nullptr);
 
 private:
   ~LoadContext() {}
 
   nsWeakPtr mTopFrameElement;
   uint64_t mNestedFrameId;
-  uint32_t mAppId;
   bool mIsContent;
   bool mUsePrivateBrowsing;
   bool mUseRemoteTabs;
-  bool mIsInBrowserElement;
+  OriginAttributes mOriginAttributes;
 #ifdef DEBUG
   bool mIsNotNull;
 #endif
 };
 
 } // namespace mozilla
 
 #endif // LoadContext_h
--- a/docshell/base/SerializedLoadContext.cpp
+++ b/docshell/base/SerializedLoadContext.cpp
@@ -57,24 +57,23 @@ void
 SerializedLoadContext::Init(nsILoadContext* aLoadContext)
 {
   if (aLoadContext) {
     mIsNotNull = true;
     mIsPrivateBitValid = true;
     aLoadContext->GetIsContent(&mIsContent);
     aLoadContext->GetUsePrivateBrowsing(&mUsePrivateBrowsing);
     aLoadContext->GetUseRemoteTabs(&mUseRemoteTabs);
-    aLoadContext->GetAppId(&mAppId);
-    aLoadContext->GetIsInBrowserElement(&mIsInBrowserElement);
+    if (!aLoadContext->GetOriginAttributes(mOriginAttributes)) {
+      NS_WARNING("GetOriginAttributes failed");
+    }
   } else {
     mIsNotNull = false;
     mIsPrivateBitValid = false;
     // none of below values really matter when mIsNotNull == false:
     // we won't be GetInterfaced to nsILoadContext
     mIsContent = true;
     mUsePrivateBrowsing = false;
     mUseRemoteTabs = false;
-    mAppId = 0;
-    mIsInBrowserElement = false;
   }
 }
 
 } // namespace IPC
--- a/docshell/base/SerializedLoadContext.h
+++ b/docshell/base/SerializedLoadContext.h
@@ -4,16 +4,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/. */
 
 #ifndef SerializedLoadContext_h
 #define SerializedLoadContext_h
 
 #include "base/basictypes.h"
 #include "ipc/IPCMessageUtils.h"
+#include "mozilla/BasePrincipal.h"
 
 class nsILoadContext;
 
 /*
  *  This file contains the IPC::SerializedLoadContext class, which is used to
  *  copy data across IPDL from Child process contexts so it is available in the
  *  Parent.
  */
@@ -43,48 +44,50 @@ public:
   // used to indicate if child-side LoadContext * was null.
   bool mIsNotNull;
   // used to indicate if child-side mUsePrivateBrowsing flag is valid, even if
   // mIsNotNull is false, i.e., child LoadContext was null.
   bool mIsPrivateBitValid;
   bool mIsContent;
   bool mUsePrivateBrowsing;
   bool mUseRemoteTabs;
-  bool mIsInBrowserElement;
-  uint32_t mAppId;
+  mozilla::OriginAttributes mOriginAttributes;
 };
 
 // Function to serialize over IPDL
 template<>
 struct ParamTraits<SerializedLoadContext>
 {
   typedef SerializedLoadContext paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
+    nsAutoCString suffix;
+    aParam.mOriginAttributes.CreateSuffix(suffix);
+
     WriteParam(aMsg, aParam.mIsNotNull);
     WriteParam(aMsg, aParam.mIsContent);
     WriteParam(aMsg, aParam.mIsPrivateBitValid);
     WriteParam(aMsg, aParam.mUsePrivateBrowsing);
     WriteParam(aMsg, aParam.mUseRemoteTabs);
-    WriteParam(aMsg, aParam.mAppId);
-    WriteParam(aMsg, aParam.mIsInBrowserElement);
+    WriteParam(aMsg, suffix);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
+    nsAutoCString suffix;
     if (!ReadParam(aMsg, aIter, &aResult->mIsNotNull) ||
         !ReadParam(aMsg, aIter, &aResult->mIsContent) ||
         !ReadParam(aMsg, aIter, &aResult->mIsPrivateBitValid) ||
         !ReadParam(aMsg, aIter, &aResult->mUsePrivateBrowsing) ||
         !ReadParam(aMsg, aIter, &aResult->mUseRemoteTabs) ||
-        !ReadParam(aMsg, aIter, &aResult->mAppId) ||
-        !ReadParam(aMsg, aIter, &aResult->mIsInBrowserElement)) {
+        !ReadParam(aMsg, aIter, &suffix)) {
       return false;
     }
+    aResult->mOriginAttributes.PopulateFromSuffix(suffix);
 
     return true;
   }
 };
 
 } // namespace IPC
 
 #endif // SerializedLoadContext_h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9383,27 +9383,17 @@ nsDocShell::JustStartedNetworkLoad()
 {
   return mDocumentRequest && mDocumentRequest != GetCurrentDocChannel();
 }
 
 nsresult
 nsDocShell::CreatePrincipalFromReferrer(nsIURI* aReferrer,
                                         nsIPrincipal** aResult)
 {
-  nsresult rv;
-
-  uint32_t appId;
-  rv = GetAppId(&appId);
-  NS_ENSURE_SUCCESS(rv, rv);
-  bool isInBrowserElement;
-  rv = GetIsInBrowserElement(&isInBrowserElement);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // TODO: Bug 1165466 - Pass mOriginAttributes directly.
-  OriginAttributes attrs(appId, isInBrowserElement);
+  OriginAttributes attrs = GetOriginAttributes();
   nsCOMPtr<nsIPrincipal> prin =
     BasePrincipal::CreateCodebasePrincipal(aReferrer, attrs);
   prin.forget(aResult);
 
   return *aResult ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
@@ -13710,16 +13700,48 @@ nsDocShell::GetAppId(uint32_t* aAppId)
   if (!parent) {
     *aAppId = nsIScriptSecurityManager::NO_APP_ID;
     return NS_OK;
   }
 
   return parent->GetAppId(aAppId);
 }
 
+OriginAttributes
+nsDocShell::GetOriginAttributes()
+{
+  OriginAttributes attrs;
+  nsRefPtr<nsDocShell> parent = GetParentDocshell();
+  if (parent) {
+    attrs.InheritFromDocShellParent(parent->GetOriginAttributes());
+  }
+
+  if (mOwnOrContainingAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
+    attrs.mAppId = mOwnOrContainingAppId;
+  }
+
+  if (mFrameType == eFrameTypeBrowser) {
+    attrs.mInBrowser = true;
+  }
+
+  return attrs;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetOriginAttributes(JS::MutableHandle<JS::Value> aVal)
+{
+  JSContext* cx = nsContentUtils::GetCurrentJSContext();
+  MOZ_ASSERT(cx);
+
+  OriginAttributes attrs = GetOriginAttributes();
+  bool ok = ToJSValue(cx, attrs, aVal);
+  NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDocShell::GetAppManifestURL(nsAString& aAppManifestURL)
 {
   uint32_t appId = nsIDocShell::GetAppId();
   if (appId != nsIScriptSecurityManager::NO_APP_ID &&
       appId != nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     nsCOMPtr<nsIAppsService> appsService =
       do_GetService(APPS_SERVICE_CONTRACTID);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -13,16 +13,17 @@
 #include "nsIDocShellTreeItem.h"
 #include "nsIBaseWindow.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIScrollable.h"
 #include "nsITextScroll.h"
 #include "nsIContentViewerContainer.h"
 #include "nsIDOMStorageManager.h"
 #include "nsDocLoader.h"
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/TimeStamp.h"
 #include "GeckoProfiler.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 #include "mozilla/LinkedList.h"
 #include "jsapi.h"
 
@@ -225,16 +226,17 @@ public:
   NS_IMETHOD GetNestedFrameId(uint64_t*) override;
   NS_IMETHOD IsAppOfType(uint32_t, bool*) override;
   NS_IMETHOD GetIsContent(bool*) override;
   NS_IMETHOD GetUsePrivateBrowsing(bool*) override;
   NS_IMETHOD SetUsePrivateBrowsing(bool) override;
   NS_IMETHOD SetPrivateBrowsing(bool) override;
   NS_IMETHOD GetUseRemoteTabs(bool*) override;
   NS_IMETHOD SetRemoteTabs(bool) override;
+  NS_IMETHOD GetOriginAttributes(JS::MutableHandle<JS::Value>) override;
 
   // Restores a cached presentation from history (mLSHE).
   // This method swaps out the content viewer and simulates loads for
   // subframes. It then simulates the completion of the toplevel load.
   nsresult RestoreFromHistory();
 
   // Perform a URI load from a refresh timer. This is just like the
   // ForceRefreshURI method on nsIRefreshURI, but makes sure to take
@@ -263,16 +265,18 @@ public:
   void NotifyAsyncPanZoomStopped();
 
   void SetInFrameSwap(bool aInSwap)
   {
     mInFrameSwap = aInSwap;
   }
   bool InFrameSwap();
 
+  mozilla::OriginAttributes GetOriginAttributes();
+
 private:
   // An observed docshell wrapper is created when recording markers is enabled.
   mozilla::UniquePtr<mozilla::ObservedDocShell> mObserved;
   bool IsObserved() const { return !!mObserved; }
 
   // It is necessary to allow adding a timeline marker wherever a docshell
   // instance is available. This operation happens frequently and needs to
   // be very fast, so instead of using a Map or having to search for some
@@ -290,16 +294,21 @@ private:
     nsDocShell*, UniquePtr<AbstractTimelineMarker>&);
 
 public:
   // Tell the favicon service that aNewURI has the same favicon as aOldURI.
   static void CopyFavicon(nsIURI* aOldURI,
                           nsIURI* aNewURI,
                           bool aInPrivateBrowsing);
 
+  static nsDocShell* Cast(nsIDocShell* aDocShell)
+  {
+    return static_cast<nsDocShell*>(aDocShell);
+  }
+
 protected:
   virtual ~nsDocShell();
   virtual void DestroyChildren() override;
 
   // Content Viewer Management
   nsresult EnsureContentViewer();
   // aPrincipal can be passed in if the caller wants. If null is
   // passed in, the about:blank principal will end up being used.
--- a/docshell/base/nsILoadContext.idl
+++ b/docshell/base/nsILoadContext.idl
@@ -4,22 +4,31 @@
  * 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/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMWindow;
 interface nsIDOMElement;
 
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+#include "mozilla/BasePrincipal.h" // for OriginAttributes
+#include "mozilla/dom/ScriptSettings.h" // for AutoJSAPI
+#include "xpcpublic.h" // for PrivilegedJunkScope
+#include "nsContentUtils.h" // for IsSystemPrincipal
+#endif
+%}
+
 /**
  * An nsILoadContext represents the context of a load.  This interface
  * can be queried for various information about where the load is
  * happening.
  */
-[scriptable, uuid(6ec837fa-af93-4350-bbb8-0985d54c74ca)]
+[scriptable, uuid(1220e340-b337-4c35-aaa1-f51362763621)]
 interface nsILoadContext : nsISupports
 {
   /**
    * associatedWindow is the window with which the load is associated, if any.
    * Note that the load may be triggered by a document which is different from
    * the document in associatedWindow, and in fact the source of the load need
    * not be same-origin with the document in associatedWindow.  This attribute
    * may be null if there is no associated window.
@@ -115,9 +124,44 @@ interface nsILoadContext : nsISupports
   readonly attribute boolean isInBrowserElement;
 
   /**
    * Returns the app id of the app the load is occurring is in. Returns
    * nsIScriptSecurityManager::NO_APP_ID if the load is not part of an app.
    */
   readonly attribute unsigned long appId;
 
+  /**
+   * A dictionary of the non-default origin attributes associated with this
+   * nsILoadContext.
+   */
+  readonly attribute jsval originAttributes;
+
+%{C++
+#ifdef MOZILLA_INTERNAL_API
+  /**
+   * The C++ getter for origin attributes.
+   */
+  bool GetOriginAttributes(mozilla::OriginAttributes& aAttrs)
+  {
+    mozilla::dom::AutoJSAPI jsapi;
+    bool ok = jsapi.Init(xpc::PrivilegedJunkScope());
+    NS_ENSURE_TRUE(ok, false);
+    JS::Rooted<JS::Value> v(jsapi.cx());
+    nsresult rv = GetOriginAttributes(&v);
+    NS_ENSURE_SUCCESS(rv, false);
+    MOZ_ASSERT(v.isObject());
+    JS::Rooted<JSObject*> obj(jsapi.cx(), &v.toObject());
+
+    // If we're JS-implemented, the object will be left in a different (System-Principaled)
+    // scope, so we may need to enter its compartment.
+    MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(nsContentUtils::ObjectPrincipal(obj)));
+    JSAutoCompartment ac(jsapi.cx(), obj);
+
+    mozilla::OriginAttributes attrs;
+    ok = attrs.Init(jsapi.cx(), v);
+    NS_ENSURE_TRUE(ok, false);
+    aAttrs = attrs;
+    return true;
+  }
+#endif
+%}
 };
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -834,25 +834,22 @@ BrowserElementParent.prototype = {
       // newURI can throw on malformed URIs.
       try {
         referrer = Services.io.newURI(_options.referrer, null, null);
       }
       catch(e) {
         debug('Malformed referrer -- ' + e);
       }
 
-      // TODO Bug 1165466: use originAttributes from nsILoadContext.
-      let attrs = {appId: this._frameLoader.loadContext.appId,
-                   inBrowser: this._frameLoader.loadContext.isInBrowserElement};
       // This simply returns null if there is no principal available
       // for the requested uri. This is an acceptable fallback when
       // calling newChannelFromURI2.
       principal =
         Services.scriptSecurityManager.createCodebasePrincipal(
-          referrer, attrs);
+          referrer, this._frameLoader.loadContext.originAttributes);
     }
 
     debug('Using principal? ' + !!principal);
 
     let channel = 
       Services.io.newChannelFromURI2(url,
                                      null,       // No document. 
                                      principal,  // Loading principal
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2969,22 +2969,23 @@ TabParent::RecvSetAllowedTouchBehavior(c
 
 already_AddRefed<nsILoadContext>
 TabParent::GetLoadContext()
 {
   nsCOMPtr<nsILoadContext> loadContext;
   if (mLoadContext) {
     loadContext = mLoadContext;
   } else {
+    // TODO Bug 1191740 - Add OriginAttributes in TabContext
+    OriginAttributes attrs = OriginAttributes(OwnOrContainingAppId(), IsBrowserElement());
     loadContext = new LoadContext(GetOwnerElement(),
-                                  OwnOrContainingAppId(),
                                   true /* aIsContent */,
                                   mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW,
                                   mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW,
-                                  IsBrowserElement());
+                                  attrs);
     mLoadContext = loadContext;
   }
   return loadContext.forget();
 }
 
 NS_IMETHODIMP
 TabParent::InjectTouchEvent(const nsAString& aType,
                             uint32_t* aIdentifiers,
@@ -3343,16 +3344,17 @@ public:
   NS_IMETHOD GetNestedFrameId(uint64_t*) NO_IMPL
   NS_IMETHOD IsAppOfType(uint32_t, bool*) NO_IMPL
   NS_IMETHOD GetIsContent(bool*) NO_IMPL
   NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL
   NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL
   NS_IMETHOD GetIsInBrowserElement(bool*) NO_IMPL
   NS_IMETHOD GetAppId(uint32_t*) NO_IMPL
+  NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL
   NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL
   NS_IMETHOD SetRemoteTabs(bool) NO_IMPL
 #undef NO_IMPL
 
 protected:
   ~FakeChannel() {}
 
   nsCOMPtr<nsIURI> mUri;
--- a/image/test/unit/test_private_channel.js
+++ b/image/test/unit/test_private_channel.js
@@ -41,17 +41,18 @@ NotificationCallbacks.prototype = {
         iid.equals(Ci.nsILoadContext))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
   getInterface: function(iid) {
     if (iid.equals(Ci.nsILoadContext))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  },
+  originAttributes: {}
 };
 
 var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png';
 
 function setup_chan(path, isPrivate, callback) {
   var uri = gIoService.newURI(gImgPath, null, null);
   var chan = gIoService.newChannelFromURI2(uri,
                                            null,      // aLoadingNode
--- a/netwerk/cookie/CookieServiceParent.cpp
+++ b/netwerk/cookie/CookieServiceParent.cpp
@@ -70,27 +70,29 @@ CookieServiceParent::GetAppInfoFromParam
                                           uint32_t& aAppId,
                                           bool& aIsInBrowserElement,
                                           bool& aIsPrivate)
 {
   aAppId = NECKO_NO_APP_ID;
   aIsInBrowserElement = false;
   aIsPrivate = false;
 
+  OriginAttributes attrs;
   const char* error = NeckoParent::GetValidatedAppInfo(aLoadContext,
                                                        Manager()->Manager(),
-                                                       &aAppId,
-                                                       &aIsInBrowserElement);
+                                                       attrs);
   if (error) {
     NS_WARNING(nsPrintfCString("CookieServiceParent: GetAppInfoFromParams: "
                                "FATAL error: %s: KILLING CHILD PROCESS\n",
                                error).get());
     return false;
   }
 
+  aAppId = attrs.mAppId;
+  aIsInBrowserElement = attrs.mInBrowser;
   if (aLoadContext.IsPrivateBitValid()) {
     aIsPrivate = aLoadContext.mUsePrivateBrowsing;
   }
   return true;
 }
 
 CookieServiceParent::CookieServiceParent()
 {
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -39,16 +39,17 @@
 #include "nsAuthInformationHolder.h"
 #include "nsIAuthPromptCallback.h"
 #include "nsPrincipal.h"
 #include "nsIOService.h"
 #include "nsINetworkPredictor.h"
 #include "mozilla/net/OfflineObserver.h"
 #include "nsISpeculativeConnect.h"
 
+using mozilla::OriginAttributes;
 using mozilla::dom::ContentParent;
 using mozilla::dom::TabContext;
 using mozilla::dom::TabParent;
 using mozilla::net::PTCPSocketParent;
 using mozilla::dom::TCPSocketParent;
 using mozilla::net::PTCPServerSocketParent;
 using mozilla::dom::TCPServerSocketParent;
 using mozilla::net::PUDPSocketParent;
@@ -104,35 +105,32 @@ PBOverrideStatusFromLoadContext(const Se
       kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
 const char*
 NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
                                  PContentParent* aContent,
-                                 uint32_t* aAppId,
-                                 bool* aInBrowserElement)
+                                 OriginAttributes& aAttrs)
 {
-  *aAppId = NECKO_UNKNOWN_APP_ID;
-  *aInBrowserElement = false;
-
   if (UsingNeckoIPCSecurity()) {
     if (!aSerialized.IsNotNull()) {
       return "SerializedLoadContext from child is null";
     }
   }
 
   nsTArray<TabContext> contextArray =
     static_cast<ContentParent*>(aContent)->GetManagedTabContext();
   for (uint32_t i = 0; i < contextArray.Length(); i++) {
     TabContext tabContext = contextArray[i];
     uint32_t appId = tabContext.OwnOrContainingAppId();
-    bool inBrowserElement = aSerialized.IsNotNull() ? aSerialized.mIsInBrowserElement
-                                                    : tabContext.IsBrowserElement();
+    bool inBrowserElement = aSerialized.IsNotNull() ?
+                              aSerialized.mOriginAttributes.mInBrowser :
+                              tabContext.IsBrowserElement();
 
     if (appId == NECKO_UNKNOWN_APP_ID) {
       continue;
     }
     // We may get appID=NO_APP if child frame is neither a browser nor an app
     if (appId == NECKO_NO_APP_ID) {
       if (tabContext.HasOwnApp()) {
         continue;
@@ -140,72 +138,67 @@ NeckoParent::GetValidatedAppInfo(const S
       if (UsingNeckoIPCSecurity() && tabContext.IsBrowserElement()) {
         // <iframe mozbrowser> which doesn't have an <iframe mozapp> above it.
         // This is not supported now, and we'll need to do a code audit to make
         // sure we can handle it (i.e don't short-circuit using separate
         // namespace if just appID==0)
         continue;
       }
     }
-    *aAppId = appId;
-    *aInBrowserElement = inBrowserElement;
+    aAttrs = OriginAttributes(appId, inBrowserElement);
     return nullptr;
   }
 
   if (contextArray.Length() != 0) {
     return "App does not have permission";
   }
 
   if (!UsingNeckoIPCSecurity()) {
     // We are running xpcshell tests
     if (aSerialized.IsNotNull()) {
-      *aAppId = aSerialized.mAppId;
-      *aInBrowserElement = aSerialized.mIsInBrowserElement;
+      aAttrs = aSerialized.mOriginAttributes;
     } else {
-      *aAppId = NECKO_NO_APP_ID;
+      aAttrs = OriginAttributes(NECKO_NO_APP_ID, false);
     }
     return nullptr;
   }
 
   return "ContentParent does not have any PBrowsers";
 }
 
 const char *
 NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                                       PContentParent* aContent,
                                       const SerializedLoadContext& aSerialized,
                                       nsCOMPtr<nsILoadContext> &aResult)
 {
-  uint32_t appId = NECKO_UNKNOWN_APP_ID;
-  bool inBrowser = false;
-  const char* error = GetValidatedAppInfo(aSerialized, aContent, &appId, &inBrowser);
+  OriginAttributes attrs;
+  const char* error = GetValidatedAppInfo(aSerialized, aContent, attrs);
   if (error) {
     return error;
   }
 
   // if !UsingNeckoIPCSecurity(), we may not have a LoadContext to set. This is
   // the common case for most xpcshell tests.
   if (aSerialized.IsNotNull()) {
     switch (aBrowser.type()) {
       case PBrowserOrId::TPBrowserParent:
       {
         nsRefPtr<TabParent> tabParent =
           TabParent::GetFrom(aBrowser.get_PBrowserParent());
         dom::Element* topFrameElement = nullptr;
         if (tabParent) {
           topFrameElement = tabParent->GetOwnerElement();
         }
-        aResult = new LoadContext(aSerialized, topFrameElement,
-                                  appId, inBrowser);
+        aResult = new LoadContext(aSerialized, topFrameElement, attrs);
         break;
       }
       case PBrowserOrId::TTabId:
       {
-        aResult = new LoadContext(aSerialized, aBrowser.get_TabId(),
-                                  appId, inBrowser);
+        aResult = new LoadContext(aSerialized, aBrowser.get_TabId(), attrs);
         break;
       }
       default:
         MOZ_CRASH();
     }
   }
 
   return nullptr;
@@ -561,17 +554,17 @@ NeckoParent::AllocPRemoteOpenFileParent(
     nsTArray<TabContext> contextArray =
       static_cast<ContentParent*>(Manager())->GetManagedTabContext();
     for (uint32_t i = 0; i < contextArray.Length(); i++) {
       TabContext tabContext = contextArray[i];
       uint32_t appId = tabContext.OwnOrContainingAppId();
       // Note: this enforces that SerializedLoadContext.appID is one of the apps
       // in the child process, but there's currently no way to verify the
       // request is not from a different app in that process.
-      if (appId == aSerialized.mAppId) {
+      if (appId == aSerialized.mOriginAttributes.mAppId) {
         nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(mozApp));
         if (NS_FAILED(rv) || !mozApp) {
           break;
         }
         rv = mozApp->HasPermission("webapps-manage", &hasManage);
         if (NS_FAILED(rv)) {
           break;
         }
@@ -873,21 +866,22 @@ NeckoParent::RecvPredPredict(const ipc::
                              const uint32_t& aReason,
                              const SerializedLoadContext& aLoadContext,
                              const bool& hasVerifier)
 {
   nsCOMPtr<nsIURI> targetURI = DeserializeURI(aTargetURI);
   nsCOMPtr<nsIURI> sourceURI = DeserializeURI(aSourceURI);
 
   // We only actually care about the loadContext.mPrivateBrowsing, so we'll just
-  // pass dummy params for nestFrameId, inBrowser and appId
+  // pass dummy params for nestFrameId, and originAttributes.
   uint64_t nestedFrameId = 0;
+  OriginAttributes attrs(NECKO_UNKNOWN_APP_ID, false);
   nsCOMPtr<nsILoadContext> loadContext;
   if (aLoadContext.IsNotNull()) {
-    loadContext = new LoadContext(aLoadContext, nestedFrameId, NECKO_UNKNOWN_APP_ID, false);
+    loadContext = new LoadContext(aLoadContext, nestedFrameId, attrs);
   }
 
   // Get the current predictor
   nsresult rv = NS_OK;
   nsCOMPtr<nsINetworkPredictor> predictor =
     do_GetService("@mozilla.org/network/predictor;1", &rv);
   NS_ENSURE_SUCCESS(rv, false);
 
@@ -904,21 +898,22 @@ NeckoParent::RecvPredLearn(const ipc::UR
                            const ipc::OptionalURIParams& aSourceURI,
                            const uint32_t& aReason,
                            const SerializedLoadContext& aLoadContext)
 {
   nsCOMPtr<nsIURI> targetURI = DeserializeURI(aTargetURI);
   nsCOMPtr<nsIURI> sourceURI = DeserializeURI(aSourceURI);
 
   // We only actually care about the loadContext.mPrivateBrowsing, so we'll just
-  // pass dummy params for nestFrameId, inBrowser and appId
+  // pass dummy params for nestFrameId, and originAttributes;
   uint64_t nestedFrameId = 0;
+  OriginAttributes attrs(NECKO_UNKNOWN_APP_ID, false);
   nsCOMPtr<nsILoadContext> loadContext;
   if (aLoadContext.IsNotNull()) {
-    loadContext = new LoadContext(aLoadContext, nestedFrameId, NECKO_UNKNOWN_APP_ID, false);
+    loadContext = new LoadContext(aLoadContext, nestedFrameId, attrs);
   }
 
   // Get the current predictor
   nsresult rv = NS_OK;
   nsCOMPtr<nsINetworkPredictor> predictor =
     do_GetService("@mozilla.org/network/predictor;1", &rv);
   NS_ENSURE_SUCCESS(rv, false);
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et 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/. */
 
+#include "mozilla/BasePrincipal.h"
 #include "mozilla/net/PNeckoParent.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/OfflineObserver.h"
 #include "nsIAuthPrompt2.h"
 #include "nsINetworkPredictor.h"
 #include "nsNetUtil.h"
 
 #ifndef mozilla_net_NeckoParent_h
@@ -33,18 +34,17 @@ class NeckoParent
 public:
   NeckoParent();
   virtual ~NeckoParent();
 
   MOZ_WARN_UNUSED_RESULT
   static const char *
   GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
                       PContentParent* aBrowser,
-                      uint32_t* aAppId,
-                      bool* aInBrowserElement);
+                      mozilla::OriginAttributes& aAttrs);
 
   /*
    * Creates LoadContext for parent-side of an e10s channel.
    *
    * PContentParent corresponds to the process that is requesting the load.
    *
    * Returns null if successful, or an error string if failed.
    */
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -516,23 +516,21 @@ HttpChannelParent::DoAsyncOpen(  const U
                                                 getter_AddRefs(appCache));
       if (NS_SUCCEEDED(rv)) {
         appCacheChan->SetApplicationCache(appCache);
         setChooseApplicationCache = false;
       }
     }
 
     if (setChooseApplicationCache) {
-      bool inBrowser = false;
+      OriginAttributes attrs;
       if (mLoadContext) {
-        mLoadContext->GetIsInBrowserElement(&inBrowser);
+        attrs.CopyFromLoadContext(mLoadContext);
       }
 
-      // TODO: Bug 1165466 - use originAttribute in nsILoadContext.
-      OriginAttributes attrs(appId, inBrowser);
       nsCOMPtr<nsIPrincipal> principal =
         BasePrincipal::CreateCodebasePrincipal(uri, attrs);
 
       bool chooseAppCache = false;
       // This works because we've already called SetNotificationCallbacks and
       // done mPBOverride logic by this point.
       chooseAppCache = NS_ShouldCheckAppCache(principal, NS_UsePrivateBrowsing(mChannel));
 
--- a/netwerk/test/unit/head_channels.js
+++ b/netwerk/test/unit/head_channels.js
@@ -206,16 +206,20 @@ ChannelEventSink.prototype = {
 
 /**
  * Class that implements nsILoadContext.  Use it as callbacks for channel when
  * test needs it.
  */
 function LoadContextCallback(appId, inBrowserElement, isPrivate, isContent) {
   this.appId = appId;
   this.isInBrowserElement = inBrowserElement;
+  this.originAttributes = {
+    appId: appId,
+    inBrowser: inBrowserElement
+  };
   this.usePrivateBrowsing = isPrivate;
   this.isContent = isContent;
 }
 
 LoadContextCallback.prototype = {
   associatedWindow: null,
   topWindow : null,
   isAppOfType: function(appType) {
--- a/netwerk/test/unit/test_cache_jar.js
+++ b/netwerk/test/unit/test_cache_jar.js
@@ -26,16 +26,20 @@ function makeChan(url, appId, inBrowser)
                              null,      // aLoadingNode
                              Services.scriptSecurityManager.getSystemPrincipal(),
                              null,      // aTriggeringPrincipal
                              Ci.nsILoadInfo.SEC_NORMAL,
                              Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel);
   chan.notificationCallbacks = {
     appId: appId,
     isInBrowserElement: inBrowser,
+    originAttributes: {
+      appId: appId,
+      inBrowser: inBrowser,
+    },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsILoadContext))
         return this;
       throw Cr.NS_ERROR_NO_INTERFACE;
     },
     getInterface: function(iid) { return this.QueryInterface(iid); }
   };
   return chan;
--- a/netwerk/test/unit/test_cacheflags.js
+++ b/netwerk/test/unit/test_cacheflags.js
@@ -28,17 +28,19 @@ LoadContext.prototype = {
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
   },
 
   getInterface: function(iid) {
     if (iid.equals(Ci.nsILoadContext))
       return this;
     throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  },
+
+  originAttributes: {}
 };
 
 PrivateBrowsingLoadContext = new LoadContext(true);
 
 function make_channel(url, flags, usePrivateBrowsing) {
   var ios = Cc["@mozilla.org/network/io-service;1"].
     getService(Ci.nsIIOService);
   var req = ios.newChannel2(url,
--- a/netwerk/test/unit/test_predictor.js
+++ b/netwerk/test/unit/test_predictor.js
@@ -33,17 +33,19 @@ LoadContext.prototype = {
 
   QueryInterface: function loadContext_QueryInterface(iid) {
     if (iid.equals(Ci.nsINetworkPredictorVerifier) ||
         iid.equals(Ci.nsILoadContext)) {
       return this;
     }
 
     throw Cr.NS_ERROR_NO_INTERFACE;
-  }
+  },
+
+  originAttributes: {}
 };
 
 var load_context = new LoadContext();
 
 var Verifier = function _verifier(testing, expected_preconnects, expected_preresolves) {
   this.verifying = testing;
   this.expected_preconnects = expected_preconnects;
   this.expected_preresolves = expected_preresolves;
--- a/netwerk/test/unit_ipc/child_app_offline.js
+++ b/netwerk/test/unit_ipc/child_app_offline.js
@@ -14,16 +14,20 @@ function makeChan(url, appId, inBrowser)
                              null,      // aLoadingNode
                              Services.scriptSecurityManager.getSystemPrincipal(),
                              null,      // aTriggeringPrincipal
                              Ci.nsILoadInfo.SEC_NORMAL,
                              Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel);
   chan.notificationCallbacks = {
     appId: appId,
     isInBrowserElement: inBrowser,
+    originAttributes: {
+      appId: appId,
+      inBrowser: inBrowser,
+    },
     QueryInterface: function(iid) {
       if (iid.equals(Ci.nsILoadContext))
         return this;
       throw Cr.NS_ERROR_NO_INTERFACE;
     },
     getInterface: function(iid) { return this.QueryInterface(iid); }
   };
   return chan;
--- a/uriloader/prefetch/OfflineCacheUpdateParent.cpp
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.cpp
@@ -46,21 +46,21 @@ namespace docshell {
 NS_IMPL_ISUPPORTS(OfflineCacheUpdateParent,
                   nsIOfflineCacheUpdateObserver,
                   nsILoadContext)
 
 //-----------------------------------------------------------------------------
 // OfflineCacheUpdateParent <public>
 //-----------------------------------------------------------------------------
 
+// TODO: Bug 1191740 - Add OriginAttributes in TabContext
 OfflineCacheUpdateParent::OfflineCacheUpdateParent(uint32_t aAppId,
                                                    bool aIsInBrowser)
     : mIPCClosed(false)
-    , mIsInBrowserElement(aIsInBrowser)
-    , mAppId(aAppId)
+    , mOriginAttributes(aAppId, aIsInBrowser)
 {
     // Make sure the service has been initialized
     nsOfflineCacheUpdateService::EnsureService();
 
     LOG(("OfflineCacheUpdateParent::OfflineCacheUpdateParent [%p]", this));
 }
 
 OfflineCacheUpdateParent::~OfflineCacheUpdateParent()
@@ -88,44 +88,46 @@ OfflineCacheUpdateParent::Schedule(const
 
     nsOfflineCacheUpdateService* service =
         nsOfflineCacheUpdateService::EnsureService();
     if (!service)
         return NS_ERROR_FAILURE;
 
     bool offlinePermissionAllowed = false;
 
-    // TODO: Bug 1165466 - use OriginAttributes
-    OriginAttributes attrs(mAppId, mIsInBrowserElement);
     nsCOMPtr<nsIPrincipal> principal =
-      BasePrincipal::CreateCodebasePrincipal(manifestURI, attrs);
+      BasePrincipal::CreateCodebasePrincipal(manifestURI, mOriginAttributes);
 
     nsresult rv = service->OfflineAppAllowed(
         principal, nullptr, &offlinePermissionAllowed);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!offlinePermissionAllowed)
         return NS_ERROR_DOM_SECURITY_ERR;
 
     nsCOMPtr<nsIURI> documentURI = DeserializeURI(aDocumentURI);
     if (!documentURI)
         return NS_ERROR_FAILURE;
 
     if (!NS_SecurityCompareURIs(manifestURI, documentURI, false))
         return NS_ERROR_DOM_SECURITY_ERR;
 
-    service->FindUpdate(manifestURI, mAppId, mIsInBrowserElement, nullptr,
+    // TODO: Bug 1197093 - add originAttributes to nsIOfflineCacheUpdate
+    service->FindUpdate(manifestURI,
+                        mOriginAttributes.mAppId,
+                        mOriginAttributes.mInBrowser,
+                        nullptr,
                         getter_AddRefs(update));
     if (!update) {
         update = new nsOfflineCacheUpdate();
 
         // Leave aDocument argument null. Only glues and children keep 
         // document instances.
         rv = update->Init(manifestURI, documentURI, nullptr, nullptr,
-                          mAppId, mIsInBrowserElement);
+                          mOriginAttributes.mAppId, mOriginAttributes.mInBrowser);
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = update->Schedule();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     update->AddObserver(this, false);
 
@@ -249,21 +251,32 @@ NS_IMETHODIMP
 OfflineCacheUpdateParent::SetRemoteTabs(bool aUseRemoteTabs)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateParent::GetIsInBrowserElement(bool *aIsInBrowserElement)
 {
-    *aIsInBrowserElement = mIsInBrowserElement;
+    *aIsInBrowserElement = mOriginAttributes.mInBrowser;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 OfflineCacheUpdateParent::GetAppId(uint32_t *aAppId)
 {
-    *aAppId = mAppId;
+    *aAppId = mOriginAttributes.mAppId;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+OfflineCacheUpdateParent::GetOriginAttributes(JS::MutableHandleValue aAttrs)
+{
+    JSContext* cx = nsContentUtils::GetCurrentJSContext();
+    MOZ_ASSERT(cx);
+
+    bool ok = ToJSValue(cx, mOriginAttributes, aAttrs);
+    NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
     return NS_OK;
 }
 
 } // namespace docshell
 } // namespace mozilla
--- a/uriloader/prefetch/OfflineCacheUpdateParent.h
+++ b/uriloader/prefetch/OfflineCacheUpdateParent.h
@@ -2,16 +2,17 @@
 /* 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 nsOfflineCacheUpdateParent_h
 #define nsOfflineCacheUpdateParent_h
 
 #include "mozilla/docshell/POfflineCacheUpdateParent.h"
+#include "mozilla/BasePrincipal.h"
 #include "nsIOfflineCacheUpdate.h"
 
 #include "nsString.h"
 #include "nsILoadContext.h"
 
 namespace mozilla {
 
 namespace ipc {
@@ -40,22 +41,20 @@ public:
     StopSendingMessagesToChild()
     {
       mIPCClosed = true;
     }
 
     OfflineCacheUpdateParent(uint32_t aAppId, bool aIsInBrowser);
 
     virtual void ActorDestroy(ActorDestroyReason aWhy) override;
-
 private:
     ~OfflineCacheUpdateParent();
 
     bool mIPCClosed;
 
-    bool     mIsInBrowserElement;
-    uint32_t mAppId;
+    mozilla::OriginAttributes mOriginAttributes;
 };
 
 } // namespace docshell
 } // namespace mozilla
 
 #endif