My Chrome extension has a Content Script that adds a DIV with a button. The OnClick JS function defined for the button, however, never executes (defined in the same Content Script), so the button does nothing. Why is that?
contentscript.js
var msg = "Click this button <button onclick='test()'>Test</button>";
var div = document.createElement( 'div' );
div.id = 'testDiv';
document.body.appendChild( div );
document.getElementById('testDiv').innerHTML = msg;
function test()
{
alert('in test()'); // Never gets here
}
manifest.json
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"css": ["contentstyle.css"],
"js": ["jquery-1.11.2.min.js", "contentscript.js"]
Look at the official docs regarding policies. Inline javascript is strictly prohibited. The docs explain how to do it. https://developer.chrome.com/extensions/contentSecurityPolicy
Related
I am trying to make a Chrome extension with a content script to inject a script into a webpage before all other scripts in the page. (I am using the xhook library to intercept XHR requests, which overwrites the XHR class. I need to do this because it is currently impossible to modify responses using Chrome extension APIs.) The "document_start" event is executed before any of the DOM is written, so I manually create the body element with the content script. However, this creates 2 body tags in the HTML, which appears to make variables defined within the injected script tag inaccessible to the code in the main page.
How should I do this?
I have simplified version of my code below:
manifest.json
{
// Required
"manifest_version": 2,
"name": "My Extension",
"version": "0.1",
"description": "My Description",
"author": "Me",
"permissions": ["https://example.com/*"],
"content_scripts": [{
"matches": ["https://example.com/*"],
"js": ["xhook.js"],
"run_at": "document_start",
"all_frames": true
}
]
}
xhook.js
var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
holder = document.createTextNode(`
//Xhook library code
// XHook - v1.4.9 - https://github.com/jpillora/xhook
//...
//Now to use the library
console.log('loading extension');
xhook.after(function (request, response) {
//console.log(request.url);
if (request.url.startsWith("https://example.com/")) {
var urlParams = new URLSearchParams(window.location.search);
fetch('https://example.com/robots.txt')
.then(
function (apiresponse) {
if (apiresponse.status == 200) {
response.text = apiresponse.text();
return;
};
if (apiresponse.status !== 200) {
console.log('File not found. Status Code: ' +
apiresponse.status);
return;
};
});
};
});
xhook.enable();`);
script_tag.appendChild(holder);
document.body = document.createElement("body");
document.head.appendChild(script_tag);
Thanks!
If the extension is loaded at document_start, document.head = null. Hence, to overcome this, do - document.lastChild.appendChild(script_tag);. This creates a script tag in your <html> hierarchy. Hope this helps.
Also, Could you please tell why are you doing the following statement
document.body = document.createElement("body"); I believe this is not required.
I am trying to make a markdown preview tab for a forum I often use that uses markdown in it's formatting but currently does not have any way to preview what that markdown will look like.
I want to use the page down markdown converter however I don't know how to use the files inside the content script
here is my manifest.json
{
"name": "Forum Post Previewer",
"version": "0.1",
"manifest_version":2,
"description":"Adds a preview tab on the post editor",
"permissions": [
"activeTab",
"*://*/*"
],
"content_scripts": [
{
"js": ["previewtab.js"]
}
],
}
and here is the previewtab.js
// Adding the preview tab
var tabs = document.getElementsByClassName("nav nav-tabs");
var list = document.createElement("li");
var tab = document.createElement("a");
tab.innerHTML = "Preview";
tab.setAttribute("data-toggle", "tab");
tab.setAttribute("href", "#tab3");
list.appendChild(tab);
document.getElementById("post-editor").parentElement.firstElementChild.appendChild(list);
var content = document.createElement("div");
content.setAttribute("class", "tab-pane");
content.setAttribute("id", "tab3");
var bar = document.createElement("div");
bar.setAttribute("id", "wmd-button-bar");
var textarea = document.createElement("textarea");
textarea.setAttribute("id", "wmd-input");
textarea.setAttribute("class", "wmd-input");
var preview = document.createElement("div");
preview.setAttribute("id", "wmd-preview");
preview.setAttribute("class", "wmd-panel wmd-preview");
content.appendChild(bar);
content.appendChild(textarea);
content.appendChild(preview);
document.getElementById("post-editor").appendChild(content);
// Using the converter
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor(converter);
editor.run();
right now I get errors when using the converter because it does not know where Markdown has come from.
Can you help me to find out how to use this external script in a chrome extension
Thanks
Just add the .js files to your extension and include them as content scripts before yours, like this:
"content_scripts": [
{
"js": ["Markdown.Converter", "Markdown.Editor", "Markdown.Sanitizer", "previewtab.js"]
}
],
I am developing a Chrome extension and my requirement is to create element(button) on page for each tab open and wants to show simple alert message on clicking button..it works properly for all but it always creating issue with Gmail,Facebook and Stackoverflow..please help me to resolve this issue.
I am having the code of adding button to a web page in my Content script.
manifest.json
....
....
"content_scripts": [
{
"matches":["http://*/*", "https://*/*"],
"css": [ "style.css" ],
"js":["contentScript.js"],
"all_frames": false,
"run_at": "document_idle"
}
]
....
....
contentScript.js
....
....
function addButton() {
document.body.innerHTML += '<button id="my_button" class="buttonCss">Show Button</button>';
var button = document.getElementById("my_button");
button.addEventListener("click", function () {
alert("hello");
}, false);
}
.....
.....
....
I think some Gmail security features is creating the issue.
document.body.innerHTML += /* ... */
I think this is your issue right there. It forces Chrome to re-create the entire DOM of body, losing attached events/data.
The proper way would be to construct and append a DOM node:
var button = document.createElement("button");
button.id = "my_button";
button.className = "buttonCss";
button.textContent = "Show Button";
document.body.appendChild(button);
Okay, so I've been at this for a while.
In the red box, I want to have some angular functionality (ng-repeats, data binding, etc...) This red box appears when text on any webpage is double clicked. However, I can't seem to find out how to actually get angular wired/hooked up to the text box example in the red popup.
It seems fairly trivial to use angular in a badge popup in chrome, as well as on options pages, etc...but I can't seem to get it working in this instance.
inject.js (which is included as a content script in manifest, below)
var displayPopup = function(event) {
var mydiv = document.createElement('div');
var $div = $('#divid').closest('.sentence');
mydiv.innerHTML = getSelectionText();
mydiv.innerHTML += currentSentence.innerHTML;
//Next line is where I want to apply some angular functionality
mydiv.innerHTML += '<div ng-app="myApp" scroll-to-me><input type="text" ng-model="data.test"><div ng-model="data.test">{{data.test}}</div></div>';
mydiv.id = "popup";
mydiv.style.position = "fixed";
mydiv.style.top = event.clientY + "px";
mydiv.style.left = event.clientX + "px";
mydiv.style.border = "4px solid #d00";
mydiv.style.background = "#fcc";
$("body").append(mydiv);
$.getJSON('http://local.wordly.com:3000/words/definitions/test', function(data) {
console.log(data);
});
}
and my manifest.json content script array looks like:
"content_scripts": [
{
"matches": [
"https://www.google.com/*"
],
"css": [
"src/inject/inject.css"
]
},
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"js/angular/angular.js", "app.js", "js/jquery/jquery.js", "src/inject/inject.js"
]
}
]
and app.js, also included in manifest, for just some skeletal app to get up and running.
var myApp = angular.module("myApp", []);
myApp.factory('Data', function(){
//return {message: "I'm data from a service"};
});
myApp.controller("SecondCtrl", function($scope, $http){
});
You need to bootstrap manually if you’re injecting the markup after the page loads. Angular will only run ng-app if it’s present when the document is loaded. Afterwards, you pass the module name to angular.bootstrap:
angular.bootstrap(mydiv, ['myApp'])
Example.
This problem seems to have been sort of resolved, as long as the URL of the page you're injecting your javascript into starts with www. What do you do if it doesn't? Here's the relevant part of my manifest:
"content_scripts": [
{
"run_at": "document_start",
"matches": ["https://groups.google.com/forum/?fromgroups=#!newtopic/opencomments-site-discussions"],
"js": ["postMsg.js"]
}
],
The problem, according to another stackoverflow post, is because the URL of the page doesn't begin with 'www'. Does that mean that you can't inject javascript into secure pages whose URL doesn't begin with 'www', or is there another way? This had never been a problem in the past, because my extension had run with Version 1 manifests.
Forgot to add the content script:
var subject = document.getElementById("p-s-0");
subject.setAttribute("value", "foo");
The element with ID "p-s-0" is the Subject field in the Google Groups Post page, so the field should display "foo".
A few issues:
That is a not valid match pattern because they only specify up to the URL path (the part before the ?).
Change the matches to:
"matches": ["https://groups.google.com/forum/*"],
The overall URL (https://groups.google.com/forum/?fromgroups=#!newtopic/opencomments-site-discussions) is not practical because Google changes the URL parameters willy nilly. For example, fromgroups is not often present, and may not have the = if it is. Additional parameters, like hl=en come and go. (This is the reason why my earlier answer worked for me, but not for you.)
So, using include_globs in the manifest would be a messy, error-prone exercise.
The solution is to checklocation.hash within the content script.
The script is set to "run_at": "document_start", so the content script is running before there is any node with id p-s-0.
Change the manifest to "run_at": "document_end".
The new Google groups is heavily AJAX driven. So, The "New Topic" page is usually "loaded" without actually loading a whole new page. This means the content script will not rerun. It needs to monitor for "new" AJAX-loaded pages.
Check for "new" pages by monitoring the hashchange event.
Additionally, the p-s-0 element is added by AJAX, and is not immediately available on a "new" page. Check for this element within a setInterval.
Putting it all together,
The manifest.json becomes:
{
"manifest_version": 2,
"content_scripts": [ {
"run_at": "document_end",
"js": [ "postMsg.js" ],
"matches": [ "https://groups.google.com/forum/*" ]
} ],
"description": "Fills in subject when posting a new topic in select google groups",
"name": "Google groups, Topic-subject filler",
"version": "1"
}
The content script(postMsg.js) becomes:
fireOnNewTopic (); // Initial run on cold start or full reload.
window.addEventListener ("hashchange", fireOnNewTopic, false);
function fireOnNewTopic () {
/*-- For the pages we want, location.hash will contain values
like: "#!newtopic/{group title}"
*/
if (location.hash) {
var locHashParts = location.hash.split ('/');
if (locHashParts.length > 1 && locHashParts[0] == '#!newtopic') {
var subjectStr = '';
switch (locHashParts[1]) {
case 'opencomments-site-discussions':
subjectStr = 'Site discussion truth';
break;
case 'greasemonkey-users':
subjectStr = 'GM wisdom';
break;
default:
break;
}
if (subjectStr) {
runPayloadCode (subjectStr);
}
}
}
}
function runPayloadCode (subjectStr) {
var targetID = 'p-s-0'
var failsafeCount = 0;
var subjectInpTimer = setInterval ( function() {
var subject = document.getElementById (targetID);
if (subject) {
clearInterval (subjectInpTimer);
subject.setAttribute ("value", subjectStr);
}
else {
failsafeCount++;
//console.log ("failsafeCount: ", failsafeCount);
if (failsafeCount > 300) {
clearInterval (subjectInpTimer);
alert ('Node id ' + targetID + ' not found!');
}
}
},
200
);
}