16

Chrome 拡張機能を開発していて、大きな問題にぶつかりました。

コンテンツ スクリプトを使用して、Web サイトに JavaScript コードを挿入しています。Web サイトには iframe があります。iframe のソース コードを変更できますが、iframe の contentWindow プロパティにアクセスできないようです。現在のキャレット位置にテキストを挿入する必要があります。

したがって、基本的に、このコードはページのコンテキストで完全に機能します。

$("#iframe1").contentWindow.document.execCommand("InsertHTML", false, 'test text');

しかし、Chrome 拡張機能のコンテキストで実行しようとすると、次のエラーが発生します。

TypeError: Cannot read property 'document' of undefined

奇妙なのは、iframe の html にアクセスできることです。したがって、このコードは chrome 拡張機能から完全に機能します。

$("#iframe1").contents().find('div').html('test')

マニフェストファイルに "all_frames": true を入れてみましたが、うまくいきません:(

4

1 に答える 1

30

コードが機能しない理由を理解するために、以前の回答の一部を含めます。

windowコンテンツ スクリプトは、ページのグローバルオブジェクトにアクセスできません。コンテンツ スクリプトの場合、以下が適用されます。

  • 変数は、windowページのグローバル オブジェクトを参照していません。代わりに、ページ上の「レイヤー」という新しいコンテキストを参照します。ページの DOM は完全にアクセス可能です。#実行環境

で構成されるドキュメントが与えられた場合   <iframe id="frameName" src="http://domain/"></iframe>:

  • フレームのコンテンツへのアクセスは、ページの同一オリジン ポリシーによって制限されます。拡張機能のアクセス許可は、ポリシーを緩和しません。
  • frames[0]frames['frameName']、(通常はフレームに含まれるグローバルwindowオブジェクトを参照) はundefinedです。
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocumentdocumentコンテンツ スクリプトはページの DOM にアクセスできるため、含まれているフレームのオブジェクトを返します。このプロパティはnull、Same origin ポリシーが適用される場合です。
    • iframe.contentDocument.defaultView(windowドキュメントに関連付けられたオブジェクトを参照) はundefinedです。
    • iframe.contentWindow未定義 です。

同じ起源のフレームの解決策

あなたの場合、次のいずれかが機能します。

// jQuery:
$("#iframe1").contents()[0].execCommand( ... );

// VanillaJS
document.getElementById("iframe1").contentDocument.execCommand( ... );

// "Unlock" contentWindow property by injecting code in context of page
var s = document.createElement('script');
s.textContent = 'document.getElementById("iframe1").contentWindow.document.execCommand( ... );';
document.head.appendChild(s);

一般的なソリューション

一般的な解決策は"all_frames": true、マニフェスト ファイルで使用しており、次のようなものを使用します。

if (window != top) {
    parent.postMessage({fromExtension:true}, '*');
    addEventListener('message', function(event) {
        if (event.data && event.data.inserHTML) {
            document.execCommand('insertHTML', false, event.data.insertHTML);
        }
    });
} else {
    var test_html = 'test string';
    // Explanation of injection at https://stackoverflow.com/a/9517879/938089 :
    // Run code in the context of the page, so that the `contentWindow`
    //  property becomes accessible
    var script = document.createElement('script');
    script.textContent = '(' + function(s_html) {
        addEventListener('message', function(event) {
            if (event.data.fromExtension === true) {
                var iframe = document.getElementById('iframe1');
                if (iframe && (iframe.contentWindow === event.source)) {
                    // Window recognised, post message back
                    iframe.contentWindow.postMessage({insertHTML: s_html}, '*');
                }
            }
        });
    } + ')(' + JSON.stringify(test_html) + ');';
    (document.head||document.documentElement).appendChild(script);
    script.parentNode.removeChild(script);
}

このデモは教育のみを目的としています。このデモを実際の拡張機能で使用しないでください。なんで?postMessageメッセージをやり取りするために使用されるためです。これらのイベントは、セキュリティ リーク (XSS: 任意の HTML インジェクション) を引き起こすクライアントによっても生成される可能性があります。

代替手段postMessageは Chrome のメッセージ API です。デモについては、この回答を参照してください。windowただし、オブジェクトを比較することはできません。あなたができるwindow.nameことは、プロパティを信頼することです。プロパティは、iframeの属性window.nameの値に自動的に設定されnameます (iframe が読み込まれるときに 1 回だけ)。

于 2012-11-07T14:24:46.467 に答える