Bug 1165162 - Make OriginAttributes a dictionary, and make it accessible as both a jsval and a canonical string. r=gabor,r=bholley,sr=sicking
authorBobby Holley <bobbyholley@gmail.com>
Thu, 14 May 2015 16:24:25 -0700
changeset 265898 72f81e3ed9aa3c442938803630b82b668c4725cc
parent 265897 121b818c01a55a4c1c672a4168bec164a6a5775f
child 265899 5bc2395aa7105f2e8b59242c01671f1584f1f4b3
push id2189
push useratolfsen@mozilla.com
push dateThu, 21 May 2015 13:24:13 +0000
reviewersgabor, bholley, sicking
bugs1165162
milestone41.0a1
Bug 1165162 - Make OriginAttributes a dictionary, and make it accessible as both a jsval and a canonical string. r=gabor,r=bholley,sr=sicking
caps/BasePrincipal.cpp
caps/BasePrincipal.h
caps/nsIPrincipal.idl
dom/webidl/SystemDictionaries.webidl
dom/webidl/moz.build
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -5,32 +5,51 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/BasePrincipal.h"
 
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsScriptSecurityManager.h"
 
+#include "mozilla/dom/ToJSValue.h"
+
 namespace mozilla {
 
 void
+BasePrincipal::OriginAttributes::CreateSuffix(nsACString& aStr)
+{
+  aStr.Truncate();
+  MOZ_RELEASE_ASSERT(mAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
+  int attrCount = 0;
+
+  if (mAppId != nsIScriptSecurityManager::NO_APP_ID) {
+    aStr.Append(attrCount++ ? "&appId=" : "!appId=");
+    aStr.AppendInt(mAppId);
+  }
+
+  if (mInBrowser) {
+    aStr.Append(attrCount++ ? "&inBrowser=1" : "!inBrowser=1");
+  }
+}
+
+void
 BasePrincipal::OriginAttributes::Serialize(nsIObjectOutputStream* aStream) const
 {
   aStream->Write32(mAppId);
-  aStream->WriteBoolean(mIsInBrowserElement);
+  aStream->WriteBoolean(mInBrowser);
 }
 
 nsresult
 BasePrincipal::OriginAttributes::Deserialize(nsIObjectInputStream* aStream)
 {
   nsresult rv = aStream->Read32(&mAppId);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = aStream->ReadBoolean(&mIsInBrowserElement);
+  rv = aStream->ReadBoolean(&mInBrowser);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 bool
 BasePrincipal::Subsumes(nsIPrincipal* aOther, DocumentDomainConsideration aConsideration)
 {
@@ -100,16 +119,32 @@ BasePrincipal::GetJarPrefix(nsACString& 
 {
   MOZ_ASSERT(AppId() != nsIScriptSecurityManager::UNKNOWN_APP_ID);
 
   mozilla::GetJarPrefix(AppId(), IsInBrowserElement(), aJarPrefix);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+BasePrincipal::GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
+{
+  if (NS_WARN_IF(!ToJSValue(aCx, mOriginAttributes, aVal))) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes)
+{
+  mOriginAttributes.CreateSuffix(aOriginAttributes);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 BasePrincipal::GetAppStatus(uint16_t* aAppStatus)
 {
   if (AppId() == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
     NS_WARNING("Asking for app status on a principal with an unknown app id");
     *aAppStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
     return NS_OK;
   }
 
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -6,16 +6,18 @@
 
 #ifndef mozilla_BasePrincipal_h
 #define mozilla_BasePrincipal_h
 
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsJSPrincipals.h"
 
+#include "mozilla/dom/SystemDictionariesBinding.h"
+
 class nsIObjectOutputStream;
 class nsIObjectInputStream;
 
 namespace mozilla {
 
 /*
  * Base class from which all nsIPrincipal implementations inherit. Use this for
  * default implementations and other commonalities between principal
@@ -34,52 +36,57 @@ public:
   NS_IMETHOD Equals(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD EqualsConsideringDomain(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD Subsumes(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
   NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
   NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
   NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) override;
   NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix) final;
+  NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
+  NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
   NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
   NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
   NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) final;
   NS_IMETHOD GetUnknownAppId(bool* aUnknownAppId) final;
 
   virtual bool IsOnCSSUnprefixingWhitelist() override { return false; }
 
   static BasePrincipal* Cast(nsIPrincipal* aPrin) { return static_cast<BasePrincipal*>(aPrin); }
 
-  struct OriginAttributes {
-    // NB: If you add any members here, you need to update Serialize/Deserialize
-    // and bump the CIDs of all the principal implementations that invoke those
-    // methods.
-    uint32_t mAppId;
-    bool mIsInBrowserElement;
+  struct OriginAttributes : public dom::OriginAttributesDictionary {
+    OriginAttributes() {}
+    OriginAttributes(uint32_t aAppId, bool aInBrowser)
+    {
+      mAppId = aAppId;
+      mInBrowser = aInBrowser;
+    }
 
-    OriginAttributes() : mAppId(nsIScriptSecurityManager::NO_APP_ID), mIsInBrowserElement(false) {}
-    OriginAttributes(uint32_t aAppId, bool aIsInBrowserElement)
-      : mAppId(aAppId), mIsInBrowserElement(aIsInBrowserElement) {}
     bool operator==(const OriginAttributes& aOther) const
     {
       return mAppId == aOther.mAppId &&
-             mIsInBrowserElement == aOther.mIsInBrowserElement;
+             mInBrowser == aOther.mInBrowser;
     }
     bool operator!=(const OriginAttributes& aOther) const
     {
       return !(*this == aOther);
     }
 
+    // Serializes 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);
+
     void Serialize(nsIObjectOutputStream* aStream) const;
     nsresult Deserialize(nsIObjectInputStream* aStream);
   };
 
   const OriginAttributes& OriginAttributesRef() { return mOriginAttributes; }
   uint32_t AppId() const { return mOriginAttributes.mAppId; }
-  bool IsInBrowserElement() const { return mOriginAttributes.mIsInBrowserElement; }
+  bool IsInBrowserElement() const { return mOriginAttributes.mInBrowser; }
 
 protected:
   virtual ~BasePrincipal() {}
 
   virtual bool SubsumesInternal(nsIPrincipal* aOther, DocumentDomainConsideration aConsider) = 0;
 
   nsCOMPtr<nsIContentSecurityPolicy> mCSP;
   OriginAttributes mOriginAttributes;
--- a/caps/nsIPrincipal.idl
+++ b/caps/nsIPrincipal.idl
@@ -15,17 +15,17 @@ struct JSPrincipals;
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 [ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
 
-[scriptable, builtinclass, uuid(7e024afa-afd4-48e7-ba11-1c7b9620b1b2)]
+[scriptable, builtinclass, uuid(74fb6760-4ae7-4ec7-8ac7-06817c60a93a)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Returns whether the other principal is equivalent to this principal.
      * Principals are considered equal if they are the same principal, or
      * they have the same origin.
      */
     boolean equals(in nsIPrincipal other);
@@ -153,16 +153,47 @@ interface nsIPrincipal : nsISerializable
      *
      * The jarPrefix is intended to be an opaque identifier. It is currently
      * "human-readable" but no callers should assume it will stay as is and
      * it might be crypto-hashed at some point.
      */
     readonly attribute AUTF8String jarPrefix;
 
     /**
+     * A dictionary of the non-default origin attributes associated with this
+     * nsIPrincipal.
+     *
+     * Attributes are tokens that are taken into account when determining whether
+     * two principals are same-origin - if any attributes differ, the principals
+     * are cross-origin, even if the scheme, host, and port are the same.
+     * Attributes should also be considered for all security and bucketing decisions,
+     * even those which make non-standard comparisons (like cookies, which ignore
+     * scheme, or quotas, which ignore subdomains).
+     *
+     * If you're looking for an easy-to-use canonical stringification of the origin
+     * attributes, see |originSuffix| below.
+     */
+    [implicit_jscontext]
+    readonly attribute jsval originAttributes;
+
+    /**
+     * A string of the form !key1=value1&key2=value2, where each pair represents
+     * an attribute with a non-default value. If all attributes have default
+     * values, this is the empty string.
+     *
+     * The value of .originSuffix is automatically serialized into .origin, so any
+     * consumers using that are automatically origin-attribute-aware. Consumers with
+     * special requirements must inspect and compare .originSuffix manually.
+     *
+     * originsuffix are intended to be a replacement for jarPrefix, which will
+     * eventually be removed.
+     */
+    readonly attribute AUTF8String originSuffix;
+
+    /**
      * The base domain of the codebase URI to which this principal pertains
      * (generally the document URI), handling null principals and
      * non-hierarchical schemes correctly.
      */
     readonly attribute ACString baseDomain;
 
     const short APP_STATUS_NOT_INSTALLED = 0;
     const short APP_STATUS_INSTALLED     = 1;
new file mode 100644
--- /dev/null
+++ b/dom/webidl/SystemDictionaries.webidl
@@ -0,0 +1,22 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ *
+ * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
+ * liability, trademark and document use rules apply.
+ */
+
+/*
+ * Used by principals and the script security manager to represent origin
+ * attributes.
+ *
+ * IMPORTANT: If you add any members here, you need to update the
+ * CreateSuffix, Serialize, and Deserialize implementations in BasePrincipal,
+ * and bump the CIDs of all the principal implementations that invoke those
+ * methods.
+ */
+dictionary OriginAttributesDictionary {
+  unsigned long appId = 0;
+  boolean inBrowser = false;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -504,16 +504,17 @@ WEBIDL_FILES = [
     'SVGTransformList.webidl',
     'SVGTSpanElement.webidl',
     'SVGUnitTypes.webidl',
     'SVGURIReference.webidl',
     'SVGUseElement.webidl',
     'SVGViewElement.webidl',
     'SVGZoomAndPan.webidl',
     'SVGZoomEvent.webidl',
+    'SystemDictionaries.webidl',
     'Telephony.webidl',
     'TelephonyCall.webidl',
     'TelephonyCallGroup.webidl',
     'TelephonyCallId.webidl',
     'Text.webidl',
     'TextDecoder.webidl',
     'TextEncoder.webidl',
     'TextTrack.webidl',