Chrome Extension: How to send selected text to external web service - google-chrome

Possible duplicate question
Prior to asking this, I did some searching and found this question that is similar by name, but I don't understand if it's what I need:
Chrome Extension: how to capture selected text and send to a web service
Especially Mohamed Mansour's answer looked promising, but after spending over an hour researching, experimenting and testing, I still don't understand it. So I hope this possibly duplicate question will at least help other people with the same confusion that I have.
Question
Besides the title of this question, I'll include some details of what I want to do:
Select text on any website
Right-click to open the context menu of my chrome extension
Send that text to my website's database.
But for testing purposes, I thought I would replace the third point with sending a basic post-request to a site like hurl.it
So how do I send such a post-request from my chrome extension to a specific external website?
I already know how to get the selected text:
manifest.json
{
"manifest_version": 2,
"name": "My Plugin",
"description": "Test extension",
"version": "0.1.20150917",
"permissions": [
"contextMenus"
],
"background": {
"scripts": ["script.js"]
}
}
script.js
function handleSelectedText(info,tab) {
var selectedText = info.selectionText;
console.log("Selected text: " + selectedText;
//This where I thought I'd send the data to my domain
//And some pseudo-code to show how I thought it would work:
/*
chrome.extension.postRequest(selectedText, function(response) {
if(response == success)
displayNotification("Yay, it worked!");
else
displayNotification("Error: " + response.errorMessage);
});
*/
}
chrome.contextMenus.create({
title: "Mark error: '%s'",
contexts:["selection"],
id: "cc-mark",
onclick: handleSelectedText,
});
If I insert Mohamed Mansour's code into mine, I just get an error saying that response is undefined, so I'm assuming I'm doing that so terribly wrong that I shouldn't even include it.
If it isn't obvious already, I should mention that I'm new to Chrome extensions.

Related

Content script code is not being executed

I've taken a look at other related SO posts and the solutions haven't helped solve my issue. This is my first chrome extension, so please bear with me!
I'm writing a simple chrome extension that searches for user provided keywords on a webpage. I can't get the content script that returns the DOM content to run. Some of the code, I've taken from an answer in another SO post, but I can't seem to get it to work for me.
I put a console.log("hello world") at the top of the file, and it doesn't show up, so I think it might be the structure of my project.
manifest.json
{
"name": "keyword search",
"version": "0.0.1",
"manifest_version": 2,
"permissions": [ "tabs" , "storage", "activeTab", "<all_urls>"],
"browser_action": {
"default_popup": "html/form.html"
},
"content_scripts": [{
"matches": [ "<all_urls>" ],
"js": [ "js/jquery.min.js", "content_scripts/content_script.js" ]
}],
"homepage_url": "http://google.com/"
}
js/popup.js
function run() {
running = true;
console.log('running');
var url = "https://www.stackoverflow.com/"
// Get KW & category for search
chrome.storage.local.get(["kw"],
function (data) {
kw = data.kw;
console.log("redirecting to find kw: " + kw);
// Send current tab to url
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.update(tabs[0].id, {url: url});
chrome.tabs.sendMessage(tabs[0].id, {type: 'DOM_request'}, searchDOM);
});
}
);
}
function searchDOM(domContent) {
console.log("beginning dom search \n" + domContent);
}
content_scripts/content_script.js
// Listen for messages
console.log("hello world")
chrome.runtime.onMessageExternal.addListener(function (msg, sender, sendResponse) {
// If the received message has the expected format...
if (msg.type === 'DOM_request') {
// Call the specified callback, passing
// the web-page's DOM content as argument
sendResponse(document.all[0].outerHTML);
}
});
console
running
redirecting to find kw: TestKeyword
beginning dom search
undefined
First, onMessageExternal is the wrong event (it's for external messaging):
you should use the standard onMessage.
Second, chrome extensions API is asynchronous so it only registers a job, returns immediately to continue to the next statement in your code without waiting for the job to complete:
chrome.tabs.update enqueues a navigation to a new URL
chrome.tabs.sendMessage enqueues a message sending job
the current page context in the tab gets destroyed along with the running content scripts
the tab starts loading the new URL
the message is delivered into the tab but there are no listeners,
but this step may instead run right after step 2 depending on various factors so the content script running in the old page will receive it which is not what you want
the tab loads the served HTML and emits a DOMContentLoaded event
your content scripts run shortly after that because of the default "run_at": "document_idle"
There are at least three methods to properly time it all:
make your content script emit a message and add an onMessage listener in the popup
use chrome.tabs.onUpdated to wait for the tab to load
use chrome.tabs.onUpdated + chrome.tabs.executeScript to simplify the entire thing
Let's take the executeScript approach.
remove "content_scripts" from manifest.json
instead of chrome.tabs.query (it's not needed) use the following:
chrome.tabs.update({url}, tab => {
chrome.tabs.onUpdated.addListener(function onUpdated(tabId, change, updatedTab) {
if (tabId === tab.id && change.status === 'complete') {
chrome.tabs.onUpdated.removeListener(onUpdated);
chrome.tabs.executeScript(tab.id, {
code: 'document.documentElement.innerHTML',
}, results => {
searchDOM(results[0]);
});
}
});
});

disable refresh / back / forward in OWN browser

Is there a way to only make my OWN browser (Chrome) not be able to go back / forward / refresh?
This happens rather often that when Im developing and playing around in devtools (Changing HTML and CSS just to try things out) I sometimes accidentally swipe back or out of habit hit refresh. I would like to be able to disable the back or forward button via some sort of extension?
I am NOT trying to disable the button on any live-website, just for me locally. Any ideas?
If you want to prevent accidental navigations, there's no need to install any extension. Just open the console, and run the following code:
window.onbeforeunload = function() {
return 'Want to unload?';
};
With this code, you will get a confirmation prompt.
If you really want to prevent the page from unloading via an extension, use the technique described in the answers to How to cancel webRequest silently in chrome extension.
Here's a minimal demo extension that adds a button to your browser. Upon click, you cannot navigate to a different page any more. You can still close the tab without any warning, though:
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.webRequest.onBeforeRequest.addListener(function(details) {
var scheme = /^https/.test(details.url) ? 'https' : 'http';
return { redirectUrl: scheme + '://robwu.nl/204' };
// Or (seems to work now, but future support not guaranteed):
// return { redirectUrl: 'javascript:' };
}, {
urls: ['*://*/*'],
types: ['main_frame'],
tabId: tab.id
}, ['blocking']);
});
manifest.json for this extension:
{
"name": "Never unload the current page any more!",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": ["background.js"],
"persistent": true
},
"browser_action": {
"default_title": ""
},
"permissions": [
"<all_urls>",
"webRequest",
"webRequestBlocking"
]
}

Redirecting http to https using a Chrome extension

I am developing a number of Node.js applications that use an https server. While I am developing them, I run them on localhost using a self-signed certificate. Basically, everything works, but I have two issues:
When I point my browser to https://localhost:3000 for the first time, it warns me about a non-trusted certificate. This is, of course, true (and important), but it's annyoing while developing. Of course, I could add the certificate as a trusted one, but they change from time to time, and I don't want to clutter the certificate store.
Sometimes I just forget to enter the https part into the address bar, so Chrome tries to load the website using http. For whatever reason Chrome does not realize that there is no webserver responding to httprequests, instead it loads and loads and loads and …
What I would like to do to solve both issues is to create a Chrome extension that resides next to the address bar and offers a button with which you can toggle its state:
If the extension is disabled, it does nothing at all.
If the extension is enabled, and you send a request to localhost (and only then!), it shall do two things:
If the request uses http, but the page is still pending after a few seconds, it shall try using https instead.
Temporarily accept any certificate, no matter whether it's trusted by the browser or not.
To make it explicit: These rules shall only active for localhost.
So, now my questions are:
Is something like this possible using a Chrome extension at all?
As I have absolutely zero experience with writing Chrome extension, what would be a good starting point, and what terms should I look for on Google?
The Google Chrome Extensions Documentation is a great place to start. Everything you describe is possible using a Chrome Extension except for the "certificate accepting" part. (I am not saying it is not possible, I just don't know if it is - but I would be very surprised (and concerned) if it were.)
Of course, there is always the --ignore-certificate-errors command-line switch, but it will not differentiate between localhost and other domains.
If you decide to implement the rest of the functionality, I suggest looking into chrome.tabs and/or chrome.webRequest first. (Let me, also, mention "content scripts" are unlikely to be of any use.)
That said, below is some code for a demo extension (just to get you started).
What is does:
When deactivated -> nothing
When activated -> Listens for tabs being directed to URLs like http://localhost[:PORT][/...] and redirects them to https (it does not wait for a response or anything, it just redirects them instantly).
How to use:
Click the browser-action icon to activate/deactivate.
It's not perfect/complete, of course, but it's a starting point :)
Extension directory structure:
extention-root-directory/
|_____ manifest.json
|_____ background.js
|_____ img/
|_____ icon19.png
|_____ icon38.png
manifest.json:
(See here for more info on the possible fields.)
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"default_locale": "en",
"offline_enabled": true,
"incognito": "split",
// The background-page will listen for
// and handle various types of events
"background": {
"persistent": false, // <-- if you use chrome.webRequest, 'true' is required
"scripts": [
"background.js"
]
},
// Will place a button next to the address-bar
// Click to enable/disable the extension (see 'background.js')
"browser_action": {
"default_title": "Test Extension"
//"default_icon": {
// "19": "img/icon19.png",
// "38": "img/icon38.png"
//},
},
"permissions": [
"tabs", // <-- let me manipulating tab URLs
"http://localhost:*/*" // <-- let me manipulate tabs with such URLs
]
}
background.js:
(Related docs: background pages, event pages, browser actions, chrome.tabs API)
/* Configuration for the Badge to indicate "ENABLED" state */
var enabledBadgeSpec = {
text: " ON ",
color: [0, 255, 0, 255]
};
/* Configuration for the Badge to indicate "DISABLED" state */
var disabledBadgeSpec = {
text: "OFF",
color: [255, 0, 0, 100]
};
/* Return whether the extension is currently enabled or not */
function isEnabled() {
var active = localStorage.getItem("active");
return (active && (active == "true")) ? true : false;
}
/* Store the current state (enabled/disabled) of the extension
* (This is necessary because non-persistent background pages (event-pages)
* do not persist variable values in 'window') */
function storeEnabled(enabled) {
localStorage.setItem("active", (enabled) ? "true" : "false");
}
/* Set the state (enabled/disabled) of the extension */
function setState(enabled) {
var badgeSpec = (enabled) ? enabledBadgeSpec : disabledBadgeSpec;
var ba = chrome.browserAction;
ba.setBadgeText({ text: badgeSpec.text });
ba.setBadgeBackgroundColor({ color: badgeSpec.color });
storeEnabled(enabled);
if (enabled) {
chrome.tabs.onUpdated.addListener(localhostListener);
console.log("Activated... :)");
} else {
chrome.tabs.onUpdated.removeListener(localhostListener);
console.log("Deactivated... :(");
}
}
/* When the URL of a tab is updated, check if the domain is 'localhost'
* and redirect 'http' to 'https' */
var regex = /^http(:\/\/localhost(?::[0-9]+)?(?:\/.*)?)$/i;
function localhostListener(tabId, info, tab) {
if (info.url && regex.test(info.url)) {
var newURL = info.url.replace(regex, "https$1");
chrome.tabs.update(tabId, { url: newURL });
console.log("Tab " + tabId + " is being redirected to: " + newURL);
}
}
/* Listen for 'browserAction.onClicked' events and toggle the state */
chrome.browserAction.onClicked.addListener(function() {
setState(!isEnabled());
});
/* Initially setting the extension's state (upon load) */
setState(isEnabled());

Chrome omnibox events without specific keywords?

I do want to check the content written in Chrome omnibox and redirect to the proper page.
Still, I cannot use specific keywords because I do want to redirect things like BUG-1234 to http://bugs.example.com/BUG-1234
I do have a regexp for this (as the BUG part can have lots of values).
How can I do this?
A chrome extension can help you, with help of Omnibox.
If i understood correctly when you enter BUG-1234 and hit Enter in Omnibox, your webpage URL Should be http://bugs.example.com/BUG-1234
Demonstration
I have used keyword as
"keyword": "BUG"
BUG, you can change it as per functionality. So when you enter B+U+G in chrome Omnibox , the search provider adds a custom layer as shown here
Image 1)
and when you enter 1234 and hit Enter or Select the suggested URL Open Bug %s ? in Omnibox, as shown here
Image 2)
It opens a web page with URL as shown here, where i used http://bugs.example.com as a test URL, which can be extended further.
Image 3)
manifest.json
Registered background Page and Omnibox with Chrome Extension, and added related permissions.
{
"name": "Bug Tracker",
"description": "This integrates chrome omnibox with bug search",
"version": "1",
"manifest_version": 2,
"background": {
"scripts": [
"background.js"
]
},
"omnibox": {
"keyword": "BUG"
},
"permissions": [
"<all_urls>"
]
}
background.js
Script for Custom Suggestions
//Set Text to show for custom suggested URL(s)
chrome.omnibox.setDefaultSuggestion({
"description": "Open Bug %s ?"
});
//Fired when Enter or a suggested Link is selected
chrome.omnibox.onInputEntered.addListener(function (bugId) {
//Use your custom URL
chrome.tabs.update({
"url": "http://bugs.example.com/BUG-" + bugId
}, function () {
console.log("Bug Page is open");
});
console.log("Input Entered is " + bugId);
});
References
Background Pages
Omnibox API
Manifest File

Capture link a user clicks on a page

I am playing around with making an extension. If a user has the extension installed, I would like to capture the link they clicked on a web page. Not quite sure how to do this, but it seems simple. I might add, I would like this to happen as long as the plugin is installed and enabled, but DON'T want the user to have to do anything in the toolbar to 'activate' it.
Not sure how to start. And I figure I have one too many JS files, but just trying to get one of them to log to the console. Neither do. My end goal is I would like to redirect them to an intranet page if they go to certain places.
background.js
var redirectedSites = ["https://www.facebook.com/profile.php?id=<SOMEPROFILEID>"];
// when the browser tries to get to a page, check it against a list
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
console.log('is this even getting hit?');
for(var i=0; i < redirectedSites.length; ++i) {
// if the attempt is to a listed site, redirect the request
if( details.url == redirectedSites[i] )
return {redirectUrl: "http://intranet/landing?from=" + details.url };
}
},
{urls: ["*://www.facebook.com/*"]},
["blocking"]
);
manifest.json
{
"name": "Capture Click",
"version": "0.1",
"description": "Simple tool that logs clicked links.",
"permissions": [
"tabs",
"webRequest",
"webRequestBlocking",
"https://*.facebook.com/*"
],
"background": {
"scripts": ["background.js"]
},
"manifest_version": 2
}
I've given some advice in the comments, but the best way to solve your actual larger problem is with a webRequest handler:
var redirectedSites = ["http://www.google.com/foobar", ...];
// when the browser tries to get to a page, check it against a list
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
for(var i=0; i < redirectedSites.length; ++i) {
// if the attempt is to a listed site, redirect the request
if( details.url == redirectedSites[i] )
return {redirectUrl: "http://intranet/landing?from=" + details.url };
}
},
{urls: ["*://www.google.com/*"]},
["blocking"]);
This is a really simple example, but I hope you get the idea. Here, details.url is the page the user is trying to get to, and the returned object has a redirectUrl property that redirects the attempt to visit the page. My example checks details.url against a list of target sites; you could use a regex or something else that's more robust.
Note that this will affect not only clicked links and typed-in URLs, but also resources (scrips, images) and Ajax requests.