Threaded View

    Thank you for reporting this bug. We will make it our priority to review this report.
  1. #1
    Sencha User
    Join Date
    Oct 2009
    Cambridge, UK
    Vote Rating
    Uberdude is on a distinguished road


    Default Response lost from upload Ajax request to iframe if document.domain changed

    Ext version tested:
    • Ext 3.3.0

    Adapter used:
    • ext

    css used:
    • default ext-all.css
    • optionally examples.css from Ext examples to make test case look nice, but this is not a UI issue.

    Browser versions tested against:
    • Chrome 10.0.648.204
    • IE9, IE7
    • FF 3.6.15 (firebug 1.7.1 installed)
    • Opera 11.01
    • Safari 5.0.3 (7533.19.4)

    Operating System:
    • Windows 7 64-bit for all browsers except IE7
    • Windows Server 2003 for IE7


    When you make an Ext.Ajax.request with a form containing a file input to be uploaded, or manually set the isUpload option to true, rather than doing a proper Ajax request Ext submits the form in the standard HTML way to a dynamically generated hidden <iframe>. The json response body is then read out of the iframe to create a faked-up Ajax response. A problem arises if the page making the upload Ajax request has changed its document.domain property, e.g. a page at includes resources from which it wishes to manipulate with javascript without violating the browser's same-origin-policy, so both set their document.domain to "". If then makes an upload Ajax request to a url on the server, the iframe into which the response is written will have its document.domain as "". Thus when the ExtJS code within Ajax.request on the page tries to extract the document body from the iframe, it will be blocked by the same-origin-policy and the response passed to the callback functions will incorrectly have empty responseText.

    Test Case:

        <title>Upload Form to iFrame with changed document.domain</title>
        <!-- TODO update these paths for your environment, they are currently based on this file being in the Ext examples/form directory -->
        <!-- ** CSS ** -->
        <!-- base library -->
        <link rel="stylesheet" type="text/css" href="../../resources/css/ext-all.css"/>
        <!-- overrides to base library -->
        <!-- page specific, just to make it pretty, no big deal if missing -->
        <link rel="stylesheet" type="text/css" href="../shared/examples.css" />
        <!-- ** Javascript ** -->
        <!-- base library -->
        <script type="text/javascript" src="../../adapter/ext/ext-base.js"></script>
        <script type="text/javascript" src="../../ext-all.js"></script>
        <h1>File Upload to iFrame with changed document.domain</h1>
        <p>The example demonstrates how shortening the document.domain causes the response from file-upload-style Ajax requests to get lost.</p>
        <p>To run this test you need to deploy it to a real web server rather than run from local file system (so that document.domain is not blank) and the server should have a urls <b>welcomeAjax</b> and <b>welcomeUpload</b>.</p>
        <p>The current value of document.domain is: </p>
        <div id="domainText"></div>    
        <div id="trimBtn"></div>
        <div id="welcomeForm"></div>
        <script type="text/javascript"> 
            Ext.BLANK_IMAGE_URL = '../../resources/images/default/s.gif';
            Ext.onReady(function() {
                var domainText = new Ext.form.TextField({
                    readOnly: true,
                    renderTo: 'domainText',
                    width: 300
                function displayDomain() {
                new Ext.Button({
                    renderTo: 'trimBtn',
                    text: 'Trim Domain',
                    handler: function() {
                        document.domain = document.domain.substr(document.domain.indexOf('.') + 1);
                var welcomeForm = new Ext.form.FormPanel({
                    bodyStyle: 'padding: 5px;',        
                    renderTo: 'welcomeForm',
                    title: 'Welcome Form',
                    width: 300,
                    height: 100,
                    items: [{
                        fieldLabel: 'Name',
                        name: 'name',
                        xtype: 'textfield'
                    buttons: [
                            text: 'Submit with Normal Ajax',
                            handler: function() {
                        }, {
                            text: 'Submit with File Upload',
                            handler: function() {
                function makeRequest(upload) {
                        url: upload ? '../welcomeUpload' : '../welcomeAjax',          // TODO Adjust these for your server
                        form: welcomeForm.getForm().getEl(),
                        isUpload: upload,
                        success: function(response) {
                            if (!response) {
                                Ext.Msg.alert('Success?', 'No response');
                            else if (!response.responseText) {
                                Ext.Msg.alert('Success?', 'Response but empty responseText, this is the symptom of the domain bug!');
                            else {
                                Ext.Msg.alert('Success', Ext.decode(response.responseText).msg);
                        failure: function() {
                            Ext.Msg.alert('Error', 'Generic fail');
    As this issue relates to document.domain, you need to deploy this file to a real webserver, preferably with a sub-domain as in "" so the document.domain can be set to "". If you have just a domain "", setting the domain to "com" is dodgy.

    The webserver also needs 2 urls to provide json respones, presented below in nodeJS code, adapt this to whatever server technology you use.

    // In our nodeJS framework this is how we make controller methods
    exports.handlers = {
        welcomeAjax: {
            handler: function(params, response) {
                response.setHeader('Content-Type', 'application/json');
                // Write json and end
                response.end(JSON.stringify({msg: 'Welcome ' +}));
        welcomeUpload: {
            handler: function(params, response) {
                // Note content type must be set to html for file uploads, not doing so is a common cause of errors and thus forum posts.
                response.setHeader('Content-Type', 'text/html');
                response.end(JSON.stringify({msg: 'Welcome ' +}));
    Steps to reproduce the problem:
    • Deploy the test harness html to your web server and create the server side urls to serve the json response.
    • On the page, type you name in the Name text box.
    • 1. Click Submit with Normal Ajax
    • 2. Click Submit with File Upload
    • 3. Click Trim Domain
    • 4. Click Submit with Normal Ajax
    • 5. Click Submit with File Upload

    The result that was expected:
    • 1. and 2. each cause a Welcome message box to appear
    • After 3. you see in the domain textbox that the domain has been shortened, e.g. from to
    • 4. and 5. each cause a Welcome message box to appear

    The result that occurs instead:
    • 1. and 2. each cause a Welcome message box to appear
    • After 3. you see in the domain textbox that the domain has been shortened, e.g. from to
    • 4. causes a Welcome message box to appear
    • 5. causes a message box with "Response but empty responseText, this is the symptom of the domain bug!" to appear. If you look in the Chrome debug console you will see an error message like "Unsafe JavaScript attempt to access frame with URL from frame with URL Domains, protocols and ports must match."

    Debugging already done:
    • I have determined that it is the browser's same-origin-policy that means the parent page cannot access the document within the dynamic iframe that contains the response body. Inspecting the html of the page (using firebug, Chrome dev tools or equivalent) I can see the iframe and it does contain the response json.
    • The problem occurs on line 6543 of ext-all-debug.js in the doFormUpload function
      doc = frame.contentWindow.document || frame.contentDocument || WINDOW.frames[id].document;
      frame.contentWindow evaluates ok, but when trying to access the document property of the resulting object generates the access denied error. The 3 OR clauses here leads to 3 copies of the error in the console

    Possible fix:

    I tried hacking the Ext source code to set the document.domain of the <iframe> when it is constructed to be the same as that of the page creating it, but failed to do so. I have managed to workaround this problem by sending the document.domain as an extra parameter on all file upload requests, and then changing the server-side code to inject a script tag which sets the document.domain of the response html document containing the json to that specified in the parameter. But this is rather unpleasant and the ideal solution would be if somehow Ext JS could change the iframe's domain.

    My workaround comprises the following changes:

    We have a wrapper around Ext.Ajax.request which, similar to within Ext, can turn a params based request into a form upload one (but with added custom functionality)
    // items are hidden form field config corresponding to the request parameters
    formPanel = new Ext.form.FormPanel({
        hidden: true,
        items: items,
        renderTo: Ext.getBody() // Have to add it to the DOM somewhere to get the <FORM>
    args.isUpload = true;
    args.form = formPanel.getForm().getEl();
    To the items is added an extra domain parameter:
        name: '__domain',
        value: document.domain,
        xtype: 'hidden'
    Then server-side the welcomeUpload handler changes to inject a script tag in the header (so the body is still just the json and can be used to reconstruct the fake XHR response):
    welcomeUpload: {
        handler: function(params, response) {
            // Note content type must be set to html for file uploads, not doing so is a common cause of errors and thus forum posts.
            response.setHeader('Content-Type', 'text/html');
            response.write('<html><head><script type="text/javascript">document.domain = "' + params.__domain + '";</script></head><body>');
            response.write(JSON.stringify({msg: 'Welcome ' +}));
    With this workaround applied, all 4 clicks in the test case cause the welcome message to appear.
    Last edited by Uberdude; 27 Jun 2011 at 1:12 AM. Reason: WIP!