diff options
Diffstat (limited to 'contexts/data/lib/closure-library/closure/goog/net/iframeio.js')
-rw-r--r-- | contexts/data/lib/closure-library/closure/goog/net/iframeio.js | 1300 |
1 files changed, 0 insertions, 1300 deletions
diff --git a/contexts/data/lib/closure-library/closure/goog/net/iframeio.js b/contexts/data/lib/closure-library/closure/goog/net/iframeio.js deleted file mode 100644 index deb8b39..0000000 --- a/contexts/data/lib/closure-library/closure/goog/net/iframeio.js +++ /dev/null @@ -1,1300 +0,0 @@ -// Copyright 2006 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 for managing requests via iFrames. Supports a number of - * methods of transfer. - * - * Gets and Posts can be performed and the resultant page read in as text, - * JSON, or from the HTML DOM. - * - * Using an iframe causes the throbber to spin, this is good for providing - * feedback to the user that an action has occurred. - * - * Requests do not affect the history stack, see goog.History if you require - * this behavior. - * - * The responseText and responseJson methods assume the response is plain, - * text. You can access the Iframe's DOM through responseXml if you need - * access to the raw HTML. - * - * Tested: - * + FF2.0 (Win Linux) - * + IE6, IE7 - * + Opera 9.1, - * + Chrome - * - Opera 8.5 fails because of no textContent and buggy innerText support - * - * NOTE: Safari doesn't fire the onload handler when loading plain text files - * - * This has been tested with Drip in IE to ensure memory usage is as constant - * as possible. When making making thousands of requests, memory usage stays - * constant for a while but then starts increasing (<500k for 2000 - * requests) -- this hasn't yet been tracked down yet, though it is cleared up - * after a refresh. - * - * - * BACKGROUND FILE UPLOAD: - * By posting an arbitrary form through an IframeIo object, it is possible to - * implement background file uploads. Here's how to do it: - * - * - Create a form: - * <pre> - * <form id="form" enctype="multipart/form-data" method="POST"> - * <input name="userfile" type="file" /> - * </form> - * </pre> - * - * - Have the user click the file input - * - Create an IframeIo instance - * <pre> - * var io = new goog.net.IframeIo; - * goog.events.listen(io, goog.net.EventType.COMPLETE, - * function() { alert('Sent'); }); - * io.sendFromForm(document.getElementById('form')); - * </pre> - * - * - * INCREMENTAL LOADING: - * Gmail sends down multiple script blocks which get executed as they are - * received by the client. This allows incremental rendering of the thread - * list and conversations. - * - * This requires collaboration with the server that is sending the requested - * page back. To set incremental loading up, you should: - * - * A) In the application code there should be an externed reference to - * <code>handleIncrementalData()</code>. e.g. - * goog.exportSymbol('GG_iframeFn', goog.net.IframeIo.handleIncrementalData); - * - * B) The response page should them call this method directly, an example - * response would look something like this: - * <pre> - * <html> - * <head> - * <meta content="text/html;charset=UTF-8" http-equiv="content-type"> - * </head> - * <body> - * <script> - * D = top.P ? function(d) { top.GG_iframeFn(window, d) } : function() {}; - * </script> - * - * <script>D([1, 2, 3, 4, 5]);</script> - * <script>D([6, 7, 8, 9, 10]);</script> - * <script>D([11, 12, 13, 14, 15]);</script> - * </body> - * </html> - * </pre> - * - * Your application should then listen, on the IframeIo instance, to the event - * goog.net.EventType.INCREMENTAL_DATA. The event object contains a - * 'data' member which is the content from the D() calls above. - * - * NOTE: There can be problems if you save a reference to the data object in IE. - * If you save an array, and the iframe is dispose, then the array looses its - * prototype and thus array methods like .join(). You can get around this by - * creating arrays using the parent window's Array constructor, or you can - * clone the array. - * - * - * EVENT MODEL: - * The various send methods work asynchronously. You can be notified about - * the current status of the request (completed, success or error) by - * listening for events on the IframeIo object itself. The following events - * will be sent: - * - goog.net.EventType.COMPLETE: when the request is completed - * (either sucessfully or unsuccessfully). You can find out about the result - * using the isSuccess() and getLastError - * methods. - * - goog.net.EventType.SUCCESS</code>: when the request was completed - * successfully - * - goog.net.EventType.ERROR: when the request failed - * - goog.net.EventType.ABORT: when the request has been aborted - * - * Example: - * <pre> - * var io = new goog.net.IframeIo(); - * goog.events.listen(io, goog.net.EventType.COMPLETE, - * function() { alert('request complete'); }); - * io.sendFromForm(...); - * </pre> - * - */ - -goog.provide('goog.net.IframeIo'); -goog.provide('goog.net.IframeIo.IncrementalDataEvent'); - -goog.require('goog.Timer'); -goog.require('goog.Uri'); -goog.require('goog.debug'); -goog.require('goog.debug.Logger'); -goog.require('goog.dom'); -goog.require('goog.events'); -goog.require('goog.events.EventTarget'); -goog.require('goog.events.EventType'); -goog.require('goog.json'); -goog.require('goog.net.ErrorCode'); -goog.require('goog.net.EventType'); -goog.require('goog.reflect'); -goog.require('goog.string'); -goog.require('goog.structs'); -goog.require('goog.userAgent'); - - - -/** - * Class for managing requests via iFrames. - * @constructor - * @extends {goog.events.EventTarget} - */ -goog.net.IframeIo = function() { - - /** - * Name for this IframeIo and frame - * @type {string} - * @private - */ - this.name_ = goog.net.IframeIo.getNextName_(); - - /** - * An array of iframes that have been finished with. We need them to be - * disposed async, so we don't confuse the browser (see below). - * @type {Array.<Element>} - * @private - */ - this.iframesForDisposal_ = []; - - // Create a lookup from names to instances of IframeIo. This is a helper - // function to be used in conjunction with goog.net.IframeIo.getInstanceByName - // to find the IframeIo object associated with a particular iframe. Used in - // incremental scripts etc. - goog.net.IframeIo.instances_[this.name_] = this; - -}; -goog.inherits(goog.net.IframeIo, goog.events.EventTarget); - - -/** - * Object used as a map to lookup instances of IframeIo objects by name. - * @type {Object} - * @private - */ -goog.net.IframeIo.instances_ = {}; - - -/** - * Prefix for frame names - * @type {string} - */ -goog.net.IframeIo.FRAME_NAME_PREFIX = 'closure_frame'; - - -/** - * Suffix that is added to inner frames used for sending requests in non-IE - * browsers - * @type {string} - */ -goog.net.IframeIo.INNER_FRAME_SUFFIX = '_inner'; - - -/** - * The number of milliseconds after a request is completed to dispose the - * iframes. This can be done lazily so we wait long enough for any processing - * that occurred as a result of the response to finish. - * @type {number} - */ -goog.net.IframeIo.IFRAME_DISPOSE_DELAY_MS = 2000; - - -/** - * Counter used when creating iframes - * @type {number} - * @private - */ -goog.net.IframeIo.counter_ = 0; - - -/** - * Form element to post to. - * @type {HTMLFormElement} - * @private - */ -goog.net.IframeIo.form_; - - -/** - * Static send that creates a short lived instance of IframeIo to send the - * request. - * @param {goog.Uri|string} uri Uri of the request, it is up the caller to - * manage query string params. - * @param {Function=} opt_callback Event handler for when request is completed. - * @param {string=} opt_method Default is GET, POST uses a form to submit the - * request. - * @param {boolean=} opt_noCache Append a timestamp to the request to avoid - * caching. - * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs that - * will be posted to the server via the iframe's form. - */ -goog.net.IframeIo.send = function( - uri, opt_callback, opt_method, opt_noCache, opt_data) { - - var io = new goog.net.IframeIo(); - goog.events.listen(io, goog.net.EventType.READY, io.dispose, false, io); - if (opt_callback) { - goog.events.listen(io, goog.net.EventType.COMPLETE, opt_callback); - } - io.send(uri, opt_method, opt_noCache, opt_data); -}; - - -/** - * Find an iframe by name (assumes the context is goog.global since that is - * where IframeIo's iframes are kept). - * @param {string} fname The name to find. - * @return {HTMLIFrameElement} The iframe element with that name. - */ -goog.net.IframeIo.getIframeByName = function(fname) { - return window.frames[fname]; -}; - - -/** - * Find an instance of the IframeIo object by name. - * @param {string} fname The name to find. - * @return {goog.net.IframeIo} The instance of IframeIo. - */ -goog.net.IframeIo.getInstanceByName = function(fname) { - return goog.net.IframeIo.instances_[fname]; -}; - - -/** - * Handles incremental data and routes it to the correct iframeIo instance. - * The HTML page requested by the IframeIo instance should contain script blocks - * that call an externed reference to this method. - * @param {Window} win The window object. - * @param {Object} data The data object. - */ -goog.net.IframeIo.handleIncrementalData = function(win, data) { - // If this is the inner-frame, then we need to use the parent instead. - var iframeName = goog.string.endsWith(win.name, - goog.net.IframeIo.INNER_FRAME_SUFFIX) ? win.parent.name : win.name; - - var iframeIoName = iframeName.substring(0, iframeName.lastIndexOf('_')); - var iframeIo = goog.net.IframeIo.getInstanceByName(iframeIoName); - if (iframeIo && iframeName == iframeIo.iframeName_) { - iframeIo.handleIncrementalData_(data); - } else { - goog.debug.Logger.getLogger('goog.net.IframeIo').info( - 'Incremental iframe data routed for unknown iframe'); - } -}; - - -/** - * @return {string} The next iframe name. - * @private - */ -goog.net.IframeIo.getNextName_ = function() { - return goog.net.IframeIo.FRAME_NAME_PREFIX + goog.net.IframeIo.counter_++; -}; - - -/** - * Gets a static form, one for all instances of IframeIo since IE6 leaks form - * nodes that are created/removed from the document. - * @return {HTMLFormElement} The static form. - * @private - */ -goog.net.IframeIo.getForm_ = function() { - if (!goog.net.IframeIo.form_) { - goog.net.IframeIo.form_ = - /** @type {HTMLFormElement} */(goog.dom.createDom('form')); - goog.net.IframeIo.form_.acceptCharset = 'utf-8'; - - // Hide the form and move it off screen - var s = goog.net.IframeIo.form_.style; - s.position = 'absolute'; - s.visibility = 'hidden'; - s.top = s.left = '-10px'; - s.width = s.height = '10px'; - s.overflow = 'hidden'; - - goog.dom.getDocument().body.appendChild(goog.net.IframeIo.form_); - } - return goog.net.IframeIo.form_; -}; - - -/** - * Adds the key value pairs from a map like data structure to a form - * @param {HTMLFormElement} form The form to add to. - * @param {Object|goog.structs.Map|goog.Uri.QueryData} data The data to add. - * @private - */ -goog.net.IframeIo.addFormInputs_ = function(form, data) { - goog.structs.forEach(data, function(value, key) { - var inp = goog.dom.createDom('input', - {'type': 'hidden', 'name': key, 'value': value}); - form.appendChild(inp); - }); -}; - - -/** - * Reference to a logger for the IframeIo objects - * @type {goog.debug.Logger} - * @private - */ -goog.net.IframeIo.prototype.logger_ = - goog.debug.Logger.getLogger('goog.net.IframeIo'); - - -/** - * Reference to form element that gets reused for requests to the iframe. - * @type {HTMLFormElement} - * @private - */ -goog.net.IframeIo.prototype.form_ = null; - - -/** - * Reference to the iframe being used for the current request, or null if no - * request is currently active. - * @type {HTMLIFrameElement} - * @private - */ -goog.net.IframeIo.prototype.iframe_ = null; - - -/** - * Name of the iframe being used for the current request, or null if no - * request is currently active. - * @type {?string} - * @private - */ -goog.net.IframeIo.prototype.iframeName_ = null; - - -/** - * Next id so that iframe names are unique. - * @type {number} - * @private - */ -goog.net.IframeIo.prototype.nextIframeId_ = 0; - - -/** - * Whether the object is currently active with a request. - * @type {boolean} - * @private - */ -goog.net.IframeIo.prototype.active_ = false; - - -/** - * Whether the last request is complete. - * @type {boolean} - * @private - */ -goog.net.IframeIo.prototype.complete_ = false; - - -/** - * Whether the last request was a success. - * @type {boolean} - * @private - */ -goog.net.IframeIo.prototype.success_ = false; - - -/** - * The URI for the last request. - * @type {goog.Uri} - * @private - */ -goog.net.IframeIo.prototype.lastUri_ = null; - - -/** - * The text content of the last request. - * @type {?string} - * @private - */ -goog.net.IframeIo.prototype.lastContent_ = null; - - -/** - * Last error code - * @type {goog.net.ErrorCode} - * @private - */ -goog.net.IframeIo.prototype.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; - - -/** - * Number of milliseconds after which an incomplete request will be aborted and - * a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no timeout is set. - * @type {number} - * @private - */ -goog.net.IframeIo.prototype.timeoutInterval_ = 0; - - -/** - * Window timeout ID used to cancel the timeout event handler if the request - * completes successfully. - * @type {?number} - * @private - */ -goog.net.IframeIo.prototype.timeoutId_ = null; - - -/** - * Window timeout ID used to detect when firefox silently fails. - * @type {?number} - * @private - */ -goog.net.IframeIo.prototype.firefoxSilentErrorTimeout_ = null; - - -/** - * Window timeout ID used by the timer that disposes the iframes. - * @type {?number} - * @private - */ -goog.net.IframeIo.prototype.iframeDisposalTimer_ = null; - - -/** - * This is used to ensure that we don't handle errors twice for the same error. - * We can reach the {@link #handleError_} method twice in IE if the form is - * submitted while IE is offline and the URL is not available. - * @type {boolean} - * @private - */ -goog.net.IframeIo.prototype.errorHandled_; - - -/** - * Sends a request via an iframe. - * - * A HTML form is used and submitted to the iframe, this simplifies the - * difference between GET and POST requests. The iframe needs to be created and - * destroyed for each request otherwise the request will contribute to the - * history stack. - * - * sendFromForm does some clever trickery (thanks jlim) in non-IE browsers to - * stop a history entry being added for POST requests. - * - * @param {goog.Uri|string} uri Uri of the request. - * @param {string=} opt_method Default is GET, POST uses a form to submit the - * request. - * @param {boolean=} opt_noCache Append a timestamp to the request to avoid - * caching. - * @param {Object|goog.structs.Map=} opt_data Map of key-value pairs. - */ -goog.net.IframeIo.prototype.send = function( - uri, opt_method, opt_noCache, opt_data) { - - if (this.active_) { - throw Error('[goog.net.IframeIo] Unable to send, already active.'); - } - - var uriObj = new goog.Uri(uri); - this.lastUri_ = uriObj; - var method = opt_method ? opt_method.toUpperCase() : 'GET'; - - if (opt_noCache) { - uriObj.makeUnique(); - } - - this.logger_.info('Sending iframe request: ' + uriObj + ' [' + method + ']'); - - // Build a form for this request - this.form_ = goog.net.IframeIo.getForm_(); - - if (method == 'GET') { - // For GET requests, we assume that the caller didn't want the queryparams - // already specified in the URI to be clobbered by the form, so we add the - // params here. - goog.net.IframeIo.addFormInputs_(this.form_, uriObj.getQueryData()); - } - - if (opt_data) { - // Create form fields for each of the data values - goog.net.IframeIo.addFormInputs_(this.form_, opt_data); - } - - // Set the URI that the form will be posted - this.form_.action = uriObj.toString(); - this.form_.method = method; - - this.sendFormInternal_(); -}; - - -/** - * Sends the data stored in an existing form to the server. The HTTP method - * should be specified on the form, the action can also be specified but can - * be overridden by the optional URI param. - * - * This can be used in conjunction will a file-upload input to upload a file in - * the background without affecting history. - * - * Example form: - * <pre> - * <form action="/server/" enctype="multipart/form-data" method="POST"> - * <input name="userfile" type="file"> - * </form> - * </pre> - * - * @param {HTMLFormElement} form Form element used to send the request to the - * server. - * @param {string=} opt_uri Uri to set for the destination of the request, by - * default the uri will come from the form. - * @param {boolean=} opt_noCache Append a timestamp to the request to avoid - * caching. - */ -goog.net.IframeIo.prototype.sendFromForm = function(form, opt_uri, - opt_noCache) { - if (this.active_) { - throw Error('[goog.net.IframeIo] Unable to send, already active.'); - } - - var uri = new goog.Uri(opt_uri || form.action); - if (opt_noCache) { - uri.makeUnique(); - } - - this.logger_.info('Sending iframe request from form: ' + uri); - - this.lastUri_ = uri; - this.form_ = form; - this.form_.action = uri.toString(); - this.sendFormInternal_(); -}; - - -/** - * Abort the current Iframe request - * @param {goog.net.ErrorCode=} opt_failureCode Optional error code to use - - * defaults to ABORT. - */ -goog.net.IframeIo.prototype.abort = function(opt_failureCode) { - if (this.active_) { - this.logger_.info('Request aborted'); - goog.events.removeAll(this.getRequestIframe_()); - this.complete_ = false; - this.active_ = false; - this.success_ = false; - this.lastErrorCode_ = opt_failureCode || goog.net.ErrorCode.ABORT; - - this.dispatchEvent(goog.net.EventType.ABORT); - - this.makeReady_(); - } -}; - - -/** @override */ -goog.net.IframeIo.prototype.disposeInternal = function() { - this.logger_.fine('Disposing iframeIo instance'); - - // If there is an active request, abort it - if (this.active_) { - this.logger_.fine('Aborting active request'); - this.abort(); - } - - // Call super-classes implementation (remove listeners) - goog.net.IframeIo.superClass_.disposeInternal.call(this); - - // Add the current iframe to the list of iframes for disposal. - if (this.iframe_) { - this.scheduleIframeDisposal_(); - } - - // Disposes of the form - this.disposeForm_(); - - // Nullify anything that might cause problems and clear state - delete this.errorChecker_; - this.form_ = null; - this.lastCustomError_ = this.lastContent_ = this.lastContentHtml_ = null; - this.lastUri_ = null; - this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; - - delete goog.net.IframeIo.instances_[this.name_]; -}; - - -/** - * @return {boolean} True if transfer is complete. - */ -goog.net.IframeIo.prototype.isComplete = function() { - return this.complete_; -}; - - -/** - * @return {boolean} True if transfer was successful. - */ -goog.net.IframeIo.prototype.isSuccess = function() { - return this.success_; -}; - - -/** - * @return {boolean} True if a transfer is in progress. - */ -goog.net.IframeIo.prototype.isActive = function() { - return this.active_; -}; - - -/** - * Returns the last response text (i.e. the text content of the iframe). - * Assumes plain text! - * @return {?string} Result from the server. - */ -goog.net.IframeIo.prototype.getResponseText = function() { - return this.lastContent_; -}; - - -/** - * Returns the last response html (i.e. the innerHtml of the iframe). - * @return {?string} Result from the server. - */ -goog.net.IframeIo.prototype.getResponseHtml = function() { - return this.lastContentHtml_; -}; - - -/** - * Parses the content as JSON. This is a safe parse and may throw an error - * if the response is malformed. - * Use goog.json.unsafeparse(this.getResponseText()) if you are sure of the - * state of the returned content. - * @return {Object} The parsed content. - */ -goog.net.IframeIo.prototype.getResponseJson = function() { - return goog.json.parse(this.lastContent_); -}; - - -/** - * Returns the document object from the last request. Not truely XML, but - * used to mirror the XhrIo interface. - * @return {HTMLDocument} The document object from the last request. - */ -goog.net.IframeIo.prototype.getResponseXml = function() { - if (!this.iframe_) return null; - - return this.getContentDocument_(); -}; - - -/** - * Get the uri of the last request. - * @return {goog.Uri} Uri of last request. - */ -goog.net.IframeIo.prototype.getLastUri = function() { - return this.lastUri_; -}; - - -/** - * Gets the last error code. - * @return {goog.net.ErrorCode} Last error code. - */ -goog.net.IframeIo.prototype.getLastErrorCode = function() { - return this.lastErrorCode_; -}; - - -/** - * Gets the last error message. - * @return {string} Last error message. - */ -goog.net.IframeIo.prototype.getLastError = function() { - return goog.net.ErrorCode.getDebugMessage(this.lastErrorCode_); -}; - - -/** - * Gets the last custom error. - * @return {Object} Last custom error. - */ -goog.net.IframeIo.prototype.getLastCustomError = function() { - return this.lastCustomError_; -}; - - -/** - * Sets the callback function used to check if a loaded IFrame is in an error - * state. - * @param {Function} fn Callback that expects a document object as it's single - * argument. - */ -goog.net.IframeIo.prototype.setErrorChecker = function(fn) { - this.errorChecker_ = fn; -}; - - -/** - * Gets the callback function used to check if a loaded IFrame is in an error - * state. - * @return {Function} A callback that expects a document object as it's single - * argument. - */ -goog.net.IframeIo.prototype.getErrorChecker = function() { - return this.errorChecker_; -}; - - -/** - * Returns the number of milliseconds after which an incomplete request will be - * aborted, or 0 if no timeout is set. - * @return {number} Timeout interval in milliseconds. - */ -goog.net.IframeIo.prototype.getTimeoutInterval = function() { - return this.timeoutInterval_; -}; - - -/** - * Sets the number of milliseconds after which an incomplete request will be - * aborted and a {@link goog.net.EventType.TIMEOUT} event raised; 0 means no - * timeout is set. - * @param {number} ms Timeout interval in milliseconds; 0 means none. - */ -goog.net.IframeIo.prototype.setTimeoutInterval = function(ms) { - // TODO (pupius) - never used - doesn't look like timeouts were implemented - this.timeoutInterval_ = Math.max(0, ms); -}; - - -/** - * Submits the internal form to the iframe. - * @private - */ -goog.net.IframeIo.prototype.sendFormInternal_ = function() { - this.active_ = true; - this.complete_ = false; - this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; - - // Make Iframe - this.createIframe_(); - - if (goog.userAgent.IE) { - // In IE we simply create the frame, wait until it is ready, then post the - // form to the iframe and wait for the readystate to change to 'complete' - - // Set the target to the iframe's name - this.form_.target = this.iframeName_ || ''; - this.appendIframe_(); - goog.events.listen(this.iframe_, goog.events.EventType.READYSTATECHANGE, - this.onIeReadyStateChange_, false, this); - - /** @preserveTry */ - try { - this.errorHandled_ = false; - this.form_.submit(); - } catch (e) { - // If submit threw an exception then it probably means the page that the - // code is running on the local file system and the form's action was - // pointing to a file that doesn't exist, causing the browser to fire an - // exception. IE also throws an exception when it is working offline and - // the URL is not available. - - goog.events.unlisten(this.iframe_, goog.events.EventType.READYSTATECHANGE, - this.onIeReadyStateChange_, false, this); - - this.handleError_(goog.net.ErrorCode.ACCESS_DENIED); - } - - } else { - // For all other browsers we do some trickery to ensure that there is no - // entry on the history stack. Thanks go to jlim for the prototype for this - - this.logger_.fine('Setting up iframes and cloning form'); - - this.appendIframe_(); - - var innerFrameName = this.iframeName_ + - goog.net.IframeIo.INNER_FRAME_SUFFIX; - - // Open and document.write another iframe into the iframe - var doc = goog.dom.getFrameContentDocument(this.iframe_); - var html = '<body><iframe id=' + innerFrameName + - ' name=' + innerFrameName + '></iframe>'; - if (document.baseURI) { - // On Safari 4 and 5 the new iframe doesn't inherit the current baseURI. - html = '<head><base href="' + goog.string.htmlEscape(document.baseURI) + - '"></head>' + html; - } - if (goog.userAgent.OPERA) { - // Opera adds a history entry when document.write is used. - // Change the innerHTML of the page instead. - doc.documentElement.innerHTML = html; - } else { - doc.write(html); - } - - // Listen for the iframe's load - goog.events.listen(doc.getElementById(innerFrameName), - goog.events.EventType.LOAD, this.onIframeLoaded_, false, this); - - // Fix text areas, since importNode won't clone changes to the value - var textareas = this.form_.getElementsByTagName('textarea'); - for (var i = 0, n = textareas.length; i < n; i++) { - // The childnodes represent the initial child nodes for the text area - // appending a text node essentially resets the initial value ready for - // it to be clones - while maintaining HTML escaping. - var value = textareas[i].value; - if (goog.dom.getRawTextContent(textareas[i]) != value) { - goog.dom.setTextContent(textareas[i], value); - textareas[i].value = value; - } - } - - // Append a cloned form to the iframe - var clone = doc.importNode(this.form_, true); - clone.target = innerFrameName; - // Work around crbug.com/66987 - clone.action = this.form_.action; - doc.body.appendChild(clone); - - // Fix select boxes, importNode won't override the default value - var selects = this.form_.getElementsByTagName('select'); - var clones = clone.getElementsByTagName('select'); - for (var i = 0, n = selects.length; i < n; i++) { - var selectsOptions = selects[i].getElementsByTagName('option'); - var clonesOptions = clones[i].getElementsByTagName('option'); - for (var j = 0, m = selectsOptions.length; j < m; j++) { - clonesOptions[j].selected = selectsOptions[j].selected; - } - } - - // Some versions of Firefox (1.5 - 1.5.07?) fail to clone the value - // attribute for <input type="file"> nodes, which results in an empty - // upload if the clone is submitted. Check, and if the clone failed, submit - // using the original form instead. - var inputs = this.form_.getElementsByTagName('input'); - var inputClones = clone.getElementsByTagName('input'); - for (var i = 0, n = inputs.length; i < n; i++) { - if (inputs[i].type == 'file') { - if (inputs[i].value != inputClones[i].value) { - this.logger_.fine('File input value not cloned properly. Will ' + - 'submit using original form.'); - this.form_.target = innerFrameName; - clone = this.form_; - break; - } - } - } - - this.logger_.fine('Submitting form'); - - /** @preserveTry */ - try { - this.errorHandled_ = false; - clone.submit(); - doc.close(); - - if (goog.userAgent.GECKO) { - // This tests if firefox silently fails, this can happen, for example, - // when the server resets the connection because of a large file upload - this.firefoxSilentErrorTimeout_ = - goog.Timer.callOnce(this.testForFirefoxSilentError_, 250, this); - } - - } catch (e) { - // If submit threw an exception then it probably means the page that the - // code is running on the local file system and the form's action was - // pointing to a file that doesn't exist, causing the browser to fire an - // exception. - - this.logger_.severe( - 'Error when submitting form: ' + goog.debug.exposeException(e)); - - goog.events.unlisten(doc.getElementById(innerFrameName), - goog.events.EventType.LOAD, this.onIframeLoaded_, false, this); - - doc.close(); - - this.handleError_(goog.net.ErrorCode.FILE_NOT_FOUND); - } - } -}; - - -/** - * Handles the load event of the iframe for IE, determines if the request was - * successful or not, handles clean up and dispatching of appropriate events. - * @param {goog.events.BrowserEvent} e The browser event. - * @private - */ -goog.net.IframeIo.prototype.onIeReadyStateChange_ = function(e) { - if (this.iframe_.readyState == 'complete') { - goog.events.unlisten(this.iframe_, goog.events.EventType.READYSTATECHANGE, - this.onIeReadyStateChange_, false, this); - var doc; - /** @preserveTry */ - try { - doc = goog.dom.getFrameContentDocument(this.iframe_); - - // IE serves about:blank when it cannot load the resource while offline. - if (goog.userAgent.IE && doc.location == 'about:blank' && - !navigator.onLine) { - this.handleError_(goog.net.ErrorCode.OFFLINE); - return; - } - } catch (ex) { - this.handleError_(goog.net.ErrorCode.ACCESS_DENIED); - return; - } - this.handleLoad_(/** @type {HTMLDocument} */(doc)); - } -}; - - -/** - * Handles the load event of the iframe for non-IE browsers. - * @param {goog.events.BrowserEvent} e The browser event. - * @private - */ -goog.net.IframeIo.prototype.onIframeLoaded_ = function(e) { - // In Opera, the default "about:blank" page of iframes fires an onload - // event that we'd like to ignore. - if (goog.userAgent.OPERA && - this.getContentDocument_().location == 'about:blank') { - return; - } - goog.events.unlisten(this.getRequestIframe_(), - goog.events.EventType.LOAD, this.onIframeLoaded_, false, this); - this.handleLoad_(this.getContentDocument_()); -}; - - -/** - * Handles generic post-load - * @param {HTMLDocument} contentDocument The frame's document. - * @private - */ -goog.net.IframeIo.prototype.handleLoad_ = function(contentDocument) { - this.logger_.fine('Iframe loaded'); - - this.complete_ = true; - this.active_ = false; - - var errorCode; - - // Try to get the innerHTML. If this fails then it can be an access denied - // error or the document may just not have a body, typical case is if there - // is an IE's default 404. - /** @preserveTry */ - try { - var body = contentDocument.body; - this.lastContent_ = body.textContent || body.innerText; - this.lastContentHtml_ = body.innerHTML; - } catch (ex) { - errorCode = goog.net.ErrorCode.ACCESS_DENIED; - } - - // Use a callback function, defined by the application, to analyse the - // contentDocument and determine if it is an error page. Applications - // may send down markers in the document, define JS vars, or some other test. - var customError; - if (!errorCode && typeof this.errorChecker_ == 'function') { - customError = this.errorChecker_(contentDocument); - if (customError) { - errorCode = goog.net.ErrorCode.CUSTOM_ERROR; - } - } - - this.logger_.finer('Last content: ' + this.lastContent_); - this.logger_.finer('Last uri: ' + this.lastUri_); - - if (errorCode) { - this.logger_.fine('Load event occurred but failed'); - this.handleError_(errorCode, customError); - - } else { - this.logger_.fine('Load succeeded'); - this.success_ = true; - this.lastErrorCode_ = goog.net.ErrorCode.NO_ERROR; - this.dispatchEvent(goog.net.EventType.COMPLETE); - this.dispatchEvent(goog.net.EventType.SUCCESS); - - this.makeReady_(); - } -}; - - -/** - * Handles errors. - * @param {goog.net.ErrorCode} errorCode Error code. - * @param {Object=} opt_customError If error is CUSTOM_ERROR, this is the - * client-provided custom error. - * @private - */ -goog.net.IframeIo.prototype.handleError_ = function(errorCode, - opt_customError) { - if (!this.errorHandled_) { - this.success_ = false; - this.active_ = false; - this.complete_ = true; - this.lastErrorCode_ = errorCode; - if (errorCode == goog.net.ErrorCode.CUSTOM_ERROR) { - this.lastCustomError_ = opt_customError; - } - this.dispatchEvent(goog.net.EventType.COMPLETE); - this.dispatchEvent(goog.net.EventType.ERROR); - - this.makeReady_(); - - this.errorHandled_ = true; - } -}; - - -/** - * Dispatches an event indicating that the IframeIo instance has received a data - * packet via incremental loading. The event object has a 'data' member. - * @param {Object} data Data. - * @private - */ -goog.net.IframeIo.prototype.handleIncrementalData_ = function(data) { - this.dispatchEvent(new goog.net.IframeIo.IncrementalDataEvent(data)); -}; - - -/** - * Finalizes the request, schedules the iframe for disposal, and maybe disposes - * the form. - * @private - */ -goog.net.IframeIo.prototype.makeReady_ = function() { - this.logger_.info('Ready for new requests'); - var iframe = this.iframe_; - this.scheduleIframeDisposal_(); - this.disposeForm_(); - this.dispatchEvent(goog.net.EventType.READY); -}; - - -/** - * Creates an iframe to be used with a request. We use a new iframe for each - * request so that requests don't create history entries. - * @private - */ -goog.net.IframeIo.prototype.createIframe_ = function() { - this.logger_.fine('Creating iframe'); - - this.iframeName_ = this.name_ + '_' + (this.nextIframeId_++).toString(36); - - var iframeAttributes = {'name': this.iframeName_, 'id': this.iframeName_}; - // Setting the source to javascript:"" is a fix to remove IE6 mixed content - // warnings when being used in an https page. - if (goog.userAgent.IE && goog.userAgent.VERSION < 7) { - iframeAttributes.src = 'javascript:""'; - } - - this.iframe_ = /** @type {HTMLIFrameElement} */(goog.dom.createDom( - 'iframe', iframeAttributes)); - - var s = this.iframe_.style; - s.visibility = 'hidden'; - s.width = s.height = '10px'; - - // There are reports that safari 2.0.3 has a bug where absolutely positioned - // iframes can't have their src set. - if (!goog.userAgent.WEBKIT) { - s.position = 'absolute'; - s.top = s.left = '-10px'; - } else { - s.marginTop = s.marginLeft = '-10px'; - } -}; - - -/** - * Appends the Iframe to the document body. - * @private - */ -goog.net.IframeIo.prototype.appendIframe_ = function() { - goog.dom.getDocument().body.appendChild(this.iframe_); -}; - - -/** - * Schedules an iframe for disposal, async. We can't remove the iframes in the - * same execution context as the response, otherwise some versions of Firefox - * will not detect that the response has correctly finished and the loading bar - * will stay active forever. - * @private - */ -goog.net.IframeIo.prototype.scheduleIframeDisposal_ = function() { - var iframe = this.iframe_; - - // There shouldn't be a case where the iframe is null and we get to this - // stage, but the error reports in http://b/909448 indicate it is possible. - if (iframe) { - // NOTE(user): Stops Internet Explorer leaking the iframe object. This - // shouldn't be needed, since the events have all been removed, which - // should in theory clean up references. Oh well... - iframe.onreadystatechange = null; - iframe.onload = null; - iframe.onerror = null; - - this.iframesForDisposal_.push(iframe); - } - - if (this.iframeDisposalTimer_) { - goog.Timer.clear(this.iframeDisposalTimer_); - this.iframeDisposalTimer_ = null; - } - - if (goog.userAgent.GECKO || goog.userAgent.OPERA) { - // For FF and Opera, we must dispose the iframe async, - // but it doesn't need to be done as soon as possible. - // We therefore schedule it for 2s out, so as not to - // affect any other actions that may have been triggered by the request. - this.iframeDisposalTimer_ = goog.Timer.callOnce( - this.disposeIframes_, goog.net.IframeIo.IFRAME_DISPOSE_DELAY_MS, this); - - } else { - // For non-Gecko browsers we dispose straight away. - this.disposeIframes_(); - } - - // Nullify reference - this.iframe_ = null; - this.iframeName_ = null; -}; - - -/** - * Disposes any iframes. - * @private - */ -goog.net.IframeIo.prototype.disposeIframes_ = function() { - if (this.iframeDisposalTimer_) { - // Clear the timer - goog.Timer.clear(this.iframeDisposalTimer_); - this.iframeDisposalTimer_ = null; - } - - while (this.iframesForDisposal_.length != 0) { - var iframe = this.iframesForDisposal_.pop(); - this.logger_.info('Disposing iframe'); - goog.dom.removeNode(iframe); - } -}; - - -/** - * Disposes of the Form. Since IE6 leaks form nodes, this just cleans up the - * DOM and nullifies the instances reference so the form can be used for another - * request. - * @private - */ -goog.net.IframeIo.prototype.disposeForm_ = function() { - if (this.form_ && this.form_ == goog.net.IframeIo.form_) { - goog.dom.removeChildren(this.form_); - } - this.form_ = null; -}; - - -/** - * @return {HTMLDocument} The appropriate content document. - * @private - */ -goog.net.IframeIo.prototype.getContentDocument_ = function() { - if (this.iframe_) { - return /** @type {HTMLDocument} */(goog.dom.getFrameContentDocument( - this.getRequestIframe_())); - } - return null; -}; - - -/** - * @return {HTMLIFrameElement} The appropriate iframe to use for requests - * (created in sendForm_). - * @private - */ -goog.net.IframeIo.prototype.getRequestIframe_ = function() { - if (this.iframe_) { - return /** @type {HTMLIFrameElement} */(goog.userAgent.IE ? this.iframe_ : - goog.dom.getFrameContentDocument(this.iframe_).getElementById( - this.iframeName_ + goog.net.IframeIo.INNER_FRAME_SUFFIX)); - } - return null; -}; - - -/** - * Tests for a silent failure by firefox that can occur when the connection is - * reset by the server or is made to an illegal URL. - * @private - */ -goog.net.IframeIo.prototype.testForFirefoxSilentError_ = function() { - if (this.active_) { - var doc = this.getContentDocument_(); - - // This is a hack to test of the document has loaded with a page that - // we can't access, such as a network error, that won't report onload - // or onerror events. - if (doc && !goog.reflect.canAccessProperty(doc, 'documentUri')) { - goog.events.unlisten(this.getRequestIframe_(), - goog.events.EventType.LOAD, this.onIframeLoaded_, false, this); - - if (navigator.onLine) { - this.logger_.warning('Silent Firefox error detected'); - this.handleError_(goog.net.ErrorCode.FF_SILENT_ERROR); - } else { - this.logger_.warning('Firefox is offline so report offline error ' + - 'instead of silent error'); - this.handleError_(goog.net.ErrorCode.OFFLINE); - } - return; - } - this.firefoxSilentErrorTimeout_ = - goog.Timer.callOnce(this.testForFirefoxSilentError_, 250, this); - } -}; - - - -/** - * Class for representing incremental data events. - * @param {Object} data The data associated with the event. - * @extends {goog.events.Event} - * @constructor - */ -goog.net.IframeIo.IncrementalDataEvent = function(data) { - goog.events.Event.call(this, goog.net.EventType.INCREMENTAL_DATA); - - /** - * The data associated with the event. - * @type {Object} - */ - this.data = data; -}; -goog.inherits(goog.net.IframeIo.IncrementalDataEvent, goog.events.Event); |