2012-11-07 11 views
9

クローム拡張機能を開発して大きな問題にぶつかっています。クロム拡張からiframeにアクセス

コンテンツスクリプトを使用して、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」入れてみました:マニフェストファイルが、運に真を:(

+2

http://stackoverflow.com/a/6645359/502149を参照してください。 iframeの内容はiframeに挿入されたコンテンツスクリプトから操作する必要があります。 –

答えて

19

あなたのコードが動作しない理由を理解するために、私はa fragment of my previous answerが含まれます:

Content scripts do not have any access to a page's global window object. For content scripts, the following applies:

  • The window variable does not refer to the page's global object. Instead, it refers to a new context, a "layer" over the page. The page's DOM is fully accessible. #execution-environment

Given a document consisting of   <iframe id="frameName" src="http://domain/"></iframe> :

  • Access to the contents of a frame is restricted by the Same origin policy of the page; the permissions of your extension does not relax the policy.
  • frames[0] and frames['frameName'] , (normally referring to the the frame's containing global window object) is undefined.
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocument returns a document object of the containing frame, because content scripts have access to the DOM of a page. This property is null when the Same origin policy applies.
    • iframe.contentDocument.defaultView (refers to the window object associated with the document) is undefined.
    • iframe.contentWindow is undefined.

あなたの場合には、同一生成元のフレーム

のためのソリューションは、以下のいずれかが動作します:

// 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があります。デモについては、this answerを参照してください。あなたはwindowオブジェクトを比較することはできません。何をにすることができますは、window.nameプロパティを頼ることです。 window.nameプロパティはiframeのname属性の値に自動的に設定されます(iframeがロードされるときに1回だけ)。

+1

ありがとうございます。非常に役立ちます! –

+0

@Rob W同じことをやろうとしましたが、クロムが「同じ起源 - ポリシー」エラーを表示しています。彼らは彼らの旗を更新したと思います。 '親がクロスオリジンフレーム(子フレーム)にアクセスするのをブロックしました' –