diff -r 000000000000 -r 6474c204b198 toolkit/modules/sessionstore/FormData.jsm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/toolkit/modules/sessionstore/FormData.jsm Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,364 @@ +/* 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/. */ + +"use strict"; + +this.EXPORTED_SYMBOLS = ["FormData"]; + +const Cu = Components.utils; +const Ci = Components.interfaces; + +Cu.import("resource://gre/modules/Timer.jsm"); +Cu.import("resource://gre/modules/XPathGenerator.jsm"); + +/** + * Returns whether the given URL very likely has input + * fields that contain serialized session store data. + */ +function isRestorationPage(url) { + return url == "about:sessionrestore" || url == "about:welcomeback"; +} + +/** + * Returns whether the given form |data| object contains nested restoration + * data for a page like about:sessionrestore or about:welcomeback. + */ +function hasRestorationData(data) { + if (isRestorationPage(data.url) && data.id) { + return typeof(data.id.sessionData) == "object"; + } + + return false; +} + +/** + * Returns the given document's current URI and strips + * off the URI's anchor part, if any. + */ +function getDocumentURI(doc) { + return doc.documentURI.replace(/#.*$/, ""); +} + +/** + * The public API exported by this module that allows to collect + * and restore form data for a document and its subframes. + */ +this.FormData = Object.freeze({ + collect: function (frame) { + return FormDataInternal.collect(frame); + }, + + restore: function (frame, data) { + FormDataInternal.restore(frame, data); + }, + + restoreTree: function (root, data) { + FormDataInternal.restoreTree(root, data); + } +}); + +/** + * This module's internal API. + */ +let FormDataInternal = { + /** + * Collect form data for a given |frame| *not* including any subframes. + * + * The returned object may have an "id", "xpath", or "innerHTML" key or a + * combination of those three. Form data stored under "id" is for input + * fields with id attributes. Data stored under "xpath" is used for input + * fields that don't have a unique id and need to be queried using XPath. + * The "innerHTML" key is used for editable documents (designMode=on). + * + * Example: + * { + * id: {input1: "value1", input3: "value3"}, + * xpath: { + * "/xhtml:html/xhtml:body/xhtml:input[@name='input2']" : "value2", + * "/xhtml:html/xhtml:body/xhtml:input[@name='input4']" : "value4" + * } + * } + * + * @param doc + * DOMDocument instance to obtain form data for. + * @return object + * Form data encoded in an object. + */ + collect: function ({document: doc}) { + let formNodes = doc.evaluate( + XPathGenerator.restorableFormNodes, + doc, + XPathGenerator.resolveNS, + Ci.nsIDOMXPathResult.UNORDERED_NODE_ITERATOR_TYPE, null + ); + + let node; + let ret = {}; + + // Limit the number of XPath expressions for performance reasons. See + // bug 477564. + const MAX_TRAVERSED_XPATHS = 100; + let generatedCount = 0; + + while (node = formNodes.iterateNext()) { + let hasDefaultValue = true; + let value; + + // Only generate a limited number of XPath expressions for perf reasons + // (cf. bug 477564) + if (!node.id && generatedCount > MAX_TRAVERSED_XPATHS) { + continue; + } + + if (node instanceof Ci.nsIDOMHTMLInputElement || + node instanceof Ci.nsIDOMHTMLTextAreaElement || + node instanceof Ci.nsIDOMXULTextBoxElement) { + switch (node.type) { + case "checkbox": + case "radio": + value = node.checked; + hasDefaultValue = value == node.defaultChecked; + break; + case "file": + value = { type: "file", fileList: node.mozGetFileNameArray() }; + hasDefaultValue = !value.fileList.length; + break; + default: // text, textarea + value = node.value; + hasDefaultValue = value == node.defaultValue; + break; + } + } else if (!node.multiple) { + // s with the multiple attribute are easier to determine the + // default value since each