services/sync/tps/extensions/mozmill/resource/modules/windows.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

     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 var EXPORTED_SYMBOLS = ["init", "map"];
     7 const Cc = Components.classes;
     8 const Ci = Components.interfaces;
     9 const Cu = Components.utils;
    11 // imports
    12 var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
    14 var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
    16 /**
    17  * The window map is used to store information about the current state of
    18  * open windows, e.g. loaded state
    19  */
    20 var map = {
    21   _windows : { },
    23   /**
    24    * Check if a given window id is contained in the map of windows
    25    *
    26    * @param {Number} aWindowId
    27    *        Outer ID of the window to check.
    28    * @returns {Boolean} True if the window is part of the map, otherwise false.
    29    */
    30   contains : function (aWindowId) {
    31     return (aWindowId in this._windows);
    32   },
    34   /**
    35    * Retrieve the value of the specified window's property.
    36    *
    37    * @param {Number} aWindowId
    38    *        Outer ID of the window to check.
    39    * @param {String} aProperty
    40    *        Property to retrieve the value from
    41    * @return {Object} Value of the window's property
    42    */
    43   getValue : function (aWindowId, aProperty) {
    44     if (!this.contains(aWindowId)) {
    45       return undefined;
    46     } else {
    47       var win = this._windows[aWindowId];
    49       return (aProperty in win) ? win[aProperty]
    50                                 : undefined;
    51     }
    52   },
    54   /**
    55    * Remove the entry for a given window
    56    *
    57    * @param {Number} aWindowId
    58    *        Outer ID of the window to check.
    59    */
    60   remove : function (aWindowId) {
    61     if (this.contains(aWindowId)) {
    62       delete this._windows[aWindowId];
    63     }
    65     // dump("* current map: " + JSON.stringify(this._windows) + "\n");
    66   },
    68   /**
    69    * Update the property value of a given window
    70    *
    71    * @param {Number} aWindowId
    72    *        Outer ID of the window to check.
    73    * @param {String} aProperty
    74    *        Property to update the value for
    75    * @param {Object}
    76    *        Value to set
    77    */
    78   update : function (aWindowId, aProperty, aValue) {
    79     if (!this.contains(aWindowId)) {
    80       this._windows[aWindowId] = { };
    81     }
    83     this._windows[aWindowId][aProperty] = aValue;
    84     // dump("* current map: " + JSON.stringify(this._windows) + "\n");
    85   },
    87   /**
    88    * Update the internal loaded state of the given content window. To identify
    89    * an active (re)load action we make use of an uuid.
    90    *
    91    * @param {Window} aId - The outer id of the window to update
    92    * @param {Boolean} aIsLoaded - Has the window been loaded
    93    */
    94   updatePageLoadStatus : function (aId, aIsLoaded) {
    95     this.update(aId, "loaded", aIsLoaded);
    97     var uuid = this.getValue(aId, "id_load_in_transition");
    99     // If no uuid has been set yet or when the page gets unloaded create a new id
   100     if (!uuid || !aIsLoaded) {
   101       uuid = uuidgen.generateUUID();
   102       this.update(aId, "id_load_in_transition", uuid);
   103     }
   105     // dump("*** Page status updated: id=" + aId + ", loaded=" + aIsLoaded + ", uuid=" + uuid + "\n");
   106   },
   108   /**
   109    * This method only applies to content windows, where we have to check if it has
   110    * been successfully loaded or reloaded. An uuid allows us to wait for the next
   111    * load action triggered by e.g. controller.open().
   112    *
   113    * @param {Window} aId - The outer id of the content window to check
   114    *
   115    * @returns {Boolean} True if the content window has been loaded
   116    */
   117   hasPageLoaded : function (aId) {
   118     var load_current = this.getValue(aId, "id_load_in_transition");
   119     var load_handled = this.getValue(aId, "id_load_handled");
   121     var isLoaded = this.contains(aId) && this.getValue(aId, "loaded") &&
   122                    (load_current !== load_handled);
   124     if (isLoaded) {
   125       // Backup the current uuid so we can check later if another page load happened.
   126       this.update(aId, "id_load_handled", load_current);
   127     }
   129     // dump("** Page has been finished loading: id=" + aId + ", status=" + isLoaded + ", uuid=" + load_current + "\n");
   131     return isLoaded;
   132   }
   133 };
   136 // Observer when a new top-level window is ready
   137 var windowReadyObserver = {
   138   observe: function (aSubject, aTopic, aData) {
   139     // Not in all cases we get a ChromeWindow. So ensure we really operate
   140     // on such an instance. Otherwise load events will not be handled.
   141     var win = utils.getChromeWindow(aSubject);
   143     // var id = utils.getWindowId(win);
   144     // dump("*** 'toplevel-window-ready' observer notification: id=" + id + "\n");
   145     attachEventListeners(win);
   146   }
   147 };
   150 // Observer when a top-level window is closed
   151 var windowCloseObserver = {
   152   observe: function (aSubject, aTopic, aData) {
   153     var id = utils.getWindowId(aSubject);
   154     // dump("*** 'outer-window-destroyed' observer notification: id=" + id + "\n");
   156     map.remove(id);
   157   }
   158 };
   160 // Bug 915554
   161 // Support for the old Private Browsing Mode (eg. ESR17)
   162 // TODO: remove once ESR17 is no longer supported
   163 var enterLeavePrivateBrowsingObserver = {
   164   observe: function (aSubject, aTopic, aData) {
   165     handleAttachEventListeners();
   166   }
   167 };
   169 /**
   170  * Attach event listeners
   171  *
   172  * @param {ChromeWindow} aWindow
   173  *        Window to attach listeners on.
   174  */
   175 function attachEventListeners(aWindow) {
   176   // These are the event handlers
   177   var pageShowHandler = function (aEvent) {
   178     var doc = aEvent.originalTarget;
   180     // Only update the flag if we have a document as target
   181     // see https://bugzilla.mozilla.org/show_bug.cgi?id=690829
   182     if ("defaultView" in doc) {
   183       var id = utils.getWindowId(doc.defaultView);
   184       // dump("*** 'pageshow' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
   185       map.updatePageLoadStatus(id, true);
   186     }
   188     // We need to add/remove the unload/pagehide event listeners to preserve caching.
   189     aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
   190     aWindow.addEventListener("pagehide", pageHideHandler, true);
   191   };
   193   var DOMContentLoadedHandler = function (aEvent) {
   194     var doc = aEvent.originalTarget;
   196     // Only update the flag if we have a document as target
   197     if ("defaultView" in doc) {
   198       var id = utils.getWindowId(doc.defaultView);
   199       // dump("*** 'DOMContentLoaded' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
   201       // We only care about error pages for DOMContentLoaded
   202       var errorRegex = /about:.+(error)|(blocked)\?/;
   203       if (errorRegex.exec(doc.baseURI)) {
   204         // Wait about 1s to be sure the DOM is ready
   205         utils.sleep(1000);
   207         map.updatePageLoadStatus(id, true);
   208       }
   210       // We need to add/remove the unload event listener to preserve caching.
   211       aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
   212     }
   213   };
   215   // beforeunload is still needed because pagehide doesn't fire before the page is unloaded.
   216   // still use pagehide for cases when beforeunload doesn't get fired
   217   var beforeUnloadHandler = function (aEvent) {
   218     var doc = aEvent.originalTarget;
   220     // Only update the flag if we have a document as target
   221     if ("defaultView" in doc) {
   222       var id = utils.getWindowId(doc.defaultView);
   223       // dump("*** 'beforeunload' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
   224       map.updatePageLoadStatus(id, false);
   225     }
   227     aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
   228   };
   230   var pageHideHandler = function (aEvent) {
   231     var doc = aEvent.originalTarget;
   233     // Only update the flag if we have a document as target
   234     if ("defaultView" in doc) {
   235       var id = utils.getWindowId(doc.defaultView);
   236       // dump("*** 'pagehide' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
   237       map.updatePageLoadStatus(id, false);
   238     }
   239     // If event.persisted is true the beforeUnloadHandler would never fire
   240     // and we have to remove the event handler here to avoid memory leaks.
   241     if (aEvent.persisted)
   242       aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
   243   };
   245   var onWindowLoaded = function (aEvent) {
   246     var id = utils.getWindowId(aWindow);
   247     // dump("*** 'load' event: id=" + id + ", baseURI=" + aWindow.document.baseURI + "\n");
   249     map.update(id, "loaded", true);
   251     // Note: Error pages will never fire a "pageshow" event. For those we
   252     // have to wait for the "DOMContentLoaded" event. That's the final state.
   253     // Error pages will always have a baseURI starting with
   254     // "about:" followed by "error" or "blocked".
   255     aWindow.addEventListener("DOMContentLoaded", DOMContentLoadedHandler, true);
   257     // Page is ready
   258     aWindow.addEventListener("pageshow", pageShowHandler, true);
   260     // Leave page (use caching)
   261     aWindow.addEventListener("pagehide", pageHideHandler, true);
   262   };
   264   // If the window has already been finished loading, call the load handler
   265   // directly. Otherwise attach it to the current window.
   266   if (aWindow.document.readyState === 'complete') {
   267     onWindowLoaded();
   268   } else {
   269     aWindow.addEventListener("load", onWindowLoaded, false);
   270   }
   271 }
   273 // Attach event listeners to all already open top-level windows
   274 function handleAttachEventListeners() {
   275   var enumerator = Cc["@mozilla.org/appshell/window-mediator;1"].
   276                    getService(Ci.nsIWindowMediator).getEnumerator("");
   277   while (enumerator.hasMoreElements()) {
   278     var win = enumerator.getNext();
   279     attachEventListeners(win);
   280   }
   281 }
   283 function init() {
   284   // Activate observer for new top level windows
   285   var observerService = Cc["@mozilla.org/observer-service;1"].
   286                         getService(Ci.nsIObserverService);
   287   observerService.addObserver(windowReadyObserver, "toplevel-window-ready", false);
   288   observerService.addObserver(windowCloseObserver, "outer-window-destroyed", false);
   289   observerService.addObserver(enterLeavePrivateBrowsingObserver, "private-browsing", false);
   291   handleAttachEventListeners();
   292 }

mercurial