Chrome Plugin Context Menu behaves differently when Plugin is being inspected - javascript

I'm working on a Google Chrome extension that stores some values in local storage when a context menu item is clicked. I'm noticing however, that the behavior of the extension is different depending on if is being inspected or not. An example plugin is as follows:
manifest.json
{
"manifest_version": 2,
"name": "contextMenuTest",
"description": "contextMenuTest",
"version": "0.1",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"storage",
"unlimitedStorage",
"contextMenus",
"notifications"
],
"icons": {
"16": "icon.png"
}
}
popup.html
<!doctype html>
<html>
<head>
<script src= "popup.js"></script>
</head>
<body>
<button id="setContextMenuButton">Set context menu</button>
<button id="showResultButton">Show Answer</button>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("setContextMenuButton").addEventListener("click", onsetcontextmenubuttonclick);
document.getElementById("showResultButton").addEventListener("click", onshowresultbuttonclick);
});
function onsetcontextmenubuttonclick() {
chrome.storage.local.clear(function() {
chrome.contextMenus.removeAll(function() {
chrome.contextMenus.create(
{
"title": "test",
"contexts": ["selection"],
"onclick": function(info, tab) {
chrome.storage.local.set({"test":"test"});
}
}
);
});
});
}
function onshowresultbuttonclick() {
chrome.storage.local.get("test", function(items) {
console.log(items);
});
}
The expected behavior occurs when inspecting the popup: inspect the popup, click the "Set Context Menu" button, select some text and right click it, click "test" in the context menu, then inspect the popup and click the "Show Answer" button. chrome.storage.local.set() set the value for "test", so chrome.storage.local.get() is able to retrieve it properly and display {test: "test"}.
However, when carrying out the same steps without the first step of inspecting the popup, {} is displayed instead, implying that chrome.storage.local.set() did not properly set the value for "test" when the popup was not being inspected.
There is a similar question here, but it didn't quite answer my question.
Why would the context menu behave differently depending on if the plugin is being inspected or not, and how would I fix this?

Related

Send a value set in chrome.storage from an options page to a content script

At a high level, I want a user to toggle the background color of a page. I need the user to do so by clicking a button on the options page. I want the background color to take effect immediately.
At a low level, I understand that I need to send a message from the options page to the content script, reading, and writing settings to chrome.storage. However, no message passing I've tried works.
I've read that, to get a message to the content script from the options page, I need the background script to act as an intermediary. I can't get that to work, either.
In order to make my example clearer, I've removed all message passing code from all the example files below.
manifest.json
{
"manifest_version": 2,
"name": "Change Background Color",
"short_name": "Change Background Color",
"author": "The Author",
"version": "1.0.0",
"version_name": "1.0",
"options_page": "options.html",
"browser_action": {
"default_title": "Change the background color of a page."
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content.js"]
}],
"permissions": [
"background",
"unlimitedStorage",
"storage",
"tabs",
"activeTab",
"http://*/",
"https://*/",
"*://*/*",
"<all_urls>"
]
}
options.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Options</title>
</head>
<body>
<button id="toggle-background-color">Toggle Background Color</button>
<script src="options.js"></script>
</body>
</html>
options.js
var toggle = false;
function saveOptions() {
toggle = !toggle;
chrome.storage.sync.set(
{
toggleBackgroundColor: toggle
},
function () {}
);
}
function retrieveOptions() {
chrome.storage.sync.get(
{
toggleBackgroundColor: false
},
function () {}
);
}
document.addEventListener('DOMContentLoaded', retrieveOptions);
document.getElementById('toggle-background-color').addEventListener('click', saveOptions);
content.js
chrome.storage.sync.get(
{
toggleBackgroundColor: true
},
function(settings) {
if (true === settings.toggleBackgroundColor) {
document.body.style.backgroundColor = 'green';
} else {
document.body.style.backgroundColor = 'red';
}
}
);
background.js
// This file is thus far empty
You don't need to send a message. Changing the value in chrome.storage.local is sufficient. All you need to do is be listening for changes using chrome.storage.onChanged in your content script.
You could change your content script to something like:
content.js:
function updatePage(){
chrome.storage.sync.get(
{
toggleBackgroundColor: true
},
function(settings) {
if (true === settings.toggleBackgroundColor) {
document.body.style.backgroundColor = 'green';
} else {
document.body.style.backgroundColor = 'red';
}
}
);
}
chrome.storage.onChanged.addListener(updatePage);
updatePage();
Note: You could directly use the changed value that is passed to the storage.onChnaged listener. However, in this simple example, that just adds complexity when you already have code that is getting the data you need from chrome.storage.local. If you are doing more things than just changing the background color, optimizing to not redo everything may be a better solution.

Chrome extension to change DOM with a button in extension popup

i am completely new to chrome extension development.
I am trying to change the DOM (append data to active webpage) when clicked on a button in the extension popup. How is this possible.
the manifest file
{
"manifest_version": 2,
"name": "test 2",
"description": "test ext 2",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*","https://*/*"],
"js": ["jquery.min.js", "main.js"]
}
],
"permissions": [
"activeTab"
]
}
suppose if the popup.html file is
<!doctype html>
<html>
<head>
<title>test extension 2</title>
<script src="popup.js"></script>
</head>
<body>
<a id="button">button</a>
</body>
</html>
and when i click on #button, i want to execute some jquery code in main.js file which will append some data to the active webpage.
Thank you.
Use Programmatic injection. You could register event listener in popup.js and call chrome.tabs.executeScript to inject some js code/file to current active tab. This requires host permissions.
popup.js
document.getElementById('button').addEventListener('click', function() {
chrome.tabs.query({ active: true, currentWindow: true}, function(activeTabs) {
// WAY 1
chrome.tabs.executeScript(activeTabs[0].id, { code: 'YOUR CODE HERE' });
});
});
Use Message Passing. You could call chrome.tabs.sendMessage in popup.js and listen to that via chrome.runtime.onMessage in content.js.
popup.js
// WAY 2 (Replace WAY1 with following line)
chrome.tabs.sendMessage(activeTabs[0].id, { action: 'executeCode' });
content.js
chrome.runtime.onMessage.addListener(function(request) {
if(request.action === 'executeCode') {
// YOUR CODE HERE
}
});
Use Storage. You could call chrome.storage.local.set in popup.js and listen to storage change in content.js via chrome.storage.onChanged.
popup.js
// WAY 3 (Replace WAY1 with following line)
chrome.storage.local.set({ action: 'executeCode' });
content.js
chrome.storage.onChanged.addListener(function(changes) {
var action = changes['action'];
if(action.newValue === 'executeCode') {
// YOUR CODE HERE
}
});

Accessing the source of one tab in the extension's tab

I need to get the source code of the Active Tab when the extension icon is clicked; it needs to be visible in a new tab (because a pop up is limited to 800px, among other reasons).
I had no issue with this when I was using a popup, however, I now want to get the data into a new tab.
What is happening is the targetTab variable used in the onClicked callback contains the correct tab information. However, once the new tab (popup.html) is opened, nothing occurs; the document.body.innerHTML does not get changed.
Any ideas?
manifest.json
{
"name": "Stock Alert",
"description": "Create an alert and display streaming stock quotes.",
"version": "1.1",
"permissions": [
"https://www.gwm.ml.wallst.com/",
"https://*/*",
"http://*/*",
"tabs",
"activeTab"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "logo.png"
},
"manifest_version": 2
}
background.js
chrome.browserAction.onClicked.addListener(function(targetTab) {
chrome.tabs.create({'url': chrome.extension.getURL('popup.html')}, function(tab)
{
chrome.tabs.executeScript(
targetTab.id,
{
code: "document.getElementsByTagName('html')[0].innerHTML;"
},
function (sourceCode)
{
document.getElementById('dump').innerHTML = sourceCode;
});
});
});
popup.html (misnomer; this is the url of the new tab created onClicked)
<html>
<head>
<title>Stock Tracker</title>
<!--<script src="background.js"></script>-->
<link href="Streamer.css.package.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="out"></div>
<div id="dump"></div>
</body>
</html>
The issue is that the callback function is executed within the background page context. It will therefore try to find <div id="dump"></div> inside that page, rather than inside the popup page.
I've fiddled around, and I've came up with the following solution.
On click, get the content of the current page
Store that content in the background page
Load the popup.html
On load, get the code content from the background page
background.js
var myCode;
chrome.browserAction.onClicked.addListener(function(targetTab) {
chrome.tabs.executeScript(
targetTab.id,
{
code: "document.getElementsByTagName('html')[0].innerHTML;"
},
function (sourceCode)
{
myCode = sourceCode;
chrome.tabs.create({'url': chrome.extension.getURL('popup.html')});
});
});
popup.html
Add <script src="popup.js"></script>
popup.js
chrome.runtime.getBackgroundPage(function(bg)
{
document.getElementById('dump').innerHTML = bg.myCode;
});

Event doesnt fire in chrome addon

So, I've been back and forth with this problem. What I want is to execute som piece of code (in a injected.js file) when a button is pressed on the website. I want to run code that I've written myself.
This should be do-able? I've looked it up, and this is taken from the developer docs:
Content scripts execute in a special environment called an isolated world. They have access to the DOM of the page they are injected into, but not to any JavaScript variables or functions created by the page. It looks to each content script as if there is no other JavaScript executing on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any functions or access any variables defined by content scripts.
As can be seen in the example, also found on the same page (http://developer.chrome.com/extensions/content_scripts.html#execution-environment) I should be able to attach a click event to something and the execute code from the same .js file when that button is clicked? Right?
Anyway, here's the code I've written:
$sidebar = $('<div><div></div></div>')
.css('background', '#eee')
.css('width', '20%')
.css('position', 'fixed')
.css('right', '0px')
.css('top', '0px')
.css('height', '100%');
$form = $('<br/><center><div class="input-append">\
<input class="span2" id="add_cat_input" type="text">\
<button class="btn" type="button">Add category</button>\
</div>\
<div class="input-append"><select id="categories">\
<option>A</option>\
<option>B</option>\
<option>C</option>\
<option>D</option>\
<option>E</option>\
</select>\
<button class="btn" type="button">Edit</button><button class="btn" type="button">Delete</button>\
</div></center>');
$('body').prepend($sidebar);
$sidebar.append($form);
$(document).ready(function() {
console.log(document.getElementById("add_cat_input"));
document.getElementById("add_cat_input").addEventListener("click", function() {
console.log('hej');
}, false);
});
But nothing happends when I press 'Add category'.
Can anyone explain why? Am I doing it wrong?
The code above is from myscript.js.
Here is my manifest.json
{
"name" : "Ď€Spend",
"version" : "0.1",
"description" : "Creates a piechart of the spendings in your bankaccount.",
"permissions": [ "cookies", "tabs", "http://*/*", "contextMenus" ],
"icons": { "16": "cookie.png", "48": "cookie.png", "128": "cookie.png" },
"browser_action": {
"default_icon": "cookie.png",
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2,
"content_scripts": [
{
"matches": ["https://internetbanken.privat.nordea.se/*"],
"css": [ "bootstrap.min.css" ],
"js": ["jquery.js", "raphael-min.js", "g.raphael-min.js", "g.pie-min.js", "myscript.js"]
}
]
}
On the way I tried to use some form of message posting instead, but this line:
var port = chrome.extension.connect();
Gets me this error:
Port error: Could not establish connection. Receiving end does not exist.
The element add_cat_input which is bound to click listener - is NOT a button, but a text field. The button "Add category" does not have an id in your code.

console.log doesn't work from chrome.tabs.executeScript

I need to execute script once user clicked my context menu item.
So for the purpose I created the context menu from my background js:
chrome.contextMenus.create({"title": title, "contexts": contexts,
"onclick": genericOnClick});
It appears as expected. Later on from the genericOnClick I try to execute my script:
chrome.tabs.executeScript(null, {code: "console.log('test 1');"}, function() {
console.log("test 2");
});
I can see that the "test 2" is printed to console but "test 1" never gets printed. What am I doing wrong?
I've tried adding the console.log sentence to a separate js file but it failed to print it as well:
chrome.tabs.executeScript(null, {"file": 'content_script.js'}, function() {
console.log("test 2");
});
Note: my content_script.js is not defined in manifest. My manifest looks like follows:
{
"name": "My First Extension",
"version": "1.0",
"manifest_version": 2,
"description": "Sample extension",
"page_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://*/*",
"https://*/*",
"tabs",
"contextMenus"
],
"background": {
"scripts": ["sample.js"]
},
"icons": {
"16": "icon16.png"
}
}
Thank you in advance.
The only piece of code from your extension that has access to the console is the content script that is injected into the original page.
From your code it looks like you are trying to write to the console from a background script. So, to write to the console from a background page you've to inject a content script to do the job for you.
In my extensions I use my own function to write messages to the console from a background script. The code for the same is given below:
function logMessage(msg){
chrome.tabs.executeScript({code:"console.log('"+msg+"')"});
}
Define the above as the first function in your background script and call it from anywhere in the background script to write messages to the console.
In your case you can use this from the genericOnClick function to write messages to the console.
// addListener
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.executeScript(null, {file: "content_script.js"}, function() {
console.log("test 2");
});
});
// Context Menu
chrome.contextMenus.create({
title: myTitle,
contexts: ['page'],
onclick: function (detail, tab) { fn(tab) }
});
so;
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
chrome.tabs.executeScript(null,{code:"document.body.style.backgroundColor='red'"});
or:
// Functional structure
function hi() { alert("hi"); };
// hi function toString after run function (hi)()
chrome.tabs.executeScript(null, { code: "(" + hi.toString() + ")()" });

Categories

Resources