jquery - 在某些页面上的 IE 中拒绝 iframe 访问

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14879192/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 14:01:57  来源:igfitidea点击:

jquery - iframe access denied in IE on some pages

jqueryiframeprintingprintthis

提问by Jason

I'm the author of printThis, a jquery plugin for printing.

我是printThis的作者,一个用于打印的jquery插件。

https://github.com/jasonday/printThis

https://github.com/jasonday/printThis

I have a user that has brought up an issue, that I have been unable to crack and unfortunately, I am unable to share the page (privacy concerns).

我有一个用户提出了一个问题,我无法破解,不幸的是,我无法共享页面(隐私问题)。

On the user's site, the issue presents on some pages in IE, but not others. The print is failing to happen, as the iframe remains empty.

在用户的站点上,该问题出现在 IE 中的某些页面上,而不是其他页面上。打印失败,因为 iframe 仍然是空的。

The error in IE is within jQuery:

IE 中的错误在 jQuery 中:

contents: function (a) {
            return f.nodeName(a,
                "iframe") ? a.contentDocument || a.contentWindow.document : f.makeArray(a.childNodes)
        }

Using logging, I was able to determine it was failing around this line:

使用日志记录,我能够确定它在这条线上失败了:

var $doc = $("#" + strFrameName).contents();

But again, this only happens on some pages and I have been unable to recreate in any instance outside of this user's site.

但同样,这只发生在某些页面上,我无法在该用户站点之外的任何实例中重新创建。

My question: Is there a better approach here? or a method to make the $docobject more bulletproof?

我的问题:这里有更好的方法吗?或使$doc物体更防弹的方法?



// -----------------------------------------------------------------------
// printThis v1.1
// Printing plug-in for jQuery
//
// Resources (based on) :
//              jPrintArea: http://plugins.jquery.com/project/jPrintArea
//              jqPrint: https://github.com/permanenttourist/jquery.jqprint
//              Ben Nadal: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm
//
// Dual licensed under the MIT and GPL licenses:
//              http://www.opensource.org/licenses/mit-license.php
//              http://www.gnu.org/licenses/gpl.html
//
// (c) Jason Day 2012
//
// Usage:
//
// $("#mySelector").printThis({
//      debug: false, //show the iframe for debugging
//      importCSS: true, // import page CSS
//      printContainer: true, // grab outer container as well as the contents of the selector
//      loadCSS: "path/to/my.css" //path to additional css file
//  });
//
// Notes:
//  - the loadCSS option does not need @media print
//------------------------------------------------------------------------

(function($) {
    var opt;

    $.fn.printThis = function (options) {
        opt = $.extend({}, $.fn.printThis.defaults, options);

        var $element = (this instanceof jQuery) ? this : $(this);

    // if Opera, open a new tab
        if ($.browser.opera)
        {
            var tab = window.open("","Print Preview");
            tab.document.open();


        }
    // add dynamic iframe to DOM
        else
        {
        var strFrameName = ("printThis-" + (new Date()).getTime());

            var $iframe = $("<iframe id='" + strFrameName +"' src='about:blank'/>");

            if (!opt.debug) { $iframe.css({ position: "absolute", width: "0px", height: "0px", left: "-600px", top: "-600px" }); }

            $iframe.appendTo("body");

        }
    // allow iframe to fully render before action
    setTimeout ( function () {

        if ($.browser.opera)
            {
        var $doc = tab.document;
        } else
        {
        var $doc = $("#" + strFrameName).contents();
        }



        // import page css
        if (opt.importCSS)
        {
                $("link[rel=stylesheet]").each(function(){
                var href = $(this).attr('href');
                if(href){
                        var media = $(this).attr('media') || 'all';
                        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + href + "' media='"+media+"'>");
                    }
        });
        }

        // add another stylesheet
        if (opt.loadCSS)
        {
        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");

        }

        //add title of the page
        if (opt.titlePage)
        {
        $doc.find("head").append('<title>'+opt.titlePage+'</title>');
        } 
        //grab outer container
        if (opt.printContainer) { $doc.find("body").append($element.outer()); }
        else { $element.each( function() { $doc.find("body").append($(this).html()); }); }

        //$doc.close();
        // print
        ($.browser.opera ? tab : $iframe[0].contentWindow).focus();
        setTimeout( function() { ($.browser.opera ? tab : $iframe[0].contentWindow).print(); if (tab) { tab.close(); } }, 1000);

        //removed iframe after 60 seconds
        setTimeout(
        function(){
        $iframe.remove();
        },
        (60 * 1000)
        );
    }, 333 );
    }


    $.fn.printThis.defaults = {
        debug: false, //show the iframe for debugging
        importCSS: true, // import page CSS
        printContainer: true, // grab outer container as well as the contents of the selector
        loadCSS: "", //path to additional css file
        titlePage: "" //add title to print page
    };


    jQuery.fn.outer = function() {
      return $($('<div></div>').html(this.clone())).html();
    }
})(jQuery);

UPDATE

更新

Issue to due to document.domain

问题是由于 document.domain

This type of page has document.domainset and IE does not inherit document.domainfrom the parent.

这种类型的页面已经document.domain设置并且 IE 不document.domain从父级继承。

To fix that portion, I changed the iframe creation to standard javascript and set the source to write document.domainon iframe creation.

为了修复该部分,我将 iframe 创建更改为标准 javascript 并将源设置为document.domain在 iframe 创建时写入。

    var printI= document.createElement('iframe');

    printI.name = "printIframe";

    printI.id = strFrameName;

    document.body.appendChild(printI);

    printI.src = "javascript:document.write('<head><script>document.domain=\"mydomain.com\";</script></head><body></body>')";


   var $iframe = $("#" + strFrameName);

So this fixes the access denied, however now the frame won't print. I've tried a lot of different methods for accessing the object, however none of them are working.

所以这修复了拒绝访问的问题,但是现在框架不会打印。我尝试了很多不同的方法来访问该对象,但是它们都不起作用。

A) how would you access the frame in this scenario (i've tried most of the methods outlined on SO) to get IE to recognize and print

A)在这种情况下,您将如何访问框架(我已经尝试了 SO 上概述的大多数方法)以使 IE 识别和打印

or

或者

B) can anyone think of a better way to get the document.domain into the iframe on creation with jQuery? (can't be afterwards, as the access denied issue will come up)

B) 任何人都可以想出更好的方法在使用 jQuery 创建时将 document.domain 放入 iframe 中吗?(不能在之后,因为会出现访问被拒绝的问题)

采纳答案by Jason

Issue is due to IE not inheriting the parent document.domain.

问题是由于 IE 没有继承父 document.domain。

Unfortunately, once you get into this murky area, it took some specific hacks to get this to work properly.

不幸的是,一旦你进入这个模糊的领域,就需要一些特定的技巧才能让它正常工作。

Basically doing a check for if document.domain is being explicitly set and the browser is IE.

基本上是检查是否明确设置了 document.domain 并且浏览器是 IE。

Full updated plugin:

完整更新的插件:

https://github.com/jasonday/printThis

https://github.com/jasonday/printThis

(function ($) {
    var opt;
    $.fn.printThis = function (options) {
        opt = $.extend({}, $.fn.printThis.defaults, options);
        var $element = this instanceof jQuery ? this : $(this);

            var strFrameName = "printThis-" + (new Date()).getTime();

            if(window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)){
                // Ugly IE hacks due to IE not inheriting document.domain from parent
                // checks if document.domain is set by comparing the host name against document.domain
                var iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\"" + document.domain + "\\";</script></head><body></body>\")";
                var printI= document.createElement('iframe');
                printI.name = "printIframe";
                printI.id = strFrameName;
                printI.className = "MSIE";
                document.body.appendChild(printI);
                printI.src = iframeSrc;

            } else {
                 // other browsers inherit document.domain, and IE works if document.domain is not explicitly set
                var $frame = $("<iframe id='" + strFrameName +"' name='printIframe' />");
                $frame.appendTo("body");
            }


            var $iframe = $("#" + strFrameName);

            // show frame if in debug mode
            if (!opt.debug) $iframe.css({
                position: "absolute",
                width: "0px",
                height: "0px",
                left: "-600px",
                top: "-600px"
            });


        // $iframe.ready() and $iframe.load were inconsistent between browsers    
        setTimeout ( function () {

            var $doc = $iframe.contents();

            // import page stylesheets
            if (opt.importCSS) $("link[rel=stylesheet]").each(function () {
                var href = $(this).attr("href");
                if (href) {
                    var media = $(this).attr("media") || "all";
                    $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>")
                }
            });

            //add title to iframe
            if (opt.pageTitle) $doc.find("head").append("<title>" + opt.pageTitle + "</title>");

            // import additional stylesheet
            if (opt.loadCSS) $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");

            // grab $.selector as container
            if (opt.printContainer) $doc.find("body").append($element.outer());

            // otherwise just print interior elements of container
            else $element.each(function () {
                $doc.find("body").append($(this).html())
            });

            if($iframe.hasClass("MSIE")){
                // check if the iframe was created with the ugly hack
                // and perform another ugly hack out of neccessity
                window.frames["printIframe"].focus();
                setTimeout(function () {
                   $doc.find("head").append("<script>  window.print(); </script>");
                }, 500 );
            } else {
                // proper method
                $iframe[0].contentWindow.focus();
                $iframe[0].contentWindow.print();  
            }

             //remove iframe after print
            if (!opt.debug) {
                setTimeout(function () {
                    $iframe.remove();
                }, 1000);
            }


        }, 333 );

    };

    // defaults
    $.fn.printThis.defaults = {
        debug: false,           // show the iframe for debugging
        importCSS: true,        // import parent page css
        printContainer: true,   // print outer container/$.selector
        loadCSS: "",            // load an additional css file
        pageTitle: ""           // add title to print page
    };

    // $.selector container
    jQuery.fn.outer = function () {
        return $($("<div></div>").html(this.clone())).html()
    }
})(jQuery);

回答by user568109

In your code, you are using setTimeoutto execute your function after the iframe has loaded.

在您的代码中,您setTimeout用于在 iframe 加载后执行您的函数。

// allow iframe to fully render before action
setTimeout ( function () {
...
}, 333 );  //333ms

but this is a mistake as you don't know if the time given is enough to load the iframe or not. Javascript execution is asynchronous so, there is no guarantee that setTimeoutwill offset the execution of the function until iframe loads. Since load time is different for different pages. Some cannot execute the code properly, pointing to the line which you find to be causing errors.

但这是一个错误,因为您不知道给定的时间是否足以加载 iframe。Javascript 执行是异步的,因此无法保证setTimeout在 iframe 加载之前会抵消函数的执行。由于不同页面的加载时间不同。有些无法正确执行代码,指向您发现导致错误的行。

var $doc = $("#" + strFrameName).contents();  //only after loading

The correct way is to use event loador onloadto get to know if the DOM object has loaded properly or not.

正确的方法是使用事件loadonload了解 DOM 对象是否已正确加载。

<script>
document.getElementById("myframe").onload = function() {
  alert("myframe is loaded");
};
</script>
//or
<iframe id="myFrame" onload="myFunction();"></iframe>

回答by allenhwkim

As long as you set iframe src, the same origin has to be verified against parent element, even if you set it as 'about:blank'. I guess IE fails in proper checkng, or some javascript ran and set the document.location to different one than the iframe is created.

只要您设置 iframe src,就必须针对父元素验证相同的来源,即使您将其设置为“about:blank”。我猜 IE 无法正确检查,或者运行了一些 javascript 并将 document.location 设置为与创建 iframe 不同的位置。

How about NOT setting src at all like the following? it still should work.

完全不设置 src 怎么样?它仍然应该工作。

var $iframe = $("<iframe id='" + strFrameName +"'/>");
$iframe.appendTo("body");
var $iframeDoc = $iframe[0].contentWindow.document;

$iframeDoc.open();
$iframeDoc.write("foo");
$iframeDoc.close();

回答by Ionut Tocila

IE works with iframe like all the other browsers (at least for main functions). You just have to keep a set of rules:

IE 像所有其他浏览器一样使用 iframe(至少对于主要功能)。你只需要遵守一套规则:

  • before you load any javascript in the iframe (that part of js which needs to know about the iframe parent), ensure that the parent has document.domain changed.
  • when all iframe resources are loaded, change document.domain to be the same as the one defined in parent. (You need to do this later because setting domain will cause the iframe resource's request to fail)

  • now you can make a reference for parent window: var winn = window.parent

  • now you can make a reference to parent HTML, in order to manipulate it: var parentContent = $('html', winn.document)
  • at this point you should have access to IE parent window/document and you can change it as you wont
  • 在 iframe 中加载任何 javascript(需要了解 iframe 父级的 js 部分)之前,请确保父级已更改 document.domain。
  • 加载所有 iframe 资源后,将 document.domain 更改为与 parent 中定义的相同。(后面需要做这个,因为设置domain会导致iframe资源的请求失败)

  • 现在你可以为父窗口做一个参考:var winn = window.parent

  • 现在您可以引用父 HTML,以便对其进行操作: var parentContent = $('html', winn.document)
  • 此时您应该可以访问 IE 父窗口/文档,您可以随意更改它

回答by splashout

This answer has already been stated in the original question UPDATE, but I wanted add a more succinct answer to the original question related to getting around the SCRIPT70 Permission denied error (I ran into this on IE11/Win7 with JQuery 3.2.1).

此答案已在原始问题 UPDATE 中陈述,但我想为与绕过 SCRIPT70 Permission denied 错误相关的原始问题添加更简洁的答案(我在 IE11/Win7 上使用 JQuery 3.2.1 遇到此问题)。

Instead of $('<iframe .../>').appendTo($('body'))

代替 $('<iframe .../>').appendTo($('body'))

Do this:

做这个:

var $iframe = $('<iframe .../>');
document.body.appendChild($iframe[0]);

Answer taken from here: https://bugs.jquery.com/ticket/13936#comment:28

从这里获取的答案:https: //bugs.jquery.com/ticket/13936#comment:28