Merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Mihai Alexandru Michis 2019-05-06 12:49:43 +03:00
commit e7d9a87493
30 changed files with 1557 additions and 398 deletions

View File

@ -389,8 +389,7 @@ Context::QuotaInitRunnable::Run() {
MOZ_DIAGNOSTIC_ASSERT(qm);
nsresult rv = qm->EnsureOriginIsInitialized(
PERSISTENCE_TYPE_DEFAULT, mQuotaInfo.mSuffix, mQuotaInfo.mGroup,
mQuotaInfo.mOrigin,
/* aCreateIfNotExists */ true, getter_AddRefs(mQuotaInfo.mDir));
mQuotaInfo.mOrigin, getter_AddRefs(mQuotaInfo.mDir));
if (NS_FAILED(rv)) {
resolver->Resolve(rv);
break;

View File

@ -17159,8 +17159,7 @@ nsresult Maintenance::DirectoryWork() {
// Ensure origin is initialized first. It will initialize all origins
// for temporary storage including IDB origins.
rv = quotaManager->EnsureOriginIsInitialized(
persistenceType, suffix, group, origin,
/* aCreateIfNotExists */ true, getter_AddRefs(directory));
persistenceType, suffix, group, origin, getter_AddRefs(directory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -19869,8 +19868,7 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
nsCOMPtr<nsIFile> dbDirectory;
nsresult rv = quotaManager->EnsureOriginIsInitialized(
persistenceType, mSuffix, mGroup, mOrigin,
/* aCreateIfNotExists */ true, getter_AddRefs(dbDirectory));
persistenceType, mSuffix, mGroup, mOrigin, getter_AddRefs(dbDirectory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -70,9 +70,9 @@ mozilla::ipc::IPCResult LSDatabaseChild::RecvRequestAllowToClose() {
}
PBackgroundLSSnapshotChild* LSDatabaseChild::AllocPBackgroundLSSnapshotChild(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) {
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
MOZ_CRASH("PBackgroundLSSnapshotChild actor should be manually constructed!");
}
@ -126,7 +126,7 @@ void LSObserverChild::ActorDestroy(ActorDestroyReason aWhy) {
mozilla::ipc::IPCResult LSObserverChild::RecvObserve(
const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
const nsString& aDocumentURI, const nsString& aKey,
const nsString& aOldValue, const nsString& aNewValue) {
const LSValue& aOldValue, const LSValue& aNewValue) {
AssertIsOnOwningThread();
if (!mObserver) {
@ -140,8 +140,8 @@ mozilla::ipc::IPCResult LSObserverChild::RecvObserve(
return IPC_FAIL_NO_REASON(this);
}
Storage::NotifyChange(/* aStorage */ nullptr, principal, aKey, aOldValue,
aNewValue,
Storage::NotifyChange(/* aStorage */ nullptr, principal, aKey,
aOldValue.AsString(), aNewValue.AsString(),
/* aStorageType */ kLocalStorageType, aDocumentURI,
/* aIsPrivate */ !!aPrivateBrowsingId,
/* aImmediateDispatch */ true);

View File

@ -78,9 +78,9 @@ class LSDatabaseChild final : public PBackgroundLSDatabaseChild {
mozilla::ipc::IPCResult RecvRequestAllowToClose() override;
PBackgroundLSSnapshotChild* AllocPBackgroundLSSnapshotChild(
const nsString& aDocumentURI, const bool& aIncreasePeakUsage,
const int64_t& aRequestedSize, const int64_t& aMinSize,
LSSnapshotInitInfo* aInitInfo) override;
const nsString& aDocumentURI, const nsString& aKey,
const bool& aIncreasePeakUsage, const int64_t& aRequestedSize,
const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
bool DeallocPBackgroundLSSnapshotChild(
PBackgroundLSSnapshotChild* aActor) override;
@ -124,8 +124,8 @@ class LSObserverChild final : public PBackgroundLSObserverChild {
const uint32_t& aPrivateBrowsingId,
const nsString& aDocumentURI,
const nsString& aKey,
const nsString& aOldValue,
const nsString& aNewValue) override;
const LSValue& aOldValue,
const LSValue& aNewValue) override;
};
/**

File diff suppressed because it is too large Load Diff

View File

@ -120,7 +120,7 @@ nsresult LSDatabase::GetLength(LSObject* aObject, uint32_t* aResult) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -140,7 +140,7 @@ nsresult LSDatabase::GetKey(LSObject* aObject, uint32_t aIndex,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -160,7 +160,7 @@ nsresult LSDatabase::GetItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -179,7 +179,7 @@ nsresult LSDatabase::GetKeys(LSObject* aObject, nsTArray<nsString>& aKeys) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -200,7 +200,7 @@ nsresult LSDatabase::SetItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -220,7 +220,7 @@ nsresult LSDatabase::RemoveItem(LSObject* aObject, const nsAString& aKey,
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, aKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -239,7 +239,7 @@ nsresult LSDatabase::Clear(LSObject* aObject, LSNotifyInfo& aNotifyInfo) {
MOZ_ASSERT(mActor);
MOZ_ASSERT(!mAllowedToClose);
nsresult rv = EnsureSnapshot(aObject);
nsresult rv = EnsureSnapshot(aObject, VoidString());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -262,7 +262,7 @@ nsresult LSDatabase::BeginExplicitSnapshot(LSObject* aObject) {
return NS_ERROR_ALREADY_INITIALIZED;
}
nsresult rv = EnsureSnapshot(aObject, /* aExplicit */ true);
nsresult rv = EnsureSnapshot(aObject, VoidString(), /* aExplicit */ true);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -290,7 +290,8 @@ nsresult LSDatabase::EndExplicitSnapshot(LSObject* aObject) {
return NS_OK;
}
nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
bool aExplicit) {
MOZ_ASSERT(aObject);
MOZ_ASSERT(mActor);
MOZ_ASSERT_IF(mSnapshot, !aExplicit);
@ -306,7 +307,7 @@ nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
LSSnapshotInitInfo initInfo;
bool ok = mActor->SendPBackgroundLSSnapshotConstructor(
actor, aObject->DocumentURI(),
actor, aObject->DocumentURI(), nsString(aKey),
/* increasePeakUsage */ true,
/* requestedSize */ 131072,
/* minSize */ 4096, &initInfo);
@ -317,7 +318,7 @@ nsresult LSDatabase::EnsureSnapshot(LSObject* aObject, bool aExplicit) {
snapshot->SetActor(actor);
// This add refs snapshot.
nsresult rv = snapshot->Init(initInfo, aExplicit);
nsresult rv = snapshot->Init(aKey, initInfo, aExplicit);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -79,7 +79,8 @@ class LSDatabase final {
private:
~LSDatabase();
nsresult EnsureSnapshot(LSObject* aObject, bool aExplicit = false);
nsresult EnsureSnapshot(LSObject* aObject, const nsAString& aKey,
bool aExplicit = false);
void AllowToClose();
};

View File

@ -59,7 +59,8 @@ void LSSnapshot::SetActor(LSSnapshotChild* aActor) {
mActor = aActor;
}
nsresult LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
nsresult LSSnapshot::Init(const nsAString& aKey,
const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
AssertIsOnOwningThread();
MOZ_ASSERT(!mSelfRef);
MOZ_ASSERT(mActor);
@ -75,16 +76,19 @@ nsresult LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit) {
for (uint32_t i = 0; i < itemInfos.Length(); i++) {
const LSItemInfo& itemInfo = itemInfos[i];
const nsString& value = itemInfo.value();
const LSValue& value = itemInfo.value();
if (loadState != LoadState::AllOrderedItems && !value.IsVoid()) {
mLoadedItems.PutEntry(itemInfo.key());
}
mValues.Put(itemInfo.key(), value);
mValues.Put(itemInfo.key(), value.AsString());
}
if (loadState == LoadState::Partial) {
if (aInitInfo.addKeyToUnknownItems()) {
mUnknownItems.PutEntry(aKey);
}
mInitLength = aInitInfo.totalLength();
mLength = mInitLength;
} else if (loadState == LoadState::AllOrderedKeys) {
@ -239,8 +243,8 @@ nsresult LSSnapshot::SetItem(const nsAString& aKey, const nsAString& aValue,
LSSetItemInfo setItemInfo;
setItemInfo.key() = aKey;
setItemInfo.oldValue() = oldValue;
setItemInfo.value() = aValue;
setItemInfo.oldValue() = LSValue(oldValue);
setItemInfo.value() = LSValue(aValue);
mWriteInfos.AppendElement(std::move(setItemInfo));
}
@ -285,7 +289,7 @@ nsresult LSSnapshot::RemoveItem(const nsAString& aKey,
LSRemoveItemInfo removeItemInfo;
removeItemInfo.key() = aKey;
removeItemInfo.oldValue() = oldValue;
removeItemInfo.oldValue() = LSValue(oldValue);
mWriteInfos.AppendElement(std::move(removeItemInfo));
}
@ -432,12 +436,15 @@ nsresult LSSnapshot::GetItemInternal(const nsAString& aKey,
} else if (mLoadedItems.GetEntry(aKey) || mUnknownItems.GetEntry(aKey)) {
result.SetIsVoid(true);
} else {
LSValue value;
nsTArray<LSItemInfo> itemInfos;
if (NS_WARN_IF(!mActor->SendLoadValueAndMoreItems(
nsString(aKey), &result, &itemInfos))) {
nsString(aKey), &value, &itemInfos))) {
return NS_ERROR_FAILURE;
}
result = value.AsString();
if (result.IsVoid()) {
mUnknownItems.PutEntry(aKey);
} else {
@ -451,7 +458,7 @@ nsresult LSSnapshot::GetItemInternal(const nsAString& aKey,
const LSItemInfo& itemInfo = itemInfos[i];
mLoadedItems.PutEntry(itemInfo.key());
mValues.Put(itemInfo.key(), itemInfo.value());
mValues.Put(itemInfo.key(), itemInfo.value().AsString());
}
if (mLoadedItems.Count() == mInitLength) {
@ -477,12 +484,15 @@ nsresult LSSnapshot::GetItemInternal(const nsAString& aKey,
case LoadState::AllOrderedKeys: {
if (mValues.Get(aKey, &result)) {
if (result.IsVoid()) {
LSValue value;
nsTArray<LSItemInfo> itemInfos;
if (NS_WARN_IF(!mActor->SendLoadValueAndMoreItems(
nsString(aKey), &result, &itemInfos))) {
nsString(aKey), &value, &itemInfos))) {
return NS_ERROR_FAILURE;
}
result = value.AsString();
MOZ_ASSERT(!result.IsVoid());
mLoadedItems.PutEntry(aKey);
@ -494,7 +504,7 @@ nsresult LSSnapshot::GetItemInternal(const nsAString& aKey,
const LSItemInfo& itemInfo = itemInfos[i];
mLoadedItems.PutEntry(itemInfo.key());
mValues.Put(itemInfo.key(), itemInfo.value());
mValues.Put(itemInfo.key(), itemInfo.value().AsString());
}
if (mLoadedItems.Count() == mInitLength) {

View File

@ -7,6 +7,8 @@
#ifndef mozilla_dom_localstorage_LSSnapshot_h
#define mozilla_dom_localstorage_LSSnapshot_h
#include "LSValue.h"
namespace mozilla {
namespace dom {
@ -112,7 +114,8 @@ class LSSnapshot final : public nsIRunnable {
bool Explicit() const { return mExplicit; }
nsresult Init(const LSSnapshotInitInfo& aInitInfo, bool aExplicit);
nsresult Init(const nsAString& aKey, const LSSnapshotInitInfo& aInitInfo,
bool aExplicit);
nsresult GetLength(uint32_t* aResult);

View File

@ -0,0 +1,19 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "LSValue.h"
namespace mozilla {
namespace dom {
const LSValue& VoidLSValue() {
static const LSValue sVoidLSValue(VoidCString(), 0, false);
return sVoidLSValue;
}
} // namespace dom
} // namespace mozilla

123
dom/localstorage/LSValue.h Normal file
View File

@ -0,0 +1,123 @@
/* -*- 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/. */
#ifndef mozilla_dom_localstorage_LSValue_h
#define mozilla_dom_localstorage_LSValue_h
#include "SnappyUtils.h"
namespace mozilla {
namespace dom {
/**
* Represents a LocalStorage value. From content's perspective, values (if
* present) are always DOMStrings. This is also true from a quota-tracking
* perspective. However, for memory and disk efficiency it's preferable to store
* the value in alternate compressed or utf-8 encoding representations. The
* LSValue type exists to support these alternate representations, dynamically
* decompressing/re-encoding to utf-16 while still tracking value size on a
* utf-16 basis for quota purposes.
*/
class LSValue final {
friend struct IPC::ParamTraits<LSValue>;
nsCString mBuffer;
uint32_t mUTF16Length;
bool mCompressed;
public:
LSValue() : mUTF16Length(0), mCompressed(false) {}
explicit LSValue(const nsACString& aBuffer, uint32_t aUTF16Length,
bool aCompressed)
: mBuffer(aBuffer),
mUTF16Length(aUTF16Length),
mCompressed(aCompressed) {}
explicit LSValue(const nsAString& aBuffer) : mUTF16Length(aBuffer.Length()) {
if (aBuffer.IsVoid()) {
mBuffer.SetIsVoid(true);
mCompressed = false;
} else {
CopyUTF16toUTF8(aBuffer, mBuffer);
nsCString buffer;
if ((mCompressed = SnappyCompress(mBuffer, buffer))) {
mBuffer = buffer;
}
}
}
bool IsVoid() const { return mBuffer.IsVoid(); }
void SetIsVoid(bool aVal) { mBuffer.SetIsVoid(aVal); }
/**
* This represents the "physical" length that the parent process uses for
* the size of value/item computation. This can also be used to see how much
* memory the value is using at rest or what the cost is for sending the value
* over IPC.
*/
uint32_t Length() const { return mBuffer.Length(); }
/*
* This represents the "logical" length that content sees and that is also
* used for quota management purposes.
*/
uint32_t UTF16Length() const { return mUTF16Length; }
bool IsCompressed() const { return mCompressed; }
bool Equals(const LSValue& aOther) const {
return mBuffer == aOther.mBuffer &&
mBuffer.IsVoid() == aOther.mBuffer.IsVoid() &&
mUTF16Length == aOther.mUTF16Length &&
mCompressed == aOther.mCompressed;
}
bool operator==(const LSValue& aOther) const { return Equals(aOther); }
bool operator!=(const LSValue& aOther) const { return !Equals(aOther); }
operator const nsCString&() const { return mBuffer; }
operator Span<const char>() const { return mBuffer; }
class Converter {
nsString mBuffer;
public:
explicit Converter(const LSValue& aValue) {
if (aValue.mBuffer.IsVoid()) {
mBuffer.SetIsVoid(true);
} else if (aValue.mCompressed) {
nsCString buffer;
MOZ_ALWAYS_TRUE(SnappyUncompress(aValue.mBuffer, buffer));
CopyUTF8toUTF16(buffer, mBuffer);
} else {
CopyUTF8toUTF16(aValue.mBuffer, mBuffer);
}
}
Converter(Converter&& aOther) : mBuffer(aOther.mBuffer) {}
~Converter() {}
operator const nsString&() const { return mBuffer; }
private:
Converter() = delete;
Converter(const Converter&) = delete;
Converter& operator=(const Converter&) = delete;
Converter& operator=(const Converter&&) = delete;
};
Converter AsString() const { return Converter(const_cast<LSValue&>(*this)); }
};
const LSValue& VoidLSValue();
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_localstorage_LSValue_h

View File

@ -22,6 +22,14 @@ namespace dom {
*/
struct LSSnapshotInitInfo
{
/**
* Boolean indicating whether the `key` provided as an argument to the
* PBackgroundLSSnapshot constructor did not exist in the Datastore and should
* be treated as an unknown and therefore undefined value. Note that `key` may
* have been provided as a void string, in which case this value is forced to
* be false.
*/
bool addKeyToUnknownItems;
/**
* As many key/value or key/void pairs as the snapshot prefill byte budget
* allowed.
@ -99,11 +107,20 @@ parent:
* consult any other threads or perform any I/O. Additionally, the response
* is explicitly bounded in size by the tunable snapshot prefill byte limit.
*
* @param key
* If key is non-void, then the snapshot is being triggered by a direct
* access to a localStorage key (get, set, or removal, with set/removal
* requiring the old value in order to properly populate the "storage"
* event), the key being requested. It's possible the key is not present in
* localStorage, in which case LSSnapshotInitInfo::addKeyToUnknownItems will
* be true indicating that there is no such key/value pair, otherwise it
* will be false.
* @param increasePeakUsage
* Whether the parent should attempt to pre-allocate some amount of quota
* usage to the Snapshot.
*/
sync PBackgroundLSSnapshot(nsString documentURI,
nsString key,
bool increasePeakUsage,
int64_t requestedSize,
int64_t minSize)

View File

@ -6,6 +6,11 @@ include protocol PBackground;
include PBackgroundSharedTypes;
include "mozilla/dom/localstorage/SerializationHelpers.h";
using mozilla::dom::LSValue
from "mozilla/dom/LSValue.h";
namespace mozilla {
namespace dom {
@ -49,8 +54,8 @@ child:
uint32_t privateBrowsingId,
nsString documentURI,
nsString key,
nsString oldValue,
nsString newValue);
LSValue oldValue,
LSValue newValue);
};
} // namespace dom

View File

@ -5,6 +5,11 @@
include PBackgroundSharedTypes;
include ProtocolTypes;
include "mozilla/dom/localstorage/SerializationHelpers.h";
using mozilla::dom::LSValue
from "mozilla/dom/LSValue.h";
namespace mozilla {
namespace dom {
@ -57,7 +62,7 @@ union LSSimpleRequestParams
struct LSItemInfo
{
nsString key;
nsString value;
LSValue value;
};
} // namespace dom

View File

@ -7,20 +7,25 @@ include protocol PBackgroundLSDatabase;
include PBackgroundLSSharedTypes;
include "mozilla/dom/localstorage/SerializationHelpers.h";
using mozilla::dom::LSValue
from "mozilla/dom/LSValue.h";
namespace mozilla {
namespace dom {
struct LSSetItemInfo
{
nsString key;
nsString oldValue;
nsString value;
LSValue oldValue;
LSValue value;
};
struct LSRemoveItemInfo
{
nsString key;
nsString oldValue;
LSValue oldValue;
};
struct LSClearInfo
@ -61,7 +66,7 @@ parent:
* consult any other threads or perform any I/O.
*/
sync LoadValueAndMoreItems(nsString key)
returns (nsString value, LSItemInfo[] itemInfos);
returns (LSValue value, LSItemInfo[] itemInfos);
/**
* Invoked on demand to load all keys in in their canonical order if they

View File

@ -10,6 +10,7 @@
#include "ipc/IPCMessageUtils.h"
#include "mozilla/dom/LSSnapshot.h"
#include "mozilla/dom/LSValue.h"
namespace IPC {
@ -20,6 +21,30 @@ struct ParamTraits<mozilla::dom::LSSnapshot::LoadState>
mozilla::dom::LSSnapshot::LoadState::Initial,
mozilla::dom::LSSnapshot::LoadState::EndGuard> {};
template <>
struct ParamTraits<mozilla::dom::LSValue> {
typedef mozilla::dom::LSValue paramType;
static void Write(Message* aMsg, const paramType& aParam) {
WriteParam(aMsg, aParam.mBuffer);
WriteParam(aMsg, aParam.mUTF16Length);
WriteParam(aMsg, aParam.mCompressed);
}
static bool Read(const Message* aMsg, PickleIterator* aIter,
paramType* aResult) {
return ReadParam(aMsg, aIter, &aResult->mBuffer) &&
ReadParam(aMsg, aIter, &aResult->mUTF16Length) &&
ReadParam(aMsg, aIter, &aResult->mCompressed);
}
static void Log(const paramType& aParam, std::wstring* aLog) {
LogParam(aParam.mBuffer, aLog);
LogParam(aParam.mUTF16Length, aLog);
LogParam(aParam.mCompressed, aLog);
}
};
} // namespace IPC
#endif // mozilla_dom_localstorage_SerializationHelpers_h

View File

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=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 "SnappyUtils.h"
#include "snappy/snappy.h"
namespace mozilla {
namespace dom {
bool SnappyCompress(const nsACString& aSource, nsACString& aDest) {
MOZ_ASSERT(!aSource.IsVoid());
size_t uncompressedLength = aSource.Length();
if (uncompressedLength <= 16) {
return false;
}
size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
aDest.SetLength(compressedLength);
snappy::RawCompress(aSource.BeginReading(), uncompressedLength,
aDest.BeginWriting(), &compressedLength);
if (compressedLength >= uncompressedLength) {
return false;
}
aDest.SetLength(compressedLength);
return true;
}
bool SnappyUncompress(const nsACString& aSource, nsACString& aDest) {
MOZ_ASSERT(!aSource.IsVoid());
const char* compressed = aSource.BeginReading();
size_t compressedLength = aSource.Length();
size_t uncompressedLength;
if (!snappy::GetUncompressedLength(compressed, compressedLength,
&uncompressedLength)) {
return false;
}
aDest.SetLength(uncompressedLength);
if (!snappy::RawUncompress(compressed, compressedLength,
aDest.BeginWriting())) {
return false;
}
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,20 @@
/* -*- 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/. */
#ifndef mozilla_dom_localstorage_SnappyUtils_h
#define mozilla_dom_localstorage_SnappyUtils_h
namespace mozilla {
namespace dom {
bool SnappyCompress(const nsACString& aSource, nsACString& aDest);
bool SnappyUncompress(const nsACString& aSource, nsACString& aDest);
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_localstorage_SnappyUtils_h

View File

@ -34,6 +34,8 @@ EXPORTS.mozilla.dom += [
'LSObject.h',
'LSObserver.h',
'LSSnapshot.h',
'LSValue.h',
'SnappyUtils.h',
]
UNIFIED_SOURCES += [
@ -45,7 +47,9 @@ UNIFIED_SOURCES += [
'LSObject.cpp',
'LSObserver.cpp',
'LSSnapshot.cpp',
'LSValue.cpp',
'ReportInternalError.cpp',
'SnappyUtils.cpp',
]
IPDL_SOURCES += [

View File

@ -218,7 +218,7 @@ const char kResourceOriginPrefix[] = "resource://";
#define LS_ARCHIVE_FILE_NAME "ls-archive.sqlite"
#define LS_ARCHIVE_TMP_FILE_NAME "ls-archive-tmp.sqlite"
const uint32_t kLocalStorageArchiveVersion = 1;
const uint32_t kLocalStorageArchiveVersion = 3;
const char kProfileDoChangeTopic[] = "profile-do-change";
@ -446,7 +446,6 @@ nsresult LoadLocalStorageArchiveVersion(mozIStorageConnection* aConnection,
return NS_OK;
}
/*
nsresult SaveLocalStorageArchiveVersion(mozIStorageConnection* aConnection,
uint32_t aVersion) {
AssertIsOnIOThread();
@ -472,7 +471,6 @@ nsresult SaveLocalStorageArchiveVersion(mozIStorageConnection* aConnection,
return NS_OK;
}
*/
/******************************************************************************
* Quota manager class declarations
@ -615,10 +613,20 @@ class OriginInfo final {
public:
OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin, uint64_t aUsage,
int64_t aAccessTime, bool aPersisted);
int64_t aAccessTime, bool aPersisted, bool aDirectoryExists);
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginInfo)
GroupInfo* GetGroupInfo() const { return mGroupInfo; }
const nsCString& Origin() const { return mOrigin; }
int64_t LockedUsage() const {
AssertCurrentThreadOwnsQuotaMutex();
return mUsage;
}
int64_t LockedAccessTime() const {
AssertCurrentThreadOwnsQuotaMutex();
@ -656,6 +664,19 @@ class OriginInfo final {
uint64_t mUsage;
int64_t mAccessTime;
bool mPersisted;
/**
* In some special cases like the LocalStorage client where it's possible to
* create a Quota-using representation but not actually write any data, we
* want to be able to track quota for an origin without creating its origin
* directory or the per-client files until they are actually needed to store
* data. In those cases, the OriginInfo will be created by
* EnsureQuotaForOrigin and the resulting mDirectoryExists will be false until
* the origin actually needs to be created. It is possible for mUsage to be
* greater than zero while mDirectoryExists is false, representing a state
* where a client like LocalStorage has reserved quota for disk writes, but
* has not yet flushed the data to disk.
*/
bool mDirectoryExists;
};
class OriginInfoLRUComparator {
@ -691,6 +712,8 @@ class GroupInfo final {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GroupInfo)
PersistenceType GetPersistenceType() const { return mPersistenceType; }
private:
// Private destructor, to discourage deletion outside of Release():
~GroupInfo() { MOZ_COUNT_DTOR(GroupInfo); }
@ -1126,6 +1149,12 @@ class GetUsageOp final : public QuotaUsageRequestBase,
private:
~GetUsageOp() {}
void ProcessOriginInternal(QuotaManager* aQuotaManager,
const PersistenceType aPersistenceType,
const nsACString& aOrigin,
const int64_t aTimestamp, const bool aPersisted,
const uint64_t aUsage);
nsresult DoDirectoryWork(QuotaManager* aQuotaManager) override;
bool IsCanceled() override;
@ -2226,26 +2255,21 @@ nsresult CreateDirectoryMetadataFiles(nsIFile* aDirectory, bool aPersisted,
const nsACString& aSuffix,
const nsACString& aGroup,
const nsACString& aOrigin,
int64_t* aTimestamp) {
int64_t aTimestamp) {
AssertIsOnIOThread();
int64_t timestamp = PR_Now();
nsresult rv =
CreateDirectoryMetadata(aDirectory, timestamp, aSuffix, aGroup, aOrigin);
CreateDirectoryMetadata(aDirectory, aTimestamp, aSuffix, aGroup, aOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = CreateDirectoryMetadata2(aDirectory, timestamp, aPersisted, aSuffix,
rv = CreateDirectoryMetadata2(aDirectory, aTimestamp, aPersisted, aSuffix,
aGroup, aOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (aTimestamp) {
*aTimestamp = timestamp;
}
return NS_OK;
}
@ -3387,6 +3411,28 @@ uint64_t QuotaManager::CollectOriginsForEviction(
return 0;
}
template <typename P>
void QuotaManager::CollectPendingOriginsForListing(P aPredicate) {
MutexAutoLock lock(mQuotaMutex);
for (auto iter = mGroupInfoPairs.Iter(); !iter.Done(); iter.Next()) {
GroupInfoPair* pair = iter.UserData();
MOZ_ASSERT(!iter.Key().IsEmpty());
MOZ_ASSERT(pair);
RefPtr<GroupInfo> groupInfo =
pair->LockedGetGroupInfo(PERSISTENCE_TYPE_DEFAULT);
if (groupInfo) {
for (RefPtr<OriginInfo>& originInfo : groupInfo->mOriginInfos) {
if (!originInfo->mDirectoryExists) {
aPredicate(originInfo);
}
}
}
}
}
nsresult QuotaManager::Init(const nsAString& aBasePath) {
mBasePath = aBasePath;
@ -3523,24 +3569,66 @@ void QuotaManager::InitQuotaForOrigin(PersistenceType aPersistenceType,
MutexAutoLock lock(mQuotaMutex);
GroupInfoPair* pair;
if (!mGroupInfoPairs.Get(aGroup, &pair)) {
pair = new GroupInfoPair();
mGroupInfoPairs.Put(aGroup, pair);
// The hashtable is now responsible to delete the GroupInfoPair.
}
RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
if (!groupInfo) {
groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
}
RefPtr<GroupInfo> groupInfo =
LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
RefPtr<OriginInfo> originInfo =
new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime, aPersisted);
new OriginInfo(groupInfo, aOrigin, aUsageBytes, aAccessTime, aPersisted,
/* aDirectoryExists */ true);
groupInfo->LockedAddOriginInfo(originInfo);
}
void QuotaManager::EnsureQuotaForOrigin(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin) {
AssertIsOnIOThread();
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
MutexAutoLock lock(mQuotaMutex);
RefPtr<GroupInfo> groupInfo =
LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
if (!originInfo) {
originInfo = new OriginInfo(
groupInfo, aOrigin, /* aUsageBytes */ 0, /* aAccessTime */ PR_Now(),
/* aPersisted */ false, /* aDirectoryExists */ false);
groupInfo->LockedAddOriginInfo(originInfo);
}
}
void QuotaManager::NoteOriginDirectoryCreated(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin,
bool aPersisted,
int64_t& aTimestamp) {
AssertIsOnIOThread();
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
int64_t timestamp;
MutexAutoLock lock(mQuotaMutex);
RefPtr<GroupInfo> groupInfo =
LockedGetOrCreateGroupInfo(aPersistenceType, aGroup);
RefPtr<OriginInfo> originInfo = groupInfo->LockedGetOriginInfo(aOrigin);
if (originInfo) {
originInfo->mPersisted = aPersisted;
originInfo->mDirectoryExists = true;
timestamp = originInfo->LockedAccessTime();
} else {
timestamp = PR_Now();
RefPtr<OriginInfo> originInfo = new OriginInfo(
groupInfo, aOrigin, /* aUsageBytes */ 0, /* aAccessTime */ timestamp,
aPersisted, /* aDirectoryExists */ true);
groupInfo->LockedAddOriginInfo(originInfo);
}
aTimestamp = timestamp;
}
void QuotaManager::DecreaseUsageForOrigin(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin,
@ -5261,7 +5349,6 @@ nsresult QuotaManager::UpgradeLocalStorageArchiveFrom0To1(
return NS_OK;
}
/*
nsresult QuotaManager::UpgradeLocalStorageArchiveFrom1To2(
nsCOMPtr<mozIStorageConnection>& aConnection) {
nsresult rv = SaveLocalStorageArchiveVersion(aConnection, 2);
@ -5271,7 +5358,16 @@ nsresult QuotaManager::UpgradeLocalStorageArchiveFrom1To2(
return NS_OK;
}
*/
nsresult QuotaManager::UpgradeLocalStorageArchiveFrom2To3(
nsCOMPtr<mozIStorageConnection>& aConnection) {
nsresult rv = SaveLocalStorageArchiveVersion(aConnection, 3);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
#ifdef DEBUG
@ -5495,17 +5591,18 @@ nsresult QuotaManager::EnsureStorageIsInitialized() {
return rv;
}
} else {
static_assert(kLocalStorageArchiveVersion == 1,
static_assert(kLocalStorageArchiveVersion == 3,
"Upgrade function needed due to LocalStorage archive "
"version increase.");
while (version != kLocalStorageArchiveVersion) {
if (version == 0) {
rv = UpgradeLocalStorageArchiveFrom0To1(connection);
} /* else if (version == 1) {
} else if (version == 1) {
rv = UpgradeLocalStorageArchiveFrom1To2(connection);
} */
else {
} else if (version == 2) {
rv = UpgradeLocalStorageArchiveFrom2To3(connection);
} else {
QM_WARNING(
"Unable to initialize LocalStorage archive, no upgrade path is "
"available!");
@ -5622,19 +5719,15 @@ void QuotaManager::OpenDirectoryInternal(
nsresult QuotaManager::EnsureOriginIsInitialized(
PersistenceType aPersistenceType, const nsACString& aSuffix,
const nsACString& aGroup, const nsACString& aOrigin,
bool aCreateIfNotExists, nsIFile** aDirectory) {
const nsACString& aGroup, const nsACString& aOrigin, nsIFile** aDirectory) {
AssertIsOnIOThread();
MOZ_ASSERT(aDirectory);
nsCOMPtr<nsIFile> directory;
bool created;
nsresult rv = EnsureOriginIsInitializedInternal(
aPersistenceType, aSuffix, aGroup, aOrigin, aCreateIfNotExists,
getter_AddRefs(directory), &created);
if (rv == NS_ERROR_NOT_AVAILABLE) {
return rv;
}
aPersistenceType, aSuffix, aGroup, aOrigin, getter_AddRefs(directory),
&created);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -5645,8 +5738,8 @@ nsresult QuotaManager::EnsureOriginIsInitialized(
nsresult QuotaManager::EnsureOriginIsInitializedInternal(
PersistenceType aPersistenceType, const nsACString& aSuffix,
const nsACString& aGroup, const nsACString& aOrigin,
bool aCreateIfNotExists, nsIFile** aDirectory, bool* aCreated) {
const nsACString& aGroup, const nsACString& aOrigin, nsIFile** aDirectory,
bool* aCreated) {
AssertIsOnIOThread();
MOZ_ASSERT(aDirectory);
MOZ_ASSERT(aCreated);
@ -5674,10 +5767,7 @@ nsresult QuotaManager::EnsureOriginIsInitializedInternal(
}
bool created;
rv = EnsureOriginDirectory(directory, aCreateIfNotExists, &created);
if (rv == NS_ERROR_NOT_AVAILABLE) {
return rv;
}
rv = EnsureOriginDirectory(directory, &created);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -5685,9 +5775,11 @@ nsresult QuotaManager::EnsureOriginIsInitializedInternal(
int64_t timestamp;
if (aPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
if (created) {
timestamp = PR_Now();
rv = CreateDirectoryMetadataFiles(directory,
/* aPersisted */ true, aSuffix, aGroup,
aOrigin, &timestamp);
aOrigin, timestamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -5708,17 +5800,15 @@ nsresult QuotaManager::EnsureOriginIsInitializedInternal(
mInitializedOrigins.AppendElement(aOrigin);
} else if (created) {
NoteOriginDirectoryCreated(aPersistenceType, aGroup, aOrigin,
/* aPersisted */ false, timestamp);
rv = CreateDirectoryMetadataFiles(directory,
/* aPersisted */ false, aSuffix, aGroup,
aOrigin, &timestamp);
aOrigin, timestamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Don't need to traverse the directory, since it's empty.
InitQuotaForOrigin(aPersistenceType, aGroup, aOrigin,
/* aUsageBytes */ 0, timestamp,
/* aPersisted */ false);
}
directory.forget(aDirectory);
@ -5796,7 +5886,6 @@ nsresult QuotaManager::EnsureTemporaryStorageIsInitialized() {
}
nsresult QuotaManager::EnsureOriginDirectory(nsIFile* aDirectory,
bool aCreateIfNotExists,
bool* aCreated) {
AssertIsOnIOThread();
MOZ_ASSERT(aDirectory);
@ -5809,10 +5898,6 @@ nsresult QuotaManager::EnsureOriginDirectory(nsIFile* aDirectory,
}
if (!exists) {
if (!aCreateIfNotExists) {
return NS_ERROR_NOT_AVAILABLE;
}
nsString leafName;
rv = aDirectory->GetLeafName(leafName);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -6261,6 +6346,27 @@ void QuotaManager::LockedRemoveQuotaForOrigin(PersistenceType aPersistenceType,
}
}
already_AddRefed<GroupInfo> QuotaManager::LockedGetOrCreateGroupInfo(
PersistenceType aPersistenceType, const nsACString& aGroup) {
mQuotaMutex.AssertCurrentThreadOwns();
MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_PERSISTENT);
GroupInfoPair* pair;
if (!mGroupInfoPairs.Get(aGroup, &pair)) {
pair = new GroupInfoPair();
mGroupInfoPairs.Put(aGroup, pair);
// The hashtable is now responsible to delete the GroupInfoPair.
}
RefPtr<GroupInfo> groupInfo = pair->LockedGetGroupInfo(aPersistenceType);
if (!groupInfo) {
groupInfo = new GroupInfo(pair, aPersistenceType, aGroup);
pair->LockedSetGroupInfo(aPersistenceType, groupInfo);
}
return groupInfo.forget();
}
already_AddRefed<OriginInfo> QuotaManager::LockedGetOriginInfo(
PersistenceType aPersistenceType, const nsACString& aGroup,
const nsACString& aOrigin) {
@ -6516,12 +6622,14 @@ bool QuotaManager::IsSanitizedOriginValid(const nsACString& aSanitizedOrigin) {
******************************************************************************/
OriginInfo::OriginInfo(GroupInfo* aGroupInfo, const nsACString& aOrigin,
uint64_t aUsage, int64_t aAccessTime, bool aPersisted)
uint64_t aUsage, int64_t aAccessTime, bool aPersisted,
bool aDirectoryExists)
: mGroupInfo(aGroupInfo),
mOrigin(aOrigin),
mUsage(aUsage),
mAccessTime(aAccessTime),
mPersisted(aPersisted) {
mPersisted(aPersisted),
mDirectoryExists(aDirectoryExists) {
MOZ_ASSERT(aGroupInfo);
MOZ_ASSERT_IF(aPersisted,
aGroupInfo->mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
@ -7624,6 +7732,47 @@ GetUsageOp::GetUsageOp(const UsageRequestParams& aParams)
MOZ_ASSERT(aParams.type() == UsageRequestParams::TAllUsageParams);
}
void GetUsageOp::ProcessOriginInternal(QuotaManager* aQuotaManager,
const PersistenceType aPersistenceType,
const nsACString& aOrigin,
const int64_t aTimestamp,
const bool aPersisted,
const uint64_t aUsage) {
if (!mGetAll && aQuotaManager->IsOriginInternal(aOrigin)) {
return;
}
OriginUsage* originUsage;
// We can't store pointers to OriginUsage objects in the hashtable
// since AppendElement() reallocates its internal array buffer as number
// of elements grows.
uint32_t index;
if (mOriginUsagesIndex.Get(aOrigin, &index)) {
originUsage = &mOriginUsages[index];
} else {
index = mOriginUsages.Length();
originUsage = mOriginUsages.AppendElement();
originUsage->origin() = aOrigin;
originUsage->persisted() = false;
originUsage->usage() = 0;
originUsage->lastAccessed() = 0;
mOriginUsagesIndex.Put(aOrigin, index);
}
if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
originUsage->persisted() = aPersisted;
}
originUsage->usage() = originUsage->usage() + aUsage;
originUsage->lastAccessed() =
std::max<int64_t>(originUsage->lastAccessed(), aTimestamp);
}
bool GetUsageOp::IsCanceled() {
AssertIsOnIOThread();
@ -7648,36 +7797,6 @@ nsresult GetUsageOp::ProcessOrigin(QuotaManager* aQuotaManager,
return rv;
}
if (!mGetAll && aQuotaManager->IsOriginInternal(origin)) {
return NS_OK;
}
OriginUsage* originUsage;
// We can't store pointers to OriginUsage objects in the hashtable
// since AppendElement() reallocates its internal array buffer as number
// of elements grows.
uint32_t index;
if (mOriginUsagesIndex.Get(origin, &index)) {
originUsage = &mOriginUsages[index];
} else {
index = mOriginUsages.Length();
originUsage = mOriginUsages.AppendElement();
originUsage->origin() = origin;
originUsage->persisted() = false;
originUsage->usage() = 0;
mOriginUsagesIndex.Put(origin, index);
}
if (aPersistenceType == PERSISTENCE_TYPE_DEFAULT) {
originUsage->persisted() = persisted;
}
originUsage->lastAccessed() = timestamp;
UsageInfo usageInfo;
rv = GetUsageForOrigin(aQuotaManager, aPersistenceType, group, origin,
&usageInfo);
@ -7685,7 +7804,8 @@ nsresult GetUsageOp::ProcessOrigin(QuotaManager* aQuotaManager,
return rv;
}
originUsage->usage() = originUsage->usage() + usageInfo.TotalUsage();
ProcessOriginInternal(aQuotaManager, aPersistenceType, origin, timestamp,
persisted, usageInfo.TotalUsage());
return NS_OK;
}
@ -7704,6 +7824,18 @@ nsresult GetUsageOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
}
}
// TraverseRepository above only consulted the filesystem. We also need to
// consider origins which may have pending quota usage, such as buffered
// LocalStorage writes for an origin which didn't previously have any
// LocalStorage data.
aQuotaManager->CollectPendingOriginsForListing([&](OriginInfo* aOriginInfo) {
ProcessOriginInternal(
aQuotaManager, aOriginInfo->GetGroupInfo()->GetPersistenceType(),
aOriginInfo->Origin(), aOriginInfo->LockedAccessTime(),
aOriginInfo->LockedPersisted(), aOriginInfo->LockedUsage());
});
return NS_OK;
}
@ -7919,7 +8051,7 @@ nsresult InitOriginOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
bool created;
nsresult rv = aQuotaManager->EnsureOriginIsInitializedInternal(
mPersistenceType.Value(), mSuffix, mGroup, mOriginScope.GetOrigin(),
/* aCreateIfNotExists */ true, getter_AddRefs(directory), &created);
getter_AddRefs(directory), &created);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -8432,29 +8564,29 @@ nsresult PersistOp::DoDirectoryWork(QuotaManager* aQuotaManager) {
}
bool created;
rv = aQuotaManager->EnsureOriginDirectory(directory,
/* aCreateIfNotExists */ true,
&created);
rv = aQuotaManager->EnsureOriginDirectory(directory, &created);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (created) {
int64_t timestamp;
rv = CreateDirectoryMetadataFiles(directory,
/* aPersisted */ true, mSuffix, mGroup,
mOriginScope.GetOrigin(), &timestamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// Directory metadata has been successfully created.
// Origin directory has been successfully created.
// Create OriginInfo too if temporary storage was already initialized.
if (aQuotaManager->IsTemporaryStorageInitialized()) {
aQuotaManager->InitQuotaForOrigin(mPersistenceType.Value(), mGroup,
mOriginScope.GetOrigin(),
/* aUsageBytes */ 0, timestamp,
/* aPersisted */ true);
aQuotaManager->NoteOriginDirectoryCreated(
mPersistenceType.Value(), mGroup, mOriginScope.GetOrigin(),
/* aPersisted */ true, timestamp);
} else {
timestamp = PR_Now();
}
rv = CreateDirectoryMetadataFiles(directory,
/* aPersisted */ true, mSuffix, mGroup,
mOriginScope.GetOrigin(), timestamp);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
// Get the persisted flag (restore the metadata file if necessary).
@ -8549,6 +8681,14 @@ nsresult ListInitializedOriginsOp::DoDirectoryWork(
}
}
// TraverseRepository above only consulted the file-system to get a list of
// known origins, but we also need to include origins that have pending quota
// usage.
aQuotaManager->CollectPendingOriginsForListing([&](OriginInfo* aOriginInfo) {
mOrigins.AppendElement(aOriginInfo->Origin());
});
return NS_OK;
}

View File

@ -145,11 +145,41 @@ class QuotaManager final : public BackgroundThreadObject {
return mTemporaryStorageInitialized;
}
/**
* For initialization of an origin where the directory already exists. This is
* used by EnsureTemporaryStorageIsInitialized/InitializeRepository once it
* has tallied origin usage by calling each of the QuotaClient InitOrigin
* methods.
*/
void InitQuotaForOrigin(PersistenceType aPersistenceType,
const nsACString& aGroup, const nsACString& aOrigin,
uint64_t aUsageBytes, int64_t aAccessTime,
bool aPersisted);
/**
* For use in special-cases like LSNG where we need to be able to know that
* there is no data stored for an origin. LSNG knows that there is 0 usage for
* its storage of an origin and wants to make sure there is a QuotaObject
* tracking this. This method will create a non-persisted, 0-usage,
* mDirectoryExists=false OriginInfo if there isn't already an OriginInfo. If
* an OriginInfo already exists, it will be left as-is, because that implies a
* different client has usages for the origin (and there's no need to add
* LSNG's 0 usage to the QuotaObject).
*/
void EnsureQuotaForOrigin(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin);
/**
* For use when creating an origin directory. It's possible that origin usage
* is already being tracked due to a call to EnsureQuotaForOrigin, and in that
* case we need to update the existing OriginInfo rather than create a new one.
*/
void NoteOriginDirectoryCreated(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin, bool aPersisted,
int64_t& aTimestamp);
void DecreaseUsageForOrigin(PersistenceType aPersistenceType,
const nsACString& aGroup,
const nsACString& aOrigin, int64_t aSize);
@ -248,6 +278,19 @@ class QuotaManager final : public BackgroundThreadObject {
uint64_t CollectOriginsForEviction(
uint64_t aMinSizeToBeFreed, nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
/**
* Helper method to invoke the provided predicate on all "pending" OriginInfo
* instances. These are origins for which the origin directory has not yet
* been created but for which quota is already being tracked. This happens,
* for example, for the LocalStorage client where an origin that previously
* was not using LocalStorage can start issuing writes which it buffers until
* eventually flushing them. We defer creating the origin directory for as
* long as possible in that case, so the directory won't exist. Logic that
* would otherwise only consult the filesystem also needs to use this method.
*/
template <typename P>
void CollectPendingOriginsForListing(P aPredicate);
void AssertStorageIsInitialized() const
#ifdef DEBUG
;
@ -262,18 +305,18 @@ class QuotaManager final : public BackgroundThreadObject {
const nsACString& aSuffix,
const nsACString& aGroup,
const nsACString& aOrigin,
bool aCreateIfNotExists,
nsIFile** aDirectory);
nsresult EnsureOriginIsInitializedInternal(
PersistenceType aPersistenceType, const nsACString& aSuffix,
const nsACString& aGroup, const nsACString& aOrigin,
bool aCreateIfNotExists, nsIFile** aDirectory, bool* aCreated);
nsresult EnsureOriginIsInitializedInternal(PersistenceType aPersistenceType,
const nsACString& aSuffix,
const nsACString& aGroup,
const nsACString& aOrigin,
nsIFile** aDirectory,
bool* aCreated);
nsresult EnsureTemporaryStorageIsInitialized();
nsresult EnsureOriginDirectory(nsIFile* aDirectory, bool aCreateIfNotExists,
bool* aCreated);
nsresult EnsureOriginDirectory(nsIFile* aDirectory, bool* aCreated);
nsresult AboutToClearOrigins(
const Nullable<PersistenceType>& aPersistenceType,
@ -399,6 +442,9 @@ class QuotaManager final : public BackgroundThreadObject {
const nsACString& aGroup,
const nsACString& aOrigin);
already_AddRefed<GroupInfo> LockedGetOrCreateGroupInfo(
PersistenceType aPersistenceType, const nsACString& aGroup);
already_AddRefed<OriginInfo> LockedGetOriginInfo(
PersistenceType aPersistenceType, const nsACString& aGroup,
const nsACString& aOrigin);
@ -440,10 +486,11 @@ class QuotaManager final : public BackgroundThreadObject {
nsresult UpgradeLocalStorageArchiveFrom0To1(
nsCOMPtr<mozIStorageConnection>& aConnection);
/*
nsresult UpgradeLocalStorageArchiveFrom1To2(
nsCOMPtr<mozIStorageConnection>& aConnection);
*/
nsresult UpgradeLocalStorageArchiveFrom1To2(
nsCOMPtr<mozIStorageConnection>& aConnection);
nsresult UpgradeLocalStorageArchiveFrom2To3(
nsCOMPtr<mozIStorageConnection>& aConnection);
nsresult InitializeRepository(PersistenceType aPersistenceType);

View File

@ -0,0 +1,65 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* This test is mainly to verify that local storage directories are not removed
* during local storage archive upgrade from version 1 to version 2.
* See bug 1546310.
*/
async function testSteps() {
const lsDirs = [
"storage/default/http+++example.com/ls",
"storage/default/http+++localhost/ls",
"storage/default/http+++www.mozilla.org/ls",
];
info("Clearing");
let request = clear();
await requestFinished(request);
info("Installing package");
// The profile contains three initialized origin directories with local
// storage data, local storage archive, a script for origin initialization,
// the storage database and the web apps store database:
// - storage/default/https+++example.com
// - storage/default/https+++localhost
// - storage/default/https+++www.mozilla.org
// - storage/ls-archive.sqlite
// - create_db.js
// - storage.sqlite
// - webappsstore.sqlite
// The file create_db.js in the package was run locally (with a build with
// local storage archive version 1), specifically it was temporarily added to
// xpcshell.ini and then executed:
// mach xpcshell-test --interactive dom/localstorage/test/unit/create_db.js
// Note: to make it become the profile in the test, additional manual steps
// are needed.
// 1. Remove the folder "storage/temporary".
installPackage("localStorageArchive2upgrade_profile");
info("Checking ls dirs");
for (let lsDir of lsDirs) {
let dir = getRelativeFile(lsDir);
exists = dir.exists();
ok(exists, "ls directory does exist");
}
request = init();
request = await requestFinished(request);
info("Checking ls dirs");
for (let lsDir of lsDirs) {
let dir = getRelativeFile(lsDir);
exists = dir.exists();
ok(exists, "ls directory does exist");
}
}

View File

@ -0,0 +1,65 @@
/**
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* This test is mainly to verify that local storage directories are not removed
* during local storage archive upgrade from version 2 to version 3.
* See bug 1513937.
*/
async function testSteps() {
const lsDirs = [
"storage/default/http+++example.com/ls",
"storage/default/http+++localhost/ls",
"storage/default/http+++www.mozilla.org/ls",
];
info("Clearing");
let request = clear();
await requestFinished(request);
info("Installing package");
// The profile contains three initialized origin directories with local
// storage data, local storage archive, a script for origin initialization,
// the storage database and the web apps store database:
// - storage/default/https+++example.com
// - storage/default/https+++localhost
// - storage/default/https+++www.mozilla.org
// - storage/ls-archive.sqlite
// - create_db.js
// - storage.sqlite
// - webappsstore.sqlite
// The file create_db.js in the package was run locally (with a build with
// local storage archive version 2), specifically it was temporarily added to
// xpcshell.ini and then executed:
// mach xpcshell-test --interactive dom/localstorage/test/unit/create_db.js
// Note: to make it become the profile in the test, additional manual steps
// are needed.
// 1. Remove the folder "storage/temporary".
installPackage("localStorageArchive3upgrade_profile");
info("Checking ls dirs");
for (let lsDir of lsDirs) {
let dir = getRelativeFile(lsDir);
exists = dir.exists();
ok(exists, "ls directory does exist");
}
request = init();
request = await requestFinished(request);
info("Checking ls dirs");
for (let lsDir of lsDirs) {
let dir = getRelativeFile(lsDir);
exists = dir.exists();
ok(exists, "ls directory does exist");
}
}

View File

@ -14,6 +14,8 @@ support-files =
idbSubdirUpgrade1_profile.zip
idbSubdirUpgrade2_profile.zip
localStorageArchive1upgrade_profile.zip
localStorageArchive2upgrade_profile.zip
localStorageArchive3upgrade_profile.zip
localStorageArchiveDowngrade_profile.zip
morgueCleanup_profile.zip
obsoleteOriginAttributes_profile.zip
@ -36,6 +38,8 @@ support-files =
[test_initTemporaryStorage.js]
[test_listInitializedOrigins.js]
[test_localStorageArchive1upgrade.js]
[test_localStorageArchive2upgrade.js]
[test_localStorageArchive3upgrade.js]
[test_localStorageArchiveDowngrade.js]
[test_morgueCleanup.js]
[test_obsoleteOriginAttributesUpgrade.js]

View File

@ -1152,7 +1152,7 @@ nsresult OpenOp::DatabaseWork() {
nsCOMPtr<nsIFile> dbDirectory;
nsresult rv = quotaManager->EnsureOriginIsInitialized(
PERSISTENCE_TYPE_DEFAULT, mSuffix, mGroup, mOrigin,
/* aCreateIfNotExists */ true, getter_AddRefs(dbDirectory));
getter_AddRefs(dbDirectory));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -264,7 +264,7 @@ parent:
returns (bool isContentOnlyTDR);
async BeginRecording(TimeStamp aRecordingStart);
async EndRecording();
sync EndRecording();
child:
// Send back Compositor Frame Metrics from APZCs so tiled layers can

View File

@ -987,6 +987,8 @@ description =
[PCompositorWidget::EnterPresentLock]
description =
platform = win
[PCompositorBridge::EndRecording]
description = This call is only used for performance analysis scenarios
[PCompositorWidget::LeavePresentLock]
description =
platform = win

View File

@ -5,6 +5,7 @@
from __future__ import absolute_import
import errno
import glob
import random
import os
import shutil
@ -41,6 +42,7 @@ class CppEclipseBackend(CommonBackend):
self._args_for_dirs = {}
self._project_name = 'Gecko'
self._workspace_dir = self._get_workspace_path()
self._workspace_lang_dir = os.path.join(self._workspace_dir, '.metadata/.plugins/org.eclipse.cdt.core')
self._project_dir = os.path.join(self._workspace_dir, self._project_name)
self._overwriting_workspace = os.path.isdir(self._workspace_dir)
@ -104,9 +106,8 @@ class CppEclipseBackend(CommonBackend):
settings_dir = os.path.join(self._project_dir, '.settings')
launch_dir = os.path.join(self._project_dir, 'RunConfigurations')
workspace_settings_dir = os.path.join(self._workspace_dir, '.metadata/.plugins/org.eclipse.core.runtime/.settings')
workspace_language_dir = os.path.join(self._workspace_dir, '.metadata/.plugins/org.eclipse.cdt.core')
for dir_name in [self._project_dir, settings_dir, launch_dir, workspace_settings_dir, workspace_language_dir]:
for dir_name in [self._project_dir, settings_dir, launch_dir, workspace_settings_dir, self._workspace_lang_dir]:
try:
os.makedirs(dir_name)
except OSError as e:
@ -125,7 +126,7 @@ class CppEclipseBackend(CommonBackend):
with open(language_path, 'wb') as fh:
self._write_language_settings(fh)
workspace_language_path = os.path.join(workspace_language_dir, 'language.settings.xml')
workspace_language_path = os.path.join(self._workspace_lang_dir, 'language.settings.xml')
with open(workspace_language_path, 'wb') as fh:
workspace_lang_settings = WORKSPACE_LANGUAGE_SETTINGS_TEMPLATE
workspace_lang_settings = workspace_lang_settings.replace("@COMPILER_FLAGS@", self._cxx + " " + self._cppflags);
@ -210,6 +211,13 @@ class CppEclipseBackend(CommonBackend):
fh.write(NOINDEX_TEMPLATE);
def _remove_noindex(self):
# Below we remove the config file that temporarily disabled the indexer
# while we were importing the project. Unfornutanely, CDT doesn't
# notice indexer settings changes in config files when it restarts. To
# work around that we remove the index database here to force it to:
for f in glob.glob(os.path.join(self._workspace_lang_dir, "*.pdom")):
os.remove(f)
noindex_path = os.path.join(self._project_dir, '.settings/org.eclipse.cdt.core.prefs')
# This may fail if the entire tree has been removed; that's fine.
try: