sku-pg
Version:
Skulpt is a Javascript implementation of Python 2.x. Python that runs in your browser!
255 lines (214 loc) • 7.59 kB
JavaScript
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* @fileoverview Class used by XHR wrappers to publish their state to IframeIo
* or other components that need to know if any XmlHttpRequests are active.
*
* IframeIo needs to know if any XmlHttpRequests have been initiated from the
* result of an incremental data response, so that it can delay the destruction
* of the iframe. Otherwise firefox will error since the source context no
* longer exists.
*
* This class means that IframeIo does not have an explicit dependency on
* XhrIo.
*
* See https://bugzilla.mozilla.org/show_bug.cgi?id=369939 for a description of
* the problem and a minimal test case.
*
* This class's methods are no-ops for non-Gecko browsers.
*
*
*/
goog.provide('goog.net.xhrMonitor');
goog.require('goog.array');
goog.require('goog.debug.Logger');
goog.require('goog.userAgent');
/**
* Class used for singleton goog.net.xhrMonitor which can be used for monitoring
* whether there any XmlHttpRequests have been opened in a given execution
* context, and allowing query of when they are closed.
* @constructor
* @private
*/
goog.net.XhrMonitor_ = function() {
if (!goog.userAgent.GECKO) return;
/**
* A map of context identifiers to an array of XHR unique IDs that were
* created in the context.
* String -> Array.<String>
* @type {Object}
* @private
*/
this.contextsToXhr_ = {};
/**
* Inverse lookup from an XHR unique ID to any context that was open when it
* was created. There should rarely be multiple open contexts, but support
* has been added for completeness.
* String -> Array.<String>
* @type {Object}
* @private
*/
this.xhrToContexts_ = {};
/**
* Stack of active contexts.
* @type {Array.<string>}
* @private
*/
this.stack_ = [];
};
/**
* Returns a string key for the argument -- Either the string itself, the
* unique ID of the object, or an empty string otherwise.
* @param {Object|string} obj The object to make a key for.
* @return {string|number} A string key for the argument.
*/
goog.net.XhrMonitor_.getKey = function(obj) {
return goog.isString(obj) ? obj :
goog.isObject(obj) ? goog.getUid(obj) :
'';
};
/**
* A reference to the xhrMonitor logger.
* @type {goog.debug.Logger}
* @private
*/
goog.net.XhrMonitor_.prototype.logger_ =
goog.debug.Logger.getLogger('goog.net.xhrMonitor');
/**
* Flag indicating that the monitor should be used.
* Should be set to false for worker threads as they do not have access
* to iframes, which is what the monitor is needed for.
* @type {boolean}
* @private
*/
goog.net.XhrMonitor_.prototype.enabled_ = goog.userAgent.GECKO;
/**
* Set the enabled flag.
* @param {boolean} val The new value.
*/
goog.net.XhrMonitor_.prototype.setEnabled = function(val) {
this.enabled_ = goog.userAgent.GECKO && val;
};
/**
* Pushes a new context onto the stack.
* @param {Object|string} context An object or string indicating the source of
* the execution context.
*/
goog.net.XhrMonitor_.prototype.pushContext = function(context) {
if (!this.enabled_) return;
var key = goog.net.XhrMonitor_.getKey(context);
this.logger_.finest('Pushing context: ' + context + ' (' + key + ')');
this.stack_.push(key);
};
/**
* Pops the most recent context off the stack.
*/
goog.net.XhrMonitor_.prototype.popContext = function() {
if (!this.enabled_) return;
var context = this.stack_.pop();
this.logger_.finest('Popping context: ' + context);
this.updateDependentContexts_(context);
};
/**
* Checks to see if there are any outstanding XmlHttpRequests that were
* started in the given context.
* @param {Object|string} context An object or string indicating the execution
* context to check.
* @return {boolean} Whether there are any outstanding requests linked to the
* context.
*/
goog.net.XhrMonitor_.prototype.isContextSafe = function(context) {
if (!this.enabled_) return true;
var deps = this.contextsToXhr_[goog.net.XhrMonitor_.getKey(context)];
this.logger_.fine('Context is safe : ' + context + ' - ' + deps);
return !deps;
};
/**
* Marks an XHR object as being open.
* @param {Object} xhr An XmlHttpRequest object that is about to be opened.
*/
goog.net.XhrMonitor_.prototype.markXhrOpen = function(xhr) {
if (!this.enabled_) return;
var uid = goog.getUid(xhr);
this.logger_.fine('Opening XHR : ' + uid);
// Update all contexts that are currently on the stack.
for (var i = 0; i < this.stack_.length; i++) {
var context = this.stack_[i];
this.addToMap_(this.contextsToXhr_, context, uid);
this.addToMap_(this.xhrToContexts_, uid, context);
}
};
/**
* Marks an XHR object as being closed.
* @param {Object} xhr An XmlHttpRequest object whose request has completed.
*/
goog.net.XhrMonitor_.prototype.markXhrClosed = function(xhr) {
if (!this.enabled_) return;
var uid = goog.getUid(xhr);
this.logger_.fine('Closing XHR : ' + uid);
// Delete the XHR look up and remove the XHR from any contexts.
delete this.xhrToContexts_[uid];
for (var context in this.contextsToXhr_) {
goog.array.remove(this.contextsToXhr_[context], uid);
if (this.contextsToXhr_[context].length == 0) {
delete this.contextsToXhr_[context];
}
}
};
/**
* Updates any contexts that were dependent on the given XHR request with any
* XHRs that were opened by the same XHR. This is used to track Iframes that
* open XHRs which then in turn open an XHR.
* @param {string} xhrUid The unique ID for the XHR to update.
* @private
*/
goog.net.XhrMonitor_.prototype.updateDependentContexts_ = function(xhrUid) {
// Update any contexts that are dependent on this XHR with any requests
// registered with the XHR as a base context. This is used for the situation
// when an XHR event triggers another XHR. The original XHR is closed, but
// the source context needs to be informed about any XHRs that were opened as
// a result of the first.
var contexts = this.xhrToContexts_[xhrUid];
var xhrs = this.contextsToXhr_[xhrUid];
if (contexts && xhrs) {
this.logger_.finest('Updating dependent contexts');
goog.array.forEach(contexts, function(context) {
goog.array.forEach(xhrs, function(xhr) {
this.addToMap_(this.contextsToXhr_, context, xhr);
this.addToMap_(this.xhrToContexts_, xhr, context);
}, this);
}, this);
}
};
/**
* Adds a value to a map of arrays. If an array hasn't been created for the
* provided key, then one is created.
* @param {Object} map The map to add to.
* @param {string|number} key the key.
* @param {string|number} value The value.
* @private
*/
goog.net.XhrMonitor_.prototype.addToMap_ = function(map, key, value) {
if (!map[key]) {
map[key] = [];
}
if (!goog.array.contains(map[key], value)) {
map[key].push(value);
}
};
/**
* Singleton XhrMonitor object
* @type {goog.net.XhrMonitor_}
*/
goog.net.xhrMonitor = new goog.net.XhrMonitor_();