fivepercentmissing
fivepercentmissing

Reputation: 21

How to execute a page's function from firefox add-on

I have a website that I want to automatically navigate through, so I decided to see if I can write a firefox add-on.

I have the following code:

manifest.json

{
  "description": "Demonstrating toolbar buttons",
  "manifest_version": 2,
  "name": "button-demo",
  "version": "1.0",
  "permissions": [
    "tabs",
    "activeTab"
  ],

  "browser_action": {
    "browser_style": true,
    "default_popup": "popup/choose_page.html",
    "default_icon": {
      "16": "icons/page-16.png",
      "32": "icons/page-32.png"
    }
  }
}

choose_page.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="choose_page.css" />
  </head>
  <body>
    <div class="page-choice">developer.mozilla.org</div>
    <div class="page-choice">support.mozilla.org</div>
    <div class="page-choice">addons.mozilla.org</div>
    <script src="choose_page.js"></script>
  </body>
</html>

choose_page.js

document.addEventListener("click", (event) => {
  if (!event.target.classList.contains("page-choice")) {
    return;
  }


  browser.tabs.query({active: true, currentWindow: true}).then(tabs => {
    let activeTab = tabs[0];
    console.log('activeTab: ', activeTab)
    console.log('activeTab id: ', activeTab.id)
    let activeTabUrl = activeTab.url;
    console.log("Active tab URL:", activeTabUrl);

    browser.tabs.executeScript(tabs[0].id, {
      code: 'top[3].function1()'
    }, (results) => {
      if (browser.runtime.lastError) {
        console.error(browser.runtime.lastError);
      } else {
        console.log('success: ', results);
      }
    });

  }).catch(error => {
    console.error("Error getting active tab URL:", error);
  });
});

I got the script from firefox tutorial. Basically, I click the extension which will bring up a popup, then I click a button.

However, I'm getting undefined behavior.

Is it possible to execute a page's function?

Thanks

I tried the code that I wrote

Upvotes: 2

Views: 45

Answers (1)

Nanigashi
Nanigashi

Reputation: 393

Although this question is a bit old, the short answer to the (slightly rephrased) question "Is it possible to execute a page's function from a Firefox extension?" is yes, but there are a few moving parts given the code snippet in the question.

The first part is that tabs.executeScript injects code into the "isolated" world of content scripts. A function defined in the web page exists in the "main" world of the web page itself. Both worlds share the DOM, but they each get their own window object. (There are several ways to load a content script. This question uses tabs.executeScript, so this answer stays with that way.)

The second part is that window.eval() can execute a page's function (in the "main" world) from a content script. An alternative method is to add the code to a <script> tag and attach it to the DOM. Be aware that the page's content security policy may prevent either or both of those methods. There are a few long-standing bugs about that, because page CSPs aren't supposed to apply to anything an extension does.1 2

The third part is that the code snippet in the question has some errors in it, particularly that tabs.executeScript has no third parameter. The example code below reuses as much of the question as possible and works.

manifest.json

{
  "manifest_version": 2,
  "name": "Answer",
  "description": "Answer a Stack Overflow question",
  "version": "0.1",
  "content_security_policy": "default-src 'none'; object-src 'none'",
  "browser_specific_settings": {
    "gecko": {
      "id": "[email protected]"
    }
  },
  "browser_action": {
    "browser_style": true,
    "default_popup": "popup.htm"
  },
  "permissions": [
    "activeTab"
  ]
}

popup.htm

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <div class="page-choice">developer.mozilla.org</div>
    <div class="page-choice">support.mozilla.org</div>
    <div class="page-choice">addons.mozilla.org</div>
    <script src="popup.js"></script>
  </body>
</html>

popup.js

( function () {
  'use strict';

  document.addEventListener( 'click', ( event )  => {
    if ( !event.target.classList.contains( 'page-choice' ) ) {
      return;
    }

    browser.tabs.query( {
      active: true,
      currentWindow: true
    } ).then( tabs => {
      let activeTab = tabs[0];
      console.log( 'activeTab: ', activeTab );
      console.log( 'activeTab id: ', activeTab.id );
      let activeTabUrl = activeTab.url;
      console.log( 'Active tab URL:', activeTabUrl );

      browser.tabs.executeScript( activeTab.id, {
        // executeScript injects into the "isolated" world
        // window.eval() in turn throws code into the "main" world
        code: 'window.eval( \'answer();\' )'
      } ).then( console.log, console.error );
    }, error => {
      console.error( 'Error getting active tab URL:', error );
    } );
  } );
} () );

page.htm (the web page)

<!DOCTYPE html><html lang="en"><head>
  <title>Answer</title>
  <script src="page.js"></script>
</head><body>
</body></html>

page.js

function answer() {
  return location.href;
}

debug console output

activeTab:  Object { id: 2, index: 1, windowId: 1, highlighted: true, active: true, attention: false, pinned: false, status: "complete", hidden: false, discarded: false, … }
activeTab id:  2
Active tab URL: file:///C:/Data/mozdev/stackoverflow/page.htm
Array [ "file:///C:/Data/mozdev/stackoverflow/page.htm" ]

Upvotes: 0

Related Questions