chrome extension not work after load new data - javascript

I developed a chrome extension which generates a custom message for me from skype on clicking it. It works fine on page load and when I scroll down and load more connection to send message extension stop work on it.
{
"name": "Page Redder",
"description": "Make the current page red",
"version": "2.0",
"content_scripts": [
{
"run_at" :"document_end",
"matches": ["<all_urls>"],
"js": ["jquery.js","script.js"]
}
],
"permissions": [
"activeTab"
],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"browser_action": {
"default_title": "Make this page red"
},
"manifest_version": 2
}
jquery file
$(document).ready(function(){
$('.mn-person-card__person-btn-ext').click(function(){
var name = $(this).attr('aria-label');
name = name.replace("Send message to ","");
var dummyContent = "Hello " + name + ", \n\nThanks for staying connected with me on LinkedIn.";
$('body').append("<textarea id='absdfjalskjdf'></textarea>");
document.getElementById('absdfjalskjdf').innerHTML = dummyContent;
var dummy =$('#absdfjalskjdf').select()
document.execCommand("Copy");
$('#absdfjalskjdf').remove();
});
});
$(document).on('change', function(){
$('.mn-person-card__person-btn-ext').click(function(){
alert("asldfkjaldfj");
var name = $(this).attr('aria-label');
name = name.replace("Send message to ","");
var dummyContent = "Hello " + name + ", \n\nThanks for staying connected with me on LinkedIn.";
$('body').append("<textarea id='absdfjalskjdf'></textarea>");
document.getElementById('absdfjalskjdf').innerHTML = dummyContent;
var dummy =$('#absdfjalskjdf').select()
document.execCommand("Copy");
$('#absdfjalskjdf').remove();
});
});
When I open all my connection, first loaded connections are work successfully but when load some more connections that time it stop to work. Please help me to make it possible.

Related

Problems with asynchronous calls in chrome extension

I had this idea for a new chrome extension:
It should create a linking QR code for the currently open website, displayed in the popup.html which can be easily scanned with the smartphone. If you watch a video on YouTube, the current time of the video should be also embedded in the QR code, which makes it possible to continue watching the video directly in the YouTube app on the smartphone.
So far so good. But now I have the following problem:
This extension works fine on all websites. Only on Youtube there seems to be a problem with the asynchrony of the onmessage listener and the sending of the message to the contentScript (requesting the current time of the viewed YouTube video).
In the debugging console I get the following errors:
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
... getting this after trying to execute line 7 of background.js
Error handling response: TypeError: Cannot read property 'videoTime' of undefined
at chrome-extension://...../background.js:8:70
... getting this after trying to execute line 8 of background.js
popup.js
$(function() {
chrome.runtime.sendMessage({text: 'sendURL'}, function(response) {
$('#qr-code').attr('src', getQRCodeImgURL(response.url));
});
});
function getQRCodeImgURL(url) {
var qrCodeURL = new URL('http://api.qrserver.com/v1/create-qr-code/');
qrCodeURL.searchParams.set('data', encodeURI(url));
qrCodeURL.searchParams.set('size', '200x200');
return qrCodeURL;
}
background.js
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
if (message.text == 'sendURL') {
chrome.tabs.query({active: true, /* lastFocusedWindow: true */}, function (tabs) {
var currentURL = new URL(tabs[0].url);
if (currentURL.href.indexOf('youtube.com/watch?v=') >= 0) { // if current website is youtube
chrome.tabs.sendMessage(tabs[0].id, { text: 'sendVideoTime' }, function (response) {
const ytVideoTime = timeStringToSeconds(response.videoTime);
var ytURL = new URL('https://youtu.be/');
ytURL.pathname = '/' + currentURL.searchParams.get('v');
ytURL.searchParams.set('t', ytVideoTime);
currentURL = ytURL;
sendResponse({ url: currentURL.href });
});
} else {
sendResponse({ url: currentURL.href });
}
});
}
return true;
});
function timeStringToSeconds(timeString) {
var seconds = 0;
var hms = timeString.split(':');
if (hms.length == 3) {
seconds = parseInt(hms[0])/* hours */ * 60 /* minutes per hour */ * 60 /* seconds per minute */;
hms.shift(); /* remove first element, for accessing first element in next step (also if hms doesnt is in this hh:mm:ss format) */
}
return seconds + (parseInt(hms[0]) * 60) /* seconds per minute */ + parseInt(hms[1]) /* seconds */;
}
contentScript.js
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if (message.text == 'sendVideoTime') {
const time = document.evaluate('//*[#id="movie_player"]/div[27]/div[2]/div[1]/div[1]/span[1]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.textContent;
sendResponse({videoTime: time});
}
return true;
});
manifest.json
{
"manifest_version": 2,
"name": "URL-QR-Code-Creator",
"version": "1.0",
"description": "This extension creates a linking QR code for the currently open website, which can be easily scanned with the smartphone. If you watch a video on the YouTube website, the current time of the video is also embedded in the QR code, which makes it possible to continue watching the video directly in the YouTube app on the smartphone.",
"icons": {
"16": "images/qr-code-16px.png",
"32": "images/qr-code-32px.png",
"48": "images/qr-code-48px.png",
"64": "images/qr-code-64px.png",
"128": "images/qr-code-128px.png"
},
"browser_action": {
"default_icon": {
"16": "images/qr-code-16px.png",
"32": "images/qr-code-32px.png",
"48": "images/qr-code-48px.png",
"64": "images/qr-code-64px.png",
"128": "images/qr-code-128px.png"
},
"default_title": "show QR-Code",
"default_popup": "popup.html"
},
"background": {
"scripts": [
"background.js"
],
"persistant": false
},
"content_scripts": [
{
"matches": ["*://www.youtube.com/watch?v=*"],
"js": ["contentScript.js"]
}
],
"permissions": [
"activeTab",
"tabs"
]
}
I did it!
At first I said goodbye to the idea of regulating access to the youtube DOM via an extra content script.
So I deleted contentScript.js and deleted it from manifest.json.
Now I simply accessed the DOM using the chrome.tabs.executeScript method in the background.js file and got the result using the callback function. Quite simply - without having to send messages around all the time.

chrome.cookies.getAll returns an empty array

I'm working on a simple chrome extension that will delete all cookies from a domain with one click but for some reason, it's not working. When I try to get all the cookies from the domain, it returns an empty array. What am I doing wrong?
Here's the js script:
$("#fixTheCookiesButton").click(() => {
// delete the cookies
chrome.cookies.getAll({domain: "https://www.youtube.com"}, (cookies) => {
console.log("deleting " + cookies.length + " cookies")
for(var i = 0; i < cookies.length; i++){
console.log(i + " deleted")
chrome.cookies.remove({
url: "https://www.youtube.com" + cookies[i].path,
name: cookies[i].name
})
}
// some other stuff that isn't relevant here
}
and here's my manifest:
{
"manifest_version": 2,
"name": "FixYT",
"version": "1.0",
"description": "Fixes that YT cookie bug with one click",
"browser_action": {
"default_title": "FixYT",
"default_popup": "popup.html"
},
"permissions": [
"cookies",
"https://www.youtube.com/",
"*://www.youtube.com/",
"tabs",
"*://*/"
]
}
I've tried looking around the internet but I can't find any solutions to this.
you should call this code block in background.js
chrome.cookies.getAll({
domain: ".youtube.com"
}, function (cookies) {
for (var i = 0; i < cookies.length; i++) {
console.log(cookies[i] + "deleted");
chrome.cookies.remove({
url: "https://" + cookies[i].domain + cookies[i].path,
name: cookies[i].name
});
}
});
As isa says, chrome.cookies is only defined in background.js
Add to manifest so we have access to chrome.cookies in background.js
 
 "permissions": [
     ...
      "cookies",
    ],
background.js
...
chrome.cookies.getAll({
}, function (theCookies) {
cookies = theCookies
console.log(cookies)
});
Sending Cookies from background.js to other views
(Not required, but still useful)
Add to panel.js to search for cookies. [This will fire when you open your extension ie (puzzle piece icon) -> click on your extension]
chrome.runtime.sendMessage({ command: "GetCookies"},
function(response) {
console.log("I received cookies!")
console.log(response)
}
);
Add to background.js logic to get cookies from the browser and check for known cookies
chrome.runtime.onMessage.addListener(function (message, sender, callback) {
if (message.command === 'GetCookies') {
checkKnownCookies()
}
});
let cookies = [] // Hold IDs of recognized cookies
function checkKnownCookies() {
chrome.cookies.getAll({
}, function (theCookies) {
cookies = theCookies
console.log(cookies)
callback(theCookies)
});
}
 
https://developer.chrome.com/docs/extensions/reference/cookies/#method-getAll
To view console for background.js go to (puzzle piece icon) -> manage extensions and click on the href link to background.html for your extension
Both permissions and host_permissions are required.
"permissions": [
"cookies",
"tabs"
],
"host_permissions": ["<all_urls>"],

How to get selected text in the background script from the active tab (after a hotkey)?

I'm trying to get the selected text from a web page after a hotkey like for example Ctrl+SHIFT+Number. I'm starting with the code from Firefox help.
The manifest.json:
{
"description": "Native messaging example extension",
"manifest_version": 2,
"name": "Native messaging example",
"version": "1.0",
"icons": {
"48": "icons/message.svg"
},
"applications": {
"gecko": {
"id": "ping_pong#example.org",
"strict_min_version": "50.0"
}
},
"background": {
"scripts": ["background.js"]
},
"commands": {
"toggle-feature": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"linux": "Ctrl+Shift+0"
},
"description": "Send a 'toggle-feature' event"
}
},
"browser_action": {
"default_icon": "icons/message.svg"
},
"permissions": ["nativeMessaging"]
}
The JavaScript file:
/*
On startup, connect to the "ping_pong" app.
*/
var port = browser.runtime.connectNative("ping_pong");
/*
Listen for messages from the app.
*/
port.onMessage.addListener((response) => {
console.log("Received: " + response);
});
/*
On a click on the browser action, send the app a message.
*/
browser.browserAction.onClicked.addListener(() => {
console.log("Sending: ping");
port.postMessage("ping");
});
browser.commands.onCommand.addListener(function(command) {
if (command == "toggle-feature") {
console.log("toggling the feature!");
text1 = window.getSelection();
console.log(text1);
}
});
The debugger says:
Selection { anchorNode: null, anchorOffset: 0, focusNode: null,
focusOffset: 0, isCollapsed: true, rangeCount: 0, caretBidiLevel: null
}
The messaging works, the hotkey works, but I can't get the selected text. Is there another method which I need to use? I tried a lot of code all yesterday, but I didn't find how to do it. Sometimes I have another error from the debugger, but I can never get the selected text. It is a problem of focus? It is crazy!
I read the code from other add-ons. It seems they use that method but maybe it is in a popup window?
I'm on Debian Stretch, and Firefox 56. I tried on 2 computers.
To get the selected text you must use a content script. Given that you are initiating getting the selected text from a hotkey defined with a manifest.json commands, you're best off using tabs.executeScript() to inject the needed code when the user presses the hotkey.
The following adapts the code you have in the question to do only the portion which is defining the hotkey and adds getting the selection (based on the code in Get the Highlighted/Selected text) using tabs.executeScript() to inject into all frames in the activeTab.
It is possible for the user to have made a selection in each existing iframe. You will need to determine how you want to handle that. The code below gets the selection from each iframe. However, it currently discards all but the last selection found (the first result is the main frame). You may want to notify the user when they have selections in multiple frames. Note that Chrome does not permit selecting text in multiple frames, but Firefox does.
The following code is tested in both Firefox and Chrome.
manifest.json:
{
"description": "Get selected text upon hotkey",
"manifest_version": 2,
"name": "Hotkey: get selected text",
"version": "1.0",
"icons": {
"48": "icon.png"
},
"background": {
"scripts": ["background.js"]
},
"commands": {
"get-selected-text": {
"suggested_key": {
"default": "Ctrl+Shift+Y",
"linux": "Ctrl+Shift+0"
},
"description": "Get the selected text from the active tab."
}
},
"permissions": [
"activeTab"
]
}
background.js:
chrome.commands.onCommand.addListener(function (command) {
if (command == "get-selected-text") {
chrome.tabs.executeScript({
code: '(' + getSelectionText.toString() + ')()',
//We should inject into all frames, because the user could have made their
// selection within any frame, or in multiple frames.
allFrames: true,
matchAboutBlank: true
}, function (results) {
selectedText = results.reduce(function (sum, value) {
//This checks all the results from the different frames to get the one
// which actually had a selection.
if (value) {
if (sum) {
//You will need to decide how you want to handle it when the user
// has things selected in more than one frame. This case is
// definitely possible (easy to demonstrate).
console.log('Selections have been made in multiple frames:');
console.log('Had:', sum, ':: found additional:', value);
}
// Currently, we just discard what was obtained first (which will be
// the main frame). You may want to concatenate the strings, but
// then you need to determine which comes first. Reasonably, that
// means determining where the iframe is located on the page with
// respect to any other selection the user has made. You may want
// to just inform the user that they need to make only one
// selection.
return value;
}
return sum;
}, '');
console.log('selectedText:', selectedText);
})
}
});
//The following code to get the selection is from an answer to "Get the
// Highlighted/Selected text" on Stack Overflow, available at:
// https://stackoverflow.com/a/5379408
// The answer is copyright 2011-2017 by Tim Down and Makyen. It is
// licensed under CC BY-SA 3.0, available at
// https://creativecommons.org/licenses/by-sa/3.0/
function getSelectionText() {
var text = "";
var activeEl = document.activeElement;
var activeElTagName = activeEl ? activeEl.tagName.toLowerCase() : null;
if (
(activeElTagName == "textarea") || (activeElTagName == "input" &&
/^(?:text|search|password|tel|url)$/i.test(activeEl.type)) &&
(typeof activeEl.selectionStart == "number")
) {
text = activeEl.value.slice(activeEl.selectionStart, activeEl.selectionEnd);
} else if (window.getSelection) {
text = window.getSelection().toString();
}
return text;
}
I found a solution:
I select the text from any webpage, and I have the text in the background.js and after I can do what I want with the text. In my specific case, I use an external program (in python) to receive the selected text.
Manifest.json
{
"description": "Native messaging + Hotkey + content-script messaging",
"manifest_version": 2,
"name": "getSelectedTextFromHotkey",
"version": "1.0",
"icons": {
"48": "icons/message.svg"
},
"applications": {
"gecko": {
"id": "gettext#example.org",
"strict_min_version": "50.0"
}
},
"background": {
"scripts": ["background.js"]
},
"commands": {
"toggle-feature": {
"suggested_key": {
"default": "Ctrl+Shift+4",
"linux": "Ctrl+Shift+5"
},
"description": "Send the selected text"
}
},
"browser_action": {
"default_icon": "icons/message.svg"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"]
}
],
"permissions": [ "<all_urls>","nativeMessaging","webRequest"]
}
Background.js
var port = browser.runtime.connectNative("gettext");
browser.runtime.onConnect.addListener(connected);
port.onMessage.addListener((response) => {
console.log("Received: " + response);
});
function onExecuted(result) {
console.log(`We executed`);
}
function onError(error) {
console.log(`Error: ${error}`);
}
browser.commands.onCommand.addListener(function(command) {
if (command == "toggle-feature") {
console.log("toggling the feature!");
var executing = browser.tabs.executeScript({ file: "/content-script.js", allFrames: false });
executing.then(onExecuted, onError);
}
});
var portFromCS;
function connected(p) {
portFromCS = p;
portFromCS.onMessage.addListener(function(m) {
console.log("message selected:")
console.log(m);
console.log("Sending: ping");
port.postMessage("ping");
});
}
content-script.js
// content-script.js
var selectedText = getSelection().toString();
var myPort = browser.runtime.connect({name:"port-from-cs"});
myPort.postMessage(selectedText);
gettext.json
{
"name": "gettext",
"description": "Native messaging + Hotkey + content-script messaging",
"path": "/home/marie/web-ext/gettext.py",
"type": "stdio",
"allowed_extensions": [ "gettext#example.org" ]
}
gettext.py
#!/usr/bin/python -u
# Note that running python with the `-u` flag is required on Windows,
# in order to ensure that stdin and stdout are opened in binary, rather
# than text, mode.
import sys, json, struct
# Read a message from stdin and decode it.
def getMessage():
rawLength = sys.stdin.read(4)
if len(rawLength) == 0:
sys.exit(0)
messageLength = struct.unpack('#I', rawLength)[0]
message = sys.stdin.read(messageLength)
return json.loads(message)
# Encode a message for transmission, given its content.
def encodeMessage(messageContent):
encodedContent = json.dumps(messageContent)
encodedLength = struct.pack('#I', len(encodedContent))
return {'length': encodedLength, 'content': encodedContent}
# Send an encoded message to stdout.
def sendMessage(encodedMessage):
sys.stdout.write(encodedMessage['length'])
sys.stdout.write(encodedMessage['content'])
sys.stdout.flush()
# BE CAREFUL, NEVER USE THE CONSOLE in the loop ! it stops the connection!!!
while True:
receivedMessage = getMessage()
if (receivedMessage == "ping"):
sendMessage(encodeMessage("pong"))
It seems to work well on Firefox.

Creating cookies from background page

I have a background script in my extension that creates a context menu item and handles it. When it is clicked, a cookie is created with specific details. Here is the source for that file:
script.js
function createC() {
var x = 1;
var y = 2;
//Create Cookie
document.cookie = document.URL + "=" + " " + x + " " + y + " ; " + 4102444799;
console.log("Cookie Created.");
}
chrome.contextMenus.create({
title: "Create Cookie",
contexts:["all"],
onclick: createC,
});
Obviously the variables used in it are for testing. When I run document.cookie; in the console, the cookie does not appear. I have tried using the chrome.cookies API and had the same issue.
Does the cookie not appear because it is created in the background script? I am trying to set it on the current tab the user is on, not the background page itself.
manifest.json
{
"manifest_version": 2,
"name": "MyExtension",
"description": "Do stuff",
"version": "0.1",
"icons": { "16": "icon.png",
"48": "icon.png",
"128": "icon.png" },
"options_page": "options.html",
"permissions": [
"tabs", "<all_urls>", "contextMenus", "cookies"
],
"background": {
"scripts": ["script.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["nav.js"]
}
]
}
In background script, 'document' is not for the current page, but for the extension background page(chrome-extension://[your extension id]/bacground.html). So, you can't use 'document.cookie', you need to try chrome.cookies.get like this:
/**
* Create cookie for the special page
* #param {Object<key, value>} detail
* #param {Function=} opt_callback
*/
function createCookie(detail, opt_callback) {
chrome.cookies.set(detail, opt_callback);
}
You need to use javascript code in specific tab if you want to use the document the current page instead of background.html.
This can be done by function executeScript, your syntax is :
chrome.tabs.executeScript( tabId, details, callback )
chrome.tabs.executeScript( MyTabIdNumberMandatoryInYourCase, MyScriptCodeInLineOrUrl, MyCallbackOptional )
tabId matches the ID of the active tab page, background.js file is executed under the main background.html, then you need to pass the correct ID if you do not pass it, and hopefully it will execute the background.html as the active tab.
All WebRequest events, has a variable called details and she carries a tabid value, and you access it via details.tabId, below is a code that I use in one of my extensions already created.
var onCompletedExecuteScriptDetails = {
// You can run all the code in the inline form,
// rather than using the parameter "file", use the "code" parameter, but is very ugly,
// is much more elegant to use the "file" mode
// my-script.js is a file with code to create cookie
file : "my-script.js"
};
var onCompletedExecuteScript = function ( details ) {
chrome.tabs.executeScript( details.tabId, onCompletedExecuteScriptDetails );
};
var onCompletedCallback = function ( details ) {
document.addEventListener( 'DOMContentLoaded', onCompletedExecuteScript( details ) );
};
var onCompletedFilter = {
urls : [
"http://*/*",
"https://*/*"
]
};
chrome.webRequest.onCompleted.addListener( onCompletedCallback, onCompletedFilter, onCompletedInfo );
executeScript
I ended up using:
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (changeInfo.url != null) {
url = changeInfo.url;
}
});
This is what I ended up doing.
In manifest.json:
"permissions": [
"cookies",
"*://*.target_website.com/",
"*://*/_generated_background_page.html"
]
In background.js
chrome.cookies.set({
"name": "cookie's name",
"url": "the URL you want to apply the cookies to",
"value": "cookie's value"
}, function(cookie) {
if (chrome.extension.lastError) {
console.log(chrome.extension.lastError);
}
if (chrome.runtime.lastError) {
console.log(chrome.runtime.lastError);
}
});

Access-Control-Allow-Origin not checking in chrome extension

As you know, when send $.ajax(..) request to another domain (cross-domain), most browser throw exception like:
XMLHttpRequest cannot load http://mysite.com/test.php. Origin
http://127.0.0.1:8888 is not allowed by Access-Control-Allow-Origin.
I am creating chrome extension and it should send a request to my website. First , i expected to see above message,too. But i confused when i see it worked fine.
First, It’s seem good, it’s working and i have what i want. But it can be horrible. Every one can use such way (only a simple script) to attack my site and grab its data.
Of course, grabbing could be happen in other ways, too.
I am new in api programing and chrome extension. Do anyone may show me the way?
manifest.json
{
"manifest_version": 2,
"name": "MyTestExtension",
"description": "this extension is for test",
"version": "1.0",
"icons": {
"128": "icon.png"
},
"browser_action": {
"default_icon": "icon.png"
},
"permissions": [
"tabs" ,
"*://*/*"
],
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["jquery-1.7.2.min.js","content_script.js"],
"run_at": "document_end"
}
]
}
content_script.js
$(document).ready(function(){
$('html').mouseup(function() {
var selectedText = getSelectedText();
if(selectedText > ''){
my_syncTest(selectedText) // here : selected test send to my site
}
});
function getSelectedText() {
if (window.getSelection) {
var selection = window.getSelection().toString();
if(selection.trim() > ''){
return selection;
}
} else if (document.selection) {
var selection = document.selection.createRange().text;
if(selection.trim() > ''){
return selection;
}
}
return '';
} });
function my_syncTest(word){
var qs = 'word='+word+'&header=555&simwords=1';
$.ajax(
{
type: "POST",
url: 'http://mysite.com/test.php',
dataType: 'json',
data : qs,
success:function(res){
console.log(res.success +" - "+ res.idWord + " - " + res.header +" - " + res.meaning);
}});
}
XMLHttpRequests from your extension work because you defined these permissions in the manifest:
"permissions": [
"*://*/*"
]
When a user installs your extension, he is informed that this extension can access his data on all sites. I prefer only including the exact site you need instead of wildcards.
http://developer.chrome.com/extensions/xhr.html
This mechanism is to protect the user, not to protect your site. If you don't want everybody to use your API, use API-keys, or look into oAuth:
http://en.wikipedia.org/wiki/OAuth
If you want to learn more about cross origin requests:
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS

Categories