Files
palemoon27/dom/base/DOMRequestHelper.jsm
T
roytam1 fe0509a62e import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 904479 - Added createPromiseWithId() that returns id of resolver r=kanru,nsm (2ac672d882)
- Bug 1166580 - Disable mozHasPendingMessage tests on non-browser platform. r=me (03c689964b)
- Bug 1162281 - Invalid system message handler in an App Manifest can break the entire system. r=fabrice (e192a95f9c)
- Bug 1198988 - Turn off some useless dump() calls r=ferjm (34fc83b236)
- Bug 1164498: Remove |DispatchBluetoothReply|, r=btian (6143335efa)
- Bug 1001757 - Add ability to store core apps outside of profile on desktop b2g; r=fabrice (f6b605e7aa)
- Bug 1155245 - Set the app status correctly for hosted certified apps in developer mode. r=fabrice (131178b80e)
- Bug 1179052 - Add some raptor markers to b2g gecko startup r=gwagner (222256fad8)
- Bug 1163904 - handle -url command line argument. r=fabrice (ee61af1ff9)
- Bug 1167275 - JS error in shell.js handleCmdLine() r=me (32e75c604f)
- Bug 1167197 - Fix GMPProvider on Android r=cpearce Bug 1181209 - Make changes to Gecko needed for b2gdroid to boot. r=fabrice (b35d3a372f)
- Bug 1158544 - Remove FTPChannelChild::mWasOpened and make the base class mWasOpened protected; r=mcmanus (9111e1bc00)
- Bug 1171716 - Part 2: Use NS_ReleaseOnMainThread in nsBaseChannel. r=froydnj (f138124f14)
- partial of Bug 1177175 - Add a UITour target inside the TP panel. (603cc719b3)
- Bug 1175545 - Dont process alt-svc on 421 r=hurley (ad0f2f6e91)
- Bug 1191291 - convert nsHttpChannel::RetargetDeliveryTo warning to log r=michal.novotny (b9c6003df8)
- Bug 1182487 - Don't try to write to HTTP cache entry in nsHttpChannel when entry is open for reading only. r=michal (b36d7014a0)
- Bug 1173069 - Don't accumulate the cache hit telemetry for intercepted channels; r=mayhemer,jdm (aaed79183d)
- Bug 1208755 HttpBaseChannel::ShouldIntercept() should not assume every channel has a LoadInfo. r=ckerschb (d55be94901)
- Bug 1201229 - Return an empty string for a header when an error occurs; r=dragana (256d0462c8)
- Bug 1048048 - add preload content policy types - web platform test updates (r=dveditz) (baa1004dd6)
- Bug 1048048 - add preload content policy types - csp changes (r=dveditz) (17914dadba)
- Bug 1048048 - add preload content policy types for stylesheets (r=cam) (29af13263a)
- Bug 1048048 - add preload content policy types (r=ehsan) (f58a32d51b)
- Bug 1201747 - Don't inspect the subject principal in StorageAllowedForPrincipal. r=mystor (4f2c100882)
- Bug 1176829 - Remove custom elements base element queue. r=smaug (03a520c13d)
- Bug 1176829 follow-up, finish removing unused member to fix bustage. CLOSED TREE (29c6150af8)
- Bug 1179909: Build fix. r=me CLOSED TREE (40e3bdb971)
- Bug 1188932 - Allow the User-Agent header to be explicitly set by requests, r=bkelly, r=jgraham (37aacbd37d)
2022-04-01 23:06:55 +08:00

337 lines
10 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/. */
/**
* Helper object for APIs that deal with DOMRequests and Promises.
* It allows objects inheriting from it to create and keep track of DOMRequests
* and Promises objects in the common scenario where requests are created in
* the child, handed out to content and delivered to the parent within an async
* message (containing the identifiers of these requests). The parent may send
* messages back as answers to different requests and the child will use this
* helper to get the right request object. This helper also takes care of
* releasing the requests objects when the window goes out of scope.
*
* DOMRequestIPCHelper also deals with message listeners, allowing to add them
* to the child side of frame and process message manager and removing them
* when needed.
*/
const Cu = Components.utils;
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
this.EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
"@mozilla.org/childprocessmessagemanager;1",
"nsIMessageListenerManager");
this.DOMRequestIpcHelper = function DOMRequestIpcHelper() {
// _listeners keeps a list of messages for which we added a listener and the
// kind of listener that we added (strong or weak). It's an object of this
// form:
// {
// "message1": true,
// "messagen": false
// }
//
// where each property is the name of the message and its value is a boolean
// that indicates if the listener is weak or not.
this._listeners = null;
this._requests = null;
this._window = null;
}
DOMRequestIpcHelper.prototype = {
/**
* An object which "inherits" from DOMRequestIpcHelper and declares its own
* queryInterface method MUST implement Ci.nsISupportsWeakReference.
*/
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
Ci.nsIObserver]),
/**
* 'aMessages' is expected to be an array of either:
* - objects of this form:
* {
* name: "messageName",
* weakRef: false
* }
* where 'name' is the message identifier and 'weakRef' a boolean
* indicating if the listener should be a weak referred one or not.
*
* - or only strings containing the message name, in which case the listener
* will be added as a strong reference by default.
*/
addMessageListeners: function(aMessages) {
if (!aMessages) {
return;
}
if (!this._listeners) {
this._listeners = {};
}
if (!Array.isArray(aMessages)) {
aMessages = [aMessages];
}
aMessages.forEach((aMsg) => {
let name = aMsg.name || aMsg;
// If the listener is already set and it is of the same type we just
// increase the count and bail out. If it is not of the same type,
// we throw an exception.
if (this._listeners[name] != undefined) {
if (!!aMsg.weakRef == this._listeners[name].weakRef) {
this._listeners[name].count++;
return;
} else {
throw Cr.NS_ERROR_FAILURE;
}
}
aMsg.weakRef ? cpmm.addWeakMessageListener(name, this)
: cpmm.addMessageListener(name, this);
this._listeners[name] = {
weakRef: !!aMsg.weakRef,
count: 1
};
});
},
/**
* 'aMessages' is expected to be a string or an array of strings containing
* the message names of the listeners to be removed.
*/
removeMessageListeners: function(aMessages) {
if (!this._listeners || !aMessages) {
return;
}
if (!Array.isArray(aMessages)) {
aMessages = [aMessages];
}
aMessages.forEach((aName) => {
if (this._listeners[aName] == undefined) {
return;
}
// Only remove the listener really when we don't have anybody that could
// be waiting on a message.
if (!--this._listeners[aName].count) {
this._listeners[aName].weakRef ?
cpmm.removeWeakMessageListener(aName, this)
: cpmm.removeMessageListener(aName, this);
delete this._listeners[aName];
}
});
},
/**
* Initialize the helper adding the corresponding listeners to the messages
* provided as the second parameter.
*
* 'aMessages' is expected to be an array of either:
*
* - objects of this form:
* {
* name: 'messageName',
* weakRef: false
* }
* where 'name' is the message identifier and 'weakRef' a boolean
* indicating if the listener should be a weak referred one or not.
*
* - or only strings containing the message name, in which case the listener
* will be added as a strong referred one by default.
*/
initDOMRequestHelper: function(aWindow, aMessages) {
// Query our required interfaces to force a fast fail if they are not
// provided. These calls will throw if the interface is not available.
this.QueryInterface(Ci.nsISupportsWeakReference);
this.QueryInterface(Ci.nsIObserver);
if (aMessages) {
this.addMessageListeners(aMessages);
}
this._id = this._getRandomId();
this._window = aWindow;
if (this._window) {
// We don't use this.innerWindowID, but other classes rely on it.
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
this.innerWindowID = util.currentInnerWindowID;
}
this._destroyed = false;
Services.obs.addObserver(this, "inner-window-destroyed",
/* weak-ref */ true);
},
destroyDOMRequestHelper: function() {
if (this._destroyed) {
return;
}
this._destroyed = true;
Services.obs.removeObserver(this, "inner-window-destroyed");
if (this._listeners) {
Object.keys(this._listeners).forEach((aName) => {
this._listeners[aName].weakRef ? cpmm.removeWeakMessageListener(aName, this)
: cpmm.removeMessageListener(aName, this);
});
}
this._listeners = null;
this._requests = null;
// Objects inheriting from DOMRequestIPCHelper may have an uninit function.
if (this.uninit) {
this.uninit();
}
this._window = null;
},
observe: function(aSubject, aTopic, aData) {
if (aTopic !== "inner-window-destroyed") {
return;
}
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (wId != this.innerWindowID) {
return;
}
this.destroyDOMRequestHelper();
},
getRequestId: function(aRequest) {
if (!this._requests) {
this._requests = {};
}
let id = "id" + this._getRandomId();
this._requests[id] = aRequest;
return id;
},
getPromiseResolverId: function(aPromiseResolver) {
// Delegates to getRequest() since the lookup table is agnostic about
// storage.
return this.getRequestId(aPromiseResolver);
},
getRequest: function(aId) {
if (this._requests && this._requests[aId]) {
return this._requests[aId];
}
},
getPromiseResolver: function(aId) {
// Delegates to getRequest() since the lookup table is agnostic about
// storage.
return this.getRequest(aId);
},
removeRequest: function(aId) {
if (this._requests && this._requests[aId]) {
delete this._requests[aId];
}
},
removePromiseResolver: function(aId) {
// Delegates to getRequest() since the lookup table is agnostic about
// storage.
this.removeRequest(aId);
},
takeRequest: function(aId) {
if (!this._requests || !this._requests[aId]) {
return null;
}
let request = this._requests[aId];
delete this._requests[aId];
return request;
},
takePromiseResolver: function(aId) {
// Delegates to getRequest() since the lookup table is agnostic about
// storage.
return this.takeRequest(aId);
},
_getRandomId: function() {
return Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator).generateUUID().toString();
},
createRequest: function() {
// If we don't have a valid window object, throw.
if (!this._window) {
Cu.reportError("DOMRequestHelper trying to create a DOMRequest without a valid window, failing.");
throw Cr.NS_ERROR_FAILURE;
}
return Services.DOMRequest.createRequest(this._window);
},
/**
* createPromise() creates a new Promise, with `aPromiseInit` as the
* PromiseInit callback. The promise constructor is obtained from the
* reference to window owned by this DOMRequestIPCHelper.
*/
createPromise: function(aPromiseInit) {
// If we don't have a valid window object, throw.
if (!this._window) {
Cu.reportError("DOMRequestHelper trying to create a Promise without a valid window, failing.");
throw Cr.NS_ERROR_FAILURE;
}
return new this._window.Promise(aPromiseInit);
},
/**
* createPromiseWithId() creates a new Promise, accepting a callback
* which is immediately called with the generated resolverId.
*/
createPromiseWithId: function(aCallback) {
return this.createPromise(function(aResolve, aReject) {
let resolverId = this.getPromiseResolverId({ resolve: aResolve, reject: aReject });
aCallback(resolverId);
}.bind(this));
},
forEachRequest: function(aCallback) {
if (!this._requests) {
return;
}
Object.keys(this._requests).forEach((aKey) => {
if (this.getRequest(aKey) instanceof this._window.DOMRequest) {
aCallback(aKey);
}
});
},
forEachPromiseResolver: function(aCallback) {
if (!this._requests) {
return;
}
Object.keys(this._requests).forEach((aKey) => {
if ("resolve" in this.getPromiseResolver(aKey) &&
"reject" in this.getPromiseResolver(aKey)) {
aCallback(aKey);
}
});
},
}