/* 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 = ["PageStyle"];

const Ci = Components.interfaces;

/**
 * The external API exported by this module.
 */
this.PageStyle = Object.freeze({
  collect: function (docShell, frameTree) {
    return PageStyleInternal.collect(docShell, frameTree);
  },

  restoreTree: function (docShell, data) {
    PageStyleInternal.restoreTree(docShell, data);
  }
});

// Signifies that author style level is disabled for the page.
const NO_STYLE = "_nostyle";

var PageStyleInternal = {
  /**
   * Collects the selected style sheet sets for all reachable frames.
   */
  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;

    if (markupDocumentViewer.authorStyleDisabled) {
      result = result || {};
      result.disabled = true;
    }

    return result && Object.keys(result).length ? result : null;
  },

  /**
   * 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;
    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);
  }
};
