mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 08:48:39 +09:00
292 lines
9.6 KiB
JavaScript
292 lines
9.6 KiB
JavaScript
/* 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/. */
|
|
|
|
var EXPORTED_SYMBOLS = ["init", "map"];
|
|
|
|
var Cc = Components.classes;
|
|
var Ci = Components.interfaces;
|
|
var Cu = Components.utils;
|
|
|
|
// imports
|
|
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
|
|
|
|
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
|
|
|
|
/**
|
|
* The window map is used to store information about the current state of
|
|
* open windows, e.g. loaded state
|
|
*/
|
|
var map = {
|
|
_windows : { },
|
|
|
|
/**
|
|
* Check if a given window id is contained in the map of windows
|
|
*
|
|
* @param {Number} aWindowId
|
|
* Outer ID of the window to check.
|
|
* @returns {Boolean} True if the window is part of the map, otherwise false.
|
|
*/
|
|
contains : function (aWindowId) {
|
|
return (aWindowId in this._windows);
|
|
},
|
|
|
|
/**
|
|
* Retrieve the value of the specified window's property.
|
|
*
|
|
* @param {Number} aWindowId
|
|
* Outer ID of the window to check.
|
|
* @param {String} aProperty
|
|
* Property to retrieve the value from
|
|
* @return {Object} Value of the window's property
|
|
*/
|
|
getValue : function (aWindowId, aProperty) {
|
|
if (!this.contains(aWindowId)) {
|
|
return undefined;
|
|
} else {
|
|
var win = this._windows[aWindowId];
|
|
|
|
return (aProperty in win) ? win[aProperty]
|
|
: undefined;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Remove the entry for a given window
|
|
*
|
|
* @param {Number} aWindowId
|
|
* Outer ID of the window to check.
|
|
*/
|
|
remove : function (aWindowId) {
|
|
if (this.contains(aWindowId)) {
|
|
delete this._windows[aWindowId];
|
|
}
|
|
|
|
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
|
|
},
|
|
|
|
/**
|
|
* Update the property value of a given window
|
|
*
|
|
* @param {Number} aWindowId
|
|
* Outer ID of the window to check.
|
|
* @param {String} aProperty
|
|
* Property to update the value for
|
|
* @param {Object}
|
|
* Value to set
|
|
*/
|
|
update : function (aWindowId, aProperty, aValue) {
|
|
if (!this.contains(aWindowId)) {
|
|
this._windows[aWindowId] = { };
|
|
}
|
|
|
|
this._windows[aWindowId][aProperty] = aValue;
|
|
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
|
|
},
|
|
|
|
/**
|
|
* Update the internal loaded state of the given content window. To identify
|
|
* an active (re)load action we make use of an uuid.
|
|
*
|
|
* @param {Window} aId - The outer id of the window to update
|
|
* @param {Boolean} aIsLoaded - Has the window been loaded
|
|
*/
|
|
updatePageLoadStatus : function (aId, aIsLoaded) {
|
|
this.update(aId, "loaded", aIsLoaded);
|
|
|
|
var uuid = this.getValue(aId, "id_load_in_transition");
|
|
|
|
// If no uuid has been set yet or when the page gets unloaded create a new id
|
|
if (!uuid || !aIsLoaded) {
|
|
uuid = uuidgen.generateUUID();
|
|
this.update(aId, "id_load_in_transition", uuid);
|
|
}
|
|
|
|
// dump("*** Page status updated: id=" + aId + ", loaded=" + aIsLoaded + ", uuid=" + uuid + "\n");
|
|
},
|
|
|
|
/**
|
|
* This method only applies to content windows, where we have to check if it has
|
|
* been successfully loaded or reloaded. An uuid allows us to wait for the next
|
|
* load action triggered by e.g. controller.open().
|
|
*
|
|
* @param {Window} aId - The outer id of the content window to check
|
|
*
|
|
* @returns {Boolean} True if the content window has been loaded
|
|
*/
|
|
hasPageLoaded : function (aId) {
|
|
var load_current = this.getValue(aId, "id_load_in_transition");
|
|
var load_handled = this.getValue(aId, "id_load_handled");
|
|
|
|
var isLoaded = this.contains(aId) && this.getValue(aId, "loaded") &&
|
|
(load_current !== load_handled);
|
|
|
|
if (isLoaded) {
|
|
// Backup the current uuid so we can check later if another page load happened.
|
|
this.update(aId, "id_load_handled", load_current);
|
|
}
|
|
|
|
// dump("** Page has been finished loading: id=" + aId + ", status=" + isLoaded + ", uuid=" + load_current + "\n");
|
|
|
|
return isLoaded;
|
|
}
|
|
};
|
|
|
|
|
|
// Observer when a new top-level window is ready
|
|
var windowReadyObserver = {
|
|
observe: function (aSubject, aTopic, aData) {
|
|
// Not in all cases we get a ChromeWindow. So ensure we really operate
|
|
// on such an instance. Otherwise load events will not be handled.
|
|
var win = utils.getChromeWindow(aSubject);
|
|
|
|
// var id = utils.getWindowId(win);
|
|
// dump("*** 'toplevel-window-ready' observer notification: id=" + id + "\n");
|
|
attachEventListeners(win);
|
|
}
|
|
};
|
|
|
|
|
|
// Observer when a top-level window is closed
|
|
var windowCloseObserver = {
|
|
observe: function (aSubject, aTopic, aData) {
|
|
var id = utils.getWindowId(aSubject);
|
|
// dump("*** 'outer-window-destroyed' observer notification: id=" + id + "\n");
|
|
|
|
map.remove(id);
|
|
}
|
|
};
|
|
|
|
// Bug 915554
|
|
// Support for the old Private Browsing Mode (eg. ESR17)
|
|
// TODO: remove once ESR17 is no longer supported
|
|
var enterLeavePrivateBrowsingObserver = {
|
|
observe: function (aSubject, aTopic, aData) {
|
|
handleAttachEventListeners();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Attach event listeners
|
|
*
|
|
* @param {ChromeWindow} aWindow
|
|
* Window to attach listeners on.
|
|
*/
|
|
function attachEventListeners(aWindow) {
|
|
// These are the event handlers
|
|
var pageShowHandler = function (aEvent) {
|
|
var doc = aEvent.originalTarget;
|
|
|
|
// Only update the flag if we have a document as target
|
|
// see https://bugzilla.mozilla.org/show_bug.cgi?id=690829
|
|
if ("defaultView" in doc) {
|
|
var id = utils.getWindowId(doc.defaultView);
|
|
// dump("*** 'pageshow' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
|
|
map.updatePageLoadStatus(id, true);
|
|
}
|
|
|
|
// We need to add/remove the unload/pagehide event listeners to preserve caching.
|
|
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
|
|
aWindow.addEventListener("pagehide", pageHideHandler, true);
|
|
};
|
|
|
|
var DOMContentLoadedHandler = function (aEvent) {
|
|
var doc = aEvent.originalTarget;
|
|
|
|
// Only update the flag if we have a document as target
|
|
if ("defaultView" in doc) {
|
|
var id = utils.getWindowId(doc.defaultView);
|
|
// dump("*** 'DOMContentLoaded' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
|
|
|
|
// We only care about error pages for DOMContentLoaded
|
|
var errorRegex = /about:.+(error)|(blocked)\?/;
|
|
if (errorRegex.exec(doc.baseURI)) {
|
|
// Wait about 1s to be sure the DOM is ready
|
|
utils.sleep(1000);
|
|
|
|
map.updatePageLoadStatus(id, true);
|
|
}
|
|
|
|
// We need to add/remove the unload event listener to preserve caching.
|
|
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
|
|
}
|
|
};
|
|
|
|
// beforeunload is still needed because pagehide doesn't fire before the page is unloaded.
|
|
// still use pagehide for cases when beforeunload doesn't get fired
|
|
var beforeUnloadHandler = function (aEvent) {
|
|
var doc = aEvent.originalTarget;
|
|
|
|
// Only update the flag if we have a document as target
|
|
if ("defaultView" in doc) {
|
|
var id = utils.getWindowId(doc.defaultView);
|
|
// dump("*** 'beforeunload' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
|
|
map.updatePageLoadStatus(id, false);
|
|
}
|
|
|
|
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
|
|
};
|
|
|
|
var pageHideHandler = function (aEvent) {
|
|
var doc = aEvent.originalTarget;
|
|
|
|
// Only update the flag if we have a document as target
|
|
if ("defaultView" in doc) {
|
|
var id = utils.getWindowId(doc.defaultView);
|
|
// dump("*** 'pagehide' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
|
|
map.updatePageLoadStatus(id, false);
|
|
}
|
|
// If event.persisted is true the beforeUnloadHandler would never fire
|
|
// and we have to remove the event handler here to avoid memory leaks.
|
|
if (aEvent.persisted)
|
|
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
|
|
};
|
|
|
|
var onWindowLoaded = function (aEvent) {
|
|
var id = utils.getWindowId(aWindow);
|
|
// dump("*** 'load' event: id=" + id + ", baseURI=" + aWindow.document.baseURI + "\n");
|
|
|
|
map.update(id, "loaded", true);
|
|
|
|
// Note: Error pages will never fire a "pageshow" event. For those we
|
|
// have to wait for the "DOMContentLoaded" event. That's the final state.
|
|
// Error pages will always have a baseURI starting with
|
|
// "about:" followed by "error" or "blocked".
|
|
aWindow.addEventListener("DOMContentLoaded", DOMContentLoadedHandler, true);
|
|
|
|
// Page is ready
|
|
aWindow.addEventListener("pageshow", pageShowHandler, true);
|
|
|
|
// Leave page (use caching)
|
|
aWindow.addEventListener("pagehide", pageHideHandler, true);
|
|
};
|
|
|
|
// If the window has already been finished loading, call the load handler
|
|
// directly. Otherwise attach it to the current window.
|
|
if (aWindow.document.readyState === 'complete') {
|
|
onWindowLoaded();
|
|
} else {
|
|
aWindow.addEventListener("load", onWindowLoaded, false);
|
|
}
|
|
}
|
|
|
|
// Attach event listeners to all already open top-level windows
|
|
function handleAttachEventListeners() {
|
|
var enumerator = Cc["@mozilla.org/appshell/window-mediator;1"].
|
|
getService(Ci.nsIWindowMediator).getEnumerator("");
|
|
while (enumerator.hasMoreElements()) {
|
|
var win = enumerator.getNext();
|
|
attachEventListeners(win);
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
// Activate observer for new top level windows
|
|
var observerService = Cc["@mozilla.org/observer-service;1"].
|
|
getService(Ci.nsIObserverService);
|
|
observerService.addObserver(windowReadyObserver, "toplevel-window-ready", false);
|
|
observerService.addObserver(windowCloseObserver, "outer-window-destroyed", false);
|
|
observerService.addObserver(enterLeavePrivateBrowsingObserver, "private-browsing", false);
|
|
|
|
handleAttachEventListeners();
|
|
}
|