I have many websites that has the following script tag in the head section:
<html>
<head>
<script>
window.doSomething(someObject);
</script>
</head>
<body>
.....
</body>
</html>
Now, I want to inject a script before any page loads so that these websites will have the access to the function doSomething which is defined my custom script. How do I make sure that my custom script is executed before any of the scripts in the HTML file?
I don't want to modify the front-end code. I thought of using extensions but they run after all the scripts on the page. Is there a way I can do this?
Even a Chrome specific way would suffice.
Declaratively injected scripts in Chrome extensions can be configured to run at document start:
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"run_at": "document_start",
"js": ["contentScript.js"]
}
],
...
}
Unfortunately that's not the whole story, because injected scripts run in an isolated environment, so you can't modify the window object of the page itself. You can, however, modify the DOM (which is still empty at this point) to add a <script> element that loads a script from your extension, as described in Method 1 in this answer. That second script will then run in the context of the page itself, and can do whatever it likes.
Related
Im trying to write a Chrome extension that has a dev tools panel. This extension needs to call functions defined on a property on window in a webpage that I also have made. In other words, the extension is only for my own web page and I control both. Example:
// This script is added in my own webpage application when it loads
window.myTest = () => { /* do something */ }
I want to be able to call the function window.myTest from my Chrome extension. I need to make similar functionality like https://github.com/zalmoxisus/redux-devtools-extension.
It seems that I need to do this by inject script/code from my backend page. I have all working, extension with backend page that gets invoked and I can see that the code that I inject gets called in the page context (testing by console.log gets written to the console output of the page).
Here is my code:
manifest.json
{
"manifest_version": 2,
"name": "MyTest",
"description": "MyTest",
"version": "0.0.1",
"minimum_chrome_version": "10.0",
"devtools_page": "devtools.html",
"background": {
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["testscript.js"]
}],
"permissions": [
"tabs",
"<all_urls>"
]
}
testscript.js
window.myTest(); // myTest is undefined in the context of the injected script
background.js
// empty, but here to be able to open background page
I also have a pannel that sends a message to the background page when a button is clicked. I know that the panel and sending the message also work. But window.myTest is still undefined.
Edit
Removed the injection from background.js, because I did not use it and have same issue as described.
Finally, I got the specs on this. Mozilla and Chrome follow the same specs for extensions.
Content scripts get a "clean" view of the DOM. This means:
Content scripts cannot see JavaScript variables defined by page
scripts.
If a page script redefines a built-in DOM property, the
content script sees the original version of the property, not the
redefined version.
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts
I'm new to Chrome extension development, I need to make something sample with extension.
I Need to add JavaScript file to all sites I browse when the extension enabled, this is the code i want to add to all pages
<script type="text/javascript" src="web-retina-emulator.js"></script>
This file make pages look like when the website on retina display.
Is there easy way to make this happen?
You can use this basic structure to add JavaScript file to all sites when the extension enabled.
Method 1
If web-retina-emulator.js is an individual file which do not use global variables or functions of pages where it is injected it is suggested to use this method
Advantage:
It has access to certain sections of chrome API*
Draw Back
It can not use javascript variables and functions of pages where it is injected.
Demonstration
manifest.json
{
"name":"Custom Script",
"description":"http://stackoverflow.com/questions/14165629/add-javascript-file-to-all-sites-i-browse",
"version":"1",
"manifest_version":2,
"content_scripts":[{
"matches":["<all_urls>"],
"js":["web-retina-emulator.js"],
}
]
}
Method 2
If web-retina-emulator.js need some javascript methods or variables of pages, use this approach
Advantage:
It has access to javascript variables and methods of pages
Draw Back
It can not use chrome API*.
Demonstration
manifest.json
{
"name":"Custom Script",
"description":"http://stackoverflow.com/questions/14165629/add-javascript-file-to-all-sites-i-browse",
"version":"1",
"manifest_version":2,
"content_scripts":[{
"matches":["<all_urls>"],
"js":["myscript.js"],
}
]
}
myscript.js
var script = document.createElement('script'); // Create a Script Tag
script.src = chrome.extension.getURL("web-retina-emulator.js"); //Fetch the content script
script.onload = function () {
this.parentNode.removeChild(this); //Remove script after script executed
};
(document.head || document.documentElement).appendChild(script); //ADD script tag
//to head or Html element
Method 3
Inserting code into a page programmatically is useful when your JavaScript or CSS code shouldn't be injected into every single page that matches the pattern — for example, if you want a script to run only when the user clicks a browser action's icon.
Demonstration
background.html
chrome.tabs.executeScript(null,
{file:"web-retina-emulator.js"});
manifest.json
Ensure permissions are set in manifest file
"permissions": [
"tabs", "http://*/*"
],
References
Content Scripts
Execute Script API
Check out tampermonkey. It's the chrome equivalent to greasemonkey.
This is what I have here:
"manifest.json"
{..."permissions": [
"https:/mywebsite.com/"],"content_scripts": [{
"matches" : ["http://*/*", "https://*/*"],
"js": ["js/jquery-1.7.2.min.js", "contentScript1.js", "contentScript2.js"],
"all_frames" : true,
"run_at": "document_end"
} ]}
"contentScript1.js"
$(document).ready(function() {
$('#someDiv').load('https://mywebsite.com/index.html');}
"contentScript2.js"
function showMessage()
{alert ('Hello World!');}
"index.html"
<img src="https://mywebsite.com/images/myimage.png">
What I m actually doing here is injecting a clickable picture to the code of the the page that I m visiting and I expect that by clicking the picture a "Hello World" message will be appeared. Despite the fact that the content scripts and the picture are loaded succesfully, when I click on the image the function is not called and I get the following error in the console:
Uncaught ReferenceError: showMessage is not defined
I suppose that it cannot find the function as it is looking for it in the website that I have injected the code and not in the content scripts. But why is that, I mean if I call the function within the content script when it is loaded and not by clicking the image, the message appears. Can anyone get me out of here?
You did not understand my solution to avoid conflicts does not work with your current code. Instead of using $.noConflict, you're wrapping your script injection function in a $().ready method.
You have to remove jQuery from the "js" part of the manifest:
"js": ["contentScript1.js"],
And contentScript1.js
function injectJs(srcFile) {
var scr = document.createElement('script');
scr.src = srcFile;
document.getElementsByTagName('head')[0].appendChild(scr);
}
injectJs(chrome.extension.getURL('js/jquery-min.js'));
injectJs(chrome.extension.getURL('js/yourscript.js'));
Don't forget to add js/yourscript.js to web_accessible_resources, so that it can be used:
"web_accessible_resources": [
"index3.html",
"js/jquery-min.js"
"js/yourscript.js"
]
In js/yourscript.js, wrap your function logic in an anonymous function in conjunction with $.noConflict. $.noConflict(true) is used to avoid conflicts with scripts in the page. It restores the original value of $ and jQuery.
(function(jQuery, $) {
// Here, you can do anything you want.
// jQuery and $ refer to the same jQuery object from `js/jquery-min.js`
})(jQuery, jQuery.noConflict(true));
After looking at your question again, I noticed that you're loading content through ajax: $('#someDiv').load(...). When the script is injected, it runs in the scope of the page. That's why your AJAX call fails: The request is blocked because of the Same origin policy.
Now, we can use a different approach to fix your code. Instead of moving the logic from Content script to the page (by an injected script), we modify the page index.html. The click event is not pre-set, but added in the content script. For example:
"index.html":
<img src="https://mywebsite.com/images/myimage.png">
"contentscript2.js":
$('#showMessage').click(showMessage);
I think I m gonna answer my own question:
The reason that this happening is because content scripts run in an isolated world
see: http://code.google.com/chrome/extensions/content_scripts.html#execution-environment
So, you simply cannot call functions, once you injected some html code, in content_scripts to perform some work in the current page of user.
What you have to do is to inject your scripts in the page as you do with html code.
So:
(1) add the files you want to inject in web resources in your manifest file
see: http://code.google.com/chrome/extensions/manifest.html#web_accessible_resources
"web_accessible_resources": [
"Script2.js",
"index.html",
"jquery-1.7.2.min.js"]
(2) in contentScript1.js (load this as a content_script)
//inject your javascript files to the head of the page
function injectJs(srcFile) {
var scr = document.createElement('script');
scr.type="text/javascript";
scr.src=srcFile;
document.getElementsByTagName('head')[0].appendChild(scr);
}
injectJs(chrome.extension.getURL('jquery-1.7.2.min.js'));
injectJs(chrome.extension.getURL('Script2.js'));
//inject your html by loading query and passing your html page
$(document).ready(function() {
$('#someDiv').load(chrome.extension.getURL('./index.html'));}
That's all!
I'm new to extension development, maybe someone has a small example ready for my problem.
I plan a more complicate code (that will execute chromium API functions), but solving this task should help me get started:
I want to create an extension that triggers a popup or alert() (just anyything) based on a website javascript call.
So for example my website has a button, when clicked on the button a javascript with a few parameters is executed.
My extension picks those parameters up and executes APIs (for my example just any popup) based on the parameters.
In my basic example I'd like to trigger some sort of popup/notification with the text supplied by the website javascript.
Also only my website domain should be allowed to trigger that, anything else should be rejected.
I'd really appreciate help.
Here is my "empty" manifest
{
"name": "Special API",
"version": "1.0",
"description": "API demo extension.",
"browser_action":
{
"default_icon": "gears.ico",
"popup": "show_credits.htm"
},
"permissions": [
"http://www.mywebsite.com/"
]
}
Here the example button in my website.com/example
<html>
<body>
<button onClick="extension_do_exec('Hellow world','abcabc')">Execute extension function</button>
</body>
</html>
The approach you described is problematic, bacause javascripts of web-pages, and javascripts of extensions are isolated from each other (there is a concept of isolated world). So it is not possible to get a value "supplied by the website javascript" directly into the extension's javascript. I'd suggest another approach. You possibly could exchange with some values by assigning them as properties to DOM objects. These properties can be accessed from a content script, injected into the web-page. Of course, the content script can determine domain of the page and work as appropriate. As for popups, these are internal pages of an extension, and you should implement some kind of messaging between them and your content script.
I want to create a tab by clicking on the browser action button and then insert a content script or execute a script. So far, its not working well.
Background.html
chrome.browserAction.onClicked.addListener(function(tab)
{
chrome.tabs.create({url: "Dreamer.html"}, function(tab) //Dreamer.html is a file in my extension
{
//Add a script
chrome.tabs.executeScript(tab.id, {file:'Dreamer.js'});
});
});
Manifest.json
{
"name" : " Dreamer",
"version" : "0.1",
"description" : "My extensionr",
"browser_action" : {"default_icon" : "App/AppData/Images/icon.png", "default_title":"Start Dreamer" },
"background_page" : "App/AppData/background.html",
"content_scripts" :[{"matches":["http://*/*"],"js":["app/view/UIManager.js"]}],
"permissions": [ "cookies", "tabs", "http://*/*", "https://*/*" ]
}
i get this error in the background page
Error during tabs.executeScript: Cannot access contents of url "chrome-extension://femiindgnlfpdpajimkmldpgpccngfmd/Dreamer.html". Extension manifest must request permission to access this host.
I would really like to know how to create a tab(new tab) and run a script immediately
EDIT:
The kind of application i am creating requires the following actions:
-Allow user to create new tab by clicking the browserAction button
-On creation of the new tab, a file in my extension (Dreamer.html) is opened
-Add a content script or execute a script in the new tab
Thanks
Is there any particular reason you need to inject the script? Since both Dreamer.html and Dreamer.js seem to be hardcoded, you could just include <script type="text/javascript" src="Dreamer.js"></script> in the former, right?
As a side benefit, if you need it to send info to the background page, you can access it directly with chrome.extension.getBackgroundPage() instead of setting up complex listeners that usually come with content scripts, too.
Injecting content scripts is for injecting scripts outside the extension sandbox. Dreamer.html, however, is a part of the extension.
Edit
If you do want an (unwieldy) example of how to execute a script in an extension page, see here:
http://code.google.com/p/chromium/issues/detail?id=30756#c11
I don't think it applies to your case, however.