зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
b56cfaae9b
Коммит
4762f2ad6c
|
@ -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>
|
Загрузка…
Ссылка в новой задаче