зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1544371 - Implement FormDataListener for contentSessionStore in C++ r=peterv
Differential Revision: https://phabricator.services.mozilla.com/D30078 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
596c01a1db
Коммит
efa34c0a06
|
@ -313,49 +313,6 @@ SessionHistoryListener.prototype.QueryInterface = ChromeUtils.generateQI([
|
|||
Ci.nsISupportsWeakReference,
|
||||
]);
|
||||
|
||||
/**
|
||||
* Listens for changes to input elements. Whenever the value of an input
|
||||
* element changes we will re-collect data for the current frame tree and send
|
||||
* a message to the parent process.
|
||||
*
|
||||
* Causes a SessionStore:update message to be sent that contains the form data
|
||||
* for all reachable frames.
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
* formdata: {url: "http://mozilla.org/", id: {input_id: "input value"}},
|
||||
* children: [
|
||||
* null,
|
||||
* {url: "http://sub.mozilla.org/", id: {input_id: "input value 2"}}
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
class FormDataListener extends Handler {
|
||||
constructor(store) {
|
||||
super(store);
|
||||
|
||||
SessionStoreUtils.addDynamicFrameFilteredListener(
|
||||
this.mm,
|
||||
"input",
|
||||
this,
|
||||
true
|
||||
);
|
||||
this.stateChangeNotifier.addObserver(this);
|
||||
}
|
||||
|
||||
handleEvent() {
|
||||
this.messageQueue.push("formdata", () => this.collect());
|
||||
}
|
||||
|
||||
onPageLoadStarted() {
|
||||
this.messageQueue.push("formdata", () => null);
|
||||
}
|
||||
|
||||
collect() {
|
||||
return SessionStoreUtils.collectFormData(this.mm.content);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -757,7 +714,6 @@ class ContentSessionStore {
|
|||
|
||||
this.handlers = [
|
||||
new EventListener(this),
|
||||
new FormDataListener(this),
|
||||
new SessionHistoryListener(this),
|
||||
new SessionStorageListener(this),
|
||||
this.stateChangeNotifier,
|
||||
|
|
|
@ -160,9 +160,27 @@ dictionary CollectedData
|
|||
sequence<object?> children;
|
||||
};
|
||||
|
||||
dictionary InputElementData {
|
||||
sequence<DOMString> id;
|
||||
sequence<DOMString> type;
|
||||
sequence<long> valueIdx;
|
||||
sequence<long> selectedIndex;
|
||||
sequence<DOMString> selectVal;
|
||||
sequence<DOMString> strVal;
|
||||
sequence<boolean> boolVal;
|
||||
};
|
||||
|
||||
dictionary UpdateSessionStoreData {
|
||||
ByteString docShellCaps;
|
||||
boolean isPrivate;
|
||||
sequence<ByteString> positions;
|
||||
sequence<long> positionDescendants;
|
||||
// The following are for input data
|
||||
InputElementData id;
|
||||
InputElementData xpath;
|
||||
sequence<long> inputDescendants;
|
||||
sequence<long> numId;
|
||||
sequence<long> numXPath;
|
||||
sequence<DOMString> innerHTML;
|
||||
sequence<ByteString> url;
|
||||
};
|
||||
|
|
|
@ -3787,9 +3787,15 @@ bool BrowserChild::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
|
|||
store->GetScrollPositions(positions, positionDescendants);
|
||||
}
|
||||
|
||||
Unused << SendSessionStoreUpdate(docShellCaps, privatedMode, positions,
|
||||
positionDescendants, aFlushId, aIsFinal,
|
||||
mSessionStoreListener->GetEpoch());
|
||||
nsTArray<InputFormData> inputs;
|
||||
nsTArray<CollectedInputDataValue> idVals, xPathVals;
|
||||
if (store->IsFormDataChanged()) {
|
||||
inputs = store->GetInputs(idVals, xPathVals);
|
||||
}
|
||||
|
||||
Unused << SendSessionStoreUpdate(
|
||||
docShellCaps, privatedMode, positions, positionDescendants, inputs,
|
||||
idVals, xPathVals, aFlushId, aIsFinal, mSessionStoreListener->GetEpoch());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "mozilla/dom/RemoteDragStartData.h"
|
||||
#include "mozilla/dom/RemoteWebProgress.h"
|
||||
#include "mozilla/dom/RemoteWebProgressRequest.h"
|
||||
#include "mozilla/dom/SessionStoreUtils.h"
|
||||
#include "mozilla/dom/SessionStoreUtilsBinding.h"
|
||||
#include "mozilla/EventStateManager.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
@ -2647,8 +2648,11 @@ 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, const uint32_t& aFlushId,
|
||||
const bool& aIsFinal, const uint32_t& aEpoch) {
|
||||
const nsTArray<int32_t>&& aPositionDescendants,
|
||||
const nsTArray<InputFormData>& aInputs,
|
||||
const nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
const nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch) {
|
||||
UpdateSessionStoreData data;
|
||||
if (aDocShellCaps.isSome()) {
|
||||
data.mDocShellCaps.Construct() = aDocShellCaps.value();
|
||||
|
@ -2661,6 +2665,30 @@ mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
|
|||
data.mPositionDescendants.Construct().Assign(
|
||||
std::move(aPositionDescendants));
|
||||
}
|
||||
if (aIdVals.Length() != 0) {
|
||||
SessionStoreUtils::ComposeInputData(aIdVals, data.mId.Construct());
|
||||
}
|
||||
if (aXPathVals.Length() != 0) {
|
||||
SessionStoreUtils::ComposeInputData(aXPathVals, data.mXpath.Construct());
|
||||
}
|
||||
if (aInputs.Length() != 0) {
|
||||
nsTArray<int> descendants, numId, numXPath;
|
||||
nsTArray<nsString> innerHTML;
|
||||
nsTArray<nsCString> url;
|
||||
for (const InputFormData& input : aInputs) {
|
||||
descendants.AppendElement(input.descendants);
|
||||
numId.AppendElement(input.numId);
|
||||
numXPath.AppendElement(input.numXPath);
|
||||
innerHTML.AppendElement(input.innerHTML);
|
||||
url.AppendElement(input.url);
|
||||
}
|
||||
|
||||
data.mInputDescendants.Construct().Assign(std::move(descendants));
|
||||
data.mNumId.Construct().Assign(std::move(numId));
|
||||
data.mNumXPath.Construct().Assign(std::move(numXPath));
|
||||
data.mInnerHTML.Construct().Assign(std::move(innerHTML));
|
||||
data.mUrl.Construct().Assign(std::move(url));
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISessionStoreFunctions> funcs =
|
||||
do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");
|
||||
|
|
|
@ -323,8 +323,11 @@ 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, const uint32_t& aFlushId,
|
||||
const bool& aIsFinal, const uint32_t& aEpoch);
|
||||
const nsTArray<int32_t>&& aPositionDescendants,
|
||||
const nsTArray<InputFormData>& aInputs,
|
||||
const nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
const nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
const uint32_t& aFlushId, const bool& aIsFinal, const uint32_t& aEpoch);
|
||||
|
||||
mozilla::ipc::IPCResult RecvBrowserFrameOpenWindow(
|
||||
PBrowserParent* aOpener, const nsString& aURL, const nsString& aName,
|
||||
|
|
|
@ -88,6 +88,8 @@ using mozilla::dom::EffectsInfo from "mozilla/dom/TabMessageUtils.h";
|
|||
using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
|
||||
using mozilla::ScrollAxis from "mozilla/PresShellForwards.h";
|
||||
using mozilla::ScrollFlags from "mozilla/PresShellForwards.h";
|
||||
using struct InputFormData from "mozilla/dom/SessionStoreMessageUtils.h";
|
||||
using struct CollectedInputDataValue from "mozilla/dom/SessionStoreMessageUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -609,6 +611,8 @@ parent:
|
|||
|
||||
async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode,
|
||||
nsCString[] aPositions, int32_t[] aPositionDescendants,
|
||||
InputFormData[] aInputs, CollectedInputDataValue[] aIdVals,
|
||||
CollectedInputDataValue[] aXPathVals,
|
||||
uint32_t aFlushId, bool aIsFinal, uint32_t aEpoch);
|
||||
|
||||
child:
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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_SessionStoreData_h
|
||||
#define mozilla_dom_SessionStoreData_h
|
||||
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/dom/SessionStoreUtilsBinding.h"
|
||||
#include "mozilla/Variant.h"
|
||||
|
||||
typedef mozilla::Variant<nsString, bool,
|
||||
mozilla::dom::CollectedNonMultipleSelectValue,
|
||||
nsTArray<nsString>>
|
||||
InputDataValue;
|
||||
|
||||
/*
|
||||
* Need two arrays based on this struct.
|
||||
* One is for elements with id one is for XPath.
|
||||
*
|
||||
* id: id or XPath
|
||||
* type: type of this input element
|
||||
* bool: value is boolean
|
||||
* string: value is nsString
|
||||
* file: value is "arrayVal"
|
||||
* singleSelect: value is "singleSelect"
|
||||
* multipleSelect: value is "arrayVal"
|
||||
*
|
||||
* There are four value types:
|
||||
* strVal: nsString
|
||||
* boolVal: boolean
|
||||
* singleSelect: single select value
|
||||
* arrayVal: nsString array
|
||||
*/
|
||||
struct CollectedInputDataValue {
|
||||
nsString id;
|
||||
nsString type;
|
||||
InputDataValue value;
|
||||
|
||||
CollectedInputDataValue() : value(false){};
|
||||
};
|
||||
|
||||
/*
|
||||
* Each index of the following array is corresponging to each frame.
|
||||
* descendants: number of child frames of this frame
|
||||
* innerHTML: innerHTML of this frame
|
||||
* url: url of this frame
|
||||
* numId: number of containing elements with id for this frame
|
||||
* numXPath: number of containing elements with XPath for this frame
|
||||
*/
|
||||
struct InputFormData {
|
||||
int32_t descendants;
|
||||
nsString innerHTML;
|
||||
nsCString url;
|
||||
int32_t numId;
|
||||
int32_t numXPath;
|
||||
};
|
||||
|
||||
#endif /* mozilla_dom_SessionStoreData_h */
|
|
@ -21,6 +21,259 @@ function UpdateSessionStore(aBrowser, aFlushId, aIsFinal, aEpoch, aData) {
|
|||
var EXPORTED_SYMBOLS = ["UpdateSessionStore"];
|
||||
|
||||
var SessionStoreFuncInternal = {
|
||||
// form data which is waiting to be updated
|
||||
_formDataId: [],
|
||||
_formDataIdValue: [],
|
||||
_formDataXPath: [],
|
||||
_formDataXPathValue: [],
|
||||
|
||||
/**
|
||||
* The data will be stored in the arrays:
|
||||
* "_formDataId, _formDataIdValue" for the elements with id.
|
||||
* "_formDataXPath, _formDataXPathValue" for the elements with XPath.
|
||||
*/
|
||||
updateFormData: function SSF_updateFormData(aType, aData) {
|
||||
let idArray = this._formDataId;
|
||||
let valueArray = this._formDataIdValue;
|
||||
|
||||
if (aType == "XPath") {
|
||||
idArray = this._formDataXPath;
|
||||
valueArray = this._formDataXPathValue;
|
||||
}
|
||||
|
||||
let valueIdx = aData.valueIdx;
|
||||
for (let i = 0; i < aData.id.length; i++) {
|
||||
idArray.push(aData.id[i]);
|
||||
|
||||
if (aData.type[i] == "singleSelect") {
|
||||
valueArray.push({
|
||||
selectedIndex: aData.selectedIndex[valueIdx[i]],
|
||||
value: aData.selectVal[valueIdx[i]],
|
||||
});
|
||||
} else if (aData.type[i] == "file") {
|
||||
valueArray.push({
|
||||
type: "file",
|
||||
fileList: aData.strVal.slice(valueIdx[i], valueIdx[++i]),
|
||||
});
|
||||
} else if (aData.type[i] == "multipleSelect") {
|
||||
valueArray.push(aData.strVal.slice(valueIdx[i], valueIdx[++i]));
|
||||
} else if (aData.type[i] == "string") {
|
||||
valueArray.push(aData.strVal[valueIdx[i]]);
|
||||
} else if (aData.type[i] == "bool") {
|
||||
valueArray.push(aData.boolVal[valueIdx[i]]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the array of formdata for this._sessionData.formdata.children
|
||||
*
|
||||
* aStartIndex: Current index for aInnerHTML/aUrl/aNumId/aNumXPath/aDescendants.
|
||||
* (aStartIndex means the index of current root frame)
|
||||
* aInnerHTML: Array for innerHTML.
|
||||
* aUrl: Array for url.
|
||||
* aNumId: Array for number of containing elements with id
|
||||
* aNumXPath: Array for number of containing elements with XPath
|
||||
* aDescendants: Array for number of descendants.
|
||||
*
|
||||
* aCurrentIdIdx: Current index for this._formDataId and this._formDataIdValue
|
||||
* aCurrentXPathIdx: Current index for this._formDataXPath and this._formDataXPathValue
|
||||
* aNumberOfDescendants: The number of descendants for current frame
|
||||
*
|
||||
* The returned array includes "aNumberOfDescendants" formdata objects.
|
||||
*/
|
||||
composeInputChildren: function SSF_composeInputChildren(
|
||||
aInnerHTML,
|
||||
aUrl,
|
||||
aCurrentIdIdx,
|
||||
aNumId,
|
||||
aCurrentXpathIdx,
|
||||
aNumXPath,
|
||||
aDescendants,
|
||||
aStartIndex,
|
||||
aNumberOfDescendants
|
||||
) {
|
||||
let children = [];
|
||||
let lastIndexOfNonNullbject = -1;
|
||||
for (let i = 0; i < aNumberOfDescendants; i++) {
|
||||
let currentIndex = aStartIndex + i;
|
||||
let obj = {};
|
||||
let objWithData = false;
|
||||
|
||||
// set url/id/xpath
|
||||
if (aUrl[currentIndex]) {
|
||||
obj.url = aUrl[currentIndex];
|
||||
objWithData = true;
|
||||
|
||||
if (aInnerHTML[currentIndex]) {
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
obj.innerHTML = aInnerHTML[currentIndex];
|
||||
}
|
||||
if (aNumId[currentIndex]) {
|
||||
let idObj = {};
|
||||
for (let idx = 0; idx < aNumId[currentIndex]; idx++) {
|
||||
idObj[
|
||||
this._formDataId[aCurrentIdIdx + idx]
|
||||
] = this._formDataIdValue[aCurrentIdIdx + idx];
|
||||
}
|
||||
obj.id = idObj;
|
||||
}
|
||||
|
||||
// We want to avoid saving data for about:sessionrestore as a string.
|
||||
// Since it's stored in the form as stringified JSON, stringifying further
|
||||
// causes an explosion of escape characters. cf. bug 467409
|
||||
if (
|
||||
obj.url == "about:sessionrestore" ||
|
||||
obj.url == "about:welcomeback"
|
||||
) {
|
||||
obj.id.sessionData = JSON.parse(obj.id.sessionData);
|
||||
}
|
||||
|
||||
if (aNumXPath[currentIndex]) {
|
||||
let xpathObj = {};
|
||||
for (let idx = 0; idx < aNumXPath[currentIndex]; idx++) {
|
||||
xpathObj[
|
||||
this._formDataXPath[aCurrentXpathIdx + idx]
|
||||
] = this._formDataXPathValue[aCurrentXpathIdx + idx];
|
||||
}
|
||||
obj.xpath = xpathObj;
|
||||
}
|
||||
}
|
||||
|
||||
// compose the descendantsTree which will be pushed into children array
|
||||
if (aDescendants[currentIndex]) {
|
||||
let descendantsTree = this.composeInputChildren(
|
||||
aInnerHTML,
|
||||
aUrl,
|
||||
aCurrentIdIdx + aNumId[currentIndex],
|
||||
aNumId,
|
||||
aCurrentXpathIdx + aNumXPath[currentIndex],
|
||||
aNumXPath,
|
||||
aDescendants,
|
||||
currentIndex + 1,
|
||||
aDescendants[currentIndex]
|
||||
);
|
||||
i += aDescendants[currentIndex];
|
||||
if (descendantsTree) {
|
||||
obj.children = descendantsTree;
|
||||
}
|
||||
}
|
||||
|
||||
if (objWithData) {
|
||||
lastIndexOfNonNullbject = children.length;
|
||||
children.push(obj);
|
||||
} else {
|
||||
children.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
if (lastIndexOfNonNullbject == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return children.slice(0, lastIndexOfNonNullbject + 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the object for this._sessionData.formdata.
|
||||
* The object contains the formdata for all reachable frames.
|
||||
*
|
||||
* "object.children" is an array with one entry per frame,
|
||||
* containing formdata as a nested data structure according
|
||||
* to the layout of the frame tree, or null if no formdata.
|
||||
*
|
||||
* Example:
|
||||
* {
|
||||
* url: "http://mozilla.org/",
|
||||
* id: {input_id: "input value"},
|
||||
* xpath: {input_xpath: "input value"},
|
||||
* children: [
|
||||
* null,
|
||||
* {url: "http://sub.mozilla.org/", id: {input_id: "input value 2"}}
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
* Each index of the following array is corresponging to each frame.
|
||||
* aDescendants: Array for number of descendants
|
||||
* aInnerHTML: Array for innerHTML
|
||||
* aUrl: Array for url
|
||||
* aNumId: Array for number of containing elements with id
|
||||
* aNumXPath: Array for number of containing elements with XPath
|
||||
*
|
||||
* Here we use [index 0] to compose the formdata object of root frame.
|
||||
* Besides, we use composeInputChildren() to get array of "object.children".
|
||||
*/
|
||||
updateInput: function SSF_updateInput(
|
||||
aSessionData,
|
||||
aDescendants,
|
||||
aInnerHTML,
|
||||
aUrl,
|
||||
aNumId,
|
||||
aNumXPath
|
||||
) {
|
||||
let obj = {};
|
||||
let objWithData = false;
|
||||
|
||||
if (aUrl[0]) {
|
||||
obj.url = aUrl[0];
|
||||
|
||||
if (aInnerHTML[0]) {
|
||||
// eslint-disable-next-line no-unsanitized/property
|
||||
obj.innerHTML = aInnerHTML[0];
|
||||
objWithData = true;
|
||||
}
|
||||
|
||||
if (aNumId[0]) {
|
||||
let idObj = {};
|
||||
for (let i = 0; i < aNumId[0]; i++) {
|
||||
idObj[this._formDataId[i]] = this._formDataIdValue[i];
|
||||
}
|
||||
obj.id = idObj;
|
||||
objWithData = true;
|
||||
}
|
||||
|
||||
// We want to avoid saving data for about:sessionrestore as a string.
|
||||
// Since it's stored in the form as stringified JSON, stringifying further
|
||||
// causes an explosion of escape characters. cf. bug 467409
|
||||
if (obj.url == "about:sessionrestore" || obj.url == "about:welcomeback") {
|
||||
obj.id.sessionData = JSON.parse(obj.id.sessionData);
|
||||
}
|
||||
|
||||
if (aNumXPath[0]) {
|
||||
let xpathObj = {};
|
||||
for (let i = 0; i < aNumXPath[0]; i++) {
|
||||
xpathObj[this._formDataXPath[i]] = this._formDataXPathValue[i];
|
||||
}
|
||||
obj.xpath = xpathObj;
|
||||
objWithData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (aDescendants.length > 1) {
|
||||
let descendantsTree = this.composeInputChildren(
|
||||
aInnerHTML,
|
||||
aUrl,
|
||||
aNumId[0],
|
||||
aNumId,
|
||||
aNumXPath[0],
|
||||
aNumXPath,
|
||||
aDescendants,
|
||||
1,
|
||||
aDescendants[0]
|
||||
);
|
||||
if (descendantsTree) {
|
||||
obj.children = descendantsTree;
|
||||
objWithData = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (objWithData) {
|
||||
aSessionData.formdata = obj;
|
||||
} else {
|
||||
aSessionData.formdata = null;
|
||||
}
|
||||
},
|
||||
|
||||
composeChildren: function SSF_composeScrollPositionsData(
|
||||
aPositions,
|
||||
aDescendants,
|
||||
|
@ -119,11 +372,32 @@ var SessionStoreFuncInternal = {
|
|||
aData.positionDescendants
|
||||
);
|
||||
}
|
||||
if (aData.id != undefined) {
|
||||
this.updateFormData("id", aData.id);
|
||||
}
|
||||
if (aData.xpath != undefined) {
|
||||
this.updateFormData("XPath", aData.xpath);
|
||||
}
|
||||
if (aData.inputDescendants != undefined) {
|
||||
this.updateInput(
|
||||
currentData,
|
||||
aData.inputDescendants,
|
||||
aData.innerHTML,
|
||||
aData.url,
|
||||
aData.numId,
|
||||
aData.numXPath
|
||||
);
|
||||
}
|
||||
|
||||
SessionStore.updateSessionStoreFromTablistener(aBrowser, {
|
||||
data: currentData,
|
||||
flushID: aFlushId,
|
||||
isFinal: aIsFinal,
|
||||
epoch: aEpoch,
|
||||
});
|
||||
this._formDataId = [];
|
||||
this._formDataIdValue = [];
|
||||
this._formDataXPath = [];
|
||||
this._formDataXPathValue = [];
|
||||
},
|
||||
};
|
||||
|
|
|
@ -5,8 +5,10 @@
|
|||
#include "mozilla/PresShell.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/SessionStoreListener.h"
|
||||
#include "mozilla/dom/SessionStoreUtils.h"
|
||||
#include "mozilla/dom/SessionStoreUtilsBinding.h"
|
||||
#include "mozilla/dom/BrowserChild.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsIBrowser.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellTreeOwner.h"
|
||||
|
@ -39,6 +41,7 @@ ContentSessionStore::ContentSessionStore(nsIDocShell* aDocShell)
|
|||
mPrivateChanged(false),
|
||||
mIsPrivate(false),
|
||||
mScrollChanged(NO_CHANGE),
|
||||
mFormDataChanged(NO_CHANGE),
|
||||
mDocCapChanged(false) {
|
||||
MOZ_ASSERT(mDocShell);
|
||||
// Check that value at startup as it might have
|
||||
|
@ -98,6 +101,7 @@ bool ContentSessionStore::GetPrivateModeEnabled() {
|
|||
|
||||
void ContentSessionStore::OnDocumentStart() {
|
||||
mScrollChanged = PAGELOADEDSTART;
|
||||
mFormDataChanged = PAGELOADEDSTART;
|
||||
nsCString caps = CollectDocShellCapabilities();
|
||||
if (!mDocCaps.Equals(caps)) {
|
||||
mDocCaps = caps;
|
||||
|
@ -168,6 +172,7 @@ nsresult TabListener::Init() {
|
|||
}
|
||||
eventTarget->AddSystemEventListener(NS_LITERAL_STRING("mozvisualscroll"),
|
||||
this, false);
|
||||
eventTarget->AddSystemEventListener(NS_LITERAL_STRING("input"), this, false);
|
||||
mEventListenerRegistered = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -285,6 +290,9 @@ TabListener::HandleEvent(Event* aEvent) {
|
|||
if (eventType.EqualsLiteral("mozvisualscroll")) {
|
||||
mSessionStore->SetScrollPositionChanged();
|
||||
AddTimerForUpdate();
|
||||
} else if (eventType.EqualsLiteral("input")) {
|
||||
mSessionStore->SetFormDataChanged();
|
||||
AddTimerForUpdate();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -394,11 +402,9 @@ int CollectPositions(BrowsingContext* aBrowsingContext,
|
|||
int currentIdx = aPositions.Length() - 1;
|
||||
|
||||
/* Collect data from all child frame */
|
||||
nsTArray<RefPtr<BrowsingContext>> children;
|
||||
aBrowsingContext->GetChildren(children);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
for (auto& child : aBrowsingContext->GetChildren()) {
|
||||
aPositionDescendants[currentIdx] +=
|
||||
CollectPositions(children[i], aPositions, aPositionDescendants);
|
||||
CollectPositions(child, aPositions, aPositionDescendants);
|
||||
}
|
||||
|
||||
return aPositionDescendants[currentIdx] + 1;
|
||||
|
@ -416,6 +422,103 @@ void ContentSessionStore::GetScrollPositions(
|
|||
mScrollChanged = NO_CHANGE;
|
||||
}
|
||||
|
||||
void CollectInput(Document& aDocument, InputFormData& aInput,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals) {
|
||||
PresShell* presShell = aDocument.GetPresShell();
|
||||
if (!presShell) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t numXPath = 0;
|
||||
uint16_t numId = 0;
|
||||
|
||||
// textarea element
|
||||
SessionStoreUtils::CollectFromTextAreaElement(aDocument, numXPath, numId,
|
||||
aXPathVals, aIdVals);
|
||||
// input element
|
||||
SessionStoreUtils::CollectFromInputElement(aDocument, numXPath, numId,
|
||||
aXPathVals, aIdVals);
|
||||
// select element
|
||||
SessionStoreUtils::CollectFromSelectElement(aDocument, numXPath, numId,
|
||||
aXPathVals, aIdVals);
|
||||
|
||||
Element* bodyElement = aDocument.GetBody();
|
||||
if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
|
||||
bodyElement->GetInnerHTML(aInput.innerHTML, IgnoreErrors());
|
||||
}
|
||||
if (aInput.innerHTML.IsEmpty() && numXPath == 0 && numId == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the frame's current URL with its form data so that we can compare
|
||||
// it when restoring data to not inject form data into the wrong document.
|
||||
nsIURI* uri = aDocument.GetDocumentURI();
|
||||
if (uri) {
|
||||
uri->GetSpecIgnoringRef(aInput.url);
|
||||
}
|
||||
aInput.numId = numId;
|
||||
aInput.numXPath = numXPath;
|
||||
}
|
||||
|
||||
int CollectInputs(BrowsingContext* aBrowsingContext,
|
||||
nsTArray<InputFormData>& aInputs,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals) {
|
||||
nsPIDOMWindowOuter* window = aBrowsingContext->GetDOMWindow();
|
||||
if (!window) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsIDocShell* docShell = window->GetDocShell();
|
||||
if (!docShell || docShell->GetCreatedDynamically()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Document* document = window->GetDoc();
|
||||
if (!document) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Collect data from current frame */
|
||||
InputFormData input;
|
||||
input.descendants = 0;
|
||||
input.numId = 0;
|
||||
input.numXPath = 0;
|
||||
CollectInput(*document, input, aIdVals, aXPathVals);
|
||||
aInputs.AppendElement(input);
|
||||
int currentIdx = aInputs.Length() - 1;
|
||||
|
||||
/* Collect data from all child frame */
|
||||
for (auto& child : aBrowsingContext->GetChildren()) {
|
||||
aInputs[currentIdx].descendants +=
|
||||
CollectInputs(child, aInputs, aIdVals, aXPathVals);
|
||||
}
|
||||
|
||||
return aInputs[currentIdx].descendants + 1;
|
||||
}
|
||||
|
||||
nsTArray<InputFormData> ContentSessionStore::GetInputs(
|
||||
nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals) {
|
||||
nsTArray<InputFormData> inputs;
|
||||
if (mFormDataChanged == PAGELOADEDSTART) {
|
||||
mFormDataChanged = NO_CHANGE;
|
||||
InputFormData input;
|
||||
input.descendants = 0;
|
||||
input.innerHTML = EmptyString();
|
||||
input.url = EmptyCString();
|
||||
input.numId = 0;
|
||||
input.numXPath = 0;
|
||||
inputs.AppendElement(input);
|
||||
} else {
|
||||
mFormDataChanged = NO_CHANGE;
|
||||
CollectInputs(nsDocShell::Cast(mDocShell)->GetBrowsingContext(), inputs,
|
||||
aIdVals, aXPathVals);
|
||||
}
|
||||
return inputs;
|
||||
}
|
||||
|
||||
bool TabListener::ForceFlushFromParent(uint32_t aFlushId, bool aIsFinal) {
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return false;
|
||||
|
@ -474,6 +577,37 @@ bool TabListener::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
|
|||
data.mPositions.Construct().Assign(std::move(positions));
|
||||
data.mPositionDescendants.Construct().Assign(std::move(descendants));
|
||||
}
|
||||
if (mSessionStore->IsFormDataChanged()) {
|
||||
nsTArray<CollectedInputDataValue> dataWithId, dataWithXpath;
|
||||
nsTArray<InputFormData> inputs =
|
||||
mSessionStore->GetInputs(dataWithId, dataWithXpath);
|
||||
nsTArray<int> descendants, numId, numXPath;
|
||||
nsTArray<nsString> innerHTML;
|
||||
nsTArray<nsCString> url;
|
||||
|
||||
if (dataWithId.Length() != 0) {
|
||||
SessionStoreUtils::ComposeInputData(dataWithId, data.mId.Construct());
|
||||
}
|
||||
if (dataWithXpath.Length() != 0) {
|
||||
SessionStoreUtils::ComposeInputData(dataWithXpath,
|
||||
data.mXpath.Construct());
|
||||
}
|
||||
|
||||
for (const InputFormData& input : inputs) {
|
||||
descendants.AppendElement(input.descendants);
|
||||
numId.AppendElement(input.numId);
|
||||
numXPath.AppendElement(input.numXPath);
|
||||
innerHTML.AppendElement(input.innerHTML);
|
||||
url.AppendElement(input.url);
|
||||
}
|
||||
if (descendants.Length() != 0) {
|
||||
data.mInputDescendants.Construct().Assign(std::move(descendants));
|
||||
data.mNumId.Construct().Assign(std::move(numId));
|
||||
data.mNumXPath.Construct().Assign(std::move(numXPath));
|
||||
data.mInnerHTML.Construct().Assign(std::move(innerHTML));
|
||||
data.mUrl.Construct().Assign(std::move(url));
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISessionStoreFunctions> funcs =
|
||||
do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");
|
||||
|
@ -515,6 +649,8 @@ void TabListener::RemoveListeners() {
|
|||
if (eventTarget) {
|
||||
eventTarget->RemoveSystemEventListener(
|
||||
NS_LITERAL_STRING("mozvisualscroll"), this, false);
|
||||
eventTarget->RemoveSystemEventListener(NS_LITERAL_STRING("input"), this,
|
||||
false);
|
||||
mEventListenerRegistered = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIPrivacyTransitionObserver.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "SessionStoreData.h"
|
||||
|
||||
class nsITimer;
|
||||
|
||||
|
@ -31,10 +32,16 @@ class ContentSessionStore {
|
|||
bool IsScrollPositionChanged() { return mScrollChanged != NO_CHANGE; }
|
||||
void GetScrollPositions(nsTArray<nsCString>& aPositions,
|
||||
nsTArray<int32_t>& aPositionDescendants);
|
||||
void SetFormDataChanged() { mFormDataChanged = WITH_CHANGE; }
|
||||
bool IsFormDataChanged() { return mFormDataChanged != NO_CHANGE; }
|
||||
nsTArray<InputFormData> GetInputs(
|
||||
nsTArray<CollectedInputDataValue>& aIdVals,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals);
|
||||
void OnDocumentStart();
|
||||
void OnDocumentEnd();
|
||||
bool UpdateNeeded() {
|
||||
return mPrivateChanged || mDocCapChanged || IsScrollPositionChanged();
|
||||
return mPrivateChanged || mDocCapChanged || IsScrollPositionChanged() ||
|
||||
IsFormDataChanged();
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -48,7 +55,8 @@ class ContentSessionStore {
|
|||
NO_CHANGE,
|
||||
PAGELOADEDSTART, // set when the state of document is STATE_START
|
||||
WITH_CHANGE, // set when the change event is observed
|
||||
} mScrollChanged;
|
||||
} mScrollChanged,
|
||||
mFormDataChanged;
|
||||
bool mDocCapChanged;
|
||||
nsCString mDocCaps;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* 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_SessionStoreMessageUtils_h
|
||||
#define mozilla_dom_SessionStoreMessageUtils_h
|
||||
|
||||
#include "ipc/IPCMessageUtils.h"
|
||||
#include "SessionStoreData.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
template <>
|
||||
struct ParamTraits<mozilla::dom::CollectedNonMultipleSelectValue> {
|
||||
typedef mozilla::dom::CollectedNonMultipleSelectValue paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam) {
|
||||
WriteParam(aMsg, aParam.mSelectedIndex);
|
||||
WriteParam(aMsg, aParam.mValue);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||
paramType* aResult) {
|
||||
return ReadParam(aMsg, aIter, &aResult->mSelectedIndex) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mValue);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<CollectedInputDataValue> {
|
||||
typedef CollectedInputDataValue paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam) {
|
||||
WriteParam(aMsg, aParam.id);
|
||||
WriteParam(aMsg, aParam.type);
|
||||
WriteParam(aMsg, aParam.value);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||
paramType* aResult) {
|
||||
return ReadParam(aMsg, aIter, &aResult->id) &&
|
||||
ReadParam(aMsg, aIter, &aResult->type) &&
|
||||
ReadParam(aMsg, aIter, &aResult->value);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ParamTraits<InputFormData> {
|
||||
typedef InputFormData paramType;
|
||||
|
||||
static void Write(Message* aMsg, const paramType& aParam) {
|
||||
WriteParam(aMsg, aParam.descendants);
|
||||
WriteParam(aMsg, aParam.innerHTML);
|
||||
WriteParam(aMsg, aParam.url);
|
||||
WriteParam(aMsg, aParam.numId);
|
||||
WriteParam(aMsg, aParam.numXPath);
|
||||
}
|
||||
|
||||
static bool Read(const Message* aMsg, PickleIterator* aIter,
|
||||
paramType* aResult) {
|
||||
return ReadParam(aMsg, aIter, &aResult->descendants) &&
|
||||
ReadParam(aMsg, aIter, &aResult->innerHTML) &&
|
||||
ReadParam(aMsg, aIter, &aResult->url) &&
|
||||
ReadParam(aMsg, aIter, &aResult->numId) &&
|
||||
ReadParam(aMsg, aIter, &aResult->numXPath);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace IPC
|
||||
|
||||
#endif // mozilla_dom_SessionStoreMessageUtils_h
|
|
@ -375,15 +375,189 @@ AppendEntryToCollectedData(nsINode* aNode, const nsAString& aId,
|
|||
return entry;
|
||||
}
|
||||
|
||||
/*
|
||||
@param aDocument: DOMDocument instance to obtain form data for.
|
||||
@param aGeneratedCount: the current number of XPath expressions in the
|
||||
returned object.
|
||||
@return aRetVal: Form data encoded in an object.
|
||||
*/
|
||||
static void CollectFromTextAreaElement(Document& aDocument,
|
||||
// A helper function to append a element into aXPathVals or aIdVals
|
||||
static void AppendEntryToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId, CollectedInputDataValue& aEntry,
|
||||
uint16_t& aNumXPath, uint16_t& aNumId,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
if (!aId.IsEmpty()) {
|
||||
aEntry.id = aId;
|
||||
aIdVals.AppendElement(aEntry);
|
||||
aNumId++;
|
||||
} else {
|
||||
nsAutoString xpath;
|
||||
aNode->GenerateXPath(xpath);
|
||||
aEntry.id = xpath;
|
||||
aXPathVals.AppendElement(aEntry);
|
||||
aNumXPath++;
|
||||
}
|
||||
}
|
||||
|
||||
/* for bool value */
|
||||
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
|
||||
const bool& aValue,
|
||||
uint16_t& aGeneratedCount,
|
||||
JSContext* aCx,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsBoolean() = aValue;
|
||||
}
|
||||
|
||||
/* for bool value */
|
||||
static void AppendValueToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId, const bool& aValue,
|
||||
uint16_t& aNumXPath, uint16_t& aNumId,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
CollectedInputDataValue entry;
|
||||
entry.type = NS_LITERAL_STRING("bool");
|
||||
entry.value = AsVariant(aValue);
|
||||
AppendEntryToCollectedData(aNode, aId, entry, aNumXPath, aNumId, aXPathVals,
|
||||
aIdVals);
|
||||
}
|
||||
|
||||
/* for nsString value */
|
||||
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
|
||||
const nsString& aValue,
|
||||
uint16_t& aGeneratedCount,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsString() = aValue;
|
||||
}
|
||||
|
||||
/* for nsString value */
|
||||
static void AppendValueToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId, const nsString& aValue,
|
||||
uint16_t& aNumXPath, uint16_t& aNumId,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
CollectedInputDataValue entry;
|
||||
entry.type = NS_LITERAL_STRING("string");
|
||||
entry.value = AsVariant(aValue);
|
||||
AppendEntryToCollectedData(aNode, aId, entry, aNumXPath, aNumId, aXPathVals,
|
||||
aIdVals);
|
||||
}
|
||||
|
||||
/* for single select value */
|
||||
static void AppendValueToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId,
|
||||
const CollectedNonMultipleSelectValue& aValue, uint16_t& aGeneratedCount,
|
||||
JSContext* aCx, Nullable<CollectedData>& aRetVal) {
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (!ToJSValue(aCx, aValue, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
}
|
||||
|
||||
/* for single select value */
|
||||
static void AppendValueToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId,
|
||||
const CollectedNonMultipleSelectValue& aValue, uint16_t& aNumXPath,
|
||||
uint16_t& aNumId, nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
CollectedInputDataValue entry;
|
||||
entry.type = NS_LITERAL_STRING("singleSelect");
|
||||
entry.value = AsVariant(aValue);
|
||||
AppendEntryToCollectedData(aNode, aId, entry, aNumXPath, aNumId, aXPathVals,
|
||||
aIdVals);
|
||||
}
|
||||
|
||||
/* special handing for input element with string type */
|
||||
static void AppendValueToCollectedData(Document& aDocument, nsINode* aNode,
|
||||
const nsAString& aId,
|
||||
const nsString& aValue,
|
||||
uint16_t& aGeneratedCount,
|
||||
JSContext* aCx,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
if (!aId.IsEmpty()) {
|
||||
// We want to avoid saving data for about:sessionrestore as a string.
|
||||
// Since it's stored in the form as stringified JSON, stringifying
|
||||
// further causes an explosion of escape characters. cf. bug 467409
|
||||
if (aId.EqualsLiteral("sessionData")) {
|
||||
nsAutoCString url;
|
||||
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
|
||||
if (url.EqualsLiteral("about:sessionrestore") ||
|
||||
url.EqualsLiteral("about:welcomeback")) {
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (JS_ParseJSON(aCx, aValue.get(), aValue.Length(), &jsval) &&
|
||||
jsval.isObject()) {
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
} else {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
AppendValueToCollectedData(aNode, aId, aValue, aGeneratedCount, aRetVal);
|
||||
}
|
||||
|
||||
static void AppendValueToCollectedData(
|
||||
Document& aDocument, nsINode* aNode, const nsAString& aId,
|
||||
const nsString& aValue, uint16_t& aNumXPath, uint16_t& aNumId,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
CollectedInputDataValue entry;
|
||||
entry.type = NS_LITERAL_STRING("string");
|
||||
entry.value = AsVariant(aValue);
|
||||
AppendEntryToCollectedData(aNode, aId, entry, aNumXPath, aNumId, aXPathVals,
|
||||
aIdVals);
|
||||
}
|
||||
|
||||
/* for nsTArray<nsString>: file and multipleSelect */
|
||||
static void AppendValueToCollectedData(nsINode* aNode, const nsAString& aId,
|
||||
const nsAString& aValueType,
|
||||
nsTArray<nsString>& aValue,
|
||||
uint16_t& aGeneratedCount,
|
||||
JSContext* aCx,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (aValueType.EqualsLiteral("file")) {
|
||||
CollectedFileListValue val;
|
||||
val.mType = aValueType;
|
||||
val.mFileList.SwapElements(aValue);
|
||||
if (!ToJSValue(aCx, val, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (!ToJSValue(aCx, aValue, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(aNode, aId, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
}
|
||||
|
||||
/* for nsTArray<nsString>: file and multipleSelect */
|
||||
static void AppendValueToCollectedData(
|
||||
nsINode* aNode, const nsAString& aId, const nsAString& aValueType,
|
||||
const nsTArray<nsString>& aValue, uint16_t& aNumXPath, uint16_t& aNumId,
|
||||
nsTArray<CollectedInputDataValue>& aXPathVals,
|
||||
nsTArray<CollectedInputDataValue>& aIdVals) {
|
||||
CollectedInputDataValue entry;
|
||||
entry.type = aValueType;
|
||||
entry.value = AsVariant(aValue);
|
||||
AppendEntryToCollectedData(aNode, aId, entry, aNumXPath, aNumId, aXPathVals,
|
||||
aIdVals);
|
||||
}
|
||||
|
||||
/* static */
|
||||
template <typename... ArgsT>
|
||||
void SessionStoreUtils::CollectFromTextAreaElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args) {
|
||||
RefPtr<nsContentList> textlist = NS_GetContentList(
|
||||
&aDocument, kNameSpaceID_XHTML, NS_LITERAL_STRING("textarea"));
|
||||
uint32_t length = textlist->Length(true);
|
||||
|
@ -405,7 +579,7 @@ static void CollectFromTextAreaElement(Document& aDocument,
|
|||
if (id.IsEmpty() && (aGeneratedCount > kMaxTraversedXPaths)) {
|
||||
continue;
|
||||
}
|
||||
nsAutoString value;
|
||||
nsString value;
|
||||
textArea->GetValue(value);
|
||||
// In order to reduce XPath generation (which is slow), we only save data
|
||||
// for form fields that have been changed. (cf. bug 537289)
|
||||
|
@ -413,21 +587,16 @@ static void CollectFromTextAreaElement(Document& aDocument,
|
|||
eCaseMatters)) {
|
||||
continue;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(textArea, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsString() = value;
|
||||
AppendValueToCollectedData(textArea, id, value, aGeneratedCount,
|
||||
std::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@param aDocument: DOMDocument instance to obtain form data for.
|
||||
@param aGeneratedCount: the current number of XPath expressions in the
|
||||
returned object.
|
||||
@return aRetVal: Form data encoded in an object.
|
||||
*/
|
||||
static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
/* static */
|
||||
template <typename... ArgsT>
|
||||
void SessionStoreUtils::CollectFromInputElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args) {
|
||||
RefPtr<nsContentList> inputlist = NS_GetContentList(
|
||||
&aDocument, kNameSpaceID_XHTML, NS_LITERAL_STRING("input"));
|
||||
uint32_t length = inputlist->Length(true);
|
||||
|
@ -461,16 +630,15 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
|
|||
if (!aInfo.IsNull() && !aInfo.Value().mCanAutomaticallyPersist) {
|
||||
continue;
|
||||
}
|
||||
nsAutoString value;
|
||||
|
||||
if (input->ControlType() == NS_FORM_INPUT_CHECKBOX ||
|
||||
input->ControlType() == NS_FORM_INPUT_RADIO) {
|
||||
bool checked = input->Checked();
|
||||
if (checked == input->DefaultChecked()) {
|
||||
continue;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsBoolean() = checked;
|
||||
AppendValueToCollectedData(input, id, checked, aGeneratedCount,
|
||||
std::forward<ArgsT>(args)...);
|
||||
} else if (input->ControlType() == NS_FORM_INPUT_FILE) {
|
||||
IgnoredErrorResult rv;
|
||||
nsTArray<nsString> result;
|
||||
|
@ -478,19 +646,10 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
|
|||
if (rv.Failed() || result.Length() == 0) {
|
||||
continue;
|
||||
}
|
||||
CollectedFileListValue val;
|
||||
val.mType = NS_LITERAL_STRING("file");
|
||||
val.mFileList.SwapElements(result);
|
||||
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (!ToJSValue(aCx, val, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
continue;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
AppendValueToCollectedData(input, id, NS_LITERAL_STRING("file"), result,
|
||||
aGeneratedCount, std::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
nsString value;
|
||||
input->GetValue(value, CallerType::System);
|
||||
// In order to reduce XPath generation (which is slow), we only save data
|
||||
// for form fields that have been changed. (cf. bug 537289)
|
||||
|
@ -501,45 +660,17 @@ static void CollectFromInputElement(JSContext* aCx, Document& aDocument,
|
|||
eCaseMatters)) {
|
||||
continue;
|
||||
}
|
||||
if (!id.IsEmpty()) {
|
||||
// We want to avoid saving data for about:sessionrestore as a string.
|
||||
// Since it's stored in the form as stringified JSON, stringifying
|
||||
// further causes an explosion of escape characters. cf. bug 467409
|
||||
if (id.EqualsLiteral("sessionData")) {
|
||||
nsAutoCString url;
|
||||
Unused << aDocument.GetDocumentURI()->GetSpecIgnoringRef(url);
|
||||
if (url.EqualsLiteral("about:sessionrestore") ||
|
||||
url.EqualsLiteral("about:welcomeback")) {
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (JS_ParseJSON(aCx, value.get(), value.Length(), &jsval) &&
|
||||
jsval.isObject()) {
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType*
|
||||
entry = AppendEntryToCollectedData(input, id, aGeneratedCount,
|
||||
aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
} else {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(input, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsString() = value;
|
||||
AppendValueToCollectedData(aDocument, input, id, value, aGeneratedCount,
|
||||
std::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@param aDocument: DOMDocument instance to obtain form data for.
|
||||
@param aGeneratedCount: the current number of XPath expressions in the
|
||||
returned object.
|
||||
@return aRetVal: Form data encoded in an object.
|
||||
*/
|
||||
static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
Nullable<CollectedData>& aRetVal) {
|
||||
/* static */
|
||||
template <typename... ArgsT>
|
||||
void SessionStoreUtils::CollectFromSelectElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args) {
|
||||
RefPtr<nsContentList> selectlist = NS_GetContentList(
|
||||
&aDocument, kNameSpaceID_XHTML, NS_LITERAL_STRING("select"));
|
||||
uint32_t length = selectlist->Length(true);
|
||||
|
@ -569,15 +700,8 @@ static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
|
|||
CollectedNonMultipleSelectValue val;
|
||||
val.mSelectedIndex = select->SelectedIndex();
|
||||
val.mValue = selectVal.AsAString();
|
||||
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (!ToJSValue(aCx, val, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
continue;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(select, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
AppendValueToCollectedData(select, id, val, aGeneratedCount,
|
||||
std::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
// <select>s with the multiple attribute are easier to determine the
|
||||
// default value since each <option> has a defaultSelected property
|
||||
|
@ -603,14 +727,10 @@ static void CollectFromSelectElement(JSContext* aCx, Document& aDocument,
|
|||
if (hasDefaultValue) {
|
||||
continue;
|
||||
}
|
||||
JS::Rooted<JS::Value> jsval(aCx);
|
||||
if (!ToJSValue(aCx, selectslist, &jsval)) {
|
||||
JS_ClearPendingException(aCx);
|
||||
continue;
|
||||
}
|
||||
Record<nsString, OwningStringOrBooleanOrObject>::EntryType* entry =
|
||||
AppendEntryToCollectedData(select, id, aGeneratedCount, aRetVal);
|
||||
entry->mValue.SetAsObject() = &jsval.toObject();
|
||||
|
||||
AppendValueToCollectedData(
|
||||
select, id, NS_LITERAL_STRING("multipleSelect"), selectslist,
|
||||
aGeneratedCount, std::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -619,11 +739,14 @@ static void CollectCurrentFormData(JSContext* aCx, Document& aDocument,
|
|||
Nullable<CollectedData>& aRetVal) {
|
||||
uint16_t generatedCount = 0;
|
||||
/* textarea element */
|
||||
CollectFromTextAreaElement(aDocument, generatedCount, aRetVal);
|
||||
SessionStoreUtils::CollectFromTextAreaElement(aDocument, generatedCount,
|
||||
aRetVal);
|
||||
/* input element */
|
||||
CollectFromInputElement(aCx, aDocument, generatedCount, aRetVal);
|
||||
SessionStoreUtils::CollectFromInputElement(aDocument, generatedCount, aCx,
|
||||
aRetVal);
|
||||
/* select element */
|
||||
CollectFromSelectElement(aCx, aDocument, generatedCount, aRetVal);
|
||||
SessionStoreUtils::CollectFromSelectElement(aDocument, generatedCount, aCx,
|
||||
aRetVal);
|
||||
|
||||
Element* bodyElement = aDocument.GetBody();
|
||||
if (aDocument.HasFlag(NODE_IS_EDITABLE) && bodyElement) {
|
||||
|
@ -1168,11 +1291,9 @@ static void CollectFrameTreeData(JSContext* aCx,
|
|||
SequenceRooter<JSObject*> rooter(aCx, &childrenData);
|
||||
uint32_t trailingNullCounter = 0;
|
||||
|
||||
nsTArray<RefPtr<BrowsingContext>> children;
|
||||
aBrowsingContext->GetChildren(children);
|
||||
for (uint32_t i = 0; i < children.Length(); i++) {
|
||||
for (auto& child : aBrowsingContext->GetChildren()) {
|
||||
NullableRootedDictionary<CollectedData> data(aCx);
|
||||
CollectFrameTreeData(aCx, children[i], data, aFunc);
|
||||
CollectFrameTreeData(aCx, child, data, aFunc);
|
||||
if (data.IsNull()) {
|
||||
childrenData.AppendElement(nullptr);
|
||||
trailingNullCounter++;
|
||||
|
@ -1206,3 +1327,61 @@ static void CollectFrameTreeData(JSContext* aCx,
|
|||
CollectFrameTreeData(aGlobal.Context(), aWindow.get(), aRetVal,
|
||||
CollectCurrentFormData);
|
||||
}
|
||||
|
||||
/* static */ void SessionStoreUtils::ComposeInputData(
|
||||
const nsTArray<CollectedInputDataValue>& aData, InputElementData& ret) {
|
||||
nsTArray<int> selectedIndex, valueIdx;
|
||||
nsTArray<nsString> id, selectVal, strVal, type;
|
||||
nsTArray<bool> boolVal;
|
||||
|
||||
for (const CollectedInputDataValue& data : aData) {
|
||||
id.AppendElement(data.id);
|
||||
type.AppendElement(data.type);
|
||||
|
||||
if (data.value.is<mozilla::dom::CollectedNonMultipleSelectValue>()) {
|
||||
valueIdx.AppendElement(selectVal.Length());
|
||||
selectedIndex.AppendElement(
|
||||
data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
|
||||
.mSelectedIndex);
|
||||
selectVal.AppendElement(
|
||||
data.value.as<mozilla::dom::CollectedNonMultipleSelectValue>()
|
||||
.mValue);
|
||||
} else if (data.value.is<nsTArray<nsString>>()) {
|
||||
// The first valueIdx is "index of the first string value"
|
||||
valueIdx.AppendElement(strVal.Length());
|
||||
strVal.AppendElements(data.value.as<nsTArray<nsString>>());
|
||||
// The second valueIdx is "index of the last string value" + 1
|
||||
id.AppendElement(data.id);
|
||||
type.AppendElement(data.type);
|
||||
valueIdx.AppendElement(strVal.Length());
|
||||
} else if (data.value.is<nsString>()) {
|
||||
valueIdx.AppendElement(strVal.Length());
|
||||
strVal.AppendElement(data.value.as<nsString>());
|
||||
} else if (data.type.EqualsLiteral("bool")) {
|
||||
valueIdx.AppendElement(boolVal.Length());
|
||||
boolVal.AppendElement(data.value.as<bool>());
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedIndex.Length() != 0) {
|
||||
ret.mSelectedIndex.Construct().Assign(std::move(selectedIndex));
|
||||
}
|
||||
if (valueIdx.Length() != 0) {
|
||||
ret.mValueIdx.Construct().Assign(std::move(valueIdx));
|
||||
}
|
||||
if (id.Length() != 0) {
|
||||
ret.mId.Construct().Assign(std::move(id));
|
||||
}
|
||||
if (selectVal.Length() != 0) {
|
||||
ret.mSelectVal.Construct().Assign(std::move(selectVal));
|
||||
}
|
||||
if (strVal.Length() != 0) {
|
||||
ret.mStrVal.Construct().Assign(std::move(strVal));
|
||||
}
|
||||
if (type.Length() != 0) {
|
||||
ret.mType.Construct().Assign(std::move(type));
|
||||
}
|
||||
if (boolVal.Length() != 0) {
|
||||
ret.mBoolVal.Construct().Assign(std::move(boolVal));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/SessionStoreUtilsBinding.h"
|
||||
#include "SessionStoreData.h"
|
||||
|
||||
class nsIDocument;
|
||||
class nsGlobalWindowInner;
|
||||
|
@ -54,6 +55,24 @@ class SessionStoreUtils {
|
|||
nsGlobalWindowInner& aWindow,
|
||||
const CollectedData& data);
|
||||
|
||||
/*
|
||||
@param aDocument: DOMDocument instance to obtain form data for.
|
||||
@param aGeneratedCount: the current number of XPath expressions in the
|
||||
returned object.
|
||||
*/
|
||||
template <typename... ArgsT>
|
||||
static void CollectFromTextAreaElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args);
|
||||
template <typename... ArgsT>
|
||||
static void CollectFromInputElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args);
|
||||
template <typename... ArgsT>
|
||||
static void CollectFromSelectElement(Document& aDocument,
|
||||
uint16_t& aGeneratedCount,
|
||||
ArgsT&&... args);
|
||||
|
||||
static void CollectFormData(const GlobalObject& aGlobal,
|
||||
WindowProxyHolder& aWindow,
|
||||
Nullable<CollectedData>& aRetVal);
|
||||
|
@ -69,6 +88,9 @@ class SessionStoreUtils {
|
|||
static void RestoreSessionStorage(
|
||||
const GlobalObject& aGlobal, nsIDocShell* aDocShell,
|
||||
const Record<nsString, Record<nsString, nsString>>& aData);
|
||||
|
||||
static void ComposeInputData(const nsTArray<CollectedInputDataValue>& aData,
|
||||
InputElementData& ret);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS.mozilla.dom += [
|
||||
'SessionStoreData.h',
|
||||
'SessionStoreListener.h',
|
||||
'SessionStoreMessageUtils.h',
|
||||
'SessionStoreUtils.h',
|
||||
]
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче