1

How can I get my extension to work on all frames like adblock does?

I tried adding "all_frames" : true to my manifest file but it didn't work.

I tried to use this code to get the text with specific ids:

var theId = "starts with something";
var myArray = [];
$('[id^="theId"]').each(function(i, obj) {
    myArray.push($(this).text());
}); 

$.unique(myArray);

console.log(myArray);

but it says my array is empty. When I inspect element on the page, I see a "top" layer, and a "target content" layer. The code only works when I execute it in the console on the "target content" layer. Can I use this code in a content script, or do I need to use background.js somehow?

defoification
  • 315
  • 6
  • 18

2 Answers2

1

I think your script may be executing before the DOM loads, try putting your function inside:

document.addEventListener('DOMContentLoaded', function() {

});

EDIT That event seems to do nothing in content scripts, I think that is because they are already loading after DOM is loaded, and never fires.

However this seems to fire but not sure why:

$(function(){
//something
});

This needs jQuery injected aswell

  • how can I check this? I don't know where to find the console.log. Also, I have tried this before, so it likely will not work – defoification May 23 '17 at 00:08
  • Is in a content script, isn´t it? – Rodrigo Gross May 23 '17 at 00:14
  • I´ve tried $(function(){ console.log("something");}); in content script and is fired, however document.addEventListener('DOMContentLoaded'... seems that is not fired, try that way – Rodrigo Gross May 23 '17 at 00:28
  • Yeah, its in a content script. I put an alert inside there and nothing popped up. Only when I put an alert as the first line – defoification May 23 '17 at 00:38
  • Are those iframes in the same domain? I´ve read that that can be a problem. – Rodrigo Gross May 23 '17 at 01:41
  • My bets are for: a) cross domain issues while trying to get iframe content, and b) dinamically created iframes that are loaded after these scripts are launched and these events are fired, in wich case you can use something like https://github.com/uzairfarooq/arrive -(watch for elements creation) – Rodrigo Gross May 23 '17 at 01:56
  • I´ve tried importing arrive.js also and with $(document).arrive(".sound", function() {console.log(this);}); it logs everytime and scrolling through soundcloud, wich may be similar case creating elements, just suggesting – Rodrigo Gross May 23 '17 at 02:21
1

Continued from SO44122853

I see you figured out that the content was loaded in an iframe. So I have a working demo here PLUNKER, the snippet is here just in case the plunker goes down.

Details are commented in PLUNKER

Demo

Not functional due to the need to run 2 separate pages

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width,initial-scale=1, user-scalable=no">
  <style></style>
</head>

<body>
  <!--iframe is same as the one on the site, with the exception
  of the src-->
  
  <iframe id="ptifrmtgtframe" name="TargetContent" title="Main Content" frameborder="0" scrolling="auto" width='90%' src="tables.html"></iframe>
  
 <!--The data is displayed as a one string-->
  <output id='display'></output>
  
  <script>
  
  // Reference the iframe
    var iFID = document.getElementById("ptifrmtgtframe");
  // Register the load event on iframe
    iFID.onload = function(e) {
      // Callback is extractText function
      return extractText('#ptifrmtgtframe', '#display', '.PSLONGEDITBOX');
    }

    /* Pass iframe and display as a single selector
    || Pass targets as a multiple selector
    */
    function extractText(iframe, display, targets) {
      var iArray = [];
      var iFrame = document.querySelector(iframe);
      var iView = document.querySelector(display);
      var iNode = "";
      
      /* .contentWindow is property that refers to content
      || that is in an iframe. This is the heart of the
      || demo.
      */
      var iContent = iFrame.contentDocument || iFrame.contentWindow.document;
      var iTarget = iContent.querySelectorAll(targets);
      
      /* .map() will call a function on each element
      || and return a new array as well.
      */
      Array.from(iTarget).map(function(node, idx) {

        iNode = node.textContent;
        iView.textContent += iNode;
        iArray.push(iNode);
        return iArray;
      });
      console.log(iArray);
    }
  </script>
</body>
Jed Fox
  • 2,979
  • 5
  • 28
  • 38
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • Register an eventListener to the button being clicked and use your code as callback. `$('button').on('click', function(e) {..CALLBACK....});` iframes are slow to load, so I used the `onload` event which is the last event during loading of a page. Did you review the [Plunker](http://embed.plnkr.co/XIUdpTT8Fd6PisbBs0Y8/)? The selectors are real ones used in your site. All you'd need to do is add your extension code (e.g. `exec.command()`.) – zer00ne May 23 '17 at 02:16
  • Yes, that'd be a much better selector, otherwise you'd have 129 cells to shift through. – zer00ne May 23 '17 at 13:04
  • Turns out I was fooled. It is still not working. The code only works when I type it into a console on the "target" layer in inspect element. Have any idea why it doesn't work on all frames? – defoification May 23 '17 at 13:50
  • Just updated `windowConent.document` with a `documentContent` option. It appears that Chrome.ext Window Object is not the same as JS Window Object thus `windowContent` property would be undefined. `documentContent` should work with Chrome ext. See this [post](https://stackoverflow.com/a/36795207/2813224) and this [post](https://stackoverflow.com/a/13271692/2813224) – zer00ne May 23 '17 at 15:29
  • BTW, at this point I'm pretty sure as far as DOM collection goes we are good. I think it might be the Chrome-ext side of things now. – zer00ne May 23 '17 at 15:35
  • how can I test this? I tried copy/paste all the code into the console and it spit out the function with parameter e. Then I tried to copy/paste part of the code and got `"cannot read property 'contentWindow' of null"` and `"cannot read property 'contentDocument' of null"` and `"cannot read property 'contentText' of null"` – defoification May 23 '17 at 16:15
  • `contenetText` ? `.textContent` property is used, but not `contentText`. If the function's code is returned, refer to it with a variable then `console.log(variable)` ex. `var x = dataArray();` `console.log(x)` As for iframe being null, read this [post](https://stackoverflow.com/a/13271692/2813224) – zer00ne May 23 '17 at 16:30
  • Last question, right now I am using an interval timer to get it to run when the frame content has loaded. Should I just use iframe.onload instead? – defoification May 23 '17 at 19:07
  • Yes, definitely go with `onload`. Iframes are sluggish so an `onload` on an iframe is the best way to trigger functions when dependant on the iframe being in the DOM. – zer00ne May 23 '17 at 19:56
  • I noticed that you are cloning iframe content. You might be interested in [`importNode()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/importNode) and [`adoptNode()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptNode). They are specialized functions of `cloneNode()` – zer00ne May 23 '17 at 20:06
  • last last question, how can i check for changes in the iframe? The iframe loads, but when the user presses the button "search on the page," new content is loaded into the iframe. I need to perform my script after the user presses this button so that the information is there – defoification May 23 '17 at 20:12
  • `$(BUTTON).on('click', function(e) {..CALLBACK....});` `BUTTON` being the search button's selector and `CALLBACK` being the function you want to run when user clicks the button. – zer00ne May 23 '17 at 23:52