"Access is Denied" on contentDocument in IE9 on same domain [duplicate]
Asked Answered
O

2

6

Short/Generic Version:

I'm working on an application that (unfortunately, for other reasons), sets document.domain at the top of every page to a substring of the "true" domain: for subdomains like sub.app.local, document.domain = "app.local". I'm also creating an iframe dynamically and adding it to the page. The iframe loads a file residing on the same server as the parent page. Later on, some Javascript needs to access the contentDocument property of the iframe.

This is fine in modern browsers, but causes problems in IE9 due to this bug. (See the top answer for a good explanation of why this is.) AFAIK, every browser newer than IE9 automatically inherits document.domain for programmatically-created iframes, so this is IE9-specific. Because of some of the unique requirements of my scenario (tldr: the iframe src needs to change), the answer in the above post didn't work for me.

Longer/Application-Specific Version:

I'm using FineUploader in an application running on IIS, uploading files to S3. Everything's working just fine in modern browsers, but IE9 support is giving me trouble, even after following the docs (Supporting IE9 and older) to configure the iframeSupport.localBlankPagePath option. I'm a little stumped!

  • I've tried this on FineUploader 4.0.3 and also 5.0.3.
  • Everything works fine in IE11. If I switch Document mode to 9 in the F12 console, it breaks.
  • I don't have CORS support turned on, because I'm not loading anything from a different domain.
  • I have a dummy/blank file on the same domain as my page.
  • I can see (in the Network panel) the HTTP 303 response from AWS that includes the Location key, and a value that points to my blank document. If I paste the entire URL into the address bar, the page loads fine and is blank as expected.
  • I've tried adding X-Frame-Options SAMEORIGIN to the server's response headers, as suggested here, but it didn't help.

The error that I get in the console is:

[Fine Uploader 5.0.3] Error when attempting to access iframe during handling of upload response (Access is denied.
)
[Fine Uploader 5.0.3] Amazon likely rejected the upload request



UPDATE

I've determined that the reason it's not working out-of-the-box is because the application sets document.domain on page load, and it's different than (a subset of) location.host. FineUploader's 303 redirect mechanism to the blank page is working fine, but the document.domain of the iframe differs from the parent (due to IE9 not inheriting the property) and so, access denied.

The actual S3 upload is working. It's just the final step of verifying the upload that fails.

Here's my client-side code:

FineUploader.js

var fu = namespace('App.FineUploader');
fu.DocId;
fu.ClientDeployId;
fu.viewModel;
fu.defaultAttachmentEndpoint = "https://s3.amazonaws.com/App.UploadBucket";
fu.FineUploaderController = "FineUploaderDocAttachment";

fu.delete = function (documentAttatchmentID, attachmentID) {
    var data = documentAttatchmentID;
    $.ajax({
        type: "POST",
        url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/delete",
        data: JSON.stringify(data),
        success: function () {
            $('#fineUploader' + attachmentID).fineUploader('reset');
            $('#fineUploader' + attachmentID).show();
            $('#aDownloadfineUploader' + attachmentID).html('');
            $('#aDownloadfineUploader' + attachmentID).hide();
            $('#aDeletefineUploader' + attachmentID).hide();
        },
        dataType: 'json',
        contentType: 'application/json'
    })
}

fu.lockAll = function () {
    $('.fineUploader').hide();
    $('a[id^="aDeletefineUploader"]').hide();
}

fu.init = function (sID) {
    $('#' + sID).fineUploaderS3({
        request: {
            endpoint: fu.defaultAttachmentEndpoint,
            accessKey: "[key]",
            params: {
                documentid: $('#' + sID).attr('documentid'),
                attachmentid: $('#' + sID).attr('attachmentid')
            }
        },
        signature: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/signtureHandler"
        },
        uploadSuccess: {
            endpoint: App.BaseUrl + "/api/" + fu.FineUploaderController + "/success"
        },
        iframeSupport: {
            localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html"
        },
        objectProperties: {
            key: function (fileId) {
                var keyRetrieval = new qq.Promise(),
                    filename = $('#' + sID).fineUploader("getName", fileId);
                var documentid = $('#' + sID).attr('documentid');
                var attachmentid = $('#' + sID).attr('attachmentid');
                var data = { name: filename, documentId: documentid, attachmentId: attachmentid }
                $.ajax({
                    type: "POST",
                    url: App.BaseUrl + "/api/" + fu.FineUploaderController + "/preUpload",
                    data: JSON.stringify(data),
                    success: null,
                    dataType: 'json',
                    contentType: 'application/json'
                }).done(function (data) {
                    keyRetrieval.success(data.key);
                }).fail(function () {
                    keyRetrieval.failure();
                });
                return keyRetrieval;
            }
        },
        validation: {
            itemLimit: 1
        },
        chunking: {
            enabled: true
        }
    }).on('error', function (event, id, name, errorReason, xhrOrXdr) {
        alert(qq.format("Error on file number {} - {}.  Reason: {}", id, name, errorReason));
    }).on('complete', function (event, id, name, response) {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            $('#' + sID).fineUploader('reset');
            App.Contracts.Create.ReloadImageLibraryList(true);
            App.Contracts.Create.HideLoader();
        } else {
            $('#aDownload' + sID).attr('href', response.url);
            $('#aDownload' + sID).html(response.name);
            $('#aDownload' + sID).show();
            $('#aDelete' + sID).show();
            $('#aDelete' + sID).attr('onclick', 'App.FineUploader.delete(' + response.daId + ',' + sID.replace('fineUploader', '') + ');');
            $('#' + sID).hide();
        }
    }).on('submitted', function () {
        if (fu.FineUploaderController === "FineUploaderDocLibraryAttachment") {
            App.Contracts.Create.ShowLoader();
        }
    });
}
Orthotropous answered 21/7, 2014 at 19:26 Comment(6)
Have you tried giving admin rights to IIS? This is a frequent source of errors.Winther
I'm fairly certain that isn't the issue here, since the parent application is running on our production servers currently with no problems. (Unless it's a specific permissions issue with one of the related files here, perhaps.)Orthotropous
Have you used native IE9, or just the quirks-mode switch in IE11? There are discrepancies, and quirks mode is known to cause problems.Emplace
I believe you need to ensure the content-type is text/html for the returned iframe too.Emplace
You'll need to show your client side code to receive any useful assistance. You'll Aldo need to tell us if the file is actually making it to S3 or not (successfully ).Chabot
@RayNicholus Sorry, new to SO! I've posted my client code (if you need more/other snippets, let me know) and an update on my debugging so far.Orthotropous
O
2

(If anyone stumbles across this answer outside of the context of FineUploader, this idea is what I based my solution on.)

To implement this, I made FineUploader's blank.html slightly non-blank:

<head>
<script type="text/javascript">
    // Provide a mechanism to override document.domain
    // inside the iframe via this url syntax: blank.html?[args]#domain.com
    if (location.hash.substring(1).length > 0)
        document.domain = location.hash.substring(1);
</script>
</head><body></body>

This gives me a way to feed the correct document.domain value from the parent page when the iframe is generated. Slight modification to the FineUploader configuration object:

$.fineUploaderS3({
[snip]
    iframeSupport: {
        localBlankPagePath: App.BaseUrl + "/Scripts/FineUploader/4.0.3/html/blank.html#" + document.domain
    },
[/snip]
}

This doesn't seem to interfere with the arguments that are prepended by AWS. We're still using FineUploader 4.0.3 in this application, but this should work with latest as well.

tl,dr; It works! Tested in IE11 Document mode and also native IE9.

Orthotropous answered 22/7, 2014 at 15:39 Comment(2)
Note that the iframeSupport option is not needed and thus ignored in IE10+. In fact, this is only used in IE7-9.Chabot
@RayNicholus Makes sense, I figured as much. Thanks for the clarification.Orthotropous
C
1

The error suggests that the page served by the iframe is indeed not the same domain as the page hosting the uploader. Either that, or you have some plug-in/extension causing trouble. According to the error, Fine Uploader is simply not able to access any content in the iframe, which happens when the domain of the iframe doesn't match the domain of the frame/page hosting the uploader.

Chabot answered 22/7, 2014 at 15:24 Comment(1)
Yep, turns out some Javascript earlier in the page was indeed setting the domain to something else (the base domain, without a subdomain part). I posted my solution below. I'm not sure if it's "best", but it works.Orthotropous

© 2022 - 2024 — McMap. All rights reserved.