Bug 1525245 - Stabilize cookiePolicy/cookiePermission for live documents - part 3 - LocalStorage and SessionStorage, r=asuth

Differential Revision: https://phabricator.services.mozilla.com/D18951

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Andrea Marchesini 2019-03-06 17:00:54 +00:00
Родитель b56cfaae9b
Коммит 4762f2ad6c
11 изменённых файлов: 58 добавлений и 228 удалений

Просмотреть файл

@ -4265,6 +4265,48 @@ Storage* nsGlobalWindowInner::GetSessionStorage(ErrorResult& aError) {
return nullptr;
}
uint32_t rejectedReason = 0;
nsContentUtils::StorageAccess access =
nsContentUtils::StorageAllowedForWindow(this, &rejectedReason);
// SessionStorage is an ephemeral per-tab per-origin storage that only lives
// as long as the tab is open, although it may survive browser restarts
// thanks to the session store. So we interpret storage access differently
// than we would for persistent per-origin storage like LocalStorage and so
// it may be okay to provide SessionStorage even when we receive a value of
// eDeny.
//
// AntiTrackingCommon::IsFirstPartyStorageAccessGranted will return false
// for 3 main reasons.
//
// 1. Cookies are entirely blocked due to a per-origin permission
// (nsICookiePermission::ACCESS_DENY for the top-level principal or this
// window's principal) or the very broad BEHAVIOR_REJECT. This will return
// eDeny with a reason of STATE_COOKIES_BLOCKED_BY_PERMISSION or
// STATE_COOKIES_BLOCKED_ALL.
//
// 2. Third-party cookies are limited via BEHAVIOR_REJECT_FOREIGN and
// BEHAVIOR_LIMIT_FOREIGN and this is a third-party window. This will return
// eDeny with a reason of STATE_COOKIES_BLOCKED_FOREIGN.
//
// 3. Tracking protection (BEHAVIOR_REJECT_TRACKER) is in effect and
// IsThirdPartyTrackingResourceWindow() returned true and there wasn't a
// permission that allows it. This will return ePartitionedOrDeny with a
// reason of STATE_COOKIES_BLOCKED_TRACKER.
//
// In the 1st case, the user has explicitly indicated that they don't want
// to allow any storage to the origin or all origins and so we throw an
// error and deny access to SessionStorage. In the 2nd case, a legacy
// decision reasoned that there's no harm in providing SessionStorage
// because the information is not durable and cannot escape the current tab.
// The rationale is similar for the 3rd case.
if (access == nsContentUtils::StorageAccess::eDeny &&
rejectedReason !=
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN) {
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsresult rv;
nsCOMPtr<nsIDOMStorageManager> storageManager =

Просмотреть файл

@ -77,7 +77,6 @@ LocalStorageCache::LocalStorageCache(const nsACString* aOriginNoSuffix)
mLoadResult(NS_OK),
mInitialized(false),
mPersistent(false),
mSessionOnlyDataSetActive(false),
mPreloadTelemetryRecorded(false) {
MOZ_COUNT_CTOR(LocalStorageCache);
}
@ -183,29 +182,7 @@ const nsCString LocalStorageCache::Origin() const {
LocalStorageCache::Data& LocalStorageCache::DataSet(
const LocalStorage* aStorage) {
uint32_t index = GetDataSetIndex(aStorage);
if (index == kSessionSet && !mSessionOnlyDataSetActive) {
// Session only data set is demanded but not filled with
// current data set, copy to session only set now.
WaitForPreload(Telemetry::LOCALDOMSTORAGE_SESSIONONLY_PRELOAD_BLOCKING_MS);
Data& defaultSet = mData[kDefaultSet];
Data& sessionSet = mData[kSessionSet];
for (auto iter = defaultSet.mKeys.Iter(); !iter.Done(); iter.Next()) {
sessionSet.mKeys.Put(iter.Key(), iter.UserData());
}
mSessionOnlyDataSetActive = true;
// This updates sessionSet.mOriginQuotaUsage and also updates global usage
// for all session only data
ProcessUsageDelta(kSessionSet, defaultSet.mOriginQuotaUsage);
}
return mData[index];
return mData[GetDataSetIndex(aStorage)];
}
bool LocalStorageCache::ProcessUsageDelta(const LocalStorage* aStorage,
@ -541,7 +518,6 @@ void LocalStorageCache::UnloadItems(uint32_t aUnloadFlags) {
if (aUnloadFlags & kUnloadSession) {
mData[kSessionSet].mKeys.Clear();
ProcessUsageDelta(kSessionSet, -mData[kSessionSet].mOriginQuotaUsage);
mSessionOnlyDataSetActive = false;
}
#ifdef DOM_STORAGE_TESTS

Просмотреть файл

@ -267,12 +267,6 @@ class LocalStorageCache : public LocalStorageCacheBridge {
// default data set.)
bool mPersistent : 1;
// - False when the session-only data set was never used.
// - True after access to session-only data has been made for the first time.
// We also fill session-only data set with the default one at that moment.
// Drops back to false when session-only data are cleared from chrome.
bool mSessionOnlyDataSetActive : 1;
// Whether we have already captured state of the cache preload on our first
// access.
bool mPreloadTelemetryRecorded : 1;

Просмотреть файл

@ -164,11 +164,5 @@ bool SessionStorage::IsForkOf(const Storage* aOther) const {
return mCache == static_cast<const SessionStorage*>(aOther)->mCache;
}
bool SessionStorage::ShouldThrowWhenStorageAccessDenied(
uint32_t aRejectedReason) {
return aRejectedReason !=
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -65,8 +65,6 @@ class SessionStorage final : public Storage {
const nsAString& aOldValue,
const nsAString& aNewValue);
bool ShouldThrowWhenStorageAccessDenied(uint32_t aRejectedReason) override;
RefPtr<SessionStorageCache> mCache;
RefPtr<SessionStorageManager> mManager;

Просмотреть файл

@ -9,7 +9,7 @@
namespace mozilla {
namespace dom {
SessionStorageCache::SessionStorageCache() : mSessionDataSetActive(false) {}
SessionStorageCache::SessionStorageCache() = default;
SessionStorageCache::DataSet* SessionStorageCache::Set(
DataSetType aDataSetType) {
@ -19,16 +19,6 @@ SessionStorageCache::DataSet* SessionStorageCache::Set(
MOZ_ASSERT(aDataSetType == eSessionSetType);
if (!mSessionDataSetActive) {
mSessionSet.mOriginQuotaUsage = mDefaultSet.mOriginQuotaUsage;
for (auto iter = mDefaultSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
mSessionSet.mKeys.Put(iter.Key(), iter.Data());
}
mSessionDataSetActive = true;
}
return &mSessionSet;
}
@ -121,17 +111,11 @@ void SessionStorageCache::Clear(DataSetType aDataSetType,
DataSet* dataSet = Set(aDataSetType);
dataSet->ProcessUsageDelta(-dataSet->mOriginQuotaUsage);
dataSet->mKeys.Clear();
if (!aByUserInteraction && aDataSetType == eSessionSetType) {
mSessionDataSetActive = false;
}
}
already_AddRefed<SessionStorageCache> SessionStorageCache::Clone() const {
RefPtr<SessionStorageCache> cache = new SessionStorageCache();
cache->mSessionDataSetActive = mSessionDataSetActive;
cache->mDefaultSet.mOriginQuotaUsage = mDefaultSet.mOriginQuotaUsage;
for (auto iter = mDefaultSet.mKeys.ConstIter(); !iter.Done(); iter.Next()) {
cache->mDefaultSet.mKeys.Put(iter.Key(), iter.Data());

Просмотреть файл

@ -60,7 +60,6 @@ class SessionStorageCache final {
DataSet mDefaultSet;
DataSet mSessionSet;
bool mSessionDataSetActive;
};
} // namespace dom

Просмотреть файл

@ -29,6 +29,20 @@ NS_INTERFACE_MAP_END
Storage::Storage(nsPIDOMWindowInner* aWindow, nsIPrincipal* aPrincipal)
: mWindow(aWindow), mPrincipal(aPrincipal), mIsSessionOnly(false) {
MOZ_ASSERT(aPrincipal);
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
mIsSessionOnly = false;
} else if (mWindow) {
uint32_t rejectedReason = 0;
nsContentUtils::StorageAccess access =
nsContentUtils::StorageAllowedForWindow(mWindow, &rejectedReason);
MOZ_ASSERT(access != nsContentUtils::StorageAccess::eDeny ||
rejectedReason ==
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN);
mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
}
}
Storage::~Storage() {}
@ -39,28 +53,10 @@ bool Storage::StoragePrefIsEnabled() {
}
bool Storage::CanUseStorage(nsIPrincipal& aSubjectPrincipal) {
// This method is responsible for correct setting of mIsSessionOnly.
if (!StoragePrefIsEnabled()) {
return false;
}
if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
mIsSessionOnly = false;
} else if (mWindow) {
uint32_t rejectedReason = 0;
nsContentUtils::StorageAccess access =
nsContentUtils::StorageAllowedForWindow(mWindow, &rejectedReason);
// Note that we allow StorageAccess::ePartitionedOrDeny because we want
// tracker to have access to their sessionStorage.
if (access == nsContentUtils::StorageAccess::eDeny &&
ShouldThrowWhenStorageAccessDenied(rejectedReason)) {
return false;
}
mIsSessionOnly = access <= nsContentUtils::StorageAccess::eSessionScoped;
}
return aSubjectPrincipal.Subsumes(mPrincipal);
}

Просмотреть файл

@ -134,23 +134,10 @@ class Storage : public nsISupports, public nsWrapperCache {
virtual ~Storage();
// The method checks whether the caller can use a storage.
// CanUseStorage is called before any DOM initiated operation
// on a storage is about to happen and ensures that the storage's
// session-only flag is properly set according the current settings.
// It is an optimization since the privileges check and session only
// state determination are complex and share the code (comes hand in
// hand together).
bool CanUseStorage(nsIPrincipal& aSubjectPrincipal);
virtual void LastRelease() {}
// This method is called when StorageAccess is not granted for the owning
// window. aRejectedReason is one of the possible blocking states from
// nsIWebProgressListener.
virtual bool ShouldThrowWhenStorageAccessDenied(uint32_t aRejectedReason) {
return true;
}
private:
nsCOMPtr<nsPIDOMWindowInner> mWindow;
nsCOMPtr<nsIPrincipal> mPrincipal;

Просмотреть файл

@ -24,7 +24,6 @@ support-files =
[test_bug746272-2.html]
skip-if = os == "android" || verify # bug 962029
[test_cookieBlock.html]
[test_cookieSession.html]
[test_embededNulls.html]
[test_keySync.html]
[test_localStorageBase.html]

Просмотреть файл

@ -1,139 +0,0 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>cookie per-session only test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript">
/*
Set cookie access to be just per session and store to the localStorage.
Content stored must prevail only for session of the browser, so it must
be accessible in another window we try to access that key in the same
storage.
*/
function pushCookie(aPermission, aNext) {
SpecialPowers.pushPermissions([{'type': 'cookie', 'allow': aPermission, 'context': document}], aNext);
}
function test1() {
localStorage.setItem("persistent1", "persistent value 1");
localStorage.setItem("persistent2", "persistent value 2");
pushCookie(SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION, test1_b);
}
function test1_b() {
localStorage.setItem("session only", "session value");
parent.is(localStorage.getItem("session only"), "session value");
parent.is(localStorage.getItem("persistent1"), "persistent value 1");
parent.is(localStorage.getItem("persistent2"), "persistent value 2");
window.location.search = '?2';
}
function test2()
{
parent.is(localStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
parent.is(localStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
parent.is(localStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
localStorage.setItem("persistent1", "changed persistent value 1");
localStorage.removeItem("persistent2");
parent.is(localStorage.getItem("session only"), "session value", "Value present when cookies in session-only mode");
parent.is(localStorage.getItem("persistent1"), "changed persistent value 1", "Persistent value present");
parent.is(localStorage.getItem("persistent2"), null, "Persistent value removed");
// This clear has to delete only changes made in session only mode
localStorage.clear();
parent.is(localStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
parent.is(localStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
parent.is(localStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
localStorage.setItem("session only 2", "must be deleted on drop of session-only cookies permissions");
pushCookie(SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT, function() { window.location.search = '?3'; });
}
function test3() {
parent.is(localStorage.getItem("session only"), null, "No value when cookies are in default mode");
parent.is(localStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
parent.is(localStorage.getItem("persistent1"), "persistent value 1", "Persistent value present");
parent.is(localStorage.getItem("persistent2"), "persistent value 2", "Persistent value present");
pushCookie(SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION, function() { window.location.search = '?4'; });
}
function test4() {
parent.is(localStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
parent.is(localStorage.getItem("session only 2"), null, "Value not present when cookies in session-only mode after delete");
parent.is(localStorage.getItem("persistent1"), "persistent value 1", "Persistent value present again");
parent.is(localStorage.getItem("persistent2"), "persistent value 2", "Persistent value present again");
pushCookie(SpecialPowers.Ci.nsICookiePermission.ACCESS_DEFAULT, function() { window.location.search = '?5'; });
}
function test5() {
localStorage.clear();
parent.is(localStorage.getItem("session only"), null, "No value when cookies are in default mode");
parent.is(localStorage.getItem("persistent1"), null, "Persistent value not present after delete");
parent.is(localStorage.getItem("persistent2"), null, "Persistent value not present after delete");
pushCookie(SpecialPowers.Ci.nsICookiePermission.ACCESS_SESSION, function() { window.location.search = '?6'; });
}
function test6() {
parent.is(localStorage.getItem("session only"), null, "Value not present when cookies in session-only mode after delete");
parent.is(localStorage.getItem("session only 2"), null, "No value when cookies are in default mode");
parent.is(localStorage.getItem("persistent1"), null, "Persistent value not present in session only after delete");
parent.is(localStorage.getItem("persistent2"), null, "Persistent value not present in session only after delete");
parent.SimpleTest.finish();
}
function startTest() {
switch (location.search) {
case '?1':
test1();
break;
case '?2':
test2();
break;
case '?3':
test3();
break;
case '?4':
test4();
break;
case '?5':
test5();
break;
case '?6':
test6();
break;
default:
SimpleTest.waitForExplicitFinish();
if (SpecialPowers.Services.lsm.nextGenLocalStorageEnabled) {
ok(true, "Test ignored when the next gen local storage is enabled.");
SimpleTest.finish();
return;
}
var iframe = document.createElement('iframe');
iframe.src = 'test_cookieSession.html?1';
document.body.appendChild(iframe);
}
}
</script>
</head>
<body onload="startTest()">
</body>
</html>