15

How can something so simple be so impossible?

All I want to do is click the browser_action button of my extension, open a form with a couple of settings, and then click the button on the form to kick off a process.

I cannot for the life of me get the button click in the background form to work.

I have tried to get the example at http://developer.chrome.com/extensions/contentSecurityPolicy.html#H2-3 to work, but it doesn't. Is there a difference between rules for browser_action and background? Is that why my event listener is not firing?

Can someone provide a working example, please?

manifest.json:

{
    "name": "Convert",
    "version": "0.1",
    "description": "Converts the current page",
    "browser_action": {
        "default_icon": "exticon.png",
        "default_popup": "background.html"
    },
    "content_scripts": [{
        "matches": ["*://*/*"],
        "js": ["contentscript_static.js"]
    }],
    "permissions": [
        "tabs", "http://*/*", "https://*/*"
    ]
}

background.html:

<html>
    <head>
        <title>Converter</title>
        <script src="background.js"/>
        <script>
        // Initialize the localStorage
        if (null == localStorage["htmlImport"])
           localStorage["htmlImport"] = false;

        // Called when the user clicks on the browser action icon.
        chrome.browserAction.onClicked.addListener(function(tab) {
            console.log('in listener');
                 // execute the content script
                 chrome.tabs.executeScript(null, 
                    {
                       file: "contentscript.js",
                       allFrames: true   // It doesn't work before 4.0.266.0.
                    });
              });

        // Listen to the requests from the content script
        chrome.extension.onRequest.addListener(
              function(request, sender, sendResponse)
              {
                 switch (request.name)
                 {
                    case "getPreferences":
                       sendResponse(
                          {
                             prefIgnoreLinks : localStorage["htmlImport"]
                          });
                       break;

                    case "PressShortcut":
                       sendResponse({});  // don't response.

                       // execute the content script
                       chrome.tabs.executeScript(null, 
                          {
                             file: "contentscript.js",
                             allFrames: true   // It doesn't work before 4.0.266.0.
                          });

                       break;

                    default:
                       sendResponse({});  // don't response.
                       break;
                 }
              });


        </script>
    </head>
    <body style='min-width:250px;'>
        Link depth: <input type='text' name='depth' value='3'/><br/>
        <input type='checkbox' name='changedomain'>Include external domains</input><br/>
        <button id='beginConvert'>Convert</button>
    </body>
</html>

background.js:

function awesome() {
  // Do something awesome!
  console.log('awesome')
}
function totallyAwesome() {
  // do something TOTALLY awesome!
  console.log('totallyAwesome')
}

function awesomeTask() {
  awesome();
  totallyAwesome();
}

function clickHandler(e) {
  setTimeout(awesomeTask, 1000);
}
// Add event listeners once the DOM has fully loaded by listening for the
// `DOMContentLoaded` event on the document, and adding your listeners to
// specific elements when it triggers.
//document.addEventListener('DOMContentLoaded', function () {
//  document.querySelector('button').addEventListener('click', clickHandler);
//});

// Add event listeners once the DOM has fully loaded by listening for the
// DOMContentLoaded event on the document, and adding your listeners to
// specific elements when it triggers.
document.addEventListener('DOMContentLoaded', function () {
//  console.log('event listener for button connected to beginConversion()');
    //document.querySelector('button').addEventListener('click', beginConversion);
    document.getElementById('beginConvert').addEventListener('click', clickHandler);
});
Lee Grey
  • 303
  • 1
  • 5
  • 16
  • Sharing the code you have or a reduced example will greatly improve everyone's ability to help you. If you are following one of the examples on the page you linked to, which one are you using? – Mike Grace Aug 16 '12 at 22:21
  • 4
    Added code samples, so that non-clairvoyant developers like Mike are also able to participate. ;-) – Lee Grey Aug 16 '12 at 22:31
  • Your manifest.json file references "contentscript_static.js" but is not included in your question. – Mike Grace Aug 16 '12 at 22:37
  • It's not a good idea to have the background.html be the content for the popup. You should have a different html file for the popup. – Mike Grace Aug 16 '12 at 22:39
  • You have embedded JS into your background.html file. I believe that this violates the new contentSecurityPolicy (in Manifest V2) which requires that JS is imported from external files. Embedding is no longer permitted. – Ian Lewis Jul 01 '13 at 13:24

2 Answers2

54

Your Goal

  • Click extension button
  • Extension popup window opens with controls
  • Execute script on current tab based on controls in extension popup

Tips

  • Think of the background page as the control hub. It takes incoming requests from various scripts in your Chrome extension, has elevated permissions to do things like cross-domain requests (if defined in the manifest), and more.
  • You should use the manifest version 2 since version 1 is deprecated.
  • Manifest version 2 doesn't allow inline scripts so all scripts will need to be loaded as their own file.

Example

manifest.json

{
    "name": "Stackoverflow Popup Example",
    "manifest_version": 2,
    "version": "0.1",
    "description": "Run process on page activated by click in extension popup",
    "browser_action": {
        "default_popup": "popup.html"
    },
    "background": {
        "scripts": ["background.js"]
    },
    "permissions": [
        "tabs", "http://*/*", "https://*/*"
    ]
}

background.js

chrome.runtime.onMessage.addListener(
    function(request, sender, sendResponse) {
        switch (request.directive) {
        case "popup-click":
            // execute the content script
            chrome.tabs.executeScript(null, { // defaults to the current tab
                file: "contentscript.js", // script to inject into page and run in sandbox
                allFrames: true // This injects script into iframes in the page and doesn't work before 4.0.266.0.
            });
            sendResponse({}); // sending back empty response to sender
            break;
        default:
            // helps debug when request directive doesn't match
            alert("Unmatched request of '" + request + "' from script to background.js from " + sender);
        }
    }
);

popup.html

<html>
    <head>
        <script src="popup.js"></script>
        <style type="text/css" media="screen">
            body { min-width:250px; text-align: center; }
            #click-me { font-size: 20px; }
        </style>
    </head>
    <body>
        <button id='click-me'>Click Me!</button>
    </body>
</html>

popup.js

function clickHandler(e) {
    chrome.runtime.sendMessage({directive: "popup-click"}, function(response) {
        this.close(); // close the popup when the background finishes processing request
    });
}

document.addEventListener('DOMContentLoaded', function () {
    document.getElementById('click-me').addEventListener('click', clickHandler);
})

contentscript.js

console.log("chrome extension party!");

Running Example Screenshots

Clicking extension button with browser window opened to exampley.com

Clicking extension button with browser window opened to exampley.com

After clicking 'Click Me!' button in extension popup

After clicking 'Click Me!' button in extension popup


Example files in zip

http://mikegrace.s3.amazonaws.com/stackoverflow/detect-button-click.zip

Ryan Wu
  • 5,963
  • 2
  • 36
  • 47
Mike Grace
  • 16,636
  • 8
  • 59
  • 79
  • Thanks very much, Mike! Is there a difference between the source in your post and the contents of the .zip file? The reason I ask is because the .zip file seems to be corrupt or invalid. – Lee Grey Aug 17 '12 at 13:33
  • 1
    There is no difference. Try again, it should work now. Also, don't forget to up vote and accept if it helped or solved your issue. – Mike Grace Aug 17 '12 at 14:17
  • 1
    Thanks, Mike, this was VERY helpful! I accepted the answer. Unfortunately, my reputation is two shy of being able to vote up an answer. I'll come back and do it when I'm less of a nobody. :-) – Lee Grey Aug 17 '12 at 15:38
  • Glad to help. You should have enough rep now ; ) – Mike Grace Aug 17 '12 at 18:15
  • I have a question: in contentscript.js, how would I access an HTML element of popup.html? – barndog Nov 13 '12 at 17:49
  • It seems that this is not working anymore. I loaded the extension from .zip file in developer mode but its not working. Its not opening any tab. It's showing error `Error during tabs.executeScript: Unknown error. sendRequest:22 chromeHidden.handleResponse` – Rajkiran Apr 25 '13 at 09:57
  • @Rajkiran looks like you are correct. I will have to look into it and correct it. I'm guessing that changes in the extension API have broken this example on the latest versions of Chrome. – Mike Grace Apr 25 '13 at 19:14
  • Hi, How do I use jQuery in this contentscript.js? I added jQuery to the `content_scripts` in the manifest, but I'm getting `Uncaught ReferenceError: $ is not defined`. – Richard Jun 13 '13 at 09:03
  • This closes the extension popup. Anyway to keep the popup open after user clicks the button? – Prajeet Shrestha Dec 19 '16 at 03:51
  • @PrajeetShrestha remove this.close(); in popup.js – Nikita Chernykh Jan 19 '17 at 15:48
  • @MikeGrace could you elaborate on how you would work w/ a more complex `contentscript.js` For example what if it ran a function you wrote? How would you return the results from that back to your `background.js` file? Would you use the `runtime.sendMessage` api with a different directive (i.e. "process-data") and have the results of the function call be part of the message? – Adam Jul 06 '17 at 19:53
3

The previous answer does not work anymore and it took me a few hours to understand how to manage a work around. I hope this can get you going faster then me.

First, you the last method in this page (by the bottom of the page) and it is asynchronous, so remember to give it a callback. The code you need is smtg like this:

chrome.browserAction.onClicked.addListener(function (tab) {
    chrome.tabs.query({'active': true}, getActiveTabCallback);
});

Second, you need to understand one thing that took me some time: if you are not using a background html page you won't be able to see any console.log in your main Chrome window. You need to go to the extension page (chrome://extensions) and click in your extensions background page link (yes, you don't have a background page but Chrome gives you a fake one). This type of extension (based on events) should have the manifest.json containing smtg like this:

"background": {
    "scripts": ["background.js"],
    "persistent": false
},

Regards!

marcelocra
  • 2,094
  • 2
  • 24
  • 37