Thursday, April 25, 2024
 Popular · Latest · Hot · Upcoming
10
rated 0 times [  16] [ 6]  / answers: 1 / hits: 24262  / 12 Years ago, wed, november 7, 2012, 12:00:00

I'm developing a chrome extension and bumped into a big problem.



I'm using content scripts to inject my javascript code on a web site. The web site has an iframe.
I can change the source code of the iframe but don't seem to get any access to the iframe's contentWindow property. I need it to insert text at the current carret position.



So basically this code works perfectly in the context of the page:



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


But when I try it to run in the context of my chrome extension I get this error:



TypeError: Cannot read property 'document' of undefined


What's strange is that I can access the html of the iframe. So this code works perfectly from the chrome extension:



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


I tried putting all_frames: true in the manifest file but no luck :(


More From » iframe

 Answers
17

To understand why your code does not work, I include 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.





Solution for same-origin frames



In your case, either of the following will work:



// 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);


Generic solution



The generic solution is using all_frames: true in the manifest file, and use something like this:



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);
}


This demo is for educational purposes only, do not use this demo in a real extension. Why? Because it uses postMessage to pass messages around. These events can also be generated by the client, which causes a security leak (XSS: arbitrary HTML injection).



The alternative to postMessage is Chrome's message API. For a demo, see this answer. You won't be able to compare the window objects though. What you can do is to rely the window.name property. The window.name property is automatically set to the value of the iframe's name attribute (just once, when the iframe is loaded).


[#82135] Tuesday, November 6, 2012, 12 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
marcos

Total Points: 331
Total Questions: 106
Total Answers: 104

Location: Gabon
Member since Sat, Jul 25, 2020
4 Years ago
marcos questions
Thu, Feb 24, 22, 00:00, 2 Years ago
Sat, Apr 17, 21, 00:00, 3 Years ago
Mon, Feb 1, 21, 00:00, 3 Years ago
Tue, Jan 26, 21, 00:00, 3 Years ago
Wed, Jan 6, 21, 00:00, 3 Years ago
;