Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to communicate from default_script to content_script in chrome extension (JavaScript) not working

Alright, so I'm changing the color scheme of a site via an extension, it's my first time using content_scripts so yes, I am a complete newbie, feel free to treat me as one.

The problem is tabs.connect it isn't working, I need the tab id or something? Here's what I have so far:

manifest.json:

{
  "manifest_version": 2,

  "name": "ROBLOX Color Scheme",
  "description": "Edit the color scheme of the roblox bar! Note: Not created by roblox.",
  "version": "1.0",

  "permissions": [
    "<all_urls>",
    "tabs"
  ],
  "browser_action": {
    "default_icon": "Icon.png",
    "default_popup": "Popup.html"
  },
  "content_scripts": [
    {
      "matches": ["http://www.roblox.com/*"],
      "js": ["ContentScript.js"]
    }
  ]
}

Popup.html:

<!DOCTYPE html>
<html>
    <head>
        <p>Choose a color:</p>
        <input type="color" id="Color" value="">
        <button type="button" id="Button">Change Color!</button>
    </head>
    <body>
        <script src="Script.js"></script>
    </body>

</html>

Script.js:

function ChangeColor() {
  var TabId;
    chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
      TabId = tabArray[0];
    });
  var port = chrome.tabs.connect(TabId, {name: "ColorShare"});
  port.postMessage({Color: document.getElementById("Color").value});
}

document.getElementById('Color').addEventListener("click", ChangeColor);

ContentScript.js:

var Color;
chrome.runtime.onConnect.addListener(function(port) {
  if (port.name == "ColorShare") then {
    port.onMessage.addListener(function(msg) {
      Color = msg.Color;
    });
  }
});

document.getElementsByClassName("header-2014 clearfix")[0].style.backgroundColor = Color;

All help is appreciated, thanks for taking your time to answer my question!

EDIT: Some files have changed now thanks to myself and the help of someone who answers, these now produce no errors, but nothing changes, any help you could possibly give would be great! Here are the current codes:

Script.js:

chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
    var TabId = tabArray[0].id;
    var port = chrome.tabs.connect(TabId, {name: "ColorShare"});

    function ChangeColor() {
        port.postMessage({Color: document.getElementById("Color").value});
    }
    document.getElementById('Color').addEventListener("click", ChangeColor);
});

ContentScript.js:

chrome.runtime.onConnect.addListener(function(port) {
    if (port.name == "ColorShare") {
        port.onMessage.addListener(function(msg) {
            document.querySelector("header-2014 clearfix").style.backgroundColor = msg.Color;
        });
    }
});

Edit: This problem was solved. I had to use chrome.storage.sync.set and chrome.storage.sync.get which has full support for content scripts! I'll post the scripts used soon!

like image 896
warspyking Avatar asked Aug 31 '14 01:08

warspyking


People also ask

How do I communicate with chrome extensions?

Chrome has documentation on sending messages from webpages to chrome extensions. You add an “externally_connectable” object to your manifest. This will specify which webpages you want to allow to communicate with your extension (in this case, localhost, since I was developing on my machine).

Can Chrome extensions communicate with each other?

In addition to sending messages between different components in your extension, you can use the messaging API to communicate with other extensions.

What is contentscript js?

A content script is a part of your extension that runs in the context of a particular web page (as opposed to background scripts which are part of the extension, or scripts which are part of the website itself, such as those loaded using the <script> element).


1 Answers

I think you’re misunderstanding the idea of a port. A port is used for several messages within a session. But the port is destroyed when the session ends (for example, when the tab is closed). Regardless, you shouldn’t be recreating the port on each click.

One of your comments mentioned refreshing the page, which leads me to think you want this color to persist across page reloads. This makes sense from a user interface perspective, but you really should have mentioned that at the beginning.

As I said, ports get destroyed when the tab is closed (or reloaded). If you want the value to persist, you’ll need a storage mechanism such as chrome.storage (you could also use local storage, but the preceding link gives several reasons why not to).

manifest.json just needs "permissions": [ "activeTab", "storage" ],. You probably also want a page action instead of a browser action (or neither, I’ll get to that).

ContentScript.js:

var myBgColor = false;

chrome.storage.sync.get("myBgColor",function(items) {
    if ( items.myBgColor ) {
        myBgColor = items.myBgColor;
        document.querySelector(".navbar").style.backgroundColor = myBgColor;
    }
});

chrome.runtime.onMessage.addListener(function(request,sender,sendResponse) {
    if ( request.setColor ) {
        document.querySelector(".navbar").style.backgroundColor = request.setColor;
        chrome.storage.sync.set({"myBgColor":request.setColor});
    } else if ( request.getColor ) {
        sendResponse({"myBgColor":myBgColor});
    }
});

I changed the argument to querySelector, since I couldn’t find the elements you were looking for on the index page.

Popup.html:

<!DOCTYPE html>
<html>
<body>
<p>Choose a color:</p>
<input type="color" id="color" value="">
<button type="button" id="button">Change Color!</button>
<script src="Script.js"></script>
</body>
</html>

I’m not sure why you had your inputs in the head of the page.

Script.js (but please rename your file to something more descriptive than Script.js):

document.getElementById('button').addEventListener("click",function() {
    chrome.tabs.query({currentWindow: true, active: true},function(tabArray) {
        chrome.tabs.sendMessage(tabArray[0].id,{"setColor":document.getElementById("color").value});
    });
});

chrome.tabs.query({currentWindow: true, active: true}, function(tabArray) {
    chrome.tabs.sendMessage(tabArray[0].id,{"getColor":1},setCurColor);
});

function setCurColor(response) {
    if ( response.myBgColor ) {
        document.getElementById("color").value = response.myBgColor;
    }
}

We’d like to have ContentScript.js onloading message Script.js if there’s a previously set background color. Unfortunately, Script.js only exists when we’ve clicked on the action icon. So we have Script.js ask ContentScript.js for the current color (if it’s been set).

As Ruwanka Madhushan noticed, your original script was failing (in part) because you were assuming the asynchronous chrome.tabs.query would complete before proceeding to the next line of code. Asynchronous javascript is very powerful, but it gives you the responsibility of not assuming the code has completed. You’ll need to use nested function calls, either anonymously (as with Script.js’s onclick) or by naming a utility function (as with setCurColor). (There are also javascript libraries to help with asynchronous functions, but I don't know them.)

Everything works, but there’s a slight problem: Popup.html closes when it loses focus - in this case, when you click on the color chooser input. The (really bad) work around is to bring up the popup and right click and select “Inspect Element”. This brings up the console for the popup, and prevents the popup from closing while you select the color. Another option may be to embed the color chooser in an iframe within the popup (I don’t know if this is possible).

But since we’re talking about options for your extension, the best alternative may be to use an options page. This would also give much more room to your html. For example, you may want to consider a button to delete localStorage.myBgColor so that you can get the default back. Or other options to customize the site, since I’m hoping you’re not going to all this trouble just to change a color. And it would hide the action icon, since presumably you’re going to set your options and then want to forget about the extension existing.

like image 148
Teepeemm Avatar answered Sep 26 '22 00:09

Teepeemm