browser/modules/SharedFrame.jsm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 this.EXPORTED_SYMBOLS = [ "SharedFrame" ];
     9 const Ci = Components.interfaces;
    10 const Cu = Components.utils;
    12 /**
    13  * The purpose of this module is to create and group various iframe
    14  * elements that are meant to all display the same content and only
    15  * one at a time. This makes it possible to have the content loaded
    16  * only once, while the other iframes can be kept as placeholders to
    17  * quickly move the content to them through the swapFrameLoaders function
    18  * when another one of the placeholder is meant to be displayed.
    19  * */
    21 let Frames = new Map();
    23 /**
    24  * The Frames map is the main data structure that holds information
    25  * about the groups being tracked. Each entry's key is the group name,
    26  * and the object holds information about what is the URL being displayed
    27  * on that group, and what is the active element on the group (the frame that
    28  * holds the loaded content).
    29  * The reference to the activeFrame is a weak reference, which allows the
    30  * frame to go away at any time, and when that happens the module considers that
    31  * there are no active elements in that group. The group can be reactivated
    32  * by changing the URL, calling preload again or adding a new element.
    33  *
    34  *
    35  *  Frames = {
    36  *    "messages-panel": {
    37  *      url: string,
    38  *      activeFrame: weakref
    39  *    }
    40  *  }
    41  *
    42  * Each object on the map is called a _SharedFrameGroup, which is an internal
    43  * class of this module which does not automatically keep track of its state. This
    44  * object should not be used externally, and all control should be handled by the
    45  * module's functions.
    46  */
    48 function UNLOADED_URL(aStr) "data:text/html;charset=utf-8,<!-- Unloaded frame " + aStr + " -->";
    51 this.SharedFrame = {
    52   /**
    53    * Creates an iframe element and track it as part of the specified group
    54    * The module must create the iframe itself because it needs to do some special
    55    * handling for the element's src attribute.
    56    *
    57    * @param aGroupName        the name of the group to which this frame belongs
    58    * @param aParent           the parent element to which the frame will be appended to
    59    * @param aFrameAttributes  an object with a list of attributes to set in the iframe
    60    *                          before appending it to the DOM. The "src" attribute has
    61    *                          special meaning here and if it's not blank it specifies
    62    *                          the URL that will be initially assigned to this group
    63    * @param aPreload          optional, tells if the URL specified in the src attribute
    64    *                          should be preloaded in the frame being created, in case
    65    *                          it's not yet preloaded in any other frame of the group.
    66    *                          This parameter has no meaning if src is blank.
    67    */
    68   createFrame: function (aGroupName, aParent, aFrameAttributes, aPreload = true) {
    69     let frame = aParent.ownerDocument.createElement("iframe");
    71     for (let [key, val] of Iterator(aFrameAttributes)) {
    72       frame.setAttribute(key, val);
    73     }
    75     let src = aFrameAttributes.src;
    76     if (!src) {
    77       aPreload = false;
    78     }
    80     let group = Frames.get(aGroupName);
    82     if (group) {
    83       // If this group has already been created
    85       if (aPreload && !group.isAlive) {
    86         // If aPreload is set and the group is not already loaded, load it.
    87         // This can happen if:
    88         // - aPreload was not used while creating the previous frames of this group, or
    89         // - the previously active frame went dead in the meantime
    90         group.url = src;
    91         this.preload(aGroupName, frame);
    92       } else {
    93         // If aPreload is not set, or the group is already loaded in a different frame,
    94         // there's not much that we need to do here: just create this frame as an
    95         // inactivate placeholder
    96         frame.setAttribute("src", UNLOADED_URL(aGroupName));
    97       }
    99     } else {
   100       // This is the first time we hear about this group, so let's start tracking it,
   101       // and also preload it if the src attribute was set and aPreload = true
   102       group = new _SharedFrameGroup(src);
   103       Frames.set(aGroupName, group);
   105       if (aPreload) {
   106         this.preload(aGroupName, frame);
   107       } else {
   108         frame.setAttribute("src", UNLOADED_URL(aGroupName));
   109       }
   110     }
   112     aParent.appendChild(frame);
   113     return frame;
   115   },
   117   /**
   118    * Function that moves the loaded content from one active frame to
   119    * another one that is currently a placeholder. If there's no active
   120    * frame in the group, the content is loaded/reloaded.
   121    *
   122    * @param aGroupName   the name of the group
   123    * @param aTargetFrame the frame element to which the content should
   124    *                     be moved to.
   125    */
   126   setOwner: function (aGroupName, aTargetFrame) {
   127     let group = Frames.get(aGroupName);
   128     let frame = group.activeFrame;
   130     if (frame == aTargetFrame) {
   131       // nothing to do here
   132       return;
   133     }
   135     if (group.isAlive) {
   136       // Move document ownership to the desired frame, and make it the active one
   137       frame.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(aTargetFrame);
   138       group.activeFrame = aTargetFrame;
   139     } else {
   140       // Previous owner was dead, reload the document at the new owner and make it the active one
   141       aTargetFrame.setAttribute("src", group.url);
   142       group.activeFrame = aTargetFrame;
   143     }
   144   },
   146   /**
   147    * Updates the current URL in use by this group, and loads it into the active frame.
   148    *
   149    * @param aGroupName  the name of the group
   150    * @param aURL        the new url
   151    */
   152   updateURL: function (aGroupName, aURL) {
   153     let group = Frames.get(aGroupName);
   154     group.url = aURL;
   156     if (group.isAlive) {
   157       group.activeFrame.setAttribute("src", aURL);
   158     }
   159   },
   161   /**
   162    * Loads the group's url into a target frame, if the group doesn't have a currently
   163    * active frame.
   164    *
   165    * @param aGroupName    the name of the group
   166    * @param aTargetFrame  the frame element which should be made active and
   167    *                      have the group's content loaded to
   168    */
   169   preload: function (aGroupName, aTargetFrame) {
   170     let group = Frames.get(aGroupName);
   171     if (!group.isAlive) {
   172       aTargetFrame.setAttribute("src", group.url);
   173       group.activeFrame = aTargetFrame;
   174     }
   175   },
   177   /**
   178    * Tells if a group currently have an active element.
   179    *
   180    * @param aGroupName  the name of the group
   181    */
   182   isGroupAlive: function (aGroupName) {
   183     return Frames.get(aGroupName).isAlive;
   184   },
   186   /**
   187    * Forgets about this group. This function doesn't need to be used
   188    * unless the group's name needs to be reused.
   189    *
   190    * @param aGroupName  the name of the group
   191    */
   192   forgetGroup: function (aGroupName) {
   193     Frames.delete(aGroupName);
   194   }
   195 }
   198 function _SharedFrameGroup(aURL) {
   199   this.url = aURL;
   200   this._activeFrame = null;
   201 }
   203 _SharedFrameGroup.prototype = {
   204   get isAlive() {
   205     let frame = this.activeFrame;
   206     return !!(frame &&
   207               frame.contentDocument &&
   208               frame.contentDocument.location);
   209   },
   211   get activeFrame() {
   212     return this._activeFrame &&
   213            this._activeFrame.get();
   214   },
   216   set activeFrame(aActiveFrame) {
   217     this._activeFrame = aActiveFrame
   218                         ? Cu.getWeakReference(aActiveFrame)
   219                         : null;
   220   }
   221 }

mercurial