Bug 1526615 - Part 2: Implement GenerateOriginKey2 which can be used on any thread; r=asuth
authorJan Varga <jan.varga@gmail.com>
Sun, 10 Feb 2019 10:20:12 +0100
changeset 520812 84806a3e88b92aadc6570f59dbe12139f77d92c6
parent 520811 16bf9fd6ae4b46f20bd40e6aaa793729e0b271d5
child 520813 29f06968e80af7188d89768f48fe02daec45f5b4
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1526615
milestone67.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 1526615 - Part 2: Implement GenerateOriginKey2 which can be used on any thread; r=asuth Differential Revision: https://phabricator.services.mozilla.com/D19283
dom/localstorage/LocalStorageCommon.cpp
dom/localstorage/LocalStorageCommon.h
dom/localstorage/moz.build
dom/localstorage/test/gtest/TestLocalStorage.cpp
dom/localstorage/test/gtest/moz.build
dom/storage/StorageUtils.h
--- a/dom/localstorage/LocalStorageCommon.cpp
+++ b/dom/localstorage/LocalStorageCommon.cpp
@@ -1,19 +1,23 @@
 /* -*- 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 "LocalStorageCommon.h"
 
+#include "mozilla/net/MozURL.h"
+
 namespace mozilla {
 namespace dom {
 
+using namespace mozilla::net;
+
 namespace {
 
 Atomic<int32_t> gNextGenLocalStorageEnabled(-1);
 
 }  // namespace
 
 const char16_t* kLocalStorageType = u"localStorage";
 
@@ -29,10 +33,92 @@ bool NextGenLocalStorageEnabled() {
 }
 
 bool CachedNextGenLocalStorageEnabled() {
   MOZ_ASSERT(gNextGenLocalStorageEnabled != -1);
 
   return !!gNextGenLocalStorageEnabled;
 }
 
+nsresult GenerateOriginKey2(const PrincipalInfo& aPrincipalInfo,
+                            nsACString& aOriginAttrSuffix,
+                            nsACString& aOriginKey) {
+  OriginAttributes attrs;
+  nsCString spec;
+
+  switch (aPrincipalInfo.type()) {
+    case PrincipalInfo::TNullPrincipalInfo: {
+      const NullPrincipalInfo& info = aPrincipalInfo.get_NullPrincipalInfo();
+
+      attrs = info.attrs();
+      spec = info.spec();
+
+      break;
+    }
+
+    case PrincipalInfo::TContentPrincipalInfo: {
+      const ContentPrincipalInfo& info =
+          aPrincipalInfo.get_ContentPrincipalInfo();
+
+      attrs = info.attrs();
+      spec = info.spec();
+
+      break;
+    }
+
+    default: {
+      spec.SetIsVoid(true);
+
+      break;
+    }
+  }
+
+  if (spec.IsVoid()) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  attrs.CreateSuffix(aOriginAttrSuffix);
+
+  RefPtr<MozURL> specURL;
+  nsresult rv = MozURL::Init(getter_AddRefs(specURL), spec);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCString host(specURL->Host());
+  uint32_t length = host.Length();
+  if (length > 0 && host.CharAt(0) == '[' && host.CharAt(length - 1) == ']') {
+    host = Substring(host, 1, length - 2);
+  }
+
+  nsCString domainOrigin(host);
+
+  if (domainOrigin.IsEmpty()) {
+    // For the file:/// protocol use the exact directory as domain.
+    if (specURL->Scheme().EqualsLiteral("file")) {
+      domainOrigin.Assign(specURL->Directory());
+    }
+  }
+
+  // Append reversed domain
+  nsAutoCString reverseDomain;
+  rv = CreateReversedDomain(domainOrigin, reverseDomain);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  aOriginKey.Append(reverseDomain);
+
+  // Append scheme
+  aOriginKey.Append(':');
+  aOriginKey.Append(specURL->Scheme());
+
+  // Append port if any
+  int32_t port = specURL->RealPort();
+  if (port != -1) {
+    aOriginKey.Append(nsPrintfCString(":%d", port));
+  }
+
+  return NS_OK;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/localstorage/LocalStorageCommon.h
+++ b/dom/localstorage/LocalStorageCommon.h
@@ -176,17 +176,27 @@
  * ~~~~~~~~~~~~~~~~~~~~~
  *
  * The local storage manager exposes some of the features that need to be
  * available only in the chrome code or tests. The manager is represented by
  * the "LocalStorageManager2" class that implements the "nsIDOMStorageManager"
  * interface.
  */
 
+#include "mozilla/Attributes.h"
+#include "nsString.h"
+
 namespace mozilla {
+
+namespace ipc {
+
+class PrincipalInfo;
+
+}  // namespace ipc
+
 namespace dom {
 
 extern const char16_t* kLocalStorageType;
 
 /**
  * Convenience data-structure to make it easier to track whether a value has
  * changed and what its previous value was for notification purposes.  Instances
  * are created on the stack by LSObject and passed to LSDatabase which in turn
@@ -215,12 +225,16 @@ class MOZ_STACK_CLASS LSNotifyInfo {
  */
 bool NextGenLocalStorageEnabled();
 
 /**
  * Cached any-thread version of NextGenLocalStorageEnabled().
  */
 bool CachedNextGenLocalStorageEnabled();
 
+nsresult GenerateOriginKey2(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
+                            nsACString& aOriginAttrSuffix,
+                            nsACString& aOriginKey);
+
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_localstorage_LocalStorageCommon_h
--- a/dom/localstorage/moz.build
+++ b/dom/localstorage/moz.build
@@ -7,16 +7,18 @@
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/xpcshell.ini'
 ]
 
 TEST_HARNESS_FILES.xpcshell.dom.localstorage.test.unit += [
     'test/unit/databaseShadowing-shared.js',
 ]
 
+TEST_DIRS += ['test/gtest']
+
 XPIDL_SOURCES += [
     'nsILocalStorageManager.idl',
 ]
 
 XPIDL_MODULE = 'dom_localstorage'
 
 EXPORTS.mozilla.dom.localstorage += [
     'ActorsParent.h',
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/test/gtest/TestLocalStorage.cpp
@@ -0,0 +1,113 @@
+/* -*- 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 "gtest/gtest.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/dom/LocalStorageCommon.h"
+#include "mozilla/dom/StorageUtils.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIURI.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::StorageUtils;
+using namespace mozilla::ipc;
+
+namespace {
+
+struct OriginKeyTest {
+  const char* mSpec;
+  const char* mOriginKey;
+};
+
+already_AddRefed<nsIPrincipal> GetCodebasePrincipal(const char* aSpec) {
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aSpec));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  OriginAttributes attrs;
+
+  nsCOMPtr<nsIPrincipal> principal =
+      BasePrincipal::CreateCodebasePrincipal(uri, attrs);
+
+  return principal.forget();
+}
+
+void CheckGeneratedOriginKey(nsIPrincipal* aPrincipal, const char* aOriginKey) {
+  nsCString originAttrSuffix;
+  nsCString originKey;
+  nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
+  if (aOriginKey) {
+    ASSERT_EQ(rv, NS_OK) << "GenerateOriginKey should not fail";
+    EXPECT_TRUE(originKey == nsDependentCString(aOriginKey));
+  } else {
+    ASSERT_NE(rv, NS_OK) << "GenerateOriginKey should fail";
+  }
+
+  PrincipalInfo principalInfo;
+  rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+  ASSERT_EQ(rv, NS_OK) << "PrincipalToPrincipalInfo should not fail";
+
+  originKey.Truncate();
+  rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
+  if (aOriginKey) {
+    ASSERT_EQ(rv, NS_OK) << "GenerateOriginKey2 should not fail";
+    EXPECT_TRUE(originKey == nsDependentCString(aOriginKey));
+  } else {
+    ASSERT_NE(rv, NS_OK) << "GenerateOriginKey2 should fail";
+  }
+}
+
+}  // namespace
+
+TEST(LocalStorage, OriginKey) {
+  // Check the system principal.
+  nsCOMPtr<nsIScriptSecurityManager> secMan =
+      nsContentUtils::GetSecurityManager();
+  ASSERT_TRUE(secMan) << "GetSecurityManager() should not fail";
+
+  nsCOMPtr<nsIPrincipal> principal;
+  secMan->GetSystemPrincipal(getter_AddRefs(principal));
+  ASSERT_TRUE(principal) << "GetSystemPrincipal() should not fail";
+
+  CheckGeneratedOriginKey(principal, nullptr);
+
+  // Check the null principal.
+  principal = NullPrincipal::CreateWithoutOriginAttributes();
+  ASSERT_TRUE(principal) << "CreateWithoutOriginAttributes() should not fail";
+
+  CheckGeneratedOriginKey(principal, nullptr);
+
+  // Check content principals.
+  static const OriginKeyTest tests[] = {
+      {"http://www.mozilla.org", "gro.allizom.www.:http:80"},
+      {"https://www.mozilla.org", "gro.allizom.www.:https:443"},
+      {"http://www.mozilla.org:32400", "gro.allizom.www.:http:32400"},
+      {"file:///Users/Joe/Sites/", "/setiS/eoJ/sresU/.:file"},
+      {"file:///Users/Joe/Sites/#foo", "/setiS/eoJ/sresU/.:file"},
+      {"file:///Users/Joe/Sites/?foo", "/setiS/eoJ/sresU/.:file"},
+      {"file:///Users/Joe/Sites", "/eoJ/sresU/.:file"},
+      {"file:///Users/Joe/Sites#foo", "/eoJ/sresU/.:file"},
+      {"file:///Users/Joe/Sites?foo", "/eoJ/sresU/.:file"},
+      {"moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc/"
+       "_generated_background_page.html",
+       "cb0c762e20f1-1769-247e-de56-f8a11735.:moz-extension"},
+      {"http://[::1]:8/test.html", "1::.:http:8"},
+  };
+
+  for (const auto& test : tests) {
+    principal = GetCodebasePrincipal(test.mSpec);
+    ASSERT_TRUE(principal) << "GetCodebasePrincipal() should not fail";
+
+    CheckGeneratedOriginKey(principal, test.mOriginKey);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/localstorage/test/gtest/moz.build
@@ -0,0 +1,17 @@
+# -*- Mode: python; 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/.
+
+UNIFIED_SOURCES = [
+    'TestLocalStorage.cpp',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul-gtest'
+
+LOCAL_INCLUDES += [
+    '/dom/localstorage',
+]
--- a/dom/storage/StorageUtils.h
+++ b/dom/storage/StorageUtils.h
@@ -2,16 +2,18 @@
 /* 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/. */
 
 #ifndef mozilla_dom_StorageUtils_h
 #define mozilla_dom_StorageUtils_h
 
+#include "nsStringFwd.h"
+
 class nsIPrincipal;
 
 namespace mozilla {
 namespace dom {
 namespace StorageUtils {
 
 nsresult GenerateOriginKey(nsIPrincipal* aPrincipal,
                            nsACString& aOriginAttrSuffix,