Sorry, I messed that up. I did not update the manifest from the last post and I forgot the content.js, that is how the Change button was sending the value and the page was getting the new style attribute value from the one entered in the text field and then to the content.js, existing content.js file added below.
I have a styles.css that changes the web page and is loaded from the popup but want to change a variable in the styles.css from the data entered at the popup.
I've added a content.js file and I can now pass a value from the Popup and it adds a new style attribute to the page but it's not updating the --ptwidth in the styles.css file. I think I need to have it in the styles.css to give the correct location and add the !important option.
I tried to ask this question before an it was closed\linked to one about webpage DOMs and don't have the reputation to post a comment and not sure I should ask my questions there if I could:
How to access the webpage DOM rather than the extension page DOM?
The styles.css injection works using the Wider button and the --ptwidth var is passed the value given (310) in the styles.CSS, at the very least I'd like to be able to enter a new value in the textbox and then use the existing Wider button to load the updated styles.css but it would be nice to have it auto update and maybe even use the slider.
The change button moves the new value entered in the text field to the content.js file and it then adds the new style attribute but it's not working. The insertCSS on the Wider button works but the info it adds is different.
Content.js adds the Style attribute section and the insertCSS adds the :root section that works.
Added by Content.js No Work:
Added by insertCSS Works and adds these two:
Columns before:
Columns after:
Rule before using the working Wider button with insertCSS:
Rules after the insertCSS injections of styles.css:
Popup:
manifest:
{
"manifest_version": 3,
"name": "Hellper",
"description": "Extension",
"version": "0.1",
"icons": { "16": "logo_16_T.png",
"48": "logo_48_T.png",
"128": "logo_128_T.png"
},
"action": {
"default_icon": "logo_16_T.png",
"default_popup":"popup.html"
},
"permissions": ["scripting", "tabs", "activeTab", "content.js"],
"host_permissions": ["<all_urls>"],
"content_scripts": [{
"js": ["jquery-2.2.0.min.js", "popup.js"],
"matches": ["https://www.google.com/*",
"https://en.wikipedia.org/*",
"https://stackoverflow.com/*"]
}]
}
popup.html:
<!doctype html>
<html>
<head>
<title>Popup</title>
</head>
<body>
<input id="button1" type=button value=clickme>
<button class="format">Wider</button>
<button class="reset">Reset</button>
<script src="jquery-2.2.0.min.js"></script>
<script src="popup.js"></script>
<!--
<h2>Background Color</h2>
<input type="color" id="color-changer" />
<h2>Rotate Page</h2>
<input type="range" min="0" max="360" step="1" value="0" id="rotate" />
-->
<h1>New Width</h1>
<p>
<input type="text" id="newWidth" value="120"/>
<input type="submit" id="btnChange" value="Change"/>
</p>
<div class="form-group">
<lable for="slider">Project/Task Width</lable>
<input type="range" min="0" max="999" step="1" value="160" id="slider" />
</div>
</body>
</html>
styles.css:
:root {
--ptwidth: 310px
}
.quixote .qx-grid .editor_grid tbody tr td input, .quixote .qx-grid .editor_grid tbody tr td .input-group {
/*max-width: 450px !important;
min-width: 450px !important;*/
max-width: var(--ptwidth) !important;
min-width: var(--ptwidth) !important;
popup.js:
$(document).ready(function() {
$('.reset').click(function() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.removeCSS({
target: { tabId: activeTab.id },
files: ["styles.css"]
});
});
})
$('.format').click(function() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"]
});
/*chrome.tabs.sendMessage(activeTab.id, {"buttonclicked": "wider"});*/
});
})
})
$(function(){
var width = $('#newWidth').val();
$('#newWidth').on("change paste keyup", function(){
width = $(this).val();
});
$('#btnChange').click(function(){
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
chrome.tabs.sendMessage(tabs[0].id, {todo: "changeWidth", sliderWidth: width})
});
});
});
content.js
let root = document.documentElement;
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.todo == "changeWidth"){
var updateWidth = request.sliderWidth;
root.style.setProperty('--ptwidth', updateWidth + "px");
// start();
}
});
It seems like there is still some work to do on this extension. I will try to provide only a solution to what was asked an leave the rest for you to work on.
Issues I will try to address
You shouldn't be reusing popup.js as a content script. You should create a separate file instead of having one script be both injected into the webpage and also being used in the popup window.
You are also sending messages from the popup window without anything listening for the messages. This also would be solved by creating a separate content script that listens for these messages.
Partial solution
I edited the manifest.json and popup.js as well as created a new file content.js.
New manifest.json
changed "popup.js" to "content.js"
{
"manifest_version": 3,
"name": "Hellper",
"description": "Extension",
"version": "0.1",
"icons": { "16": "logo_16_T.png",
"48": "logo_48_T.png",
"128": "logo_128_T.png"
},
"action": {
"default_icon": "logo_16_T.png",
"default_popup":"popup.html"
},
"permissions": ["scripting", "tabs", "activeTab"],
"host_permissions": ["<all_urls>"],
"content_scripts": [{
"js": ["jquery-2.2.0.min.js", "content.js"],
"matches": ["https://www.google.com/*",
"https://en.wikipedia.org/*",
"https://stackoverflow.com/*"]
}]
}
New popup.js
only edited the last function block
added a listener for the slider changes
coupled the values in the text box and slider, so when one changes, the other changes as well
changed the message parameter from "sliderWidth" to "newWidth" to make it more general
moved retrieval of the width value into the listeners so the new changed value can be sent along
I also suggest removing the change button entirely because the other listeners make it unnecessary
// Unchanged
$(document).ready(function () {
$(".reset").click(function () {
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
var activeTab = tabs[0];
chrome.scripting.removeCSS({
target: { tabId: activeTab.id },
files: ["styles.css"],
});
});
});
$(".format").click(function () {
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"],
});
/*chrome.tabs.sendMessage(activeTab.id, {"buttonclicked": "wider"});*/
});
});
});
// Changed
$(function () {
// text input listener
$("#newWidth").on("change paste keyup", function () {
const width = $(this).val();
// update slider
$("#slider").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
// listener for change button press
// button might not be needed anymore because of the text input listener above
$("#btnChange").click(function () {
const width = $("#newWidth").val();
// update slider
$("#slider").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
// listener for slider changes
$("#slider").on("input", function () {
const width = $("#slider").val();
// update text box
$("#newWidth").val(width);
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { todo: "changeWidth", newWidth: width });
});
});
});
New content.js
created this script to listen for messages sent from the popup and make the changes to the CSS based on the message
$(document).ready(function () {
// listen for messages sent to the tab the content script is running in
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
// check to see if the message received is something that needs to be acted on
if (request.todo === "changeWidth") {
// pull the width data from the message
const newWidth = request.newWidth;
// set the style attribute of :root to override the styles.css value for --ptwidth
document.documentElement.style.setProperty("--ptwidth", newWidth + "px");
}
// send a response to avoid errors in popup.js
sendResponse("Width updated");
});
});
Hopefully that get you on the right track for finishing your extension!
Don't forget to check out the developer documentation for Chrome extensions. There are several useful posts for teaching how to accomplish the various parts of an extension.
[1] Message Passing: https://developer.chrome.com/docs/extensions/mv3/messaging/
[2] Content Scripts: https://developer.chrome.com/docs/extensions/mv3/content_scripts/
I got this working by using the chrome.scripting.insertCSS to replace the --ptwidth value after the styles.css has been injected. So far everything works with just the popup.js and no need for the content.js file.
I use this to load the styles.css with it's default value.
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
files: ["styles.css"]
});
});
Then this when I get a new value from the textbox or slider.
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
//alert(readsavewidth);
$("#slider").val(readsavewidth);
$("#newWidth").val(readsavewidth);
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${readsavewidth + "px"} }`
});
});
});
I also used the chrome.storage.local to set and get the last value used.
$(function () {
// text input listener
$("#newWidth").on("change paste keyup", function () {
width = $(this).val();
//Save it to the localStorage variable which will always remember what you store in it
chrome.storage.local.set({'savewidth': width});
// update slider
$("#slider").val(width);
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${width + "px"} }`
});
});
});
// listener for slider changes
$("#slider").on("input", function () {
width = $("#slider").val();
// update text box
$("#newWidth").val(width);
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${width + "px"} }`
});
});
//Pull text from user inputbox
var data = width;
//Save it to the localStorage variable which will always remember what you store in it
chrome.storage.local.set({'savewidth': data});
});
});
Then I get the value when the popup opens and it then overights the --ptwidth value using the chrome.scripting.insertCSS funtion.
chrome.storage.local.get('savewidth', function (result) {
readsavewidth = result.savewidth;
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
//alert(readsavewidth);
$("#slider").val(readsavewidth);
$("#newWidth").val(readsavewidth);
chrome.scripting.insertCSS({
target: { tabId: activeTab.id, allFrames: true },
css: `:root { --ptwidth: ${readsavewidth + "px"} }`
});
});
});
Related
I need to hide or show an column with my extension. This one I want to hide/show
I have this code:
manifest.json
{
"manifest_version": 2,
"name": "Chat GPT Column Hider",
"description": "This extension allows you to hide the left column in Chat GPT.",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"https://chat.openai.com/"
]
}
background.js
function hideColumn() {
// Get the left column element and hide it
var column = document.querySelector('div.dark.hidden.bg-gray-900.md\\:fixed.md\\:inset-y-0.md\\:flex.md\\:w-[260px].md\\:flex-col');
column.style.display = 'none';
}
function showColumn() {
// Get the left column element and show it
var column = document.querySelector('div.dark.hidden.bg-gray-900.md\\:fixed.md\\:inset-y-0.md\\:flex.md\\:w-[260px].md\\:flex-col');
column.style.display = 'block';
}
popup.html
<button id="hide-button">Hide Column</button>
<button id="show-button">Show Column</button>
<script src="popup.js"></script>
popup.js
// Get the hide and show button elements
var hideButton = document.querySelector('#hide-button');
var showButton = document.querySelector('#show-button');
// Add event listeners to the buttons
hideButton.addEventListener('click', function() {
// When the hide button is clicked, call the hideColumn function
chrome.tabs.executeScript({
code: 'hideColumn();'
});
});
showButton.addEventListener('click', function() {
// When the show button is clicked, call the showColumn function
chrome.tabs.executeScript({
code: 'showColumn();'
});
});
And we get this UI:
But if I press buttons I'm getting an error: Uncaught ReferenceError: hideColumn is not defined
I rewrote your extension.
manifest.json
{
"name": "Chat GPT Column Hider",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"scripting"
],
"host_permissions": [
"https://chat.openai.com/"
],
"action": {
"default_popup": "popup.html"
}
}
popup.html
<html>
<body>
<button id="hide-button">Hide Column</button>
<button id="show-button">Show Column</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
// Get the hide and show button elements
var hideButton = document.querySelector('#hide-button');
var showButton = document.querySelector('#show-button');
function hideColumn() {
// Get the left column element and hide it
var column = document.getElementsByClassName('dark hidden bg-gray-900 md:fixed md:inset-y-0 md:flex md:w-[260px] md:flex-col');
column[0].style.display = 'none';
}
function showColumn() {
// Get the left column element and show it
var column = document.getElementsByClassName('dark hidden bg-gray-900 md:fixed md:inset-y-0 md:flex md:w-[260px] md:flex-col');
column[0].style.display = 'block';
}
// Add event listeners to the buttons
hideButton.addEventListener('click', function () {
// When the hide button is clicked, call the hideColumn function
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: hideColumn
});
});
});
showButton.addEventListener('click', function () {
// When the show button is clicked, call the showColumn function
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: showColumn
});
});
});
The Manifest V3 JavaScript Chrome extension only has one button. When the button is clicked the browser should go to multiple pages and perform an action on each page before going to the next one.
What's currently happening, is the browser jumping to the last page and not performing any action.
How to fix my code, so that the extension visits each page one by one (in the same tab), then waits on the page until a button is detected, clicks the button, checks if the button is clicked, and then goes to the next page?
Manifest.json:
{
"name": "Twitch Follower",
"version": "0.1",
"manifest_version": 3,
"description": "Automatically follows Twitch users.",
"action": {
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"tabs",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"host_permissions": [
"https://www.twitch.tv/*"
]
}
popup.html:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/water.css#2/out/water.css">
</head>
<body>
<button id="start">Start</button>
<script src="popup.js"></script>
</body>
</html>
popup.js:
start.addEventListener("click", async () => {
chrome.runtime.sendMessage({ start: true });
});
background.js:
const list = ["https://www.twitch.tv/hello1", "https://www.twitch.tv/hello2"];
function clickFollow() {
document.querySelector("button[data-a-target='follow-button']").click();
}
function handleFollowButton(followButton) {
setTimeout(clickFollow, 1000);
}
// Check for a button
async function checkButton() {
const findButton = new Promise((resolve, reject) => {
const observerConfig = { attributes: true, attributeOldValue: true };
const observer = new MutationObserver((mutations) => {
const followButton = document.querySelector("button[data-a-target='follow-button']");
const unfollowButton = document.querySelector("button[data-a-target='unfollow-button']");
// If follow button is found, click it
if (followButton) {
handleFollowButton(followButton);
}
// If unfollow button is found, end observing and continue
if (unfollowButton) {
unfollowButtonFound = True;
observer.disconnect();
resolve;
}
});
window.setTimeout(() => {
if (!unfollowButtonFound) {
reject;
}
}, 5000);
observer.observe(document, {
childList: true,
subtree: true,
});
});
await findButton;
return true;
}
// When the start button is clicked
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.start === true) {
// Loop through every link
for (const link of list) {
console.log(link);
// Open the link and inject the script
chrome.tabs.update({ url: link }, (tab) => {
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
func: checkButton,
},
(result) => {
console.log(results);
}
);
});
}
}
});
I would like to implement a hotkey that triggers a function in my content script.
The content script (main.js) is executed on page laod from my popup.js file.
i have added the command into my manifest.json and when I add the onCommand listener to my popup.js I can console log that it gets triggered when I press my hotkey (Ctrl+Shift+K)
However I can not get it passed to my content script.
manifest.json
{
"manifest_version": 2,
"name": "Ccghjj",
"description": "hdjdjdjsjs",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": ["tabs", "*storage", "activeTab"],
"content_scripts": [
{
"matches": ["*://"],
"css": ["style.css"],
"js": ["jquery.js", "main.js"]
}
],
"content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'",
"web_accessible_resources": ["toolbar.html", "style.css"],
"commands": {
"show_deals": {
"suggested_key": {
"default": "Ctrl+Shift+K"
},
"description": "Highlight Deals"
}
}
}
popup.js
function registerButtonAction(tabId, button, action) {
// clicking button will send a message to
// content script in the same tab as the popup
button.addEventListener('click', () => chrome.tabs.sendMessage(tabId, { [action]: true }));
}
function setupButtons(tabId) {
// add click actions to each 3 buttons
registerButtonAction(tabId, document.getElementById('start-btn'), 'startSearch');
registerButtonAction(tabId, document.getElementById('deals-btn'), 'startDeals');
registerButtonAction(tabId, document.getElementById('stop-btn'), 'stopSearch');
}
function injectStartSearchScript() {
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
// Injects JavaScript code into a page
// chrome.tabs.executeScript(tabs[0].id, { file: 'main.js' });
// add click handlers for buttons
setupButtons(tabs[0].id);
});
}
injectStartSearchScript();
// hotkey command listener
chrome.commands.onCommand.addListener((show_deals) => {
console.log(`Command "${show_deals}" triggered`);
// how can I get to main.js to call deals()
});
main.js (content script)
async function deals() {
// should be fired when I press my hotkey Ctrl+Shift+K
Use messages like below
popup.js
chrome.commands.onCommand.addListener((show_deals) => {
console.log(`Command "${show_deals}" triggered`);
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tab.id, { msg: "deals"})
})
});
and in your content script
if (!window.firstTimeExecuted) {
window.firstTimeExecuted = true;
chrome.runtime.onMessage.addListener((data, sender, sendResponse) => {
if (data.msg == 'deals') {
deals()
}
});
}
I'm developing a screenshot taking google chrome extension. But my problem is, that the screenshot don't have the original size, it is much bigger. If the size of my web document has 1272x594 pixel, the screenshot will be 2544x1188 pixel.
The minimal working example takes a screenshot of the current tab, after clicking on "Test Plugin" in context menu and displays the screenshot in the current tab as an overlay.
My minimal working example:
manifest.json
{
"name": "Minimal working Example",
"version": "1.0",
"description": "",
"permissions": ["contextMenus", "tabs", "activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"manifest_version": 2
}
background.js
chrome.runtime.onInstalled.addListener(function() {
chrome.contextMenus.create({
id: 'minimalExampe',
title: 'Test Plugin',
contexts: ['all']
});
});
chrome.contextMenus.onClicked.addListener(function(itemData) {
if (itemData.menuItemId == "minimalExampe") {
console.log('clicked');
// checks, if content.js is already injected, otherwise content.js is added
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {func: 'alreadyInjected'}, function (response) {
if (response === undefined) {
chrome.tabs.executeScript({
file: 'content.js'
});
console.log('Script added');
} else {
console.log('Script was already injected');
}
//does the screenshot
chrome.tabs.captureVisibleTab(null, {format: 'png'}, function (screen) {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {func: 'showPicture', picture: screen});
});
});
});
});
}
});
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
switch(request.func) {
case 'showPicture':
showPicture(request.picture);
break;
case 'alreadyInjected':
sendResponse({alreadyInjected: true});
break;
}
});
function showPicture(picture) {
console.log(window.innerWidth);
console.log(window.innerHeight);
var img = new Image();
img.src = picture;
img.style.zIndex = '1000';
img.style.position = 'absolute';
img.style.top = '0';
img.style.left = '0';
img.onload = function () {
console.log(img.naturalWidth);
console.log(img.naturalHeight);
};
document.body.appendChild(img);
}
Does anybody know why? Every help is welcome. I already searched on Stackoverflow, but there is only one same question with no answer because no minimal working example or anything else was provided.
Best,
Klaus
In background.js:
function checkForValidUrl(tabId, changeInfo, tab) {
if(changeInfo.status === "loading") {
if (tab.url.indexOf('google.com') > -1 || tab.url.indexOf('amazon.com') > -1) {
chrome.pageAction.show(tabId);
}
}
};
chrome.tabs.onUpdated.addListener(checkForValidUrl);
chrome.pageAction.onClicked.addListener(function(tab){
if (tab.url.indexOf('google.com') > -1){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tab.id, {google_go: "go"});
});
chrome.tabs.create({url: "http://facebook.com", "active":true});
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name == "facebook_chanel");
port.onMessage.addListener(function(msg) {
if (msg.facebook_go == "go")
port.postMessage(request);
});
});
});
}
In facebook.js:
$(document).ready(function(){
var port = chrome.runtime.connect({name: "facebook_chanel"});
port.postMessage({facebook_go: "go"});
port.onMessage.addListener(function(msg) {
console.log(msg);
});
});
I go too google. Press on pageAction, I see tab with facebook, see one Object in console. In html/background.html console (Chrome) I see the error Assertion failed: in
console.assert(port.name == "facebook_chanel");
I go to google again, press pageAction, and I see at new facebook page one old and 2 new Objects.
How to fix it? Thanks.
UPDATE
in background.js
chrome.tabs.create({url: "http://facebook.com", "active":true}, function(tab){
chrome.runtime.onMessage.addListener( function(request, sender, sendResponse) {
chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
chrome.tabs.sendMessage(tab[0].id, request);
});
});
});
in facebook.js:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(request);
});
Its right way? But I have clear console, whats wrong?
It is still not very clear what you are trying to achieve and why you chose to use porsistent ports (instead of single messaging) and page-actions, but from what I did understand, context-menus could be a better/cleaner approach.
So, below is the source code of a sample extension that registers a context-menu for when some text is selected (i.e. select/highlight some text and right click) and then opens an Facebook tab and sends the selected text for further processing (by the FB tab's content script).
If you still need a page-action instead of a context-menu, change the code like this:
Add listener for chrome.pageAction.onClicked in background-page.
Inject some code into the original tab (e.g. google.com) to retrieve ad return the current selection (using chrome.tabs.executeScript()).
Handle the acquired selected text as demonstrated in the code below.
manifest.json:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"offline_enabled": false,
"background": {
"persistent": false,
"scripts": [
"background.js"
]
},
"content_scripts": [{
"matches": ["*://*.facebook.com/"],
"js": ["content.js"],
"run_at": "document_end",
"all_frames": false
}],
"permissions": [
"contextMenus"
]
}
content.js:
chrome.runtime.onMessage.addListener(function(msg) {
/* Input validation */
if (!msg.source || !msg.text) {
console.error('Bad message format: ', msg);
return;
}
/* Handle the selected text */
switch (msg.source) {
case 'Amazon':
case 'Google':
alert('Selected on ' + msg.source + ':\n' + msg.text);
break;
default:
alert('Unknown source: ' + msg.source);
break;
}
});
background.js:
var contextMenuID = 'copyToFB';
var googleRegex = /^https?:\/\/(?:[^\.]+\.)?google\..+/;
var amazonRegex = /^https?:\/\/(?:[^\.]+\.)?amazon\..+/;
/* `onUpdated` listener factory (for when FB has loaded) */
var listenerFactory = function(trgTabID, msgSrc, selectedText) {
return function(tabId, info, tab) {
if ((trgTabID === tabId) && (info.status === 'complete')) {
chrome.tabs.onUpdated.removeListener(arguments.callee);
chrome.tabs.sendMessage(trgTabID, {
source: msgSrc,
text: selectedText
});
}
}
};
chrome.contextMenus.create({
type: 'normal',
id: contextMenuID,
title: 'Copy to Facebook',
contexts: ['selection'],
// For some reason documentsUrlPatterns
// does not seem to work (at least on Windows).
// Theoratically, you should be able to limit the
// context-menu on specific sites only
//documentUrlPatterns: [
//'*://*.google.*/*',
//'*://*.amazon.*/*'
//],
enabled: true
});
chrome.contextMenus.onClicked.addListener(function(info, tab) {
console.log('Context menu clicked: ', info.menuItemId);
if (info.menuItemId === contextMenuID) {
var selectedText = info.selectionText;
console.log('Selected text: ', selectedText);
if (!selectedText) {
alert('Nothing to copy to Facebook !');
return;
}
var url = info.frameUrl ? info.frameUrl : info.pageUrl;
if (googleRegex.test(url)) {
console.log('Google.com URL: ', url);
/* Handle text selected on `google.com` */
chrome.tabs.create({
url: 'http://facebook.com/',
active: true
}, function(tab) {
chrome.tabs.onUpdated.addListener(
listenerFactory(tab.id, 'Google', selectedText));
});
} else if (amazonRegex.test(url)) {
console.log('Amazon.com URL: ', url);
/* Handle text selected on `amazon.com` */
chrome.tabs.create({
url: 'http://facebook.com/',
active: true
}, function(tab) {
chrome.tabs.onUpdated.addListener(
listenerFactory(tab.id, 'Amazon', selectedText));
});
} else {
console.log('Non-matching URL: ', url);
}
}
});
Final notes:
Because documentUrlPatterns does not seem to work (at least on Windows), the context-menu is shown on every page (when you select some text). You could add extra listeners (e.g. for chrome.tabs.onActivated etc) to remove or disable the context menu when the user is not on one of the permitted URLs.
It might be a good idea to keep track of an ope FB tab and not create a new one every time. (Or you could also look for an already opened (e.g. by the user) FB tab.)