--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -3,22 +3,18 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var EXPORTED_SYMBOLS = ["ContentRestore"];
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
-ChromeUtils.defineModuleGetter(this, "DocShellCapabilities",
- "resource:///modules/sessionstore/DocShellCapabilities.jsm");
ChromeUtils.defineModuleGetter(this, "FormData",
"resource://gre/modules/FormData.jsm");
-ChromeUtils.defineModuleGetter(this, "ScrollPosition",
- "resource://gre/modules/ScrollPosition.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
@@ -163,18 +159,17 @@ ContentRestoreInternal.prototype = {
this.restoreTabContent(null, false, callbacks.onLoadFinished);
});
webNavigation.sessionHistory.legacySHistory.addSHistoryListener(listener);
this._historyListener = listener;
// Make sure to reset the capabilities and attributes in case this tab gets
// reused.
- let disallow = new Set(tabData.disallow && tabData.disallow.split(","));
- DocShellCapabilities.restore(this.docShell, disallow);
+ ssu.restoreDocShellCapabilities(this.docShell, tabData.disallow);
if (tabData.storage && this.docShell instanceof Ci.nsIDocShell) {
SessionStorage.restore(this.docShell, tabData.storage);
delete tabData.storage;
}
// Add a progress listener to correctly handle browser.loadURI()
// calls from foreign code.
@@ -322,17 +317,17 @@ ContentRestoreInternal.prototype = {
// current |frame| and its descendants, if |data.url| is given but
// doesn't match the loaded document's URL.
return FormData.restore(frame, data);
});
// Restore scroll data.
restoreFrameTreeData(window, scrollPositions, (frame, data) => {
if (data.scroll) {
- ScrollPosition.restore(frame, data.scroll);
+ ssu.restoreScrollPosition(frame, data.scroll);
}
});
},
/**
* Cancel an ongoing restore. This function can be called any time between
* restoreHistory and restoreDocument.
*
--- a/browser/components/sessionstore/ContentSessionStore.jsm
+++ b/browser/components/sessionstore/ContentSessionStore.jsm
@@ -17,20 +17,16 @@ function debug(msg) {
Services.console.logStringMessage("SessionStoreContent: " + msg);
}
ChromeUtils.defineModuleGetter(this, "FormData",
"resource://gre/modules/FormData.jsm");
ChromeUtils.defineModuleGetter(this, "ContentRestore",
"resource:///modules/sessionstore/ContentRestore.jsm");
-ChromeUtils.defineModuleGetter(this, "DocShellCapabilities",
- "resource:///modules/sessionstore/DocShellCapabilities.jsm");
-ChromeUtils.defineModuleGetter(this, "ScrollPosition",
- "resource://gre/modules/ScrollPosition.jsm");
ChromeUtils.defineModuleGetter(this, "SessionHistory",
"resource://gre/modules/sessionstore/SessionHistory.jsm");
ChromeUtils.defineModuleGetter(this, "SessionStorage",
"resource:///modules/sessionstore/SessionStorage.jsm");
ChromeUtils.defineModuleGetter(this, "Utils",
"resource://gre/modules/sessionstore/Utils.jsm");
const ssu = Cc["@mozilla.org/browser/sessionstore/utils;1"]
@@ -348,17 +344,17 @@ class ScrollPositionListener extends Han
this.messageQueue.push("scroll", () => this.collect());
}
onPageLoadStarted() {
this.messageQueue.push("scroll", () => null);
}
collect() {
- return mapFrameTree(this.mm, ScrollPosition.collect);
+ return mapFrameTree(this.mm, ssu.collectScrollPosition.bind(ssu));
}
}
/**
* 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.
*
@@ -413,19 +409,17 @@ class DocShellCapabilitiesListener exten
* that have just been collected. If nothing changed we won't send a message.
*/
this._latestCapabilities = "";
this.stateChangeNotifier.addObserver(this);
}
onPageLoadStarted() {
- // The order of docShell capabilities cannot change while we're running
- // so calling join() without sorting before is totally sufficient.
- let caps = DocShellCapabilities.collect(this.mm.docShell).join(",");
+ let caps = ssu.collectDocShellCapabilities(this.mm.docShell);
// Send new data only when the capability list changes.
if (caps != this._latestCapabilities) {
this._latestCapabilities = caps;
this.messageQueue.push("disallow", () => caps || null);
}
}
}
deleted file mode 100644
--- a/browser/components/sessionstore/DocShellCapabilities.jsm
+++ /dev/null
@@ -1,53 +0,0 @@
-/* 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";
-
-var EXPORTED_SYMBOLS = ["DocShellCapabilities"];
-
-/**
- * The external API exported by this module.
- */
-var DocShellCapabilities = Object.freeze({
- collect(docShell) {
- return DocShellCapabilitiesInternal.collect(docShell);
- },
-
- restore(docShell, disallow) {
- return DocShellCapabilitiesInternal.restore(docShell, disallow);
- },
-});
-
-const CAPABILITIES_TO_IGNORE = new Set(["Javascript"]);
-
-/**
- * Internal functionality to save and restore the docShell.allow* properties.
- */
-var DocShellCapabilitiesInternal = {
- // List of docShell capabilities to (re)store. These are automatically
- // retrieved from a given docShell if not already collected before.
- // This is made so they're automatically in sync with all nsIDocShell.allow*
- // properties.
- caps: null,
-
- allCapabilities(docShell) {
- if (!this.caps) {
- let keys = Object.keys(docShell);
- this.caps = keys.filter(k => k.startsWith("allow")).map(k => k.slice(5));
- }
- return this.caps;
- },
-
- collect(docShell) {
- let caps = this.allCapabilities(docShell);
- return caps.filter(cap => !docShell["allow" + cap]
- && !CAPABILITIES_TO_IGNORE.has(cap));
- },
-
- restore(docShell, disallow) {
- let caps = this.allCapabilities(docShell);
- for (let cap of caps)
- docShell["allow" + cap] = !disallow.has(cap);
- },
-};
--- a/browser/components/sessionstore/moz.build
+++ b/browser/components/sessionstore/moz.build
@@ -7,17 +7,16 @@
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
JAR_MANIFESTS += ['jar.mn']
EXTRA_JS_MODULES.sessionstore = [
'ContentRestore.jsm',
'ContentSessionStore.jsm',
- 'DocShellCapabilities.jsm',
'GlobalState.jsm',
'RecentlyClosedTabsAndWindowsMenuUtils.jsm',
'RunState.jsm',
'SessionCookies.jsm',
'SessionFile.jsm',
'SessionMigration.jsm',
'SessionSaver.jsm',
'SessionStartup.jsm',
--- a/toolkit/components/sessionstore/nsISessionStoreUtils.idl
+++ b/toolkit/components/sessionstore/nsISessionStoreUtils.idl
@@ -1,16 +1,19 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface mozIDOMWindowProxy;
+interface nsIDocShell;
webidl EventTarget;
+webidl Document;
+interface mozIDOMWindow;
/**
* A callback passed to nsISessionStoreUtils.forEachNonDynamicChildFrame().
*/
[function, scriptable, uuid(8199ebf7-76c0-43d6-bcbe-913dd3de3ebf)]
interface nsISessionStoreUtilsFrameCallback : nsISupports
{
/**
@@ -59,9 +62,39 @@ interface nsISessionStoreUtils : nsISupp
* This is needed, instead of the normal removeEventListener, because the
* caller doesn't actually have something that WebIDL considers an
* EventListener.
*/
void removeDynamicFrameFilteredListener(in EventTarget target,
in AString type,
in nsISupports listener,
in boolean useCapture);
+
+ /*
+ * Save the docShell.allow* properties
+ */
+ ACString collectDocShellCapabilities(in nsIDocShell docShell);
+
+ /*
+ * Restore the docShell.allow* properties
+ */
+ void restoreDocShellCapabilities(in nsIDocShell docShell,
+ in ACString disallowCapabilities);
+
+ /**
+ * Collects scroll position data for any given |frame| in the frame hierarchy.
+ *
+ * @param document (DOMDocument)
+ *
+ * @return {scroll: "x,y"} e.g. {scroll: "100,200"}
+ * Returns null when there is no scroll data we want to store for the
+ * given |frame|.
+ */
+ ACString collectScrollPosition(in Document document);
+
+ /**
+ * Restores scroll position data for any given |frame| in the frame hierarchy.
+ *
+ * @param frame (DOMWindow)
+ * @param value (ACString)
+ */
+ void restoreScrollPosition(in mozIDOMWindow frame, in ACString data);
};
--- a/toolkit/components/sessionstore/nsSessionStoreUtils.cpp
+++ b/toolkit/components/sessionstore/nsSessionStoreUtils.cpp
@@ -5,16 +5,21 @@
#include "nsSessionStoreUtils.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/EventListenerBinding.h"
#include "mozilla/dom/EventTarget.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsPIDOMWindow.h"
#include "nsIDocShell.h"
+#include "nsGlobalWindowOuter.h"
+#include "nsIScrollableFrame.h"
+#include "nsPresContext.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsPrintfCString.h"
using namespace mozilla::dom;
namespace {
class DynamicFrameEventFilter final : public nsIDOMEventListener
{
public:
@@ -153,8 +158,133 @@ nsSessionStoreUtils::RemoveDynamicFrameF
NS_ENSURE_TRUE(aTarget, NS_ERROR_NO_INTERFACE);
nsCOMPtr<nsIDOMEventListener> listener = do_QueryInterface(aListener);
NS_ENSURE_TRUE(listener, NS_ERROR_NO_INTERFACE);
aTarget->RemoveEventListener(aType, listener, aUseCapture);
return NS_OK;
}
+
+NS_IMETHODIMP
+nsSessionStoreUtils::CollectDocShellCapabilities(nsIDocShell* aDocShell,
+ nsACString& aDisallowCapabilities)
+{
+ bool allow;
+
+#define TRY_ALLOWPROP(y) \
+ PR_BEGIN_MACRO \
+ aDocShell->GetAllow##y(&allow); \
+ if (!allow) { \
+ if (!aDisallowCapabilities.IsEmpty()) { \
+ aDisallowCapabilities.Append(','); \
+ } \
+ aDisallowCapabilities.Append(#y); \
+ } \
+ PR_END_MACRO
+
+ TRY_ALLOWPROP(Plugins);
+ // Bug 1328013 : Don't collect "AllowJavascript" property
+ // TRY_ALLOWPROP(Javascript);
+ TRY_ALLOWPROP(MetaRedirects);
+ TRY_ALLOWPROP(Subframes);
+ TRY_ALLOWPROP(Images);
+ TRY_ALLOWPROP(Media);
+ TRY_ALLOWPROP(DNSPrefetch);
+ TRY_ALLOWPROP(WindowControl);
+ TRY_ALLOWPROP(Auth);
+ TRY_ALLOWPROP(ContentRetargeting);
+ TRY_ALLOWPROP(ContentRetargetingOnChildren);
+
+#undef TRY_ALLOWPROP
+
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSessionStoreUtils::RestoreDocShellCapabilities(nsIDocShell* aDocShell,
+ const nsACString& aDisallowCapabilities)
+{
+ aDocShell->SetAllowPlugins(true);
+ aDocShell->SetAllowJavascript(true);
+ aDocShell->SetAllowMetaRedirects(true);
+ aDocShell->SetAllowSubframes(true);
+ aDocShell->SetAllowImages(true);
+ aDocShell->SetAllowMedia(true);
+ aDocShell->SetAllowDNSPrefetch(true);
+ aDocShell->SetAllowWindowControl(true);
+ aDocShell->SetAllowContentRetargeting(true);
+ aDocShell->SetAllowContentRetargetingOnChildren(true);
+
+ nsCCharSeparatedTokenizer tokenizer(aDisallowCapabilities, ',');
+ while (tokenizer.hasMoreTokens()) {
+ const nsACString& token = tokenizer.nextToken();
+ if (token.EqualsLiteral("Plugins")) {
+ aDocShell->SetAllowPlugins(false);
+ } else if (token.EqualsLiteral("Javascript")) {
+ aDocShell->SetAllowJavascript(false);
+ } else if (token.EqualsLiteral("MetaRedirects")) {
+ aDocShell->SetAllowMetaRedirects(false);
+ } else if (token.EqualsLiteral("Subframes")) {
+ aDocShell->SetAllowSubframes(false);
+ } else if (token.EqualsLiteral("Images")) {
+ aDocShell->SetAllowImages(false);
+ } else if (token.EqualsLiteral("Media")) {
+ aDocShell->SetAllowMedia(false);
+ } else if (token.EqualsLiteral("DNSPrefetch")) {
+ aDocShell->SetAllowDNSPrefetch(false);
+ } else if (token.EqualsLiteral("WindowControl")) {
+ aDocShell->SetAllowWindowControl(false);
+ } else if (token.EqualsLiteral("ContentRetargeting")) {
+ bool allow;
+ aDocShell->GetAllowContentRetargetingOnChildren(&allow);
+ aDocShell->SetAllowContentRetargeting(false); //will also set AllowContentRetargetingOnChildren
+ aDocShell->SetAllowContentRetargetingOnChildren(allow); // restore the allowProp to original
+ } else if (token.EqualsLiteral("ContentRetargetingOnChildren")) {
+ aDocShell->SetAllowContentRetargetingOnChildren(false);
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSessionStoreUtils::CollectScrollPosition(nsIDocument* aDocument,
+ nsACString& aRet)
+{
+ aRet.Truncate();
+
+ nsIPresShell* presShell = aDocument->GetShell();
+ if (!presShell) {
+ return NS_OK;
+ }
+
+ nsIScrollableFrame* frame = presShell->GetRootScrollFrameAsScrollable();
+ if (!frame) {
+ return NS_OK;
+ }
+
+ nsPoint scrollPos = frame->GetScrollPosition();
+ int scrollX = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.x);
+ int scrollY = nsPresContext::AppUnitsToIntCSSPixels(scrollPos.y);
+
+ if ((scrollX != 0) || (scrollY != 0)) {
+ const nsPrintfCString position("%d,%d", scrollX, scrollY);
+ aRet.Assign(position);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSessionStoreUtils::RestoreScrollPosition(mozIDOMWindow* aWindow,
+ const nsACString& aPos)
+{
+ nsCCharSeparatedTokenizer tokenizer(aPos, ',');
+ nsAutoCString token(tokenizer.nextToken());
+ int pos_X = atoi(token.get());
+ token = tokenizer.nextToken();
+ int pos_Y = atoi(token.get());
+ nsGlobalWindowInner::Cast(aWindow)->ScrollTo(pos_X, pos_Y);
+
+ return NS_OK;
+}
--- a/toolkit/modules/sessionstore/FormData.jsm
+++ b/toolkit/modules/sessionstore/FormData.jsm
@@ -127,17 +127,17 @@ var FormDataInternal = {
* }
* }
*
* @param doc
* DOMDocument instance to obtain form data for.
* @return object
* Form data encoded in an object.
*/
- collect({document: doc}) {
+ collect(doc) {
let formNodes = doc.evaluate(
this.restorableFormNodesXPath,
doc,
this.resolveNS.bind(this),
doc.defaultView.XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null
);
let node;
--- a/toolkit/modules/sessionstore/Utils.jsm
+++ b/toolkit/modules/sessionstore/Utils.jsm
@@ -156,17 +156,28 @@ var Utils = Object.freeze({
* wish to save for that respective frame.
* @return {object[]} An array with one entry per dataCollector, containing
* the collected data as a nested data structure according
* to the layout of the frame tree, or null if no data was
* returned by the respective dataCollector.
*/
mapFrameTree(frame, ...dataCollectors) {
// Collect data for the current frame.
- let objs = dataCollectors.map((dataCollector) => dataCollector(frame) || {});
+ let objs = dataCollectors.map(function(dataCollector) {
+ let obj = dataCollector(frame.document);
+ if (!obj || typeof(obj) == "object") {
+ return obj || {};
+ }
+ // Currently, we return string type when collecting scroll position.
+ // Will switched to webidl and return objects in the future.
+ if (typeof(obj) == "string") {
+ return {scroll: obj};
+ }
+ return obj;
+ });
let children = dataCollectors.map(() => []);
// Recurse into child frames.
ssu.forEachNonDynamicChildFrame(frame, (subframe, index) => {
let results = this.mapFrameTree(subframe, ...dataCollectors);
if (!results) {
return;
}