I'm building an extension. When I click on the extension's icon, a UI is being loaded, from which I click a button, and a screenshot is performed.
For now, what's happening is that i'm using html2canvas, and in order to screenshot the current active tab, I need to pass that tab's HTML Source ( for now i'm capturing my own extension's window -_- ).
I was attempting on following the answer [here][1], but without success. That means, I was attempting to retrieve in the content_script.js the current page's HTML, and send it via sendMessage:
function DOMtoString(document_root) {
var html = "",
node = document_root.firstChild;
while (node) {
switch (node.nodeType) {
case Node.ELEMENT_NODE:
html += node.outerHTML;
break;
case Node.TEXT_NODE:
html += node.nodeValue;
break;
case Node.CDATA_SECTION_NODE:
html += "<![CDATA[" + node.nodeValue + "]]>";
break;
case Node.COMMENT_NODE:
html += "<!--" + node.nodeValue + "-->";
break;
case Node.DOCUMENT_TYPE_NODE:
// (X)HTML documents are identified by public identifiers
html +=
"<!DOCTYPE " +
node.name +
(node.publicId ? ' PUBLIC "' + node.publicId + '"' : "") +
(!node.publicId && node.systemId ? " SYSTEM" : "") +
(node.systemId ? ' "' + node.systemId + '"' : "") +
">\n";
break;
}
node = node.nextSibling;
}
return html;
}
chrome.runtime.sendMessage({
action: "getSource",
source: DOMtoString(document),
});
then, in a file which popup is mounting, I have done the following, in order to attempt and grab that message (important to state - i was also trying the following code in background.js, same result):
chrome.runtime.onMessage.addListener(function (request, sender) {
console.log("IN THE LISTENER backfgroundE - " + message);
if (request.action == "getSource") {
message.innerText = request.source;
}
});
but for some odd reason the message is not even arriving to neither my mounted-by-popup.html file, nor in my background.js file.
Any ideas?
Manifest file:
{
"name": "Chrome plugin for Juno issues report",
"description": "A issues report tool as a Chrome plugin",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_title": "Juno issue report"
},
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "index.html"
},
"icons": {
"16": "juno-icon.png",
"48": "juno-icon.png",
"128": "juno-icon.png"
},
"permissions": ["desktopCapture", "tabs", "downloads", "<all_urls>"],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_script.js"]
}
]
}
I'll just add that the names of the files are aligned with the names mentioned in the manifest file.
Image of my files tree can be seen here:
[![enter image description here][2]][2]
Regards!
[1]: Getting the source HTML of the current page from chrome extension
[2]: https://i.stack.imgur.com/jh3Gf.png
Related
This question already has answers here:
Add a property to a JavaScript object using a variable as the name? [duplicate]
(14 answers)
Closed 5 years ago.
First attempt at a chrome extension here. I want to add some persistent checkboxes to a transaction list on my banks website to aid reconciling with an external budgeting tool. I've got everything working but I can't retrieve the data from the chrome storage API (using local). The result is always undefined, even though I'm testing retrieving the value in the same callback where it was successfully written to storage.
manifest.json:
{
"name": "Reconciler",
"version": "1.0.0",
"manifest_version": 2,
"background": {
"scripts": ["injector.js"],
"persistent": false
},
"permissions": [
"activeTab",
"storage",
"https://mybank*"
],
"browser_action": {
"default_title": "Add reconciling buttons"
}
}
injector.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.insertCSS(null, {file: "reconciler.css"});
chrome.tabs.executeScript(null, {file: "jquery-3.2.0.min.js"});
chrome.tabs.executeScript(null, {file: "md5.js"});
chrome.tabs.executeScript(null, {file: "reconciler.js"});
});
reconciler.js:
$("#transactions thead tr").append("<th class='reconcile'>Reconcile</th>");
$("#transactions tbody tr").each(function(){
$this = $(this);
var rel_hash = "md5_" + hex_md5($this.find("img.expand-trans").first().attr("rel"));
$this.append("<td class='reconcile'><input type='checkbox' id='" + rel_hash + "' name='" + rel_hash + "'></td>");
chrome.storage.local.get(rel_hash, function(items) {
$("#"+rel_hash).attr("checked", items[rel_hash]);
});
});
$("#transactions input[type=checkbox]").change(function(){
var rel_hash = $(this).attr("id");
var checked = this.checked;
chrome.storage.local.set({rel_hash: checked}, function() {
if(chrome.runtime.lastError) {
console.error(
"Error setting " + key + " to " + JSON.stringify(data) +
": " + chrome.runtime.lastError.message
);
} else {
console.log('Saved ' + rel_hash + '=' + checked);
chrome.storage.local.get(rel_hash, function(items) {
console.log(rel_hash + "=" + items[rel_hash]);
});
}
});
});
console output when checking and unchecking:
Saved md5_516654acf57d9bd95cdbe497f7ca6c8d=true
md5_516654acf57d9bd95cdbe497f7ca6c8d=undefined
Saved md5_516654acf57d9bd95cdbe497f7ca6c8d=false
md5_516654acf57d9bd95cdbe497f7ca6c8d=undefined
Saved md5_ee541d5b1d95768cef9c257ca88c8ced=true
md5_ee541d5b1d95768cef9c257ca88c8ced=undefined
The problem was not with the storage API interaction; it was with how I was trying to define the object to send to the storage API. I needed to set a dynamic key:
....
var data = {};
data[rel_hash] = checked;
chrome.storage.local.set(data, function() {
....
I am (trying to) implementing a Chrome Extension, that uses the Tumblr API. For that to work I need to authorize via OAuth (1.0a).
I managed to get most of the authorization to work but I think I am missing something...
My manifest.json of the Chrome Extension I am writing looks like this for now:
{
"name": "Tumblr - Tiled Dashboard",
"version": "0.1.1",
"manifest_version": 2,
"description": "This extension modifies the look of your Tumblr dashboard.",
"icons": {
"16": "images/icon_16.png",
"48": "images/icon_48.png",
"128": "images/icon_128.png"
},
"background": {
"page": "background.html"
},
"content_scripts": [
{
"matches": [ "*://*.tumblr.com/dashboard" ],
"css": [ "styles.css" ],
"js": [ "jquery-2.1.3.min.js", "masonry.min.js", "code.js" ]
}
],
"permissions": [
"https://www.google-analytics.com/",
"https://api.tumblr.com/v2/*",
"webRequest",
"storage",
"tabs",
"https://www.google.com/accounts/OAuthGetRequestToken", "https://www.google.com/accounts/OAuthAuthorizeToken", "https://www.google.com/accounts/OAuthGetAccessToken"
],
"web_accessible_resources": [ "chrome_ex_oauth.html", "injectedCode.js" ],
"content_security_policy": "script-src 'self' https://api.tumblr.com/v2/ https://ssl.google-analytics.com; object-src 'self'",
"homepage_url": "http://desvre.tumblr.com/",
"author": "Franz Spitaler"
}
...I think this should be ok. In my background.html there are in fact only the included scripts (google analytics and the three oauth files I got from here: https://developer.chrome.com/extensions/tut_oauth . Those three files are also included in the fourth file I downloaded from the previous source ("chrome_ex_oauth.html").
Now when I reload the Chrome Extension in the Extensions (Ctrl + r) the Redirecting page opens and redirects me to the Tumblr authorization page, where I can allow the access.
Since I also added the "chrome_ex_oauth.html" to the "web_accessible_resources" in the manifest.json this works.
The problem occurs after the click on the 'allow' button. I simply get back to the redirecting page ("chrome_ex_oauth.html") and nothing more happens. When I open up the console, I can see an error message like the following:
GET https://www.tumblr.com/oauth/access_token?oauth_consumer_key=MY_CONSUMER_KEY&oauth_nonce=D3VeV&oauth_signature=kvhL%2F9GSMuiODoPR%2FyUrUiqzqF0%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1424250463&oauth_token=6khqzjiMFbM7hcqqnNf8hm9ttDELKUVYo2TBQmyLOtepGN9KhJ&oauth_verifier= 400 (Bad Request)
As described in the OAuth tutorial page from Google I use this to initialize the 'background page' (which leads to the error message):
var oauth = ChromeExOAuth.initBackgroundPage({
'request_url': 'https://www.tumblr.com/oauth/request_token',
'authorize_url': 'https://www.tumblr.com/oauth/authorize',
'access_url': 'https://www.tumblr.com/oauth/access_token',
'consumer_key': 'MY_CONSUMER_KEY',
'consumer_secret': 'MY_SECRET_CONSUMER_KEY',
'app_name': 'Tumblr Tiled Dashboard'
});
Did I miss something important here? I think the manifest.json file is ok (permissions, web_accessible_resources?!).
Thank you for any help. There is really no really great tutorial for OAuth out there for Google Extensions (except that linked page)...
As #abraham pointed out, there was a missing parameter, as seen in my posted error.
I was able to track down the problem and found it in the function of the chrome_ex_oauth.js file. I changed the function from:
ChromeExOAuth.formDecode = function(encoded) {
var params = encoded.split("&");
var decoded = {};
for (var i = 0, param; param = params[i]; i++) {
var keyval = param.split("=");
if (keyval.length == 2) {
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1]);
decoded[key] = val;
}
}
return decoded;
};
to this:
ChromeExOAuth.formDecode = function(encoded) {
var params = encoded.split("&");
var decoded = {};
for (var i = 0, param; param = params[i]; i++) {
var keyval = param.split("=");
if (keyval.length == 2) {
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1]);
decoded[key] = val;
}
else if (keyval.length == 3){
var key = ChromeExOAuth.fromRfc3986(keyval[0]);
var val = ChromeExOAuth.fromRfc3986(keyval[1].split("#")[0]);
decoded[key] = val;
}
}
return decoded;
};
Where the last parameter was not identified correctly because the ending of the url I got looks like this: #_=_
With the keyval[1].split('#')[0] I get the exact part of the parameter, that I need!
Thank you for the help, everything seems to work now. A request that needs OAuth authorization did, at least!
I'm trying to figure out exactly how Content Scripts work.
I'm writing an extension with a context menu that, once the option is clicked, the next click will result in an alert dialog with information about the page the element is on and the id and name of the element clicked.
I think my problem is including the name of the content script file somewhere on my manifest, since I am using executeScript to use it instead of having it injected on every single web page but I cannot figure out where to put it.
Please let me know if I can provide more helpful information.
Here are copies of my manifest, the main script I use and the script that I am trying to insert.
Manifest.json
{
"name": "Omnixx Page Object Builder",
"description": "Finds information about an element clicked, started with a context menu",
"version": "0.3",
"permissions": ["tabs", "contextMenus", "activeTab"],
"background": {
"persistent": false,
"scripts": ["menuBuilder.js"]
},
"manifest_version": 2
}
menuBuilder.js
function onClickHandler(info, tab) {
if (info.menuItemId == "start") {
console.log("Started trying to find elements");
chrome.tabs.executeScript(null, {file:"elementTracker.js"});
console.log("Started listening...");
}
};
chrome.contextMenus.onClicked.addListener(onClickHandler);
chrome.runtime.onInstalled.addListener(function() {
chrome.contextMenus.create({"title": "Find element information", "id": "start", "contexts":["all", "page", "frame", "selection", "link", "editable", "image","video", "audio"], });
});
elementTracker.js
function click(event) {
// Begin building the message
var msgToWrite = "";
var curscreen = window.HIDEFRAME.document.getElementsByName("curscreen")[1].value;
msgToWrite += "curscreen: '" + curscreen + "'\n";
var url = content.document.URL;
msgToWrite += "URL: " + url + "\n";
// A try-catch block is necessary for finding the frame; otherwise the code
// will fail for pages without frames
try {
var frame = event.originalTarget.ownerDocument.defaultView.frameElement;
var frameName = frame.getAttribute("name");
if (frameName != null) {
msgToWrite += "Frame: '" + frameName + "'\n";
} else {
msgToWrite += "No frame found\n";
}
} catch (e) {
msgToWrite += "No frame found\n";
}
// Get the element's ID
var elemID = event.target.getAttribute("id");
if (elemID != null) {
msgToWrite += "ID: '" + elemID + "'\n";
} else {
msgToWrite += "No ID found\n";
}
// Get the element's name
var elemName = event.target.getAttribute("name");
if (elemName != null) {
msgToWrite += "Name: '" + elemName + "'";
} else {
msgToWrite += "No name found";
}
// Create a pop-up message
alert(msgToWrite);
}
document.addEventListener('click', click);
What I think is happening is that you're trying to call another event listener at the end of your elementTracker.js file. You don't need that because you're initializing the script from the context menu call.
Change the last line in that script to click(event). The script will be injected to the page from the context menu.
I'm developing a chrome extension , and I want it to get the selected text by user , the code works fine when I run it on a similar non-extension page but it doesn't work in my background page extension . even when I try to get the value of the selection in the console.log it doesn't return anything . here's my javascipt file :
window.onload = myOnload;
function myOnload(){
var button = document.getElementById("button");
chrome.contextMenus.create({
"title":"MissLang Fix",
"contexts":["all"],
"onclick": myClick2
});
document.getElementById("input").innerHTML = "mojtaba";
button.onclick= function(){myClick()};
}
function myClick2(){
var txt = '';
if (chrome.extension.getBackgroundPage().window.getSelection)
txt = chrome.extension.getBackgroundPage().window.getSelection();
else if (chrome.extension.getBackgroundPage().document.getSelection)
txt = chrome.extension.getBackgroundPage().document.getSelection();
else if (chrome.extension.getBackgroundPage().document.selection)
txt = chrome.extension.getBackgroundPage().document.selection.createRange().text;
console.log("this is the console message");
console.log("txt: " + txt);
}
function myClick(){
var myString = document.getElementById("input").value;
for(var i=0;i<myString.length;i++){
myString = myString.replace("q","ض");
myString = myString.replace("w","ص");
}
document.getElementById("output").innerHTML = myString;
}
and here's my manifest.json file :
{
"name": "MissLang Fix extension",
"manifest_version": 2,
"version": "1.0",
"description": "fixing english to farsi typings.",
"icons":{
"16":"icon_16.png",
"128":"icon_128.png"
},
"background":{
"scripts":["script.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "missLangFix.html"
},
"permissions": [
"tabs",
"contextMenus"
]
}
I'm new to this so please bear with me. I am trying to write a chrome extension that does the following:
Detect www.website.com/anypage.html. If this website is detected, then do the following.
Don't load the URL.
Instead, write a blank document with a hyperlink to www.website.com/anypage.html?ie=UTF8
The script is set to run at document start (in the manifest).
Here is my code:
Detect URL:
var regExp = /website.com/gi;
var match = 0;
testString = window.location.href.toString();
if(regExp.test(testString) {
match = 1;
Write blank document with link to the URL with the UTF8 encoding tag:
document.write("<a href=" + window.location.href + "?ie=UTF8>Title of Link</a>");
This doesn't work as expected, and just shows a blank page. Anyone have any ideas?
Thanks!
EDIT: Here is the full code:
checklink(); // If there is a match, then checklink will return a 1. If it's already tagged, it will return a 5.
var matchLink = null;
if (checklink() === 1) {
matchLink = window.location.href.toString();
if (checklink() != 1) {
matchLink = null;
function checklink() { //checks to see if the current URL matches website.com
var regExp = /website.com/gi,
testString = window.location.href.toString(),
match = 0,
tagged = 0;
if (regExp.test(testString)) { //if there is a match, returns 1
match = 1;
var regExp2 = /UTF8/gi;
if (regExp2.test(testString)) { //if UTF8 is found, then it returns 5
tagged = 5;
return(match + tagged);
function tagUTF() {
if (matchLink) {
var newLink = matchLink + "?ie=UTF8";
document.write("Link");
if (matchLink) {
tagUTF();
}
The chrome content script has access to the DOM, so you could just replace the contents of the body element of the current page with a new node that has your anchor tag either using dom manipulation methods or innerHTML:
document.body.innerHTML = "<a href=" + window.location.href + "?ie=UTF8>Title of Link</a>";
Please note, this assumes that the JavaScript that is doing the DOM manipulation was properly added for your Chrome extension as a "content script":
http://code.google.com/chrome/extensions/content_scripts.html
EDIT:
Here is the code I used to make it work for me locally:
manifest.json
{
"name": "Test",
"version": "1.0",
"description": "Test",
"permissions": [
"<all_urls>"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content-script.js"],
"run_at": "document_end"
}
]
}
content-script.js
document.body.innerHTML = "<a href='test'>test</a>";