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