2

I'm trying to access some data from an iframe nested within an iframe, from developers console:

Object.keys(document.getElementById("contentBody").
  contentDocument.getElementById('rawContent').
  contentDocument.defaultView.window.messages)

["29c736c0ed25463c8436f4990ab6c6ec.zip", 
 "235819a8cf11488e83f0336603b71711.zip", 
 "66c9260590834d9698568c8a676ef406.zip", 
 "fae95e31cb424cd6ad21302217ef2cdc.zip", 
 "f554f712141047aa9aa24f765073e305.zip", 
 "e5c41819578240e0868f43ab6301aeb3.zip"]

That's what I expect back, but I've tried to get that very same info from a google chrome extension that I'm developing and for some reason I cant access messages array, this is the manifest file and contentscript.js (I've tried everything that came to my mind and searching for a few hours without success :/):

content.js

var iframeContentBody = document.getElementById('contentBody');
var innerDocContentBody = iframeContentBody.contentDocument;
var iframeRawContent = innerDocContentBody.getElementById('rawContent');
var innerDocRawContent = iframeRawContent.contentDocument; // iframeRawContent is undefined here
console.log(iframeRawContent.messages);  // this prints undefined

manifest:

{                                                                                                                                                                                                                  
  "manifest_version": 2,                                                                                                                                                                                           

  "name": "Read Comments",
  "description": "Read all comments from the current forum",
  "version": "1.0",
  "content_scripts": [{
    "matches": ["*://*.forum.net/*"],
    "js": ["content.js"]
  }],
 "browser_action": {
   "default_title": "Read Comments"
 },
 "permissions": ["activeTab", "tabs"]
} 

Gists to set everything up:

HTML Example

after downloading and placing these 3 files in the same folder, run this:

python -m SimpleHTTPServer 80 # You may need to run it with sudo 

then go to localhost/test.html and you're all set, if you test the line that I posted in the console you should see [1,2,3]

Extension example

this is the extension code

Developers console: developers console

Chrome extension with "all_frames": true chrome extension

Hacky solution: Partial solution In this gist there is a way to do it, it's hard to detect when the iframe has been loaded, and it's harded to detect when the iframe inside the another iframe has been loaded, so a setTimeout gives enough time to get it done, then adding a script element to the dom seems to bypass all security measures that chrome extensions may have and it does get the content of the attribute without any other issue, still this seems hacky and it's not what I'm trying to do, I'm looking for a clean solution or a clean way to access the dom of a nested iframe as the example code states...

Thanks, any suggestion is welcome.

kainlite
  • 1,024
  • 17
  • 35
  • I've tried to create an example in jsfiddle but I got caught in iframe madness :/ – kainlite Jun 18 '15 at 01:39
  • I assume that means `innerDocContentBody` is defined? Have you considered having your content_script run with `"all_frames":true` and figuring out if you're in the right frame? – Teepeemm Jun 18 '15 at 01:40
  • It's supposed to run on the main frame, I tried with all_frames and just saw a ton more errors, yeah it's defined, idk how can I provide a testing environment for this – kainlite Jun 18 '15 at 01:42
  • Found a way to do it: https://gist.github.com/kainlite/5aa24366cef9dac8b09c after downloading and placing these 3 files in the same folder, run this: python -m SimpleHTTPServer then go to localhost:8000/test.html and you're all set, if you test the line that I posted in the console you should see [1,2,3] – kainlite Jun 18 '15 at 02:07
  • https://gist.github.com/kainlite/dde05645f90158b43624 this would be the extension code – kainlite Jun 18 '15 at 02:18
  • Are you able to create a webpage with the iframes as well? That seems to be what's really causing the trouble. There's also several "Related" links that SO is showing about figuring out which iframe you're in. Could you use `"all_frames":true`, but exit the script if `window.parent===window.top`? – Teepeemm Jun 18 '15 at 02:57
  • What makes you think that I'm in the wrong frame? have you tested the code that I've provided? – kainlite Jun 18 '15 at 03:47
  • Well, I've provided all the information that I can, I hope someone has a real clue of what is going on here... – kainlite Jun 18 '15 at 04:23
  • I have found a way to do it, but it doesn't feel right, I mean it's kinda hacky, I was looking for a clean solution and at the same time explain myself what is happening inside the dom and all these iframes, gist: https://gist.github.com/kainlite/b3a35c9ef801974cc056 – kainlite Jun 19 '15 at 20:44
  • I think that means the way to go would be `iframe.addEventListener("load",callback)` for each level of iframe-ness (if it's not already loaded). You have to add the script tag to the DOM, since a content script can’t breach the isolated worlds ([here’s a good answer to help you](http://stackoverflow.com/a/9517879/2336725)). My remark about "which iframe you're in" was because "a ton more errors" suggests your script is running in the wrong frames. If it only ran in the right frame, your `content` could just have `console.log(messages);`, and you wouldn’t need event listeners or setTimeout. – Teepeemm Jun 19 '15 at 21:30
  • Yeah, I want to run my script in the top frame, which was being filtered by csp, if you bring a working example as an answer I will accept it, that answer that you linked is a really good answer, but that's not my question... thanks :) – kainlite Jun 19 '15 at 22:59
  • Some of this depends on what you want to do. Do you just need to log `messages` to the console? Or do you need the content script in the top window to read `messages`? Or write to `messages` and delete or create new ones? – Teepeemm Jun 20 '15 at 20:13
  • I need to read messages array when the nested iframe has been loaded and ready to work with from the content script... – kainlite Jun 20 '15 at 20:21

2 Answers2

1

This was my solution after all, between what we talk over comments and my research over docs and other threads:

Content script:

(function () {
  document.addEventListener("DOMContentLoaded", function () {
    contentBody = document.getElementById("contentBody");
    contentBody.addEventListener("load", function () {
      rawContent = contentBody.contentDocument.getElementById("rawContent");
      if (rawContent) {
        var s = document.createElement("script");
        s.src = chrome.extension.getURL('injected.js');
        s.onload = function() {
          this.parentNode.removeChild(this);
        };

        (document.head||document.documentElement).appendChild(s);
      }
    });
  });
})();

Injected file:

keys = Object.keys(document.getElementById("contentBody").contentDocument.getElementById("rawContent").contentDocument.defaultView.window.messages);

console.log(keys);

Manifest:

{
  "manifest_version": 2,

  "name": "Read Comments",
  "description": "Read all comments from the current forum",
  "version": "0.0.1",

  "content_scripts": [{
    "matches": ["*://localhost/*"],
    "run_at": "document_start",
    "js": ["content.js"]
  }],

  "browser_action": {
    "default_title": "Read Comments"
  },

  "permissions": [
  ],

  "web_accessible_resources": ["content.js", "injected.js"]
}

As a simple explanation the main issue was the asyc load of iframes and the moment when the extension code ran, so after listening to a lot of events and discarding the ones that doesn't have the required elements on the dom everything went fine...

kainlite
  • 1,024
  • 17
  • 35
0

For completeness, here’s a version with "all_frames":true. There are two problems to work around: (1) getting messages from the inner frame to the top and (2) getting messages from the isolated world of the webpage to the isolated world of the content script (I assume you’re wanting to do more than just write messages to the console). This solves both at once by using postMessage.

if ( window.top !== window.parent ) {
    var s = document.createElement("script");
    s.textContent = "postMessage(messages,'*');";
    s.onload = function() {
        this.parentNode.removeChild(this);
    };
    document.head.appendChild(s);
} else if ( window.top === window ) {
    addEventListener('message',function(e) {
        console.log(e.data);
    });
}

I must confess I’ve not actually tested it out. You may need to try making the injected script send a message from the webpage.

Teepeemm
  • 4,331
  • 5
  • 35
  • 58