diff --git a/browser/components/sessionstore/content/content-sessionStore.js b/browser/components/sessionstore/content/content-sessionStore.js
index 4fb77aa4722..9b592daabc3 100644
--- a/browser/components/sessionstore/content/content-sessionStore.js
+++ b/browser/components/sessionstore/content/content-sessionStore.js
@@ -244,20 +244,34 @@ let ScrollPositionListener = {
* currently applied style to the chrome process.
*
* Causes a SessionStore:update message to be sent that contains the currently
- * selected pageStyle, if any. The pageStyle is represented by a string.
+ * selected pageStyle for all reachable frames.
+ *
+ * Example:
+ * {pageStyle: "Dusk", children: [null, {pageStyle: "Mozilla"}]}
*/
let PageStyleListener = {
init: function () {
Services.obs.addObserver(this, "author-style-disabled-changed", true);
Services.obs.addObserver(this, "style-sheet-applicable-state-changed", true);
+ gFrameTree.addObserver(this);
},
observe: function (subject, topic) {
- if (subject.defaultView && subject.defaultView.top == content) {
- MessageQueue.push("pageStyle", () => PageStyle.collect(docShell) || null);
+ let frame = subject.defaultView;
+
+ if (frame && gFrameTree.contains(frame)) {
+ MessageQueue.push("pageStyle", () => this.collect());
}
},
+ collect: function () {
+ return PageStyle.collect(docShell, gFrameTree);
+ },
+
+ onFrameTreeReset: function () {
+ MessageQueue.push("pageStyle", () => null);
+ },
+
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference])
};
diff --git a/browser/components/sessionstore/src/PageStyle.jsm b/browser/components/sessionstore/src/PageStyle.jsm
index 0907d24242d..c677adc5dc8 100644
--- a/browser/components/sessionstore/src/PageStyle.jsm
+++ b/browser/components/sessionstore/src/PageStyle.jsm
@@ -12,13 +12,17 @@ const Ci = Components.interfaces;
* The external API exported by this module.
*/
this.PageStyle = Object.freeze({
- collect: function (docShell) {
- return PageStyleInternal.collect(docShell);
+ collect: function (docShell, frameTree) {
+ return PageStyleInternal.collect(docShell, frameTree);
},
restore: function (docShell, frameList, pageStyle) {
PageStyleInternal.restore(docShell, frameList, pageStyle);
},
+
+ restoreTree: function (docShell, data) {
+ PageStyleInternal.restoreTree(docShell, data);
+ }
});
// Signifies that author style level is disabled for the page.
@@ -26,47 +30,29 @@ const NO_STYLE = "_nostyle";
let PageStyleInternal = {
/**
- * Find out the title of the style sheet selected for the given
- * docshell. Recurse into frames if needed.
+ * Collects the selected style sheet sets for all reachable frames.
*/
- collect: function (docShell) {
+ collect: function (docShell, frameTree) {
+ let result = frameTree.map(({document: doc}) => {
+ let style;
+
+ if (doc) {
+ // http://dev.w3.org/csswg/cssom/#persisting-the-selected-css-style-sheet-set
+ style = doc.selectedStyleSheetSet || doc.lastStyleSheetSet;
+ }
+
+ return style ? {pageStyle: style} : null;
+ });
+
let markupDocumentViewer =
docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
+
if (markupDocumentViewer.authorStyleDisabled) {
- return NO_STYLE;
+ result = result || {};
+ result.disabled = true;
}
- let content = docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
-
- return this.collectFrame(content);
- },
-
- /**
- * Determine the title of the currently enabled style sheet (if any);
- * recurse through the frameset if necessary.
- * @param content is a frame reference
- * @returns the title style sheet determined to be enabled (empty string if none)
- */
- collectFrame: function (content) {
- const forScreen = /(?:^|,)\s*(?:all|screen)\s*(?:,|$)/i;
-
- let sheets = content.document.styleSheets;
- for (let i = 0; i < sheets.length; i++) {
- let ss = sheets[i];
- let media = ss.media.mediaText;
- if (!ss.disabled && ss.title && (!media || forScreen.test(media))) {
- return ss.title;
- }
- }
-
- for (let i = 0; i < content.frames.length; i++) {
- let selectedPageStyle = this.collectFrame(content.frames[i]);
- if (selectedPageStyle) {
- return selectedPageStyle;
- }
- }
-
- return "";
+ return result && Object.keys(result).length ? result : null;
},
/**
@@ -91,4 +77,51 @@ let PageStyleInternal = {
});
}
},
+
+ /**
+ * Restores pageStyle data for the current frame hierarchy starting at the
+ * |docShell's| current DOMWindow using the given pageStyle |data|.
+ *
+ * Warning: If the current frame hierarchy doesn't match that of the given
+ * |data| object we will silently discard data for unreachable frames. We may
+ * as well assign page styles to the wrong frames if some were reordered or
+ * removed.
+ *
+ * @param docShell (nsIDocShell)
+ * @param data (object)
+ * {
+ * disabled: true, // when true, author styles will be disabled
+ * pageStyle: "Dusk",
+ * children: [
+ * null,
+ * {pageStyle: "Mozilla", children: [ ... ]}
+ * ]
+ * }
+ */
+ restoreTree: function (docShell, data) {
+ let disabled = data.disabled || false;
+ let markupDocumentViewer =
+ docShell.contentViewer.QueryInterface(Ci.nsIMarkupDocumentViewer);
+ markupDocumentViewer.authorStyleDisabled = disabled;
+
+ function restoreFrame(root, data) {
+ if (data.hasOwnProperty("pageStyle")) {
+ root.document.selectedStyleSheetSet = data.pageStyle;
+ }
+
+ if (!data.hasOwnProperty("children")) {
+ return;
+ }
+
+ let frames = root.frames;
+ data.children.forEach((child, index) => {
+ if (child && index < frames.length) {
+ restoreFrame(frames[index], child);
+ }
+ });
+ }
+
+ let ifreq = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
+ restoreFrame(ifreq.getInterface(Ci.nsIDOMWindow), data);
+ }
};
diff --git a/browser/components/sessionstore/src/SessionStore.jsm b/browser/components/sessionstore/src/SessionStore.jsm
index b12f0d2c4d9..ebea9743661 100644
--- a/browser/components/sessionstore/src/SessionStore.jsm
+++ b/browser/components/sessionstore/src/SessionStore.jsm
@@ -2976,11 +2976,17 @@ let SessionStoreInternal = {
}
let frameList = this.getFramesToRestore(aBrowser);
- let pageStyle = RestoreData.get(aBrowser, "pageStyle") || "";
+ let pageStyle = RestoreData.get(aBrowser, "pageStyle") || {};
let scrollPositions = RestoreData.get(aBrowser, "scroll") || {};
- PageStyle.restore(aBrowser.docShell, frameList, pageStyle);
- ScrollPosition.restoreTree(aBrowser.contentWindow, scrollPositions);
+ // Support the old pageStyle format.
+ if (typeof(pageStyle) === "string") {
+ PageStyle.restore(aBrowser.docShell, frameList, pageStyle);
+ } else {
+ ScrollPosition.restoreTree(aBrowser.contentWindow, scrollPositions);
+ }
+
+ PageStyle.restoreTree(aBrowser.docShell, pageStyle);
TextAndScrollData.restore(frameList);
let tab = aBrowser.__SS_restore_tab;
diff --git a/browser/components/sessionstore/test/browser_pageStyle.js b/browser/components/sessionstore/test/browser_pageStyle.js
index 155cd47ce0f..7a3a9b19963 100644
--- a/browser/components/sessionstore/test/browser_pageStyle.js
+++ b/browser/components/sessionstore/test/browser_pageStyle.js
@@ -61,7 +61,8 @@ add_task(function nested_page_style() {
gBrowser.removeTab(tab);
let [{state: {pageStyle}}] = JSON.parse(ss.getClosedTabData(window));
- is(pageStyle, "alternate", "correct pageStyle persisted");
+ let expected = JSON.stringify({children: [{pageStyle: "alternate"}]});
+ is(JSON.stringify(pageStyle), expected, "correct pageStyle persisted");
});
function getStyleSheets(browser) {
diff --git a/browser/components/sessionstore/test/browser_pageStyle_sample.html b/browser/components/sessionstore/test/browser_pageStyle_sample.html
index dd2758f0089..810054049b3 100644
--- a/browser/components/sessionstore/test/browser_pageStyle_sample.html
+++ b/browser/components/sessionstore/test/browser_pageStyle_sample.html
@@ -11,9 +11,6 @@
-
-
-