author | Tim Taubert <ttaubert@mozilla.com> |
Thu, 19 Dec 2013 12:21:51 +0100 | |
changeset 161355 | 9f97b68c6bdf00bfcfd246be4bd8d5d9985da36a |
parent 161354 | 0ecaf7b5bc926a7c12e84e37187f83fe766ba151 |
child 161356 | 2898275c1c43545f04d54e8843fc302cc4e9148a |
push id | 25878 |
push user | kwierso@gmail.com |
push date | Fri, 20 Dec 2013 03:09:21 +0000 |
treeherder | mozilla-central@599100c4ebfe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | yoric |
bugs | 950132 |
milestone | 29.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/components/sessionstore/content/content-sessionStore.js +++ b/browser/components/sessionstore/content/content-sessionStore.js @@ -239,30 +239,44 @@ let ScrollPositionListener = { }; /** * Listens for changes to the page style. Whenever a different page style is * selected or author styles are enabled/disabled we send a message with the * 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]) }; /** * Listens for changes to docShell capabilities. Whenever a new load is started * we need to re-check the list of capabilities and send message when it has * changed.
--- a/browser/components/sessionstore/src/PageStyle.jsm +++ b/browser/components/sessionstore/src/PageStyle.jsm @@ -7,71 +7,57 @@ this.EXPORTED_SYMBOLS = ["PageStyle"]; 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. 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; }, /** * Restore the selected style sheet of all the frames in frameList * to match |pageStyle|. * @param docShell the root docshell of all the frames * @param frameList a list of [frame, data] pairs, where frame is a * DOM window and data is the session restore data associated with @@ -86,9 +72,56 @@ let PageStyleInternal = { markupDocumentViewer.authorStyleDisabled = disabled; for (let [frame, data] of frameList) { Array.forEach(frame.document.styleSheets, function(aSS) { aSS.disabled = aSS.title && aSS.title != pageStyle; }); } }, + + /** + * 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); + } };
--- a/browser/components/sessionstore/src/SessionStore.jsm +++ b/browser/components/sessionstore/src/SessionStore.jsm @@ -2971,21 +2971,27 @@ let SessionStoreInternal = { restoreDocument: function ssi_restoreDocument(aWindow, aBrowser, aEvent) { // wait for the top frame to be loaded completely if (!aEvent || !aEvent.originalTarget || !aEvent.originalTarget.defaultView || aEvent.originalTarget.defaultView != aEvent.originalTarget.defaultView.top) { return; } 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; // Drop all the state associated with restoring the tab. We're // done with that now. delete aBrowser.__SS_data; delete aBrowser.__SS_restore_data;
--- a/browser/components/sessionstore/test/browser_pageStyle.js +++ b/browser/components/sessionstore/test/browser_pageStyle.js @@ -56,17 +56,18 @@ add_task(function nested_page_style() { let tab = gBrowser.addTab(URL_NESTED); let browser = tab.linkedBrowser; yield promiseBrowserLoaded(browser); yield enableSubDocumentStyleSheetsForSet(browser, "alternate"); 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) { return sendMessage(browser, "ss-test:getStyleSheets"); } function enableStyleSheetsForSet(browser, name) { return sendMessage(browser, "ss-test:enableStyleSheetsForSet", name);
--- a/browser/components/sessionstore/test/browser_pageStyle_sample.html +++ b/browser/components/sessionstore/test/browser_pageStyle_sample.html @@ -6,14 +6,11 @@ <link href="404.css" title="default" rel="stylesheet"> <link href="404.css" title="alternate" rel="alternate stylesheet"> <link href="404.css" title="altERnate" rel=" styLEsheet altERnate "> <link href="404.css" title="media_empty" rel="alternate stylesheet" media=""> <link href="404.css" title="media_all" rel="alternate stylesheet" media="all"> <link href="404.css" title="media_ALL" rel="alternate stylesheet" media=" ALL "> <link href="404.css" title="media_screen" rel="alternate stylesheet" media="screen"> <link href="404.css" title="media_print_screen" rel="alternate stylesheet" media="print,screen"> - <link href="404.css" title="fail_media_print" rel="alternate stylesheet" media="print"> - <link href="404.css" title="fail_media_projection" rel="stylesheet" media="projection"> - <link href="404.css" title="fail_media_invalid" rel="alternate stylesheet" media="hallo"> </head> <body></body> </html>