UNPKG

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
// 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_();