chrome.experimental.notification (Rich notifications) and external URLs - google-chrome-extension

I'm trying to open up a rich notification using the following:
var options = {
templateType: "basic",
title: "John Doe",
message: "Lorem ipsum",
iconUrl: "https://www.MyExternalURL.com/image.jpg"
};
chrome.experimental.notification.create("notifyId", options, function(id) {
console.log("Succesfully created notification");
});
But for some reason this does not work, but if I replace the options with the following:
var options = {
templateType: "basic",
title: request.name,
message: request.message,
iconUrl: chrome.runtime.getURL("/images/cat.png"),
};
And the notification works perfectly.
Here are the important stuff in my manifest file
{
"manifest_version": 2,
"name": ...,
"description": ...,
"version": ...,
"icons": {
...
},
"content_scripts": [
...
],
"background": {
"scripts": ["background.js"]
},
"permissions": [
"tabs",
"experimental"
],
"web_accessible_resources": [
"https://www.MyExternalURL.com/*"
]
}
How should I use an external image as the iconURL?

Script and object resources can only be loaded from the extension's package, not from the web at large. This ensures that your extension only executes the code you've specifically approved, preventing an active network attacker from maliciously redirecting your request for a resource.
So, your code does not work for loading an external JPEG Icon iconUrl: "https://www.MyExternalURL.com/image.jpg".
Instead download the file from https://www.MyExternalURL.com/image.jpg and put it inline as in approach #2.
More over web_accessible_resources does not accept any content with HTTP URL(S), it only accepts an array of strings specifying the paths (relative to the package root) of packaged resources that are expected to be usable in the context of a web page.
Reference
CSP
Web Accessible Resources

Fetching the blob of an external image by Ajax, apply data URI that is converted it.
var options = {
type: "basic",
title: "TITLE",
message: "MESSAGE"
};
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://www.MyExternalURL.com/image.jpg");
xhr.responseType = "blob";
xhr.onload = function(){
var blob = this.response;
options.iconUrl = window.URL.createObjectURL(blob);
chrome.notifications.create("notifyId", options, function(notId){});
};
xhr.send(null);

Related

save REQUEST body(payload) of POST REQUEST through chrome extension

we are looking to read/save a POST REQUEST's body/payload through chrome extension.
Scenario: a target webpage will submit a post request to server once in a while.
Requirement: chrome extension's popup.html has a button with id='startIntercept' and upon clicked we need to save the next firing post request's all details(headers, payload) to a session variable.
we only need to save what post request is been sent from current tab. we are not bothered what response we received for the post request.
through a source we came to know a way to achieve it by "Overwriting window.XMLHttpRequest.prototype.open method" by saving old open method to a variable and create a new open method to intercept any request and save it then pass parameters to old open method to get usual expected page functionality.
code used:
//popup.js
let startIntercept = document.getElementById("startIntercept");
startIntercept.addEventListener("click", async () => {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["startWork.js"],
});
});
//startWork.js
let oldXHROpen = window.XMLHttpRequest.prototype.open;
console.log('script injected');
window.XMLHttpRequest.prototype.open = function() {
this.addEventListener("load", function() {
const responseBody = this.responseText;
sessionStorage.setItem('payload', responseBody );
});
return oldXHROpen.apply(this, arguments);
};
//manifest.json
"name": "extTracker",
"description": "want to save post request details",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"host_permissions": ["<all_urls>"],
"permissions": ["storage", "activeTab", "scripting"],
"web_accessible_resources": [{
"resources": ["/assets/media/blinkSd.mp3"],
"matches": ["<all_urls>"],
"use_dynamic_url": true
}],
"action": {
"default_popup": "popup.html",
},
*once we have a working model we will enhance manifest file/filter requests to intercept accordingly
the above code fails to save/intercept any request made from current tab even through script 'startWork.js' is successfully injected.
why it fails?? any better approach to achieve this functionality?
Your Expertise and Time are very much appreciated. Thank You

In Chrome Extension development, how do I pass a variable from background.js to a content script that embedded as a script node?

Update: I am sorry that I wasn't clear about my case. The content script is not literally content script but a js file embedded into the page via the createElement method.
It's something like:
// this is "content.js"
var scriptElement = document.createElement('script');
scriptElement.src = 'chrome-extension://' + chrome.runtime.id + '/js/page-inject.js';
(document.head || document.documentElement).appendChild(scriptElement);
I want to use it in "page-inject.js", not the "content.js"
I thought it was very simple but failed after hours of retrying and searching.
I have a background.js like this:
chrome.tabs.query({active: true, currentWindow: true},
function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {
code: 'window.my_var = 123'
}, function() {
chrome.tabs.executeScript(tabs[0].id, {file: 'js/content.js'});
});
});
After this, when I type "window.my_var" in the console of that page. It's not working. The wanted "123" is not showing up.
If I replace the code: 'window.my_var = 123' to alert(123), it works. But what I need is just to pass a variable from my background script (background.js) to the web page that my extension injected into.
Is there anything wrong with my try?
As #wOxxOm already pointed out you how to see the variable inject using the chrome.tabs.executeScript in the web page console by changing the Top in the console.
There is another way you can easily pass values from background to content script using messaging.
Se below a simple example:
manifest.json
{
"name": "test",
"version": "0.0.1",
"manifest_version": 2,
"description": "Test ",
"background": {
"scripts": [
"background.js"
]
},
"browser_action": {
"default_title": "Test "
},
"permissions": [
"*://*/*",
"tabs"
], "content_scripts" : [{
"matches" : ["*://*/*"],
"js" : ["content.js"]
}]
}
background.js
chrome.browserAction.onClicked.addListener(function(){
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {data: "some value"});
});
});
content.js
chrome.extension.onMessage.addListener(handleMessage);
function handleMessage(request) {
console.log(request.data);
}
chrome.extension.getBackgroundPage() will return the global window object of the background page. You can access scripts and any data in the background worker.
Example
const backgroundDOMWindow = chrome.extension.getBackgroundPage();
const myGlobalVariable = backgroundDOMWindow.window['myGlobalVariable'];
Adding script to global background object "background.js"
document.write(
'<script src="main.js"></script>'+
'<script src="main2.js"></script>'
);

Extension can't do ajax request

I want to manipulate a web page based on answer of ajax request. Basicly I'll search title of page in youtube and retrieve first youtube.
var search_input = $("#title").text();
var keyword= encodeURIComponent(search_input);
// Youtube API
var yt_url='http://gdata.youtube.com/feeds/api/videos?q='+keyword+'&format=5&max-results=1&v=2&alt=jsonc';
$.ajax
({
type: "GET",
url: yt_url,
dataType:"jsonp",
success: function(response)
{
console.log("succeded");
if(response.data.items)
{
$.each(response.data.items, function(i,data)
{
var video_id=data.id;
var video_title=data.title;
var video_viewCount=data.viewCount;
// IFRAME Embed for YouTube
var video_frame="<iframe src='http://www.youtube.com/embed/"+video_id+"' frameborder='0' type='text/html'></iframe>";
var final="<div>"+video_frame+"</div><div id='count'>"+video_viewCount+" Views</div>";
$("#videos").html(final); // Result
});
}
else
{
$("#videos").html("<div id='no'>No Video</div>");
}
}
});
And here's manifest.json
{
"content_scripts": [
{
"matches": [
"https://*.website.com/*",
"*://*.youtube.com/*"
],
"js": [
"jquery.js",
"mymanipulatır.js"
]
}
],
"name": "Name",
"icons": {
"128": "128x128.png"
},
"homepage_url": "http://github.com/",
"version": "1.4",
"manifest_version": 2,
"developer": {
"name": "may"
},
"description": "youtube"
}
Extension throws Uncaught ReferenceError: jQuery11100021788313053548336_1393869626682 is not defined when trying to make ajax request. What am I missing?
When doing cross-site XHR's from a content script injected into a web page, you need to declare the url patterns you want to be able to connect to in the "permissions" section of your manifest the same way as if you were doing the XHR from inside one of your extensions own standalone pages (eg the background page, a browser action popup, etc.).
See: https://developer.chrome.com/extensions/xhr

Inserting code into a Confluence page programmatically using a Chrome content script

I am trying to develop a Chrome extension to open Office documents stored in Confluence in a new tab using the IE Tab extension.
In the 'View Page Attachment' screen, there is an 'Edit in Office' link for Office file attachments. The link has a click event that creates a new instance of a URLLauncher, which is used to open the document. This feature is not supported in Chrome, so I want to add my own URLLauncher prototype into the web page to make it work.
In short, this is my idea:
Create a Chrome extension with a content script that injects a URLLauncher prototype into the 'View Page Attachment' page (I don't know if this is the right approach, so I am open to suggestions).
When the user clicks on the 'Edit in Office' link, the URLLauncher.open method opens the file attachment in a new tab by calling the IE Tab extension.
I can see the 'Hi there!' alert every time I load a web page, and that confirms that content.js is being injected. Nevertheless, the URLLauncher is not available in the web page. I think this is because the global window object of the content script is distinct from the page/extension's global namespace (i.e., window.URLLauncher is undefined). How could I reorganize my code to overcome this obstacle?
These are my files:
manifest.json
{
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
]
},
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
} ],
"description": "This is a test extension",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"name": "Test extension",
"version": "1.0.0"
}
background.js
chrome.tabs.executeScript(null, {
code: "document.body.appendChild(document.createElement('script')).src='" +
chrome.extension.getURL("content.js") + "';"
}, null);
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
content.js
var prefixUrl = 'chrome-extension://hehijbfgiekmjfkfjpbkbammjbdenadd/iecontainer.html#url=';
alert('Hi there!');
function URLLauncher() {
}
URLLauncher.prototype = {
open : function(urlStr) {
var newUrl = prefixUrl + 'https://host.com' + encodeURI(urlStr);
chrome.runtime.sendMessage({id: "doUrlLaunch", nUrl: newUrl}, function(response) {
});
}
}
Thanks in advance.
UPDATE 1
I edited the files following the instructions given by Rob W and this page ('Message Passing'); now the code is injected in the page itself, but a major problem still remains. The actual JS code sends a message to the content script, but the message is not caught by the listener, so the new tab is not created and the callback function does not receive a response; the error message I got: Error in event handler for (unknown): TypeError: Cannot read property 'success' of undefined.
These are the updated files:
manifest.json
{
"manifest_version": 2,
"content_scripts": [ {
"js": [ "content.js" ],
"matches": [ "<all_urls>" ]
} ],
"web_accessible_resources": [ "script.js" ],
"description": "This is a test extension",
"permissions": [
"tabs", "http://*/*", "https://*/*"
],
"name": "Test extension",
"version": "1.0.0",
"externally_connectable": {
"ids": ["*"],
"matches": ["*://*.hostname.com/*"]
}
}
content.js
var s = document.createElement('script');
s.src = chrome.extension.getURL("script.js");
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
chrome.runtime.onMessage.addListener(
//Unreachable code!
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
script.js
var prefixUrl = 'chrome-extension://hehijbfgiekmjfkfjpbkbammjbdenadd/iecontainer.html#url=';
function URLLauncher() {
}
URLLauncher.prototype = {
open : function(urlStr) {
var newUrl = prefixUrl + 'https://hostname.com' + encodeURI(urlStr);
chrome.runtime.sendMessage({id: "doUrlLaunch", nUrl: newUrl}, function(response) {
if (!response.success)
console.log('Something went wrong...');
});
}
}
Not sure if you're still interested in an answer, but in your edited files your problem is where your listener sits.
chrome.runtime.sendMessage does not reach content scripts; it is intended for extension pages. Message passing to content scripts works through chrome.tabs.sendMessage, but this is irrelevant for this task.
Content scripts can't call chrome.tabs as they do not have the required API access.
A solution would be to call a background script, that can receive those messages and can call the required API.
Therefore, you need a third script, taking out this code from content.js:
// background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.id == "doUrlLaunch") {
chrome.tabs.create({ url: request.nUrl, selected: true });
sendResponse({result: "goodbye"});
}
}
);
And modify your manifest:
"background": { "scripts": [ "background.js" ] },

How to allow an extension for two domains only?

I've written a google chrome extension. It's okay and works now but I want the extension to be usebale only on two domains because it's written for these two websites only and is useless for others. There is a context menu only. For now it hasn't even popup, or action button in the top right corner (hidden by default). How can achieve this?
My current manifest.json:
{
"manifest_version": 2,
"background": {
"scripts": ["scripts/jquery.min.js", "scripts/background.js"]
},
"name": "Export Entries",
"description": "some descriptions here",
"version": "1.0",
"icons": {
"16": "images/logo.png",
"48": "images/logo.png",
"128": "images/logo.png"
},
"permissions": ["downloads", "tabs", "contextMenus", "http://my-own-domain-accessed-via-ajax-for-creating-some-files-there.com/*"],
"content_scripts": [{
"matches": ["*://allowed-domain1.com/*", "*://allowed-domain2.com/*"],
"css": ["styles/style.css"],
"js": ["scripts/jquery.min.js", "scripts/popup.js", "scripts/background.js"],
"run_at": "document_end"
}],
"web_accessible_resources": [
"images/logo.png"
]
}
As I understand the extension cannot be disabled absolutely, its process will run in background again. But it's not a problem. I just want to not display the context menu item on other websites.
background.js creates the context menu item and handles its click event:
function exportEntries(info, tab) {
if (info['linkUrl'].indexOf('domain1.com/user/') > -1) {
var user = info['linkUrl'].substr('27');
} else {
var user = null; // export all entries from this topic
}
$.ajax({
url: 'http://my-own-domain-which-creates-the-file.eu/exportEntries/create.php',
method: 'POST',
data: {
topic: tab.url,
user: user
}
}).done(function(url) {
forceDownload(url);
});
}
function forceDownload(url) {
var filename = url.replace(/^.*\/|\.[^.]*$/g, '');
chrome.downloads.download({
url: url,
saveAs: true
}, // options array
function(id) {
// callback function
}
);
};
document.addEventListener('DOMContentLoaded', function() {
chrome.contextMenus.create({
'title': 'Export Entries',
'contexts': ['link'],
'onclick': function(info, tab) {
exportEntries(info, tab);
}
});
});
create.php is on my own domain. It just gets the current page's URL and the user's nickname. Then export all entries from the given topic (i.e. page URL) for the given user, creates a file (.txt, .pdf etc.) and sends back url for downloading the file.
popup.html, popup.js, css file and other stuff is not used for now.
Remove all of your content scripts, they're useless, because the chrome.contextMenus API can only be used on the background page.
To limit the context menu entry to certain domains, pecify the documentUrlPatterns key when you create the context menu using the chrome.contextMenus.create:
chrome.contextMenus.create({
'title': 'Export Entries',
'contexts': ['link'],
'onclick': function(info, tab) {
exportEntries(info, tab);
},
'documentUrlPatterns': [
'*://allowed-domain1.com/*',
'*://allowed-domain2.com/*'
]
});
According to the content scripts documentation:
"If you want to inject the code only sometimes, use the permissions field instead, as described in Programmatic injection."
So instead of
"content_scripts": [
{
"matches": [ "http://allowed-domain.com" ]
}
],
use
permissions: [
"tabs", "http://allowed-domain.com"
],

Resources