Bug 1549975 - Implement SessionStorageListener for contentSessionStore in C++ r=peterv,mikedeboer

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

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Alphan Chen 2019-08-27 14:47:27 +00:00
Родитель 329cb69c2c
Коммит 9fceaaa7ff
11 изменённых файлов: 352 добавлений и 257 удалений

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

@ -25,9 +25,6 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/sessionstore/SessionHistory.jsm"
);
// A bound to the size of data to store for DOM Storage.
const DOM_STORAGE_LIMIT_PREF = "browser.sessionstore.dom_storage_limit";
// This pref controls whether or not we send updates to the parent on a timeout
// or not, and should only be used for tests or debugging.
const TIMEOUT_DISABLED_PREF = "browser.sessionstore.debug.no_auto_updates";
@ -313,144 +310,6 @@ SessionHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
Ci.nsISupportsWeakReference,
]);
/**
* Listens for changes to the DOMSessionStorage. Whenever new keys are added,
* existing ones removed or changed, or the storage is cleared we will send a
* message to the parent process containing up-to-date sessionStorage data.
*
* Causes a SessionStore:update message to be sent that contains the current
* DOMSessionStorage contents. The data is a nested object using host names
* as keys and per-host DOMSessionStorage data as values.
*/
class SessionStorageListener extends Handler {
constructor(store) {
super(store);
// We don't want to send all the session storage data for all the frames
// for every change. So if only a few value changed we send them over as
// a "storagechange" event. If however for some reason before we send these
// changes we have to send over the entire sessions storage data, we just
// reset these changes.
this._changes = undefined;
// The event listener waiting for MozSessionStorageChanged events.
this._listener = null;
Services.obs.addObserver(this, "browser:purge-sessionStorage");
this.stateChangeNotifier.addObserver(this);
this.resetEventListener();
}
uninit() {
Services.obs.removeObserver(this, "browser:purge-sessionStorage");
}
observe() {
// Collect data on the next tick so that any other observer
// that needs to purge data can do its work first.
setTimeoutWithTarget(() => this.collect(), 0, this.mm.tabEventTarget);
}
resetChanges() {
this._changes = undefined;
}
resetEventListener() {
if (!this._listener) {
this._listener = SessionStoreUtils.addDynamicFrameFilteredListener(
this.mm,
"MozSessionStorageChanged",
this,
true
);
}
}
removeEventListener() {
SessionStoreUtils.removeDynamicFrameFilteredListener(
this.mm,
"MozSessionStorageChanged",
this._listener,
true
);
this._listener = null;
}
handleEvent(event) {
if (!this.mm.docShell) {
return;
}
let { content } = this.mm;
// How much data does DOMSessionStorage contain?
let usage = content.windowUtils.getStorageUsage(event.storageArea);
// Don't store any data if we exceed the limit. Wipe any data we previously
// collected so that we don't confuse websites with partial state.
if (usage > Services.prefs.getIntPref(DOM_STORAGE_LIMIT_PREF)) {
this.messageQueue.push("storage", () => null);
this.removeEventListener();
this.resetChanges();
return;
}
let { url, key, newValue } = event;
let uri = Services.io.newURI(url);
let domain = uri.prePath;
if (!this._changes) {
this._changes = {};
}
if (!this._changes[domain]) {
this._changes[domain] = {};
}
// If the key isn't defined, then .clear() was called, and we send
// up null for this domain to indicate that storage has been cleared
// for it.
if (!key) {
this._changes[domain] = null;
} else {
this._changes[domain][key] = newValue;
}
this.messageQueue.push("storagechange", () => {
let tmp = this._changes;
// If there were multiple changes we send them merged.
// First one will collect all the changes the rest of
// these messages will be ignored.
this.resetChanges();
return tmp;
});
}
collect() {
if (!this.mm.docShell) {
return;
}
let { content } = this.mm;
// We need the entire session storage, let's reset the pending individual change
// messages.
this.resetChanges();
this.messageQueue.push("storage", () => {
let data = SessionStoreUtils.collectSessionStorage(content);
return Object.keys(data).length ? data : null;
});
}
onPageLoadCompleted() {
this.collect();
}
onPageLoadStarted() {
this.resetEventListener();
this.collect();
}
}
/**
* A message queue that takes collected data and will take care of sending it
* to the chrome process. It allows flushing using synchronous messages and
@ -715,7 +574,6 @@ class ContentSessionStore {
this.handlers = [
new EventListener(this),
new SessionHistoryListener(this),
new SessionStorageListener(this),
this.stateChangeNotifier,
this.messageQueue,
];

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

@ -112,16 +112,6 @@ namespace SessionStoreUtils {
boolean restoreFormData(Document document, optional CollectedData data = {});
/**
* Updates all sessionStorage "super cookies"
* @param content
* A tab's global, i.e. the root frame we want to collect for.
* @return Returns a nested object that will have hosts as keys and per-origin
* session storage data as strings. For example:
* {"https://example.com^userContextId=1": {"key": "value", "my_number": "123"}}
*/
record<DOMString, record<DOMString, DOMString>> collectSessionStorage(WindowProxy window);
/**
* Restores all sessionStorage "super cookies".
* @param aDocShell
@ -183,4 +173,9 @@ dictionary UpdateSessionStoreData {
sequence<long> numXPath;
sequence<DOMString> innerHTML;
sequence<ByteString> url;
// for sessionStorage
sequence<ByteString> storageOrigins;
sequence<DOMString> storageKeys;
sequence<DOMString> storageValues;
boolean isFullStorage;
};

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

@ -3857,9 +3857,17 @@ bool BrowserChild::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
inputs = store->GetInputs(idVals, xPathVals);
}
nsTArray<nsCString> origins;
nsTArray<nsString> keys, values;
bool isFullStorage = false;
if (store->IsStorageUpdated()) {
isFullStorage = store->GetAndClearStorageChanges(origins, keys, values);
}
Unused << SendSessionStoreUpdate(
docShellCaps, privatedMode, positions, positionDescendants, inputs,
idVals, xPathVals, aFlushId, aIsFinal, mSessionStoreListener->GetEpoch());
idVals, xPathVals, origins, keys, values, isFullStorage, aFlushId,
aIsFinal, mSessionStoreListener->GetEpoch());
return true;
}

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

@ -2690,11 +2690,13 @@ void BrowserParent::ReconstructWebProgressAndRequest(
mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const nsTArray<nsCString>&& aPositions,
const nsTArray<int32_t>&& aPositionDescendants,
nsTArray<nsCString>&& aPositions,
nsTArray<int32_t>&& aPositionDescendants,
const nsTArray<InputFormData>& aInputs,
const nsTArray<CollectedInputDataValue>& aIdVals,
const nsTArray<CollectedInputDataValue>& aXPathVals,
nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
nsTArray<nsString>&& aValues, const bool aIsFullStorage,
const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch) {
UpdateSessionStoreData data;
if (aDocShellCaps.isSome()) {
@ -2732,6 +2734,16 @@ mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
data.mInnerHTML.Construct().Assign(std::move(innerHTML));
data.mUrl.Construct().Assign(std::move(url));
}
// In normal case, we only update the storage when needed.
// However, we need to reset the session storage(aOrigins.Length() will be 0)
// if the usage is over the "browser_sessionstore_dom_storage_limit".
// In this case, aIsFullStorage is true.
if (aOrigins.Length() != 0 || aIsFullStorage) {
data.mStorageOrigins.Construct().Assign(std::move(aOrigins));
data.mStorageKeys.Construct().Assign(std::move(aKeys));
data.mStorageValues.Construct().Assign(std::move(aValues));
data.mIsFullStorage.Construct() = aIsFullStorage;
}
nsCOMPtr<nsISessionStoreFunctions> funcs =
do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");

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

@ -333,11 +333,13 @@ class BrowserParent final : public PBrowserParent,
mozilla::ipc::IPCResult RecvSessionStoreUpdate(
const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
const nsTArray<nsCString>&& aPositions,
const nsTArray<int32_t>&& aPositionDescendants,
nsTArray<nsCString>&& aPositions,
nsTArray<int32_t>&& aPositionDescendants,
const nsTArray<InputFormData>& aInputs,
const nsTArray<CollectedInputDataValue>& aIdVals,
const nsTArray<CollectedInputDataValue>& aXPathVals,
nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
nsTArray<nsString>&& aValues, const bool aIsFullStorage,
const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch);
mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(

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

@ -620,6 +620,8 @@ parent:
nsCString[] aPositions, int32_t[] aPositionDescendants,
InputFormData[] aInputs, CollectedInputDataValue[] aIdVals,
CollectedInputDataValue[] aXPathVals,
nsCString[] aOrigins, nsString[] aKeys,
nsString[] aValues, bool aIsFullStorage,
uint32_t aFlushId, bool aIsFinal, uint32_t aEpoch);
child:

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

@ -349,6 +349,34 @@ var SessionStoreFuncInternal = {
return null;
},
updateStorage: function SSF_updateStorage(aOrigins, aKeys, aValues) {
let data = {};
for (let i = 0; i < aOrigins.length; i++) {
// If the key isn't defined, then .clear() was called, and we send
// up null for this domain to indicate that storage has been cleared
// for it.
if (aKeys[i] == "") {
while (aOrigins[i + 1] == aOrigins[i]) {
i++;
}
data[aOrigins[i]] = null;
} else {
let hostData = {};
hostData[aKeys[i]] = aValues[i];
while (aOrigins[i + 1] == aOrigins[i]) {
i++;
hostData[aKeys[i]] = aValues[i];
}
data[aOrigins[i]] = hostData;
}
}
if (aOrigins.length > 0) {
return data;
}
return null;
},
updateSessionStore: function SSF_updateSessionStore(
aBrowser,
aFlushId,
@ -388,6 +416,18 @@ var SessionStoreFuncInternal = {
aData.numXPath
);
}
if (aData.isFullStorage != undefined) {
let storage = this.updateStorage(
aData.storageOrigins,
aData.storageKeys,
aData.storageValues
);
if (aData.isFullStorage) {
currentData.storage = storage;
} else {
currentData.storagechange = storage;
}
}
SessionStore.updateSessionStoreFromTablistener(aBrowser, {
data: currentData,

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

@ -7,6 +7,7 @@
#include "mozilla/dom/SessionStoreListener.h"
#include "mozilla/dom/SessionStoreUtils.h"
#include "mozilla/dom/SessionStoreUtilsBinding.h"
#include "mozilla/dom/StorageEvent.h"
#include "mozilla/dom/BrowserChild.h"
#include "nsGenericHTMLElement.h"
#include "nsIBrowser.h"
@ -42,6 +43,7 @@ ContentSessionStore::ContentSessionStore(nsIDocShell* aDocShell)
mIsPrivate(false),
mScrollChanged(NO_CHANGE),
mFormDataChanged(NO_CHANGE),
mStorageStatus(NO_STORAGE),
mDocCapChanged(false) {
MOZ_ASSERT(mDocShell);
// Check that value at startup as it might have
@ -99,6 +101,18 @@ bool ContentSessionStore::GetPrivateModeEnabled() {
return mIsPrivate;
}
void ContentSessionStore::SetFullStorageNeeded() {
// We need the entire session storage, reset the pending individual change
ResetStorageChanges();
mStorageStatus = FULLSTORAGE;
}
void ContentSessionStore::ResetStorageChanges() {
mOrigins.Clear();
mKeys.Clear();
mValues.Clear();
}
void ContentSessionStore::OnDocumentStart() {
mScrollChanged = PAGELOADEDSTART;
mFormDataChanged = PAGELOADEDSTART;
@ -107,9 +121,14 @@ void ContentSessionStore::OnDocumentStart() {
mDocCaps = caps;
mDocCapChanged = true;
}
SetFullStorageNeeded();
}
void ContentSessionStore::OnDocumentEnd() { mScrollChanged = WITH_CHANGE; }
void ContentSessionStore::OnDocumentEnd() {
mScrollChanged = WITH_CHANGE;
SetFullStorageNeeded();
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabListener)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
@ -131,6 +150,8 @@ TabListener::TabListener(nsIDocShell* aDocShell, Element* aElement)
mProgressListenerRegistered(false),
mEventListenerRegistered(false),
mPrefObserverRegistered(false),
mStorageObserverRegistered(false),
mStorageChangeListenerRegistered(false),
mUpdatedTimer(nullptr),
mTimeoutDisabled(false),
mUpdateInterval(15000),
@ -138,6 +159,19 @@ TabListener::TabListener(nsIDocShell* aDocShell, Element* aElement)
MOZ_ASSERT(mDocShell);
}
EventTarget* TabListener::GetEventTarget() {
if (mOwnerContent) {
return mOwnerContent;
}
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mDocShell);
if (window) {
return window->GetChromeEventHandler();
}
return nullptr;
}
nsresult TabListener::Init() {
TabListener::UpdateSessionStore();
nsresult rv = mDocShell->AddWeakPrivacyTransitionObserver(this);
@ -157,16 +191,14 @@ nsresult TabListener::Init() {
mPrefObserverRegistered = true;
}
nsCOMPtr<EventTarget> eventTarget = nullptr;
if (mOwnerContent) {
eventTarget = mOwnerContent;
} else {
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mDocShell);
if (window) {
eventTarget = window->GetChromeEventHandler();
}
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
NS_WARNING_ASSERTION(obs, "no observer service");
if (obs) {
obs->AddObserver(this, "browser:purge-sessionStorage", true);
mStorageObserverRegistered = true;
}
nsCOMPtr<EventTarget> eventTarget = GetEventTarget();
if (!eventTarget) {
return NS_OK;
}
@ -174,6 +206,9 @@ nsresult TabListener::Init() {
this, false);
eventTarget->AddSystemEventListener(NS_LITERAL_STRING("input"), this, false);
mEventListenerRegistered = true;
eventTarget->AddSystemEventListener(
NS_LITERAL_STRING("MozSessionStorageChanged"), this, false);
mStorageChangeListenerRegistered = true;
return NS_OK;
}
@ -251,6 +286,7 @@ NS_IMETHODIMP TabListener::OnStateChange(nsIWebProgress* aWebProgress,
if (aStateFlags & (nsIWebProgressListener::STATE_START)) {
mSessionStore->OnDocumentStart();
ResetStorageChangeListener();
} else if (aStateFlags & (nsIWebProgressListener::STATE_STOP)) {
mSessionStore->OnDocumentEnd();
}
@ -293,7 +329,25 @@ TabListener::HandleEvent(Event* aEvent) {
} else if (eventType.EqualsLiteral("input")) {
mSessionStore->SetFormDataChanged();
AddTimerForUpdate();
} else if (eventType.EqualsLiteral("MozSessionStorageChanged")) {
auto event = static_cast<StorageEvent*>(aEvent);
RefPtr<Storage> changingStorage = event->GetStorageArea();
if (!changingStorage) {
return NS_OK;
}
// How much data does DOMSessionStorage contain?
int64_t storageUsage = changingStorage->GetOriginQuotaUsage();
if (storageUsage > StaticPrefs::browser_sessionstore_dom_storage_limit()) {
RemoveStorageChangeListener();
mSessionStore->ResetStorageChanges();
mSessionStore->ResetStorage();
return NS_OK;
}
if (mSessionStore->AppendSessionStorageChange(event)) {
AddTimerForUpdate();
}
}
return NS_OK;
}
@ -332,35 +386,42 @@ NS_IMETHODIMP TabListener::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
nsresult TabListener::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT(!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID),
"unexpected topic!");
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
bool timeoutDisabled;
if (NS_SUCCEEDED(
prefBranch->GetBoolPref(kTimeOutDisable, &timeoutDisabled))) {
if (mTimeoutDisabled != timeoutDisabled) {
mTimeoutDisabled = timeoutDisabled;
if (mUpdatedTimer) {
StopTimerForUpdate();
AddTimerForUpdate();
}
}
if (!strcmp(aTopic, "browser:purge-sessionStorage")) {
mSessionStore->SetFullStorageNeeded();
AddTimerForUpdate();
return NS_OK;
}
int32_t interval = 0;
if (NS_SUCCEEDED(prefBranch->GetIntPref(kPrefInterval, &interval))) {
if (mUpdateInterval != interval) {
mUpdateInterval = interval;
if (mUpdatedTimer) {
StopTimerForUpdate();
AddTimerForUpdate();
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(aSubject);
bool timeoutDisabled;
if (NS_SUCCEEDED(
prefBranch->GetBoolPref(kTimeOutDisable, &timeoutDisabled))) {
if (mTimeoutDisabled != timeoutDisabled) {
mTimeoutDisabled = timeoutDisabled;
if (mUpdatedTimer) {
StopTimerForUpdate();
AddTimerForUpdate();
}
}
}
int32_t interval = 0;
if (NS_SUCCEEDED(prefBranch->GetIntPref(kPrefInterval, &interval))) {
if (mUpdateInterval != interval) {
mUpdateInterval = interval;
if (mUpdatedTimer) {
StopTimerForUpdate();
AddTimerForUpdate();
}
}
}
return NS_OK;
}
return NS_OK;
NS_ERROR("Unexpected topic");
return NS_ERROR_UNEXPECTED;
}
nsCString CollectPosition(Document& aDocument) {
@ -399,9 +460,10 @@ int CollectPositions(BrowsingContext* aBrowsingContext,
/* Collect data from current frame */
aPositions.AppendElement(CollectPosition(*document));
aPositionDescendants.AppendElement(0);
int currentIdx = aPositions.Length() - 1;
unsigned long currentIdx = aPositions.Length() - 1;
/* Collect data from all child frame */
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
aPositionDescendants[currentIdx] +=
CollectPositions(child, aPositions, aPositionDescendants);
@ -487,9 +549,10 @@ int CollectInputs(BrowsingContext* aBrowsingContext,
input.numXPath = 0;
CollectInput(*document, input, aIdVals, aXPathVals);
aInputs.AppendElement(input);
int currentIdx = aInputs.Length() - 1;
unsigned long currentIdx = aInputs.Length() - 1;
/* Collect data from all child frame */
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
aInputs[currentIdx].descendants +=
CollectInputs(child, aInputs, aIdVals, aXPathVals);
@ -519,6 +582,54 @@ nsTArray<InputFormData> ContentSessionStore::GetInputs(
return inputs;
}
bool ContentSessionStore::AppendSessionStorageChange(StorageEvent* aEvent) {
// We will collect the full SessionStore if mStorageStatus is FULLSTORAGE.
// These partial changes can be skipped in this case.
if (mStorageStatus == FULLSTORAGE) {
return false;
}
nsAutoString origin;
aEvent->GetUrl(origin);
nsCOMPtr<nsIURI> newUri;
nsresult rv =
NS_NewURI(getter_AddRefs(newUri), NS_ConvertUTF16toUTF8(origin));
if (NS_FAILED(rv)) {
return false;
}
newUri->GetPrePath(*mOrigins.AppendElement());
aEvent->GetKey(*mKeys.AppendElement());
aEvent->GetNewValue(*mValues.AppendElement());
mStorageStatus = STORAGECHANGE;
return true;
}
bool ContentSessionStore::GetAndClearStorageChanges(
nsTArray<nsCString>& aOrigins, nsTArray<nsString>& aKeys,
nsTArray<nsString>& aValues) {
MOZ_ASSERT(IsStorageUpdated());
bool isFullStorage = false;
if (mStorageStatus == RESET) {
isFullStorage = true;
} else if (mStorageStatus == FULLSTORAGE) {
MOZ_ASSERT(mDocShell);
SessionStoreUtils::CollectedSessionStorage(
nsDocShell::Cast(mDocShell)->GetBrowsingContext(), aOrigins, aKeys,
aValues);
isFullStorage = true;
} else if (mStorageStatus == STORAGECHANGE) {
aOrigins.SwapElements(mOrigins);
aKeys.SwapElements(mKeys);
aValues.SwapElements(mValues);
}
ResetStorageChanges();
mStorageStatus = NO_STORAGE;
return isFullStorage;
}
bool TabListener::ForceFlushFromParent(uint32_t aFlushId, bool aIsFinal) {
if (!XRE_IsParentProcess()) {
return false;
@ -608,6 +719,15 @@ bool TabListener::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
data.mUrl.Construct().Assign(std::move(url));
}
}
if (mSessionStore->IsStorageUpdated()) {
nsTArray<nsCString> origins;
nsTArray<nsString> keys, values;
data.mIsFullStorage.Construct() =
mSessionStore->GetAndClearStorageChanges(origins, keys, values);
data.mStorageOrigins.Construct().Assign(std::move(origins));
data.mStorageKeys.Construct().Assign(std::move(keys));
data.mStorageValues.Construct().Assign(std::move(values));
}
nsCOMPtr<nsISessionStoreFunctions> funcs =
do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");
@ -626,6 +746,33 @@ bool TabListener::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
return true;
}
void TabListener::ResetStorageChangeListener() {
if (mStorageChangeListenerRegistered) {
return;
}
nsCOMPtr<EventTarget> eventTarget = GetEventTarget();
if (!eventTarget) {
return;
}
eventTarget->AddSystemEventListener(
NS_LITERAL_STRING("MozSessionStorageChanged"), this, false);
mStorageChangeListenerRegistered = true;
}
void TabListener::RemoveStorageChangeListener() {
if (!mStorageChangeListenerRegistered) {
return;
}
nsCOMPtr<EventTarget> eventTarget = GetEventTarget();
if (eventTarget) {
eventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("MozSessionStorageChanged"), this, false);
mStorageChangeListenerRegistered = false;
}
}
void TabListener::RemoveListeners() {
if (mProgressListenerRegistered) {
nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(mDocShell);
@ -635,33 +782,38 @@ void TabListener::RemoveListeners() {
}
}
if (mEventListenerRegistered) {
nsCOMPtr<EventTarget> eventTarget = nullptr;
if (mOwnerContent) {
eventTarget = mOwnerContent;
} else {
nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mDocShell);
if (window) {
eventTarget = window->GetChromeEventHandler();
}
}
if (mEventListenerRegistered || mStorageChangeListenerRegistered) {
nsCOMPtr<EventTarget> eventTarget = GetEventTarget();
if (eventTarget) {
eventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("mozvisualscroll"), this, false);
eventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("input"), this,
false);
mEventListenerRegistered = false;
if (mEventListenerRegistered) {
eventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("mozvisualscroll"), this, false);
eventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("input"), this,
false);
mEventListenerRegistered = false;
}
if (mStorageChangeListenerRegistered) {
eventTarget->RemoveSystemEventListener(
NS_LITERAL_STRING("MozSessionStorageChanged"), this, false);
mStorageChangeListenerRegistered = false;
}
}
}
if (mPrefObserverRegistered) {
if (mPrefObserverRegistered || mStorageObserverRegistered) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
if (!obs) {
return;
}
if (mPrefObserverRegistered) {
obs->RemoveObserver(this, kTimeOutDisable);
obs->RemoveObserver(this, kPrefInterval);
mPrefObserverRegistered = false;
}
if (mStorageObserverRegistered) {
obs->RemoveObserver(this, "browser:purge-sessionStorage");
mStorageObserverRegistered = false;
}
}
}

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

@ -17,6 +17,8 @@ class nsITimer;
namespace mozilla {
namespace dom {
class StorageEvent;
class ContentSessionStore {
public:
explicit ContentSessionStore(nsIDocShell* aDocShell);
@ -37,11 +39,37 @@ class ContentSessionStore {
nsTArray<InputFormData> GetInputs(
nsTArray<CollectedInputDataValue>& aIdVals,
nsTArray<CollectedInputDataValue>& aXPathVals);
// Use "mStorageStatus" to manage the status of storageChanges
bool IsStorageUpdated() { return mStorageStatus != NO_STORAGE; }
void ResetStorage() { mStorageStatus = RESET; }
/*
There are three situations we need entire session storage:
1. OnDocumentStart: PageLoad started
2. OnDocumentEnd: PageLoad completed
3. receive "browser:purge-sessionStorage" event
Use SetFullStorageNeeded() to set correct "mStorageStatus" and
reset the pending individual change.
*/
void SetFullStorageNeeded();
void ResetStorageChanges();
// GetAndClearStorageChanges() is used for getting storageChanges.
// It clears the stored storage changes before returning.
// It will return true if it is a entire session storage.
// Otherwise, it will return false.
bool GetAndClearStorageChanges(nsTArray<nsCString>& aOrigins,
nsTArray<nsString>& aKeys,
nsTArray<nsString>& aValues);
// Using AppendSessionStorageChange() to append session storage change when
// receiving "MozSessionStorageChanged".
// Return true if there is a new storage change which is appended.
bool AppendSessionStorageChange(StorageEvent* aEvent);
void OnDocumentStart();
void OnDocumentEnd();
bool UpdateNeeded() {
return mPrivateChanged || mDocCapChanged || IsScrollPositionChanged() ||
IsFormDataChanged();
IsFormDataChanged() || IsStorageUpdated();
}
private:
@ -57,8 +85,18 @@ class ContentSessionStore {
WITH_CHANGE, // set when the change event is observed
} mScrollChanged,
mFormDataChanged;
enum {
NO_STORAGE,
RESET,
FULLSTORAGE,
STORAGECHANGE,
} mStorageStatus;
bool mDocCapChanged;
nsCString mDocCaps;
// mOrigins, mKeys, mValues are for sessionStorage partial changes
nsTArray<nsCString> mOrigins;
nsTArray<nsString> mKeys;
nsTArray<nsString> mValues;
};
class TabListener : public nsIDOMEventListener,
@ -68,6 +106,7 @@ class TabListener : public nsIDOMEventListener,
public nsSupportsWeakReference {
public:
explicit TabListener(nsIDocShell* aDocShell, Element* aElement);
EventTarget* GetEventTarget();
nsresult Init();
ContentSessionStore* GetSessionStore() { return mSessionStore; }
// the function is called only when TabListener is in parent process
@ -89,6 +128,8 @@ class TabListener : public nsIDOMEventListener,
void AddTimerForUpdate();
void StopTimerForUpdate();
bool UpdateSessionStore(uint32_t aFlushId = 0, bool aIsFinal = false);
void ResetStorageChangeListener();
void RemoveStorageChangeListener();
virtual ~TabListener();
nsCOMPtr<nsIDocShell> mDocShell;
@ -97,6 +138,8 @@ class TabListener : public nsIDOMEventListener,
bool mProgressListenerRegistered;
bool mEventListenerRegistered;
bool mPrefObserverRegistered;
bool mStorageObserverRegistered;
bool mStorageChangeListenerRegistered;
// Timer used to update data
nsCOMPtr<nsITimer> mUpdatedTimer;
bool mTimeoutDisabled;

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

@ -1076,10 +1076,10 @@ bool SessionStoreUtils::RestoreFormData(const GlobalObject& aGlobal,
}
/* Read entries in the session storage data contained in a tab's history. */
static void ReadAllEntriesFromStorage(
nsPIDOMWindowOuter* aWindow,
nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
static void ReadAllEntriesFromStorage(nsPIDOMWindowOuter* aWindow,
nsTArray<nsCString>& aOrigins,
nsTArray<nsString>& aKeys,
nsTArray<nsString>& aValues) {
nsCOMPtr<nsIDocShell> docShell = aWindow->GetDocShell();
if (!docShell) {
return;
@ -1102,7 +1102,7 @@ static void ReadAllEntriesFromStorage(
nsAutoCString origin;
nsresult rv = principal->GetOrigin(origin);
if (NS_FAILED(rv) || aVisitedOrigins.Contains(origin)) {
if (NS_FAILED(rv) || aOrigins.Contains(origin)) {
// Don't read a host twice.
return;
}
@ -1128,81 +1128,62 @@ static void ReadAllEntriesFromStorage(
return;
}
Record<nsString, Record<nsString, nsString>>::EntryType* recordEntry =
nullptr;
for (uint32_t i = 0; i < len; i++) {
Record<nsString, nsString>::EntryType entry;
nsString key, value;
mozilla::IgnoredErrorResult res;
storage->Key(i, entry.mKey, *principal, res);
storage->Key(i, key, *principal, res);
if (res.Failed()) {
continue;
}
storage->GetItem(entry.mKey, entry.mValue, *principal, res);
storage->GetItem(key, value, *principal, res);
if (res.Failed()) {
continue;
}
if (!recordEntry) {
recordEntry = aRetVal.Entries().AppendElement();
recordEntry->mKey = NS_ConvertUTF8toUTF16(origin);
aVisitedOrigins.PutEntry(origin);
}
recordEntry->mValue.Entries().AppendElement(std::move(entry));
aKeys.AppendElement(key);
aValues.AppendElement(value);
aOrigins.AppendElement(origin);
}
}
/* Collect Collect session storage from current frame and all child frame */
static void CollectedSessionStorageInternal(
JSContext* aCx, BrowsingContext* aBrowsingContext,
nsTHashtable<nsCStringHashKey>& aVisitedOrigins,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
/* static */
void SessionStoreUtils::CollectedSessionStorage(
BrowsingContext* aBrowsingContext, nsTArray<nsCString>& aOrigins,
nsTArray<nsString>& aKeys, nsTArray<nsString>& aValues) {
/* Collect session store from current frame */
nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
if (!window) {
return;
}
ReadAllEntriesFromStorage(window, aVisitedOrigins, aRetVal);
ReadAllEntriesFromStorage(window, aOrigins, aKeys, aValues);
/* Collect session storage from all child frame */
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
if (!docShell) {
return;
}
int32_t length;
nsresult rv = docShell->GetInProcessChildCount(&length);
if (NS_FAILED(rv)) {
return;
}
for (int32_t i = 0; i < length; ++i) {
nsCOMPtr<nsIDocShellTreeItem> item;
docShell->GetInProcessChildAt(i, getter_AddRefs(item));
if (!item) {
// This is not going to work for fission. Bug 1572084 for tracking it.
for (BrowsingContext* child : aBrowsingContext->GetChildren()) {
window = child->GetDOMWindow();
if (!window) {
return;
}
nsCOMPtr<nsIDocShell> childDocShell(do_QueryInterface(item));
if (!childDocShell) {
docShell = window->GetDocShell();
if (!docShell) {
return;
}
bool isDynamic = false;
rv = childDocShell->GetCreatedDynamically(&isDynamic);
nsresult rv = docShell->GetCreatedDynamically(&isDynamic);
if (NS_SUCCEEDED(rv) && isDynamic) {
continue;
}
CollectedSessionStorageInternal(aCx, childDocShell->GetBrowsingContext(),
aVisitedOrigins, aRetVal);
SessionStoreUtils::CollectedSessionStorage(child, aOrigins, aKeys, aValues);
}
}
/* static */
void SessionStoreUtils::CollectSessionStorage(
const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
Record<nsString, Record<nsString, nsString>>& aRetVal) {
nsTHashtable<nsCStringHashKey> visitedOrigins;
CollectedSessionStorageInternal(aGlobal.Context(), aWindow.get(),
visitedOrigins, aRetVal);
}
/* static */
void SessionStoreUtils::RestoreSessionStorage(
const GlobalObject& aGlobal, nsIDocShell* aDocShell,
@ -1290,6 +1271,7 @@ static void CollectFrameTreeData(JSContext* aCx,
SequenceRooter<JSObject*> rooter(aCx, &childrenData);
uint32_t trailingNullCounter = 0;
// This is not going to work for fission. Bug 1572084 for tracking it.
for (auto& child : aBrowsingContext->GetChildren()) {
NullableRootedDictionary<CollectedData> data(aCx);
CollectFrameTreeData(aCx, child, data, aFunc);

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

@ -81,9 +81,10 @@ class SessionStoreUtils {
static bool RestoreFormData(const GlobalObject& aGlobal, Document& aDocument,
const CollectedData& aData);
static void CollectSessionStorage(
const GlobalObject& aGlobal, WindowProxyHolder& aWindow,
Record<nsString, Record<nsString, nsString>>& aRetVal);
static void CollectedSessionStorage(BrowsingContext* aBrowsingContext,
nsTArray<nsCString>& aOrigins,
nsTArray<nsString>& aKeys,
nsTArray<nsString>& aValues);
static void RestoreSessionStorage(
const GlobalObject& aGlobal, nsIDocShell* aDocShell,